static void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; duk_uint_fast32_t i; #ifdef DUK_USE_DEBUG duk_size_t count_free = 0; #endif duk_size_t count_keep = 0; DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); for (i = 0; i < heap->st_size; i++) { h = heap->st[i]; if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } else if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); count_keep++; continue; } #ifdef DUK_USE_DEBUG count_free++; #endif #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen for unreachable strings, * because we refcount finalize all unreachable objects which * should have decreased unreachable string refcounts to zero * (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif DUK_DDD(DUK_DDDPRINT("sweep string, not reachable: %p", (void *) h)); /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); /* remove the string (mark DELETED), could also call * duk_heap_string_remove() but that would be slow and * pointless because we already know the slot. */ heap->st[i] = DUK_STRTAB_DELETED_MARKER(heap); /* then free */ #if 1 DUK_FREE(heap, (duk_heaphdr *) h); /* no inner refs/allocs, just free directly */ #else duk_heap_free_heaphdr_raw(heap, (duk_heaphdr *) h); /* this would be OK but unnecessary */ #endif } #ifdef DUK_USE_DEBUG DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %d freed, %d kept", (int) count_free, (int) count_keep)); #endif *out_count_keep = count_keep; }
static void duk__dump_stringtable(duk_heap *heap) { duk_uint_fast32_t i; char buf[64+1]; DUK_D(DUK_DPRINT("stringtable %p, used %ld, size %ld, load %ld%%", (void *) heap->st, (long) heap->st_used, (long) heap->st_size, (long) (((double) heap->st_used) / ((double) heap->st_size) * 100.0))); for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) { duk_hstring *e = heap->st[i]; if (!e) { DUK_D(DUK_DPRINT(" [%ld]: NULL", (long) i)); } else if (e == DUK_STRTAB_DELETED_MARKER(heap)) { DUK_D(DUK_DPRINT(" [%ld]: DELETED", (long) i)); } else { duk__sanitize_snippet(buf, sizeof(buf), e); #ifdef DUK_USE_REFERENCE_COUNTING DUK_D(DUK_DPRINT(" [%ld]: %p (flags: 0x%08lx, ref: %ld) '%s', strhash=0x%08lx, blen=%ld, clen=%ld, " "arridx=%ld, internal=%ld, reserved_word=%ld, strict_reserved_word=%ld, eval_or_arguments=%ld", (long) i, (void *) e, (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) e), (long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) e), (const char *) buf, (unsigned long) e->hash, (long) e->blen, (long) e->clen, (long) (DUK_HSTRING_HAS_ARRIDX(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_INTERNAL(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_RESERVED_WORD(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(e) ? 1 : 0))); #else DUK_D(DUK_DPRINT(" [%ld]: %p (flags: 0x%08lx) '%s', strhash=0x%08lx, blen=%ld, clen=%ld, " "arridx=%ld, internal=%ld, reserved_word=%ld, strict_reserved_word=%ld, eval_or_arguments=%ld", (long) i, (void *) e, (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) e), (const char *) buf, (long) e->hash, (long) e->blen, (long) e->clen, (long) (DUK_HSTRING_HAS_ARRIDX(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_INTERNAL(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_RESERVED_WORD(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(e) ? 1 : 0), (long) (DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(e) ? 1 : 0))); #endif } } }
static void dump_stringtable(duk_heap *heap) { duk_uint32_t i; char buf[64+1]; DUK_DPRINT("stringtable %p, used %d, size %d, load %d%%", (void *) heap->st, (int) heap->st_used, (int) heap->st_size, (int) (((double) heap->st_used) / ((double) heap->st_size) * 100.0)); for (i = 0; i < heap->st_size; i++) { duk_hstring *e = heap->st[i]; if (!e) { DUK_DPRINT(" [%d]: NULL", i); } else if (e == DUK_STRTAB_DELETED_MARKER(heap)) { DUK_DPRINT(" [%d]: DELETED", i); } else { sanitize_snippet(buf, sizeof(buf), e); /* FIXME: all string flags not printed now */ #ifdef DUK_USE_REFERENCE_COUNTING DUK_DPRINT(" [%d]: %p (flags: 0x%08x, ref: %d) '%s', strhash=0x%08x, blen=%d, clen=%d, arridx=%d, internal=%d", i, (void *) e, (int) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) e), (int) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) e), buf, (int) e->hash, (int) e->blen, (int) e->clen, DUK_HSTRING_HAS_ARRIDX(e) ? 1 : 0, DUK_HSTRING_HAS_INTERNAL(e) ? 1 : 0); #else DUK_DPRINT(" [%d]: %p (flags: 0x%08x) '%s', strhash=0x%08x, blen=%d, clen=%d, arridx=%d, internal=%d", i, (void *) e, (int) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) e), buf, (int) e->hash, (int) e->blen, (int) e->clen, DUK_HSTRING_HAS_ARRIDX(e) ? 1 : 0, DUK_HSTRING_HAS_INTERNAL(e) ? 1 : 0); #endif } } }
DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) { duk_uint_fast32_t i; duk_hstring *h; #if defined(DUK_USE_HEAPPTR16) if (heap->strtable16) { #else if (heap->strtable) { #endif for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) { #if defined(DUK_USE_HEAPPTR16) h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } DUK_ASSERT(h != NULL); /* strings may have inner refs (extdata) in some cases */ duk_free_hstring_inner(heap, h); DUK_FREE(heap, h); #if 0 /* not strictly necessary */ heap->strtable[i] = NULL; #endif } #if defined(DUK_USE_HEAPPTR16) DUK_FREE(heap, heap->strtable16); #else DUK_FREE(heap, heap->strtable); #endif #if 0 /* not strictly necessary */ heap->strtable = NULL; #endif } }
static void duk__free_stringtable(duk_heap *heap) { duk_uint_fast32_t i; /* strings are only tracked by stringtable */ if (heap->st) { for (i = 0; i < heap->st_size; i++) { duk_hstring *e = heap->st[i]; if (e == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } /* strings have no inner allocations so free directly */ DUK_DDD(DUK_DDDPRINT("FINALFREE (string): %!iO", e)); DUK_FREE(heap, e); #if 0 /* not strictly necessary */ heap->st[i] = NULL; #endif } DUK_FREE(heap, heap->st); #if 0 /* not strictly necessary */ heap->st = NULL; #endif } }
DUK_INTERNAL duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, duk_realloc_function realloc_func, duk_free_function free_func, void *alloc_udata, duk_fatal_function fatal_func) { duk_heap *res = NULL; DUK_D(DUK_DPRINT("allocate heap")); /* * Debug dump type sizes */ #ifdef DUK_USE_DEBUG duk__dump_type_sizes(); duk__dump_type_limits(); #endif /* * If selftests enabled, run them as early as possible */ #ifdef DUK_USE_SELF_TESTS DUK_D(DUK_DPRINT("running self tests")); duk_selftest_run_tests(); DUK_D(DUK_DPRINT("self tests passed")); #endif #ifdef DUK_USE_COMPUTED_NAN do { /* Workaround for some exotic platforms where NAN is missing * and the expression (0.0 / 0.0) does NOT result in a NaN. * Such platforms use the global 'duk_computed_nan' which must * be initialized at runtime. Use 'volatile' to ensure that * the compiler will actually do the computation and not try * to do constant folding which might result in the original * problem. */ volatile double dbl1 = 0.0; volatile double dbl2 = 0.0; duk_computed_nan = dbl1 / dbl2; } while (0); #endif /* * Computed values (e.g. INFINITY) */ #ifdef DUK_USE_COMPUTED_INFINITY do { /* Similar workaround for INFINITY. */ volatile double dbl1 = 1.0; volatile double dbl2 = 0.0; duk_computed_infinity = dbl1 / dbl2; } while (0); #endif /* * Allocate heap struct * * Use a raw call, all macros expect the heap to be initialized */ res = (duk_heap *) alloc_func(alloc_udata, sizeof(duk_heap)); if (!res) { goto error; } /* * Zero the struct, and start initializing roughly in order */ DUK_MEMZERO(res, sizeof(*res)); /* explicit NULL inits */ #ifdef DUK_USE_EXPLICIT_NULL_INIT res->alloc_udata = NULL; res->heap_allocated = NULL; #ifdef DUK_USE_REFERENCE_COUNTING res->refzero_list = NULL; res->refzero_list_tail = NULL; #endif #ifdef DUK_USE_MARK_AND_SWEEP res->finalize_list = NULL; #endif res->heap_thread = NULL; res->curr_thread = NULL; res->heap_object = NULL; res->log_buffer = NULL; #if defined(DUK_USE_STRTAB_CHAIN) /* nothing to NULL */ #elif defined(DUK_USE_STRTAB_PROBE) #if defined(DUK_USE_HEAPPTR16) res->strtable16 = (duk_uint16_t *) NULL; #else res->strtable = (duk_hstring **) NULL; #endif #endif { duk_small_uint_t i; for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { res->strs[i] = NULL; } } #endif /* DUK_USE_EXPLICIT_NULL_INIT */ res->alloc_func = alloc_func; res->realloc_func = realloc_func; res->free_func = free_func; res->alloc_udata = alloc_udata; res->fatal_func = fatal_func; #if defined(DUK_USE_HEAPPTR16) res->heapptr_null16 = DUK_USE_HEAPPTR_ENC16((void *) NULL); res->heapptr_deleted16 = DUK_USE_HEAPPTR_ENC16((void *) DUK_STRTAB_DELETED_MARKER(res)); #endif /* res->mark_and_sweep_trigger_counter == 0 -> now causes immediate GC; which is OK */ res->call_recursion_depth = 0; res->call_recursion_limit = DUK_HEAP_DEFAULT_CALL_RECURSION_LIMIT; /* XXX: use the pointer as a seed for now: mix in time at least */ /* The casts through duk_intr_pt is to avoid the following GCC warning: * * warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] * * This still generates a /Wp64 warning on VS2010 when compiling for x86. */ res->hash_seed = (duk_uint32_t) (duk_intptr_t) res; res->rnd_state = (duk_uint32_t) (duk_intptr_t) res; #ifdef DUK_USE_INTERRUPT_COUNTER /* zero value causes an interrupt before executing first instruction */ DUK_ASSERT(res->interrupt_counter == 0); DUK_ASSERT(res->interrupt_init == 0); #endif #ifdef DUK_USE_EXPLICIT_NULL_INIT res->lj.jmpbuf_ptr = NULL; #endif DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN); /* zero */ DUK_TVAL_SET_UNDEFINED_UNUSED(&res->lj.value1); DUK_TVAL_SET_UNDEFINED_UNUSED(&res->lj.value2); #if (DUK_STRTAB_INITIAL_SIZE < DUK_UTIL_MIN_HASH_PRIME) #error initial heap stringtable size is defined incorrectly #endif /* * Init stringtable: fixed variant */ #if defined(DUK_USE_STRTAB_CHAIN) DUK_MEMZERO(res->strtable, sizeof(duk_strtab_entry) * DUK_STRTAB_CHAIN_SIZE); #ifdef DUK_USE_EXPLICIT_NULL_INIT { duk_small_uint_t i; for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { #if defined(DUK_USE_HEAPPTR16) res->strtable[i].u.str16 = res->heapptr_null16; #else res->strtable[i].u.str = NULL; #endif } } #endif /* DUK_USE_EXPLICIT_NULL_INIT */ #endif /* DUK_USE_STRTAB_CHAIN */ /* * Init stringtable: probe variant */ #if defined(DUK_USE_STRTAB_PROBE) #if defined(DUK_USE_HEAPPTR16) res->strtable16 = (duk_uint16_t *) alloc_func(alloc_udata, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); if (!res->strtable16) { goto error; } #else /* DUK_USE_HEAPPTR16 */ res->strtable = (duk_hstring **) alloc_func(alloc_udata, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); if (!res->strtable) { goto error; } #endif /* DUK_USE_HEAPPTR16 */ res->st_size = DUK_STRTAB_INITIAL_SIZE; #ifdef DUK_USE_EXPLICIT_NULL_INIT { duk_small_uint_t i; DUK_ASSERT(res->st_size == DUK_STRTAB_INITIAL_SIZE); for (i = 0; i < DUK_STRTAB_INITIAL_SIZE; i++) { #if defined(DUK_USE_HEAPPTR16) res->strtable16[i] = res->heapptr_null16; #else res->strtable[i] = NULL; #endif } } #else /* DUK_USE_EXPLICIT_NULL_INIT */ #if defined(DUK_USE_HEAPPTR16) DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); #else DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); #endif #endif /* DUK_USE_EXPLICIT_NULL_INIT */ #endif /* DUK_USE_STRTAB_PROBE */ /* * Init stringcache */ #ifdef DUK_USE_EXPLICIT_NULL_INIT { duk_small_uint_t i; for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { res->strcache[i].h = NULL; } } #endif /* XXX: error handling is incomplete. It would be cleanest if * there was a setjmp catchpoint, so that all init code could * freely throw errors. If that were the case, the return code * passing here could be removed. */ /* * Init built-in strings */ DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS")); if (!duk__init_heap_strings(res)) { goto error; } /* * Init the heap thread */ DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD")); if (!duk__init_heap_thread(res)) { goto error; } /* * Init the heap object */ DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT")); DUK_ASSERT(res->heap_thread != NULL); res->heap_object = duk_hobject_alloc(res, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); if (!res->heap_object) { goto error; } DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object); /* * Init log buffer */ DUK_DD(DUK_DDPRINT("HEAP: INIT LOG BUFFER")); res->log_buffer = (duk_hbuffer_dynamic *) duk_hbuffer_alloc(res, DUK_BI_LOGGER_SHORT_MSG_LIMIT, 1 /*dynamic*/); if (!res->log_buffer) { goto error; } DUK_HBUFFER_INCREF(res->heap_thread, res->log_buffer); /* * All done */ DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res)); return res; error: DUK_D(DUK_DPRINT("heap allocation failed")); if (res) { /* assumes that allocated pointers and alloc funcs are valid * if res exists */ DUK_ASSERT(res->alloc_func != NULL); DUK_ASSERT(res->realloc_func != NULL); DUK_ASSERT(res->free_func != NULL); duk_heap_free(res); } return NULL; }
DUK_LOCAL void duk__free_stringtable(duk_heap *heap) { duk_uint_fast32_t i; /* strings are only tracked by stringtable */ #if defined(DUK_USE_HEAPPTR16) if (heap->strtable16) { #else if (heap->strtable) { #endif for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) { duk_hstring *e; #if defined(DUK_USE_HEAPPTR16) e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]); #else e = heap->strtable[i]; #endif if (e == NULL || e == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } DUK_ASSERT(e != NULL); /* strings may have inner refs (extdata) in some cases */ duk_free_hstring_inner(heap, (duk_hstring *) e); DUK_DDD(DUK_DDDPRINT("FINALFREE (string): %!iO", (duk_heaphdr *) e)); DUK_FREE(heap, e); #if 0 /* not strictly necessary */ heap->strtable[i] = NULL; #endif } #if defined(DUK_USE_HEAPPTR16) DUK_FREE(heap, heap->strtable16); #else DUK_FREE(heap, heap->strtable); #endif #if 0 /* not strictly necessary */ heap->strtable = NULL; #endif } } DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { duk_hthread *thr; duk_heaphdr *curr; #ifdef DUK_USE_DEBUG duk_size_t count_obj = 0; #endif DUK_ASSERT(heap != NULL); DUK_ASSERT(heap->heap_thread != NULL); #ifdef DUK_USE_REFERENCE_COUNTING DUK_ASSERT(heap->refzero_list == NULL); /* refzero not running -> must be empty */ #endif #ifdef DUK_USE_MARK_AND_SWEEP DUK_ASSERT(heap->finalize_list == NULL); /* mark-and-sweep not running -> must be empty */ #endif /* XXX: here again finalizer thread is the heap_thread which needs * to be coordinated with finalizer thread fixes. */ thr = heap->heap_thread; DUK_ASSERT(thr != NULL); curr = heap->heap_allocated; while (curr) { if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) { /* Only objects in heap_allocated may have finalizers. Check that * the object itself has a _Finalizer property so that we don't * execute finalizers for e.g. Proxy objects. */ DUK_ASSERT(thr != NULL); DUK_ASSERT(curr != NULL); if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) { duk_hobject_run_finalizer(thr, (duk_hobject *) curr); } #ifdef DUK_USE_DEBUG count_obj++; #endif } curr = DUK_HEAPHDR_GET_NEXT(curr); } /* Note: count includes all objects, not only those with an actual finalizer. */ #ifdef DUK_USE_DEBUG DUK_D(DUK_DPRINT("checked %ld objects for finalizers before freeing heap", (long) count_obj)); #endif }
DUK_LOCAL void duk__sweep_stringtable_probe(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; duk_uint_fast32_t i; #ifdef DUK_USE_DEBUG duk_size_t count_free = 0; #endif duk_size_t count_keep = 0; DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); for (i = 0; i < heap->st_size; i++) { #if defined(DUK_USE_HEAPPTR16) h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { continue; } else if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); count_keep++; continue; } #ifdef DUK_USE_DEBUG count_free++; #endif #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen for unreachable strings, * because we refcount finalize all unreachable objects which * should have decreased unreachable string refcounts to zero * (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif DUK_DDD(DUK_DDDPRINT("sweep string, not reachable: %p", (void *) h)); /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); /* remove the string (mark DELETED), could also call * duk_heap_string_remove() but that would be slow and * pointless because we already know the slot. */ #if defined(DUK_USE_HEAPPTR16) heap->strtable16[i] = heap->heapptr_deleted16; #else heap->strtable[i] = DUK_STRTAB_DELETED_MARKER(heap); #endif /* free inner references (these exist e.g. when external * strings are enabled) */ duk_free_hstring_inner(heap, (duk_hstring *) h); /* finally free the struct itself */ DUK_FREE(heap, h); } #ifdef DUK_USE_DEBUG DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", (long) count_free, (long) count_keep)); #endif *out_count_keep = count_keep; }
DUK_INTERNAL duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, duk_realloc_function realloc_func, duk_free_function free_func, void *heap_udata, duk_fatal_function fatal_func) { duk_heap *res = NULL; DUK_D(DUK_DPRINT("allocate heap")); /* * Debug dump type sizes */ #if defined(DUK_USE_DEBUG) duk__dump_misc_options(); duk__dump_type_sizes(); duk__dump_type_limits(); #endif /* * If selftests enabled, run them as early as possible. */ #if defined(DUK_USE_SELF_TESTS) if (duk_selftest_run_tests(alloc_func, realloc_func, free_func, heap_udata) > 0) { fatal_func(heap_udata, "self test(s) failed"); } #endif /* * Important assert-like checks that should be enabled even * when assertions are otherwise not enabled. */ #if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE) /* Can't check sizeof() using preprocessor so explicit check. * This will be optimized away in practice; unfortunately a * warning is generated on some compilers as a result. */ #if defined(DUK_USE_PACKED_TVAL) if (sizeof(duk_tval) != 8) { #else if (sizeof(duk_tval) != 16) { #endif fatal_func(heap_udata, "sizeof(duk_tval) not 8 or 16, cannot use DUK_USE_EXEC_REGCONST_OPTIMIZE option"); } #endif /* DUK_USE_EXEC_REGCONST_OPTIMIZE */ /* * Computed values (e.g. INFINITY) */ #if defined(DUK_USE_COMPUTED_NAN) do { /* Workaround for some exotic platforms where NAN is missing * and the expression (0.0 / 0.0) does NOT result in a NaN. * Such platforms use the global 'duk_computed_nan' which must * be initialized at runtime. Use 'volatile' to ensure that * the compiler will actually do the computation and not try * to do constant folding which might result in the original * problem. */ volatile double dbl1 = 0.0; volatile double dbl2 = 0.0; duk_computed_nan = dbl1 / dbl2; } while (0); #endif #if defined(DUK_USE_COMPUTED_INFINITY) do { /* Similar workaround for INFINITY. */ volatile double dbl1 = 1.0; volatile double dbl2 = 0.0; duk_computed_infinity = dbl1 / dbl2; } while (0); #endif /* * Allocate heap struct * * Use a raw call, all macros expect the heap to be initialized */ res = (duk_heap *) alloc_func(heap_udata, sizeof(duk_heap)); if (!res) { goto error; } /* * Zero the struct, and start initializing roughly in order */ DUK_MEMZERO(res, sizeof(*res)); /* explicit NULL inits */ #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->heap_udata = NULL; res->heap_allocated = NULL; #if defined(DUK_USE_REFERENCE_COUNTING) res->refzero_list = NULL; res->refzero_list_tail = NULL; #endif #if defined(DUK_USE_MARK_AND_SWEEP) res->finalize_list = NULL; #endif res->heap_thread = NULL; res->curr_thread = NULL; res->heap_object = NULL; #if defined(DUK_USE_STRTAB_CHAIN) /* nothing to NULL */ #elif defined(DUK_USE_STRTAB_PROBE) #if defined(DUK_USE_HEAPPTR16) res->strtable16 = (duk_uint16_t *) NULL; #else res->strtable = (duk_hstring **) NULL; #endif #endif #if defined(DUK_USE_ROM_STRINGS) /* no res->strs[] */ #else /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_HEAPPTR16) /* res->strs16[] is zeroed and zero decodes to NULL, so no NULL inits. */ #else { duk_small_uint_t i; for (i = 0; i < DUK_HEAP_NUM_STRINGS; i++) { res->strs[i] = NULL; } } #endif #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_DEBUGGER_SUPPORT) res->dbg_read_cb = NULL; res->dbg_write_cb = NULL; res->dbg_peek_cb = NULL; res->dbg_read_flush_cb = NULL; res->dbg_write_flush_cb = NULL; res->dbg_request_cb = NULL; res->dbg_udata = NULL; res->dbg_step_thread = NULL; #endif #endif /* DUK_USE_EXPLICIT_NULL_INIT */ res->alloc_func = alloc_func; res->realloc_func = realloc_func; res->free_func = free_func; res->heap_udata = heap_udata; res->fatal_func = fatal_func; #if defined(DUK_USE_HEAPPTR16) /* XXX: zero assumption */ res->heapptr_null16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) NULL); res->heapptr_deleted16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) DUK_STRTAB_DELETED_MARKER(res)); #endif /* res->mark_and_sweep_trigger_counter == 0 -> now causes immediate GC; which is OK */ res->call_recursion_depth = 0; res->call_recursion_limit = DUK_USE_NATIVE_CALL_RECLIMIT; /* XXX: use the pointer as a seed for now: mix in time at least */ /* The casts through duk_intr_pt is to avoid the following GCC warning: * * warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] * * This still generates a /Wp64 warning on VS2010 when compiling for x86. */ #if defined(DUK_USE_ROM_STRINGS) /* XXX: make a common DUK_USE_ option, and allow custom fixed seed? */ DUK_D(DUK_DPRINT("using rom strings, force heap hash_seed to fixed value 0x%08lx", (long) DUK__FIXED_HASH_SEED)); res->hash_seed = (duk_uint32_t) DUK__FIXED_HASH_SEED; #else /* DUK_USE_ROM_STRINGS */ res->hash_seed = (duk_uint32_t) (duk_intptr_t) res; res->rnd_state = (duk_uint32_t) (duk_intptr_t) res; #if !defined(DUK_USE_STRHASH_DENSE) res->hash_seed ^= 5381; /* Bernstein hash init value is normally 5381; XOR it in in case pointer low bits are 0 */ #endif #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->lj.jmpbuf_ptr = NULL; #endif DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN); /* zero */ DUK_TVAL_SET_UNDEFINED(&res->lj.value1); DUK_TVAL_SET_UNDEFINED(&res->lj.value2); #if (DUK_STRTAB_INITIAL_SIZE < DUK_UTIL_MIN_HASH_PRIME) #error initial heap stringtable size is defined incorrectly #endif /* * Init stringtable: fixed variant */ #if defined(DUK_USE_STRTAB_CHAIN) DUK_MEMZERO(res->strtable, sizeof(duk_strtab_entry) * DUK_STRTAB_CHAIN_SIZE); #if defined(DUK_USE_EXPLICIT_NULL_INIT) { duk_small_uint_t i; for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { #if defined(DUK_USE_HEAPPTR16) res->strtable[i].u.str16 = res->heapptr_null16; #else res->strtable[i].u.str = NULL; #endif } } #endif /* DUK_USE_EXPLICIT_NULL_INIT */ #endif /* DUK_USE_STRTAB_CHAIN */ /* * Init stringtable: probe variant */ #if defined(DUK_USE_STRTAB_PROBE) #if defined(DUK_USE_HEAPPTR16) res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); if (!res->strtable16) { goto error; } #else /* DUK_USE_HEAPPTR16 */ res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); if (!res->strtable) { goto error; } #endif /* DUK_USE_HEAPPTR16 */ res->st_size = DUK_STRTAB_INITIAL_SIZE; #if defined(DUK_USE_EXPLICIT_NULL_INIT) { duk_small_uint_t i; DUK_ASSERT(res->st_size == DUK_STRTAB_INITIAL_SIZE); for (i = 0; i < DUK_STRTAB_INITIAL_SIZE; i++) { #if defined(DUK_USE_HEAPPTR16) res->strtable16[i] = res->heapptr_null16; #else res->strtable[i] = NULL; #endif } } #else /* DUK_USE_EXPLICIT_NULL_INIT */ #if defined(DUK_USE_HEAPPTR16) DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); #else DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); #endif #endif /* DUK_USE_EXPLICIT_NULL_INIT */ #endif /* DUK_USE_STRTAB_PROBE */ /* * Init stringcache */ #if defined(DUK_USE_EXPLICIT_NULL_INIT) { duk_small_uint_t i; for (i = 0; i < DUK_HEAP_STRCACHE_SIZE; i++) { res->strcache[i].h = NULL; } } #endif /* XXX: error handling is incomplete. It would be cleanest if * there was a setjmp catchpoint, so that all init code could * freely throw errors. If that were the case, the return code * passing here could be removed. */ /* * Init built-in strings */ DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS")); if (!duk__init_heap_strings(res)) { goto error; } /* * Init the heap thread */ DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD")); if (!duk__init_heap_thread(res)) { goto error; } /* * Init the heap object */ DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT")); DUK_ASSERT(res->heap_thread != NULL); res->heap_object = duk_hobject_alloc(res, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); if (!res->heap_object) { goto error; } DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object); /* * All done */ DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res)); return res; error: DUK_D(DUK_DPRINT("heap allocation failed")); if (res) { /* assumes that allocated pointers and alloc funcs are valid * if res exists */ DUK_ASSERT(res->alloc_func != NULL); DUK_ASSERT(res->realloc_func != NULL); DUK_ASSERT(res->free_func != NULL); duk_heap_free(res); } return NULL; }