void duk_hbuffer_append_byte(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_uint8_t byte) { DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), &byte, 1); }
void duk_hbuffer_insert_byte(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint8_t byte) { DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); duk_hbuffer_insert_bytes(thr, buf, offset, &byte, 1); }
void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf) { DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); duk_hbuffer_resize(thr, buf, 0, 0); }
duk_ret_t duk_bi_duktape_object_line(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_activation *act_caller; duk_hobject *h_func; duk_hbuffer_fixed *pc2line; duk_uint_fast32_t pc; duk_uint_fast32_t line; if (thr->callstack_top < 2) { return 0; } act_caller = thr->callstack + thr->callstack_top - 2; h_func = act_caller->func; DUK_ASSERT(h_func != NULL); if (!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h_func)) { return 0; } /* FIXME: shared helper */ duk_push_hobject(ctx, h_func); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line)); pc = (duk_uint_fast32_t) act_caller->pc; line = duk_hobject_pc2line_query(pc2line, (duk_uint_fast32_t) pc); } else { line = 0; } duk_push_int(ctx, (int) line); /* FIXME: typing */ return 1; }
DUK_EXTERNAL void *duk_steal_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { duk_hthread *thr = (duk_hthread *) ctx; duk_hbuffer_dynamic *h; void *ptr; duk_size_t sz; DUK_ASSERT(ctx != NULL); h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index); DUK_ASSERT(h != NULL); if (!DUK_HBUFFER_HAS_DYNAMIC(h)) { DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_BUFFER_NOT_DYNAMIC); } /* Forget the previous allocation, setting size to 0 and alloc to * NULL. Caller is responsible for freeing the previous allocation. * Getting the allocation and clearing it is done in the same API * call to avoid any chance of a realloc. */ ptr = DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h); sz = DUK_HBUFFER_DYNAMIC_GET_SIZE(h); if (out_size) { *out_size = sz; } DUK_HBUFFER_DYNAMIC_SET_DATA_PTR_NULL(thr->heap, h); DUK_HBUFFER_DYNAMIC_SET_SIZE(h, 0); DUK_HBUFFER_DYNAMIC_SET_ALLOC_SIZE(h, 0); return ptr; }
void duk_hbuffer_remove_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, size_t length) { char *p; size_t end_offset; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT(offset >= 0); /* always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT(length >= 0); /* always true */ DUK_ASSERT(offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); end_offset = offset + length; if (end_offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not strictly from end of buffer; need to shuffle data */ DUK_MEMMOVE(p + offset, p + end_offset, DUK_HBUFFER_GET_SIZE(buf) - end_offset); /* always > 0 */ } DUK_MEMSET(p + DUK_HBUFFER_GET_SIZE(buf) - length, 0, length); /* always > 0 */ buf->size -= length; /* Note: no shrink check, intentional */ }
void duk_hbuffer_append_bytes(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_uint8_t *data, size_t length) { DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT(data != NULL); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), data, length); }
void duk_hbuffer_compact(duk_hthread *thr, duk_hbuffer_dynamic *buf) { size_t curr_size; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); curr_size = DUK_HBUFFER_GET_SIZE(buf); duk_hbuffer_resize(thr, buf, curr_size, curr_size); }
DUK_INTERNAL void duk_free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); if (DUK_HBUFFER_HAS_DYNAMIC(h)) { duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(g))); DUK_FREE(heap, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(g)); } }
static void duk__free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); if (DUK_HBUFFER_HAS_DYNAMIC(h)) { duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", g->curr_alloc)); DUK_FREE(heap, g->curr_alloc); } }
duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_activation *act; duk_hobject *h_func; duk_hbuffer_fixed *pc2line; duk_uint_fast32_t pc; duk_uint_fast32_t line; duk_int_t level; /* -1 = top callstack entry, callstack[callstack_top - 1] * -callstack_top = bottom callstack entry, callstack[0] */ level = duk_to_int(ctx, 0); if (level >= 0 || -level > (duk_int_t) thr->callstack_top) { return 0; } DUK_ASSERT(level >= -((duk_int_t) thr->callstack_top) && level <= -1); act = thr->callstack + thr->callstack_top + level; duk_push_object(ctx); h_func = act->func; DUK_ASSERT(h_func != NULL); duk_push_hobject(ctx, h_func); pc = (duk_uint_fast32_t) act->pc; duk_push_int(ctx, (int) pc); /* FIXME: typing */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line)); pc = (duk_uint_fast32_t) act->pc; line = duk_hobject_pc2line_query(pc2line, (duk_uint_fast32_t) pc); } else { line = 0; } duk_pop(ctx); duk_push_int(ctx, (int) line); /* FIXME: typing */ /* Providing access to e.g. act->lex_env would be dangerous: these * internal structures must never be accessible to the application. * Duktape relies on them having consistent data, and this consistency * is only asserted for, not checked for. */ /* [ level obj func pc line ] */ /* FIXME: version specific array format instead? */ duk_def_prop_stridx(ctx, -4, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WEC); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WEC); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LC_FUNCTION, DUK_PROPDESC_FLAGS_WEC); return 1; }
size_t duk_hbuffer_append_cstring(duk_hthread *thr, duk_hbuffer_dynamic *buf, const char *str) { size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(str != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); len = DUK_STRLEN(str); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), (duk_uint8_t *) str, len); return len; }
size_t duk_hbuffer_append_hstring(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_hstring *str) { size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(str != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); len = DUK_HSTRING_GET_BYTELEN(str); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), (duk_uint8_t *) DUK_HSTRING_GET_DATA(str), len); return len; }
size_t duk_hbuffer_insert_cstring(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, const char *str) { size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(str != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); len = strlen(str); duk_hbuffer_insert_bytes(thr, buf, offset, (duk_uint8_t *) str, len); return len; }
void duk_hbuffer_append_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t src_offset, size_t length) { DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(src_offset >= 0); /* always true */ DUK_ASSERT(src_offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(length >= 0); /* always true */ DUK_ASSERT(src_offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ duk_hbuffer_insert_slice(thr, buf, DUK_HBUFFER_GET_SIZE(buf), src_offset, length); }
size_t duk_hbuffer_insert_cesu8(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint32_t codepoint) { duk_uint8_t tmp[DUK_UNICODE_MAX_CESU8_LENGTH]; size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT(codepoint >= 0 && codepoint <= 0x10ffff); /* if not in this range, results are garbage (but no crash) */ /* Intentionally no fast path: insertion is not that central */ /* FIXME: cp -> duk_codepoint_t */ len = (size_t) duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, tmp); duk_hbuffer_insert_bytes(thr, buf, offset, tmp, len); return len; }
size_t duk_hbuffer_insert_xutf8(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint32_t codepoint) { duk_uint8_t tmp[DUK_UNICODE_MAX_XUTF8_LENGTH]; size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); /* No range assertion for 'codepoint' */ /* Intentionally no fast path: insertion is not that central */ /* FIXME: cp -> duk_codepoint_t */ len = (size_t) duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, tmp); duk_hbuffer_insert_bytes(thr, buf, offset, tmp, len); return len; }
DUK_EXTERNAL void duk_config_buffer(duk_context *ctx, duk_idx_t idx, void *ptr, duk_size_t len) { duk_hthread *thr = (duk_hthread *) ctx; duk_hbuffer_external *h; DUK_ASSERT(ctx != NULL); h = (duk_hbuffer_external *) duk_require_hbuffer(ctx, idx); DUK_ASSERT(h != NULL); if (!DUK_HBUFFER_HAS_EXTERNAL(h)) { DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); } DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h)); DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(thr->heap, h, ptr); DUK_HBUFFER_EXTERNAL_SET_SIZE(h, len); }
DUK_LOCAL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h) { duk_fixedbuffer *fb = st->fb; duk_size_t i, n; duk_uint8_t *p; if (duk_fb_is_full(fb)) { return; } /* terminal type: no depth check */ if (!h) { duk_fb_put_cstring(fb, "NULL"); return; } if (DUK_HBUFFER_HAS_DYNAMIC(h)) { if (DUK_HBUFFER_HAS_EXTERNAL(h)) { duk_hbuffer_external *g = (duk_hbuffer_external *) h; duk_fb_sprintf(fb, "buffer:external:%p:%ld", (void *) DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(NULL, g), (long) DUK_HBUFFER_EXTERNAL_GET_SIZE(g)); } else { duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h; duk_fb_sprintf(fb, "buffer:dynamic:%p:%ld", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(NULL, g), (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(g)); } } else { duk_fb_sprintf(fb, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h)); } #if defined(DUK_USE_REFERENCE_COUNTING) duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr)); #endif if (st->hexdump) { duk_fb_sprintf(fb, "=["); n = DUK_HBUFFER_GET_SIZE(h); p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(NULL, h); for (i = 0; i < n; i++) { duk_fb_sprintf(fb, "%02lx", (unsigned long) p[i]); } duk_fb_sprintf(fb, "]"); } }
DUK_EXTERNAL void *duk_resize_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t new_size) { duk_hthread *thr = (duk_hthread *) ctx; duk_hbuffer_dynamic *h; DUK_ASSERT_CTX_VALID(ctx); h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, idx); DUK_ASSERT(h != NULL); if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) { DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); } /* maximum size check is handled by callee */ duk_hbuffer_resize(thr, h, new_size); return DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h); }
void *duk_resize_buffer(duk_context *ctx, duk_idx_t index, duk_size_t new_size) { duk_hthread *thr = (duk_hthread *) ctx; duk_hbuffer_dynamic *h; DUK_ASSERT(ctx != NULL); h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index); DUK_ASSERT(h != NULL); if (!DUK_HBUFFER_HAS_DYNAMIC(h)) { DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "buffer is not dynamic"); } /* maximum size check is handled by callee */ duk_hbuffer_resize(thr, h, new_size, new_size); /* snug */ return DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(h); }
void duk_hbuffer_insert_bytes(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint8_t *data, size_t length) { char *p; /* XXX: allow inserts with offset > curr_size? i.e., insert zeroes automatically? */ DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(offset >= 0); /* unsigned, so always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* equality is OK (= append) */ DUK_ASSERT(data != NULL); DUK_ASSERT_DISABLE(length >= 0); /* unsigned, so always true */ if (length == 0) { return; } if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) { duk_hbuffer_resize(thr, buf, DUK_HBUFFER_GET_SIZE(buf), duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length)); } DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length); p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); if (offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not an append */ DUK_ASSERT(DUK_HBUFFER_GET_SIZE(buf) - offset > 0); /* not a zero byte memmove */ DUK_MEMMOVE((void *) (p + offset + length), (void *) (p + offset), DUK_HBUFFER_GET_SIZE(buf) - offset); } DUK_MEMCPY((void *) (p + offset), data, length); buf->size += length; }
size_t duk_hbuffer_append_cesu8(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_uint32_t codepoint) { duk_uint8_t tmp[DUK_UNICODE_MAX_CESU8_LENGTH]; size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT(codepoint >= 0 && codepoint <= 0x10ffff); /* if not in this range, results are garbage (but no crash) */ if (codepoint < 0x80 && DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) > 0) { /* fast path: ASCII and there is spare */ duk_uint8_t *p = ((duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf)) + DUK_HBUFFER_GET_SIZE(buf); *p = (duk_uint8_t) codepoint; buf->size += 1; return 1; } /* FIXME: cp -> duk_codepoint_t */ len = (size_t) duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, tmp); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), tmp, len); return len; }
void duk_hbuffer_remove_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, size_t length) { char *p; size_t end_offset; DUK_UNREF(thr); DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(offset >= 0); /* always true */ DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(length >= 0); /* always true */ DUK_ASSERT(offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); end_offset = offset + length; if (end_offset < DUK_HBUFFER_GET_SIZE(buf)) { /* not strictly from end of buffer; need to shuffle data */ DUK_MEMMOVE(p + offset, p + end_offset, DUK_HBUFFER_GET_SIZE(buf) - end_offset); /* always > 0 */ } /* Here we want to zero data even with automatic buffer zeroing * disabled as we depend on this internally too. */ DUK_MEMZERO(p + DUK_HBUFFER_GET_SIZE(buf) - length, length); /* always > 0 */ buf->size -= length; /* Note: no shrink check, intentional */ }
size_t duk_hbuffer_append_xutf8(duk_hthread *thr, duk_hbuffer_dynamic *buf, duk_uint32_t codepoint) { duk_uint8_t tmp[DUK_UNICODE_MAX_XUTF8_LENGTH]; size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); /* No range assertion for 'codepoint' */ if (codepoint < 0x80 && DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) > 0) { /* fast path: ASCII and there is spare */ duk_uint8_t *p = ((duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf)) + DUK_HBUFFER_GET_SIZE(buf); *p = (duk_uint8_t) codepoint; buf->size += 1; return 1; } /* FIXME: cp -> duk_codepoint_t */ len = (size_t) duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, tmp); duk_hbuffer_insert_bytes(thr, buf, DUK_HBUFFER_GET_SIZE(buf), tmp, len); return len; }
void duk_to_fixed_buffer(duk_context *ctx, duk_idx_t index) { duk_hbuffer_dynamic *h_src; duk_uint8_t *data; duk_size_t size; index = duk_require_normalize_index(ctx, index); h_src = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index); DUK_ASSERT(h_src != NULL); if (!DUK_HBUFFER_HAS_DYNAMIC(h_src)) { return; } size = DUK_HBUFFER_GET_SIZE(h_src); data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, size); if (size > 0U) { DUK_ASSERT(data != NULL); DUK_MEMCPY(data, DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(h_src), size); } duk_replace(ctx, index); }
void duk_debug_dump_hobject(duk_hobject *obj) { duk_uint_fast32_t i; const char *str_empty = ""; const char *str_excl = "!"; DUK_D(DUK_DPRINT("=== hobject %p ===", (void *) obj)); if (!obj) { return; } DUK_D(DUK_DPRINT(" %sextensible", DUK_HOBJECT_HAS_EXTENSIBLE(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sconstructable", DUK_HOBJECT_HAS_CONSTRUCTABLE(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sbound", DUK_HOBJECT_HAS_BOUND(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %scompiledfunction", DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snativefunction", DUK_HOBJECT_HAS_NATIVEFUNCTION(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sthread", DUK_HOBJECT_HAS_THREAD(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sarray_part", DUK_HOBJECT_HAS_ARRAY_PART(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sstrict", DUK_HOBJECT_HAS_STRICT(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snewenv", DUK_HOBJECT_HAS_NEWENV(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snamebinding", DUK_HOBJECT_HAS_NAMEBINDING(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %screateargs", DUK_HOBJECT_HAS_CREATEARGS(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %senvrecclosed", DUK_HOBJECT_HAS_ENVRECCLOSED(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_array", DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_stringobj", DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_arguments", DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_dukfunc", DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_bufferobj", DUK_HOBJECT_HAS_EXOTIC_BUFFEROBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_proxyobj", DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" class: number %d -> %s", (int) DUK_HOBJECT_GET_CLASS_NUMBER(obj), duk__class_names[(DUK_HOBJECT_GET_CLASS_NUMBER(obj)) & ((1 << DUK_HOBJECT_FLAG_CLASS_BITS) - 1)])); DUK_D(DUK_DPRINT(" prototype: %p -> %!O", (void *) obj->prototype, (duk_heaphdr *) obj->prototype)); DUK_D(DUK_DPRINT(" props: p=%p, e_size=%d, e_used=%d, a_size=%d, h_size=%d", (void *) obj->p, (int) obj->e_size, (int) obj->e_used, (int) obj->a_size, (int) obj->h_size)); /* * Object (struct layout) specific dumping. Inline code here * instead of helpers, to ensure debug line prefix is identical. */ if (DUK_HOBJECT_IS_COMPILEDFUNCTION(obj)) { duk_hcompiledfunction *h = (duk_hcompiledfunction *) obj; DUK_D(DUK_DPRINT(" hcompiledfunction")); DUK_D(DUK_DPRINT(" data: %!O", h->data)); DUK_D(DUK_DPRINT(" nregs: %d", (int) h->nregs)); DUK_D(DUK_DPRINT(" nargs: %d", (int) h->nargs)); if (h->data && DUK_HBUFFER_HAS_DYNAMIC(h->data) && DUK_HBUFFER_GET_DATA_PTR(h->data)) { DUK_D(DUK_DPRINT(" consts: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(h))); DUK_D(DUK_DPRINT(" funcs: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(h))); DUK_D(DUK_DPRINT(" bytecode: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(h))); } else { DUK_D(DUK_DPRINT(" consts: ???")); DUK_D(DUK_DPRINT(" funcs: ???")); DUK_D(DUK_DPRINT(" bytecode: ???")); } } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(obj)) { duk_hnativefunction *h = (duk_hnativefunction *) obj; DUK_D(DUK_DPRINT(" hnativefunction")); /* XXX: h->func, cannot print function pointers portably */ DUK_D(DUK_DPRINT(" nargs: %d", (int) h->nargs)); } else if (DUK_HOBJECT_IS_THREAD(obj)) { duk_hthread *thr = (duk_hthread *) obj; duk_tval *p; DUK_D(DUK_DPRINT(" hthread")); DUK_D(DUK_DPRINT(" strict: %d", (int) thr->strict)); DUK_D(DUK_DPRINT(" state: %d", (int) thr->state)); DUK_D(DUK_DPRINT(" valstack_max: %d, callstack_max:%d, catchstack_max: %d", thr->valstack_max, thr->callstack_max, thr->catchstack_max)); DUK_D(DUK_DPRINT(" callstack: ptr %p, size %d, top %d, preventcount %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->callstack, thr->callstack_size, thr->callstack_top, thr->callstack_preventcount, thr->callstack_top, thr->callstack_top * sizeof(duk_activation), thr->callstack_size, thr->callstack_size * sizeof(duk_activation))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); for (i = 0; i <= thr->callstack_size; i++) { if (i == thr->callstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (!thr->callstack) { DUK_DEBUG_SUMMARY_CHAR('@'); } else if (i < thr->callstack_size) { if (i < thr->callstack_top) { /* tailcalling is nice to see immediately; other flags (e.g. strict) * not that important. */ if (thr->callstack[i].flags & DUK_ACT_FLAG_TAILCALLED) { DUK_DEBUG_SUMMARY_CHAR('/'); } DUK_DEBUG_SUMMARY_CHAR(duk__get_act_summary_char(&thr->callstack[i])); } else { DUK_DEBUG_SUMMARY_CHAR('.'); } } } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" valstack: ptr %p, end %p (%d), bottom %p (%d), top %p (%d), used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->valstack, (void *) thr->valstack_end, (int) (thr->valstack_end - thr->valstack), (void *) thr->valstack_bottom, (int) (thr->valstack_bottom - thr->valstack), (void *) thr->valstack_top, (int) (thr->valstack_top - thr->valstack), (int) (thr->valstack_top - thr->valstack), (int) (thr->valstack_top - thr->valstack) * sizeof(duk_tval), (int) (thr->valstack_end - thr->valstack), (int) (thr->valstack_end - thr->valstack) * sizeof(duk_tval))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); p = thr->valstack; while (p <= thr->valstack_end) { i = (duk_uint_fast32_t) (p - thr->valstack); if (thr->callstack && thr->callstack_top > 0 && i == (duk_size_t) (thr->callstack + thr->callstack_top - 1)->idx_bottom) { DUK_DEBUG_SUMMARY_CHAR('>'); } if (p == thr->valstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (p < thr->valstack_end) { if (p < thr->valstack_top) { DUK_DEBUG_SUMMARY_CHAR(duk__get_tval_summary_char(p)); } else { /* XXX: safe printer for these? would be nice, because * we could visualize whether the values are in proper * state. */ DUK_DEBUG_SUMMARY_CHAR('.'); } } p++; } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" catchstack: ptr %p, size %d, top %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->catchstack, thr->catchstack_size, thr->catchstack_top, thr->catchstack_top, thr->catchstack_top * sizeof(duk_catcher), thr->catchstack_size, thr->catchstack_size * sizeof(duk_catcher))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); for (i = 0; i <= thr->catchstack_size; i++) { if (i == thr->catchstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (!thr->catchstack) { DUK_DEBUG_SUMMARY_CHAR('@'); } else if (i < thr->catchstack_size) { if (i < thr->catchstack_top) { DUK_DEBUG_SUMMARY_CHAR(duk__get_cat_summary_char(&thr->catchstack[i])); } else { DUK_DEBUG_SUMMARY_CHAR('.'); } } } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" resumer: ptr %p", (void *) thr->resumer)); #if 0 /* worth dumping? */ for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_D(DUK_DPRINT(" builtins[%d] -> %!@O", i, thr->builtins[i])); } #endif } if (obj->p) { DUK_D(DUK_DPRINT(" props alloc size: %d", (int) DUK_HOBJECT_P_COMPUTE_SIZE(obj->e_size, obj->a_size, obj->h_size))); } else { DUK_D(DUK_DPRINT(" props alloc size: n/a")); } DUK_D(DUK_DPRINT(" prop entries:")); for (i = 0; i < obj->e_size; i++) { duk_hstring *k; duk_propvalue *v; k = DUK_HOBJECT_E_GET_KEY(obj, i); v = DUK_HOBJECT_E_GET_VALUE_PTR(obj, i); if (i >= obj->e_used) { DUK_D(DUK_DPRINT(" [%d]: UNUSED", i)); continue; } if (!k) { DUK_D(DUK_DPRINT(" [%d]: NULL", i)); continue; } if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i)) { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %!O -> get:%p set:%p; get %!O; set %!O", i, DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i), k, (void *) v->a.get, (void *) v->a.set, (duk_heaphdr *) v->a.get, (duk_heaphdr *) v->a.set)); } else { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %!O -> %!T", i, DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i), k, &v->v)); } } DUK_D(DUK_DPRINT(" array entries:")); for (i = 0; i < obj->a_size; i++) { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %d -> %!T", i, 1, /* implicit attributes */ 1, 1, 0, i, DUK_HOBJECT_A_GET_VALUE_PTR(obj, i))); } DUK_D(DUK_DPRINT(" hash entries:")); for (i = 0; i < obj->h_size; i++) { duk_uint32_t t = DUK_HOBJECT_H_GET_INDEX(obj, i); if (t == DUK_HOBJECT_HASHIDX_UNUSED) { DUK_D(DUK_DPRINT(" [%d]: unused", i)); } else if (t == DUK_HOBJECT_HASHIDX_DELETED) { DUK_D(DUK_DPRINT(" [%d]: deleted", i)); } else { DUK_D(DUK_DPRINT(" [%d]: %d", i, (int) t)); } } }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_int(ctx, (int) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_int_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hthread); } else { hdr_size = (duk_int_t) sizeof(duk_hobject); } duk_push_int(ctx, (int) hdr_size); duk_push_int(ctx, (int) DUK_HOBJECT_E_ALLOC_SIZE(h_obj)); duk_push_int(ctx, (int) h_obj->e_size); duk_push_int(ctx, (int) h_obj->e_used); duk_push_int(ctx, (int) h_obj->a_size); duk_push_int(ctx, (int) h_obj->h_size); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = ((duk_hcompiledfunction *) h_obj)->data; if (h_data) { duk_push_int(ctx, DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_int(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { /* XXX: when usable_size == 0, dynamic buf ptr may now be NULL, in which case * the second allocation does not exist. */ duk_hbuffer_dynamic *h_dyn = (duk_hbuffer_dynamic *) h; duk_push_int(ctx, (int) (sizeof(duk_hbuffer_dynamic))); duk_push_int(ctx, (int) (DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_dyn))); } else { duk_push_int(ctx, (int) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* FIXME: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
void duk_hbuffer_insert_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t dst_offset, size_t src_offset, size_t length) { char *p; size_t src_end_offset; /* source end (exclusive) in initial buffer */ size_t len; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); DUK_ASSERT_DISABLE(dst_offset >= 0); /* always true */ DUK_ASSERT(dst_offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(src_offset >= 0); /* always true */ DUK_ASSERT(src_offset <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ DUK_ASSERT_DISABLE(length >= 0); /* always true */ DUK_ASSERT(src_offset + length <= DUK_HBUFFER_GET_SIZE(buf)); /* allow equality */ if (length == 0) { return; } if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) { duk_hbuffer_resize(thr, buf, DUK_HBUFFER_GET_SIZE(buf), duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length)); } DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length); p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf); /* * src_offset and dst_offset refer to the state of the buffer * before any changes are made. This must be taken into account * when moving data around; in particular, the source data may * "straddle" the dst_offset, so the insert may need to be handled * in two pieces. */ src_end_offset = src_offset + length; /* create a hole for the insert */ len = DUK_HBUFFER_GET_SIZE(buf) - dst_offset; if (len > 0) { DUK_MEMMOVE(p + dst_offset + length, p + dst_offset, len); } if (src_offset < dst_offset) { if (src_end_offset <= dst_offset) { /* entire source is before 'dst_offset' */ DUK_MEMCPY(p + dst_offset, p + src_offset, length); } else { /* part of the source is before 'dst_offset'; straddles */ len = dst_offset - src_offset; DUK_ASSERT(len >= 1 && len < length); DUK_ASSERT(length - len >= 1); DUK_MEMCPY(p + dst_offset, p + src_offset, len); DUK_MEMCPY(p + dst_offset + len, p + src_offset + length + len, /* take above memmove() into account */ length - len); } } else { /* entire source is after 'dst_offset' */ DUK_MEMCPY(p + dst_offset, p + src_offset + length, /* take above memmove() into account */ length); } buf->size += length; }
void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t new_size, size_t new_usable_size) { size_t new_alloc_size; void *res; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(new_usable_size >= new_size); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); /* * Maximum size check * * XXX: check against usable size? */ if (new_size > DUK_HBUFFER_MAX_BYTELEN) { DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, "buffer too long"); } /* * Note: use indirect realloc variant just in case mark-and-sweep * (finalizers) might resize this same buffer during garbage * collection. */ /* FIXME: maybe remove safety NUL term for buffers? */ new_alloc_size = new_usable_size + 1; /* +1 for safety nul term */ res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_alloc_size); if (res) { DUK_DDDPRINT("resized dynamic buffer %p:%d:%d -> %p:%d:%d", buf->curr_alloc, buf->size, buf->usable_size, res, new_size, new_usable_size); /* * The entire allocated buffer area, regardless of actual used size, * is kept zeroed in resizes for simplicity. If the buffer is grown, * zero the new part (the safety NUL byte is re-zeroed every time). * Another policy would be to ensure data is zeroed as the used part * is extended (with one safety NUL byte) this is much more simple, * and not a big deal because the spart part is relatively small. */ if (new_alloc_size > buf->usable_size) { /* When new_usable_size == old_usable_size, one byte will * be rezeroed (the safety NUL byte). */ DUK_ASSERT(new_alloc_size - buf->usable_size > 0); #ifdef DUK_USE_ZERO_BUFFER_DATA DUK_MEMZERO((void *) ((char *) res + buf->usable_size), new_alloc_size - buf->usable_size); #endif } buf->size = new_size; buf->usable_size = new_usable_size; buf->curr_alloc = res; } else { DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "failed to resize buffer from %d:%d to %d:%d", buf->size, buf->usable_size, new_size, new_usable_size); } DUK_ASSERT(res != NULL); }