int duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr) { size_t alloc_size; int i; DUK_ASSERT(heap != NULL); DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->valstack == NULL); DUK_ASSERT(thr->valstack_end == NULL); DUK_ASSERT(thr->valstack_bottom == NULL); DUK_ASSERT(thr->valstack_top == NULL); DUK_ASSERT(thr->callstack == NULL); DUK_ASSERT(thr->catchstack == NULL); /* valstack */ alloc_size = sizeof(duk_tval) * DUK_VALSTACK_INITIAL_SIZE; thr->valstack = (duk_tval *) DUK_ALLOC(heap, alloc_size); if (!thr->valstack) { goto fail; } DUK_MEMSET(thr->valstack, 0, alloc_size); thr->valstack_end = thr->valstack + DUK_VALSTACK_INITIAL_SIZE; thr->valstack_bottom = thr->valstack; thr->valstack_top = thr->valstack; for (i = 0; i < DUK_VALSTACK_INITIAL_SIZE; i++) { DUK_TVAL_SET_UNDEFINED_UNUSED(&thr->valstack[i]); } /* callstack */ alloc_size = sizeof(duk_activation) * DUK_CALLSTACK_INITIAL_SIZE; thr->callstack = (duk_activation *) DUK_ALLOC(heap, alloc_size); if (!thr->callstack) { goto fail; } DUK_MEMSET(thr->callstack, 0, alloc_size); thr->callstack_size = DUK_CALLSTACK_INITIAL_SIZE; DUK_ASSERT(thr->callstack_top == 0); /* catchstack */ alloc_size = sizeof(duk_catcher) * DUK_CATCHSTACK_INITIAL_SIZE; thr->catchstack = (duk_catcher *) DUK_ALLOC(heap, alloc_size); if (!thr->catchstack) { goto fail; } DUK_MEMSET(thr->catchstack, 0, alloc_size); thr->catchstack_size = DUK_CATCHSTACK_INITIAL_SIZE; DUK_ASSERT(thr->catchstack_top == 0); return 1; fail: DUK_FREE(heap, thr->valstack); DUK_FREE(heap, thr->callstack); DUK_FREE(heap, thr->catchstack); thr->valstack = NULL; thr->callstack = NULL; thr->catchstack = NULL; return 0; }
DUK_EXTERNAL void *duk_alloc(duk_context *ctx, duk_size_t size) { duk_hthread *thr = (duk_hthread *) ctx; DUK_ASSERT_CTX_VALID(ctx); return DUK_ALLOC(thr->heap, size); }
DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_hcompfunc *res; res = (duk_hcompfunc *) DUK_ALLOC(heap, sizeof(duk_hcompfunc)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hcompfunc)); duk__init_object_parts(heap, &res->obj, hobject_flags); #if defined(DUK_USE_EXPLICIT_NULL_INIT) #if defined(DUK_USE_HEAPPTR16) /* NULL pointer is required to encode to zero, so memset is enough. */ #else res->data = NULL; res->funcs = NULL; res->bytecode = NULL; #endif res->lex_env = NULL; res->var_env = NULL; #endif return res; }
void *duk_alloc(duk_context *ctx, size_t size) { duk_hthread *thr = (duk_hthread *) ctx; DUK_ASSERT(ctx != NULL); return DUK_ALLOC(thr->heap, size); }
static duk_hstring *duk__alloc_init_hstring(duk_heap *heap, duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_hstring *res = NULL; duk_uint8_t *data; duk_size_t alloc_size; duk_uarridx_t dummy; /* NUL terminate for convenient C access */ alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1); res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto error; } DUK_MEMZERO(res, sizeof(duk_hstring)); #ifdef DUK_USE_EXPLICIT_NULL_INIT DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { DUK_HSTRING_SET_ARRIDX(res); } /* All strings beginning with 0xff are treated as "internal", * even strings interned by the user. This allows user code to * create internal properties too, and makes behavior consistent * in case user code happens to use a string also used by Duktape * (such as string has already been interned and has the 'internal' * flag set). */ if (blen > 0 && str[0] == (duk_uint8_t) 0xff) { DUK_HSTRING_SET_INTERNAL(res); } res->hash = strhash; res->blen = blen; res->clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); /* clen <= blen */ data = (duk_uint8_t *) (res + 1); DUK_MEMCPY(data, str, blen); data[blen] = (duk_uint8_t) 0; DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld", (unsigned long) DUK_HSTRING_GET_HASH(res), (long) DUK_HSTRING_GET_BYTELEN(res), (long) DUK_HSTRING_GET_CHARLEN(res), (long) DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0)); return res; error: DUK_FREE(heap, res); return NULL; }
DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_uint16_t *lst; duk_uint16_t *new_lst; duk_size_t i, n; duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str16 == null16) { e->u.str16 = h16; } else { /* Now two entries in the same slot, alloc list */ lst = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * 2); if (lst == NULL) { return 1; /* fail */ } lst[0] = e->u.str16; lst[1] = h16; e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) lst); e->listlen = 2; } } else { DUK_ASSERT(e->u.strlist16 != null16); lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); DUK_ASSERT(lst != NULL); for (i = 0, n = e->listlen; i < n; i++) { if (lst[i] == null16) { lst[i] = h16; return 0; } } if (e->listlen + 1 == 0) { /* Overflow, relevant mainly when listlen is 16 bits. */ return 1; /* fail */ } new_lst = (duk_uint16_t *) DUK_REALLOC(heap, lst, sizeof(duk_uint16_t) * (e->listlen + 1)); if (new_lst == NULL) { return 1; /* fail */ } new_lst[e->listlen++] = h16; e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) new_lst); } return 0; }
DUK_INTERNAL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) { void *res; DUK_ASSERT(heap != NULL); DUK_ASSERT_DISABLE(size >= 0); res = DUK_ALLOC(heap, size); if (DUK_LIKELY(res != NULL)) { duk_memzero(res, size); } return res; }
/* Overflow, relevant mainly when listlen is 16 bits. */ return 1; /* fail */ } new_lst = (duk_uint16_t *) DUK_REALLOC(heap, lst, sizeof(duk_uint16_t) * (e->listlen + 1)); if (new_lst == NULL) { return 1; /* fail */ } new_lst[e->listlen++] = h16; e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) new_lst); } return 0; } #else /* DUK_USE_HEAPPTR16 */ DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_hstring **lst; duk_hstring **new_lst; duk_size_t i, n; DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str == NULL) { e->u.str = h; } else { /* Now two entries in the same slot, alloc list */ lst = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * 2); if (lst == NULL) { return 1; /* fail */ } lst[0] = e->u.str; lst[1] = h; e->u.strlist = lst; e->listlen = 2; } } else { DUK_ASSERT(e->u.strlist != NULL); lst = e->u.strlist; for (i = 0, n = e->listlen; i < n; i++) { if (lst[i] == NULL) { lst[i] = h; return 0; } } if (e->listlen + 1 == 0) { /* Overflow, relevant mainly when listlen is 16 bits. */ return 1; /* fail */ } new_lst = (duk_hstring **) DUK_REALLOC(heap, e->u.strlist, sizeof(duk_hstring *) * (e->listlen + 1)); if (new_lst == NULL) { return 1; /* fail */ } new_lst[e->listlen++] = h; e->u.strlist = new_lst; } return 0; }
DUK_INTERNAL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) { void *res; DUK_ASSERT(heap != NULL); DUK_ASSERT_DISABLE(size >= 0); res = DUK_ALLOC(heap, size); if (DUK_LIKELY(res != NULL)) { /* assume memset with zero size is OK */ DUK_MEMZERO(res, size); } return res; }
DUK_INTERNAL duk_harray *duk_harray_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_harray *res; res = (duk_harray *) DUK_ALLOC(heap, sizeof(duk_harray)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_harray)); duk__init_object_parts(heap, &res->obj, hobject_flags); DUK_ASSERT(res->length == 0); return res; }
static duk_hstring *duk__alloc_init_hstring(duk_heap *heap, duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_hstring *res = NULL; duk_uint8_t *data; duk_uint32_t alloc_size; duk_uint32_t dummy; /* NUL terminate for convenient C access */ alloc_size = sizeof(duk_hstring) + blen + 1; res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto error; } DUK_MEMZERO(res, sizeof(duk_hstring)); #ifdef DUK_USE_EXPLICIT_NULL_INIT DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { DUK_HSTRING_SET_ARRIDX(res); } res->hash = strhash; res->blen = blen; res->clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); /* clen <= blen */ data = (duk_uint8_t *) (res + 1); DUK_MEMCPY(data, str, blen); data[blen] = (duk_uint8_t) 0; DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08x, blen=%d, clen=%d, arridx=%d", DUK_HSTRING_GET_HASH(res), DUK_HSTRING_GET_BYTELEN(res), DUK_HSTRING_GET_CHARLEN(res), DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0)); return res; error: DUK_FREE(heap, res); return NULL; }
DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_hnatfunc *res; res = (duk_hnatfunc *) DUK_ALLOC(heap, sizeof(duk_hnatfunc)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hnatfunc)); duk__init_object_parts(heap, &res->obj, hobject_flags); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->func = NULL; #endif return res; }
duk_hnativefunction *duk_hnativefunction_alloc(duk_heap *heap, int hobject_flags) { duk_hnativefunction *res; res = (duk_hnativefunction *) DUK_ALLOC(heap, sizeof(duk_hnativefunction)); if (!res) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hnativefunction)); duk__init_object_parts(heap, &res->obj, hobject_flags); #ifdef DUK_USE_EXPLICIT_NULL_INIT res->func = NULL; #endif return res; }
DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_hthread *res; res = (duk_hthread *) DUK_ALLOC(heap, sizeof(duk_hthread)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hthread)); duk__init_object_parts(heap, &res->obj, hobject_flags); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->ptr_curr_pc = NULL; res->heap = NULL; res->valstack = NULL; res->valstack_end = NULL; res->valstack_bottom = NULL; res->valstack_top = NULL; res->callstack = NULL; res->catchstack = NULL; res->resumer = NULL; res->compile_ctx = NULL, #if defined(DUK_USE_HEAPPTR16) res->strs16 = NULL; #else res->strs = NULL; #endif { int i; for (i = 0; i < DUK_NUM_BUILTINS; i++) { res->builtins[i] = NULL; } } #endif /* when nothing is running, API calls are in non-strict mode */ DUK_ASSERT(res->strict == 0); res->heap = heap; res->valstack_max = DUK_VALSTACK_DEFAULT_MAX; res->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; res->catchstack_max = DUK_CATCHSTACK_DEFAULT_MAX; return res; }
DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_hbufobj *res; res = (duk_hbufobj *) DUK_ALLOC(heap, sizeof(duk_hbufobj)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hbufobj)); duk__init_object_parts(heap, &res->obj, hobject_flags); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->buf = NULL; res->buf_prop = NULL; #endif DUK_ASSERT_HBUFOBJ_VALID(res); return res; }
void *duk_heap_mem_alloc_checked(duk_hthread *thr, size_t size, const char *filename, int line) { #else void *duk_heap_mem_alloc_checked(duk_hthread *thr, size_t size) { #endif void *res; DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(size >= 0); res = DUK_ALLOC(thr->heap, size); if (!res) { #ifdef DUK_USE_VERBOSE_ERRORS DUK_ERROR_RAW(filename, line, thr, DUK_ERR_ALLOC_ERROR, "memory alloc failed"); #else DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "memory alloc failed"); #endif } return res; }
duk_hobject *duk_hobject_alloc(duk_heap *heap, int hobject_flags) { duk_hobject *res; DUK_ASSERT(heap != NULL); /* different memory layout, alloc size, and init */ DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPILEDFUNCTION) == 0); DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATIVEFUNCTION) == 0); DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_THREAD) == 0); res = (duk_hobject *) DUK_ALLOC(heap, sizeof(duk_hobject)); if (!res) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hobject)); duk__init_object_parts(heap, res, hobject_flags); return res; }
DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobject_flags) { duk_hobject *res; DUK_ASSERT(heap != NULL); /* different memory layout, alloc size, and init */ DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPFUNC) == 0); DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATFUNC) == 0); DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_THREAD) == 0); res = (duk_hobject *) DUK_ALLOC(heap, sizeof(duk_hobject)); if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hobject)); duk__init_object_parts(heap, res, hobject_flags); return res; }
duk_hthread *duk_hthread_alloc(duk_heap *heap, int hobject_flags) { duk_hthread *res; res = (duk_hthread *) DUK_ALLOC(heap, sizeof(duk_hthread)); if (!res) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hthread)); duk__init_object_parts(heap, &res->obj, hobject_flags); #ifdef DUK_USE_EXPLICIT_NULL_INIT res->heap = NULL; res->valstack = NULL; res->valstack_end = NULL; res->valstack_bottom = NULL; res->valstack_top = NULL; res->callstack = NULL; res->catchstack = NULL; res->resumer = NULL; res->strs = NULL; { int i; for (i = 0; i < DUK_NUM_BUILTINS; i++) { res->builtins[i] = NULL; } } #endif /* when nothing is running, API calls are in non-strict mode */ DUK_ASSERT(res->strict == 0); res->heap = heap; res->valstack_max = DUK_VALSTACK_DEFAULT_MAX; res->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; res->catchstack_max = DUK_CATCHSTACK_DEFAULT_MAX; return res; }
DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { duk_context *ctx; duk_hobject *h1; #if defined(DUK_USE_ROM_GLOBAL_CLONE) duk_hobject *h2; duk_uint8_t *props; duk_size_t alloc_size; #endif ctx = (duk_context *) thr; /* XXX: refactor into internal helper, duk_clone_hobject() */ #if defined(DUK_USE_ROM_GLOBAL_INHERIT) /* Inherit from ROM-based global object: less RAM usage, less transparent. */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), DUK_BIDX_GLOBAL); h1 = duk_get_hobject(ctx, -1); DUK_ASSERT(h1 != NULL); #elif defined(DUK_USE_ROM_GLOBAL_CLONE) /* Clone the properties of the ROM-based global object to create a * fully RAM-based global object. Uses more memory than the inherit * model but more compliant. */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), DUK_BIDX_OBJECT_PROTOTYPE); h1 = duk_get_hobject(ctx, -1); DUK_ASSERT(h1 != NULL); h2 = thr->builtins[DUK_BIDX_GLOBAL]; DUK_ASSERT(h2 != NULL); /* Copy the property table verbatim; this handles attributes etc. * For ROM objects it's not necessary (or possible) to update * refcounts so leave them as is. */ alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h2); DUK_ASSERT(alloc_size > 0); props = DUK_ALLOC(thr->heap, alloc_size); if (!props) { DUK_ERROR_ALLOC_FAILED(thr); return; } DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h2) != NULL); DUK_MEMCPY((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h2), alloc_size); /* XXX: keep property attributes or tweak them here? * Properties will now be non-configurable even when they're * normally configurable for the global object. */ DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h1) == NULL); DUK_HOBJECT_SET_PROPS(thr->heap, h1, props); DUK_HOBJECT_SET_ESIZE(h1, DUK_HOBJECT_GET_ESIZE(h2)); DUK_HOBJECT_SET_ENEXT(h1, DUK_HOBJECT_GET_ENEXT(h2)); DUK_HOBJECT_SET_ASIZE(h1, DUK_HOBJECT_GET_ASIZE(h2)); DUK_HOBJECT_SET_HSIZE(h1, DUK_HOBJECT_GET_HSIZE(h2)); #else #error internal error in defines #endif duk_hobject_compact_props(thr, h1); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL])); /* no need to decref */ thr->builtins[DUK_BIDX_GLOBAL] = h1; DUK_HOBJECT_INCREF(thr, h1); DUK_D(DUK_DPRINT("duplicated global object: %!O", h1)); /* Create a fresh object environment for the global scope. This is * needed so that the global scope points to the newly created RAM-based * global object. */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), -1); /* no prototype */ h1 = duk_get_hobject(ctx, -1); DUK_ASSERT(h1 != NULL); duk_dup(ctx, -2); duk_dup(ctx, -1); /* -> [ ... new_global new_globalenv new_global new_global ] */ duk_xdef_prop_stridx(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); /* always provideThis=true */ duk_hobject_compact_props(thr, h1); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL); DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV])); /* no need to decref */ thr->builtins[DUK_BIDX_GLOBAL_ENV] = h1; DUK_HOBJECT_INCREF(thr, h1); DUK_D(DUK_DPRINT("duplicated global env: %!O", h1)); duk_pop_2(ctx); }
DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) { #else DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) { #endif duk_uint32_t i; duk_uint32_t step; duk_uint32_t hash; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); #endif DUK_ASSERT(size > 0); hash = DUK_HSTRING_GET_HASH(h); i = DUK__HASH_INITIAL(hash, size); step = DUK__HASH_PROBE_STEP(hash); for (;;) { #if defined(DUK_USE_HEAPPTR16) duk_uint16_t e16 = entries16[i]; #else duk_hstring *e = entries[i]; #endif #if defined(DUK_USE_HEAPPTR16) if (e16 == null16) { #else if (!e) { #endif DUK_UNREACHABLE(); break; } #if defined(DUK_USE_HEAPPTR16) if (e16 == h16) { #else if (e == h) { #endif /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("free matching hit: %ld", (long) i)); #if defined(DUK_USE_HEAPPTR16) entries16[i] = heap->heapptr_deleted16; #else entries[i] = DUK__DELETED_MARKER(heap); #endif break; } DUK_DDD(DUK_DDDPRINT("free matching miss: %ld", (long) i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(hash, size)); } } DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) { #ifdef DUK_USE_MARK_AND_SWEEP duk_small_uint_t prev_mark_and_sweep_base_flags; #endif #ifdef DUK_USE_DEBUG duk_uint32_t old_used = heap->st_used; #endif duk_uint32_t old_size = heap->st_size; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t *old_entries = heap->strtable16; duk_uint16_t *new_entries = NULL; #else duk_hstring **old_entries = heap->strtable; duk_hstring **new_entries = NULL; #endif duk_uint32_t new_used = 0; duk_uint32_t i; #ifdef DUK_USE_DEBUG DUK_UNREF(old_used); /* unused with some debug level combinations */ #endif #ifdef DUK_USE_DDDPRINT DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load", (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used, (long) (((double) old_used) / ((double) old_size) * 100.0), (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used_probe(heap), (long) (((double) duk__count_used_probe(heap)) / ((double) new_size) * 100.0))); #endif DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used_probe(heap)); /* required for rehash to succeed, equality not that useful */ DUK_ASSERT(old_entries); #ifdef DUK_USE_MARK_AND_SWEEP DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); #endif /* * The attempt to allocate may cause a GC. Such a GC must not attempt to resize * the stringtable (though it can be swept); finalizer execution and object * compaction must also be postponed to avoid the pressure to add strings to the * string table. */ #ifdef DUK_USE_MARK_AND_SWEEP prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; heap->mark_and_sweep_base_flags |= \ DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive call here */ DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings */ DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ #endif #if defined(DUK_USE_HEAPPTR16) new_entries = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * new_size); #else new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size); #endif #ifdef DUK_USE_MARK_AND_SWEEP heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; #endif if (!new_entries) { goto resize_error; } #ifdef DUK_USE_EXPLICIT_NULL_INIT for (i = 0; i < new_size; i++) { #if defined(DUK_USE_HEAPPTR16) new_entries[i] = heap->heapptr_null16; #else new_entries[i] = NULL; #endif } #else #if defined(DUK_USE_HEAPPTR16) /* Relies on NULL encoding to zero. */ DUK_MEMZERO(new_entries, sizeof(duk_uint16_t) * new_size); #else DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size); #endif #endif /* Because new_size > duk__count_used_probe(heap), guaranteed to work */ for (i = 0; i < old_size; i++) { duk_hstring *e; #if defined(DUK_USE_HEAPPTR16) e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, old_entries[i]); #else e = old_entries[i]; #endif if (e == NULL || e == DUK__DELETED_MARKER(heap)) { continue; } /* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */ duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e); } #ifdef DUK_USE_DDPRINT DUK_DD(DUK_DDPRINT("resized stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load", (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used, (long) (((double) old_used) / ((double) old_size) * 100.0), (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) new_used, (long) (((double) new_used) / ((double) new_size) * 100.0))); #endif #if defined(DUK_USE_HEAPPTR16) DUK_FREE(heap, heap->strtable16); heap->strtable16 = new_entries; #else DUK_FREE(heap, heap->strtable); heap->strtable = new_entries; #endif heap->st_size = new_size; heap->st_used = new_used; /* may be less, since DELETED entries are NULLed by rehash */ return 0; /* OK */ resize_error: DUK_FREE(heap, new_entries); return 1; /* FAIL */ } DUK_LOCAL duk_bool_t duk__resize_strtab_probe(duk_heap *heap) { duk_uint32_t new_size; duk_bool_t ret; new_size = (duk_uint32_t) duk__count_used_probe(heap); if (new_size >= 0x80000000UL) { new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME; } else { new_size = duk_util_get_hash_prime(DUK_STRTAB_GROW_ST_SIZE(new_size)); new_size = duk_util_get_hash_prime(new_size); } DUK_ASSERT(new_size > 0); /* rehash even if old and new sizes are the same to get rid of * DELETED entries. */ ret = duk__resize_strtab_raw_probe(heap, new_size); return ret; }
DUK_LOCAL duk_hstring *duk__alloc_init_hstring(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash, const duk_uint8_t *extdata) { duk_hstring *res = NULL; duk_uint8_t *data; duk_size_t alloc_size; duk_uarridx_t dummy; duk_uint32_t clen; #if defined(DUK_USE_STRLEN16) /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */ if (blen > 0xffffUL) { DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern")); return NULL; } #endif if (extdata) { alloc_size = (duk_size_t) sizeof(duk_hstring_external); res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto alloc_error; } DUK_MEMZERO(res, sizeof(duk_hstring_external)); #ifdef DUK_USE_EXPLICIT_NULL_INIT DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA); ((duk_hstring_external *) res)->extdata = extdata; } else { /* NUL terminate for convenient C access */ alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1); res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); if (!res) { goto alloc_error; } DUK_MEMZERO(res, sizeof(duk_hstring)); #ifdef DUK_USE_EXPLICIT_NULL_INIT DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr); #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); data = (duk_uint8_t *) (res + 1); DUK_MEMCPY(data, str, blen); data[blen] = (duk_uint8_t) 0; } if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { DUK_HSTRING_SET_ARRIDX(res); } /* All strings beginning with 0xff are treated as "internal", * even strings interned by the user. This allows user code to * create internal properties too, and makes behavior consistent * in case user code happens to use a string also used by Duktape * (such as string has already been interned and has the 'internal' * flag set). */ if (blen > 0 && str[0] == (duk_uint8_t) 0xff) { DUK_HSTRING_SET_INTERNAL(res); } DUK_HSTRING_SET_HASH(res, strhash); DUK_HSTRING_SET_BYTELEN(res, blen); clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); DUK_ASSERT(clen <= blen); DUK_HSTRING_SET_CHARLEN(res, clen); DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%ld", (unsigned long) DUK_HSTRING_GET_HASH(res), (long) DUK_HSTRING_GET_BYTELEN(res), (long) DUK_HSTRING_GET_CHARLEN(res), (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0), (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0))); return res; alloc_error: DUK_FREE(heap, res); return NULL; }
duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, size_t size, int dynamic) { duk_hbuffer *res = NULL; size_t alloc_size; DUK_DDD(DUK_DDDPRINT("allocate hbuffer")); if (dynamic) { alloc_size = sizeof(duk_hbuffer_dynamic); } else { alloc_size = sizeof(duk_hbuffer_fixed) + size; } #ifdef DUK_USE_ZERO_BUFFER_DATA /* zero everything */ res = (duk_hbuffer *) DUK_ALLOC_ZEROED(heap, alloc_size); #else res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size); #endif if (!res) { goto error; } #ifndef DUK_USE_ZERO_BUFFER_DATA /* if no buffer zeroing, zero the header anyway */ DUK_MEMZERO((void *) res, dynamic ? sizeof(duk_hbuffer_dynamic) : sizeof(duk_hbuffer_fixed)); #endif if (dynamic) { duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res; void *ptr; if (size > 0) { DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer")); #ifdef DUK_USE_ZERO_BUFFER_DATA ptr = DUK_ALLOC_ZEROED(heap, size); #else ptr = DUK_ALLOC(heap, size); #endif if (!ptr) { /* Because size > 0, NULL check is correct */ goto error; } h->curr_alloc = ptr; h->usable_size = size; /* snug */ } else { #ifdef DUK_USE_EXPLICIT_NULL_INIT h->curr_alloc = NULL; #endif DUK_ASSERT(h->usable_size == 0); } } res->size = size; DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER); if (dynamic) { DUK_HBUFFER_SET_DYNAMIC(res); } DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr); DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", res)); return res; error: DUK_DD(DUK_DDPRINT("hbuffer allocation failed")); DUK_FREE(heap, res); return NULL; }
DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags) { duk_hbuffer *res = NULL; duk_size_t alloc_size; DUK_DDD(DUK_DDDPRINT("allocate hbuffer")); /* Size sanity check. Should not be necessary because caller is * required to check this, but we don't want to cause a segfault * if the size wraps either in duk_size_t computation or when * storing the size in a 16-bit field. */ if (size > DUK_HBUFFER_MAX_BYTELEN) { DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size)); return NULL; } if (flags & DUK_BUF_FLAG_DYNAMIC) { alloc_size = sizeof(duk_hbuffer_dynamic); } else { alloc_size = sizeof(duk_hbuffer_fixed) + size; DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed)); /* no wrapping */ } res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size); if (!res) { goto error; } /* zero everything unless requested not to do so */ #if defined(DUK_USE_ZERO_BUFFER_DATA) DUK_MEMZERO((void *) res, (flags & DUK_BUF_FLAG_NOZERO) ? ((flags & DUK_BUF_FLAG_DYNAMIC) ? sizeof(duk_hbuffer_dynamic) : sizeof(duk_hbuffer_fixed)) : alloc_size); #else DUK_MEMZERO((void *) res, (flags & DUK_BUF_FLAG_DYNAMIC) ? sizeof(duk_hbuffer_dynamic) : sizeof(duk_hbuffer_fixed)); #endif if (flags & DUK_BUF_FLAG_DYNAMIC) { duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res; void *ptr; if (size > 0) { DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer")); #ifdef DUK_USE_ZERO_BUFFER_DATA ptr = DUK_ALLOC_ZEROED(heap, size); #else ptr = DUK_ALLOC(heap, size); #endif if (!ptr) { /* Because size > 0, NULL check is correct */ goto error; } DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr); DUK_HBUFFER_DYNAMIC_SET_ALLOC_SIZE(h, size); /* snug */ } else { #ifdef DUK_USE_EXPLICIT_NULL_INIT h->curr_alloc = NULL; #endif DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h) == 0); } } DUK_HBUFFER_SET_SIZE(res, size); DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER); if (flags & DUK_BUF_FLAG_DYNAMIC) { DUK_HBUFFER_SET_DYNAMIC(res); } DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr); DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res)); return res; error: DUK_DD(DUK_DDPRINT("hbuffer allocation failed")); DUK_FREE(heap, res); return NULL; }
static int duk__resize_strtab_raw(duk_heap *heap, duk_uint32_t new_size) { #ifdef DUK_USE_MARK_AND_SWEEP int prev_mark_and_sweep_base_flags; #endif #ifdef DUK_USE_DEBUG duk_uint32_t old_used = heap->st_used; #endif duk_uint32_t old_size = heap->st_size; duk_hstring **old_entries = heap->st; duk_hstring **new_entries = NULL; duk_uint32_t new_used = 0; duk_uint32_t i; #ifdef DUK_USE_DEBUG DUK_UNREF(old_used); /* unused with some debug level combinations */ #endif #ifdef DUK_USE_DDDPRINT DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %d entries, %d bytes, %d used, %d%% load -> %d entries, %d bytes, %d used, %d%% load", (int) old_size, (int) (sizeof(duk_hstring *) * old_size), (int) old_used, (int) (((double) old_used) / ((double) old_size) * 100.0), (int) new_size, (int) (sizeof(duk_hstring *) * new_size), (int) duk__count_used(heap), (int) (((double) duk__count_used(heap)) / ((double) new_size) * 100.0))); #endif DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used(heap)); /* required for rehash to succeed, equality not that useful */ DUK_ASSERT(old_entries); #ifdef DUK_USE_MARK_AND_SWEEP DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); #endif /* * The attempt to allocate may cause a GC. Such a GC must not attempt to resize * the stringtable (though it can be swept); finalizer execution and object * compaction must also be postponed to avoid the pressure to add strings to the * string table. */ #ifdef DUK_USE_MARK_AND_SWEEP prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; heap->mark_and_sweep_base_flags |= \ DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive call here */ DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings */ DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ #endif new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size); #ifdef DUK_USE_MARK_AND_SWEEP heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; #endif if (!new_entries) { goto error; } #ifdef DUK_USE_EXPLICIT_NULL_INIT for (i = 0; i < new_size; i++) { new_entries[i] = NULL; } #else DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size); #endif /* Because new_size > duk__count_used(heap), guaranteed to work */ for (i = 0; i < old_size; i++) { duk_hstring *e; e = old_entries[i]; if (e == NULL || e == DUK__DELETED_MARKER(heap)) { continue; } /* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */ duk__insert_hstring(heap, new_entries, new_size, &new_used, e); } #ifdef DUK_USE_DDPRINT DUK_DD(DUK_DDPRINT("resized stringtable: %d entries, %d bytes, %d used, %d%% load -> %d entries, %d bytes, %d used, %d%% load", (int) old_size, (int) (sizeof(duk_hstring *) * old_size), (int) old_used, (int) (((double) old_used) / ((double) old_size) * 100.0), (int) new_size, (int) (sizeof(duk_hstring *) * new_size), (int) new_used, (int) (((double) new_used) / ((double) new_size) * 100.0))); #endif DUK_FREE(heap, heap->st); heap->st = new_entries; heap->st_size = new_size; heap->st_used = new_used; /* may be less, since DELETED entries are NULLed by rehash */ return 0; /* OK */ error: DUK_FREE(heap, new_entries); return 1; /* FAIL */ }
/* Allocate a new duk_hbuffer of a certain type and return a pointer to it * (NULL on error). Write buffer data pointer to 'out_bufdata' (only if * allocation successful). */ DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk_small_uint_t flags, void **out_bufdata) { duk_hbuffer *res = NULL; duk_size_t header_size; duk_size_t alloc_size; DUK_ASSERT(heap != NULL); DUK_ASSERT(out_bufdata != NULL); DUK_DDD(DUK_DDDPRINT("allocate hbuffer")); /* Size sanity check. Should not be necessary because caller is * required to check this, but we don't want to cause a segfault * if the size wraps either in duk_size_t computation or when * storing the size in a 16-bit field. */ if (size > DUK_HBUFFER_MAX_BYTELEN) { DUK_D(DUK_DPRINT("hbuffer alloc failed: size too large: %ld", (long) size)); return NULL; /* no need to write 'out_bufdata' */ } if (flags & DUK_BUF_FLAG_EXTERNAL) { header_size = sizeof(duk_hbuffer_external); alloc_size = sizeof(duk_hbuffer_external); } else if (flags & DUK_BUF_FLAG_DYNAMIC) { header_size = sizeof(duk_hbuffer_dynamic); alloc_size = sizeof(duk_hbuffer_dynamic); } else { header_size = sizeof(duk_hbuffer_fixed); alloc_size = sizeof(duk_hbuffer_fixed) + size; DUK_ASSERT(alloc_size >= sizeof(duk_hbuffer_fixed)); /* no wrapping */ } res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size); if (DUK_UNLIKELY(res == NULL)) { goto alloc_error; } /* zero everything unless requested not to do so */ #if defined(DUK_USE_ZERO_BUFFER_DATA) DUK_MEMZERO((void *) res, (flags & DUK_BUF_FLAG_NOZERO) ? header_size : alloc_size); #else DUK_MEMZERO((void *) res, header_size); #endif if (flags & DUK_BUF_FLAG_EXTERNAL) { duk_hbuffer_external *h; h = (duk_hbuffer_external *) res; DUK_UNREF(h); *out_bufdata = NULL; #if defined(DUK_USE_EXPLICIT_NULL_INIT) #if defined(DUK_USE_HEAPPTR16) /* the compressed pointer is zeroed which maps to NULL, so nothing to do. */ #else DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(heap, h, NULL); #endif #endif DUK_ASSERT(DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(heap, h) == NULL); } else if (flags & DUK_BUF_FLAG_DYNAMIC) { duk_hbuffer_dynamic *h = (duk_hbuffer_dynamic *) res; void *ptr; if (size > 0) { DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL)); /* alloc external with size zero */ DUK_DDD(DUK_DDDPRINT("dynamic buffer with nonzero size, alloc actual buffer")); #if defined(DUK_USE_ZERO_BUFFER_DATA) ptr = DUK_ALLOC_ZEROED(heap, size); #else ptr = DUK_ALLOC(heap, size); #endif if (DUK_UNLIKELY(ptr == NULL)) { /* Because size > 0, NULL check is correct */ goto alloc_error; } *out_bufdata = ptr; DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, ptr); } else { *out_bufdata = NULL; #if defined(DUK_USE_EXPLICIT_NULL_INIT) #if defined(DUK_USE_HEAPPTR16) /* the compressed pointer is zeroed which maps to NULL, so nothing to do. */ #else DUK_HBUFFER_DYNAMIC_SET_DATA_PTR(heap, h, NULL); #endif #endif DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap, h) == NULL); } } else { *out_bufdata = (void *) ((duk_hbuffer_fixed *) res + 1); } DUK_HBUFFER_SET_SIZE(res, size); DUK_HEAPHDR_SET_TYPE(&res->hdr, DUK_HTYPE_BUFFER); if (flags & DUK_BUF_FLAG_DYNAMIC) { DUK_HBUFFER_SET_DYNAMIC(res); if (flags & DUK_BUF_FLAG_EXTERNAL) { DUK_HBUFFER_SET_EXTERNAL(res); } } else { DUK_ASSERT(!(flags & DUK_BUF_FLAG_EXTERNAL)); } DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &res->hdr); DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res)); return res; alloc_error: DUK_DD(DUK_DDPRINT("hbuffer allocation failed")); DUK_FREE(heap, res); return NULL; /* no need to write 'out_bufdata' */ }