DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) { /* Free strings in the stringtable and any allocations needed * by the stringtable itself. */ duk_uint_fast32_t i, j; duk_strtab_entry *e; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t *lst; duk_uint16_t null16 = heap->heapptr_null16; #else duk_hstring **lst; #endif duk_hstring *h; for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { e = heap->strtable + i; if (e->listlen > 0) { #if defined(DUK_USE_HEAPPTR16) lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); #else lst = e->u.strlist; #endif DUK_ASSERT(lst != NULL); for (j = 0; j < e->listlen; j++) { #if defined(DUK_USE_HEAPPTR16) h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[j]); lst[j] = null16; #else h = lst[j]; lst[j] = NULL; #endif /* strings may have inner refs (extdata) in some cases */ if (h != NULL) { duk_free_hstring_inner(heap, h); DUK_FREE(heap, h); } } #if defined(DUK_USE_HEAPPTR16) e->u.strlist16 = null16; #else e->u.strlist = NULL; #endif DUK_FREE(heap, lst); } else { #if defined(DUK_USE_HEAPPTR16) h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); e->u.str16 = null16; #else h = e->u.str; e->u.str = NULL; #endif if (h != NULL) { duk_free_hstring_inner(heap, h); DUK_FREE(heap, h); } } e->listlen = 0; } }
DUK_LOCAL void duk__sweep_string_chain16(duk_heap *heap, duk_uint16_t *slot, duk_size_t *count_keep, duk_size_t *count_free) { duk_uint16_t h16 = *slot; duk_hstring *h; duk_uint16_t null16 = heap->heapptr_null16; if (h16 == null16) { /* nop */ return; } h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, h16); DUK_ASSERT(h != NULL); if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); (*count_keep)++; } else { #if defined(DUK_USE_REFERENCE_COUNTING) DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); *slot = null16; /* free inner references (these exist e.g. when external * strings are enabled) */ duk_free_hstring_inner(heap, h); DUK_FREE(heap, h); (*count_free)++; } }
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_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_uint16_t *lst; duk_size_t i, n; duk_uint16_t null16 = heap->heapptr_null16; DUK_ASSERT(heap != NULL); slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str16 != null16) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } 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) { duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[i]); DUK_ASSERT(h != NULL); if (DUK_HSTRING_GET_BYTELEN(h) == blen && DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { return h; } } } } return NULL; }
DUK_LOCAL void duk__sweep_stringtable_chain(duk_heap *heap, duk_size_t *out_count_keep) { duk_strtab_entry *e; duk_uint_fast32_t i; duk_size_t count_free = 0; duk_size_t count_keep = 0; duk_size_t j, n; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t *lst; #else duk_hstring **lst; #endif DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); /* 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). */ for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { e = heap->strtable + i; if (e->listlen == 0) { #if defined(DUK_USE_HEAPPTR16) duk__sweep_string_chain16(heap, &e->u.str16, &count_keep, &count_free); #else duk__sweep_string_chain(heap, &e->u.str, &count_keep, &count_free); #endif } else { #if defined(DUK_USE_HEAPPTR16) lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); #else lst = e->u.strlist; #endif for (j = 0, n = e->listlen; j < n; j++) { #if defined(DUK_USE_HEAPPTR16) duk__sweep_string_chain16(heap, lst + j, &count_keep, &count_free); #else duk__sweep_string_chain(heap, lst + j, &count_keep, &count_free); #endif } } } DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", (long) count_free, (long) count_keep)); *out_count_keep = count_keep; }
DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) { duk_uint32_t i; duk_hstring *h; DUK_ASSERT(heap != NULL); #if defined(DUK_USE_HEAPPTR16) DUK_ASSERT(heap->strtable16 != NULL); #else DUK_ASSERT(heap->strtable != NULL); #endif for (i = 0; i < heap->st_size; i++) { #if defined(DUK_USE_HEAPPTR16) h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]); #else h = heap->strtable[i]; #endif DUK_DD(DUK_DDPRINT("[%03d] -> %p", (int) i, (void *) h)); } }
DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) { duk_small_uint_t slotidx; duk_strtab_entry *e; duk_uint16_t *lst; duk_size_t i, n; duk_uint16_t h16; duk_uint16_t null16 = heap->heapptr_null16; 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); DUK_ASSERT(h != NULL); h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); e = heap->strtable + slotidx; if (e->listlen == 0) { if (e->u.str16 == h16) { e->u.str16 = null16; return; } } 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] == h16) { lst[i] = null16; return; } } } DUK_D(DUK_DPRINT("failed to find string that should be in stringtable")); DUK_UNREACHABLE(); return; }
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 } }
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_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) { duk_strtab_entry *e; duk_small_uint_t i; duk_size_t j, n, used; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t *lst; duk_uint16_t null16 = heap->heapptr_null16; #else duk_hstring **lst; #endif DUK_ASSERT(heap != NULL); for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { e = heap->strtable + i; if (e->listlen == 0) { #if defined(DUK_USE_HEAPPTR16) DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0))); #else DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0))); #endif } else { used = 0; #if defined(DUK_USE_HEAPPTR16) lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); #else lst = e->u.strlist; #endif DUK_ASSERT(lst != NULL); for (j = 0, n = e->listlen; j < n; j++) { #if defined(DUK_USE_HEAPPTR16) if (lst[j] != null16) { #else if (lst[j] != NULL) { #endif used++; } } DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (int) i, (int) used, (int) e->listlen)); } } } #endif /* DUK_USE_DEBUG */ #endif /* DUK_USE_STRTAB_CHAIN */ /* * String table algorithm: closed hashing with a probe sequence * * This is the default algorithm and works fine for environments with * minimal memory constraints. */ #if defined(DUK_USE_STRTAB_PROBE) /* Count actually used (non-NULL, non-DELETED) entries. */ DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) { duk_int_t res = 0; duk_uint_fast32_t i, n; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t deleted16 = heap->heapptr_deleted16; #endif n = (duk_uint_fast32_t) heap->st_size; for (i = 0; i < n; i++) { #if defined(DUK_USE_HEAPPTR16) if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) { #else if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) { #endif res++; } } return res; } #if defined(DUK_USE_HEAPPTR16) DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { #else DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { #endif duk_uint32_t i; duk_uint32_t step; #if defined(DUK_USE_HEAPPTR16) duk_uint16_t null16 = heap->heapptr_null16; duk_uint16_t deleted16 = heap->heapptr_deleted16; #endif DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size); step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h)); for (;;) { #if defined(DUK_USE_HEAPPTR16) duk_uint16_t e16 = entries16[i]; #else duk_hstring *e = entries[i]; #endif #if defined(DUK_USE_HEAPPTR16) /* XXX: could check for e16 == 0 because NULL is guaranteed to * encode to zero. */ if (e16 == null16) { #else if (e == NULL) { #endif DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i)); #if defined(DUK_USE_HEAPPTR16) entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); #else entries[i] = h; #endif (*p_used)++; break; #if defined(DUK_USE_HEAPPTR16) } else if (e16 == deleted16) { #else } else if (e == DUK__DELETED_MARKER(heap)) { #endif /* st_used remains the same, DELETED is counted as used */ DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i)); #if defined(DUK_USE_HEAPPTR16) entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); #else entries[i] = h; #endif break; } DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); } } #if defined(DUK_USE_HEAPPTR16) DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { #else DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { #endif duk_uint32_t i; duk_uint32_t step; DUK_ASSERT(size > 0); i = DUK__HASH_INITIAL(strhash, size); step = DUK__HASH_PROBE_STEP(strhash); for (;;) { duk_hstring *e; #if defined(DUK_USE_HEAPPTR16) e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]); #else e = entries[i]; #endif if (!e) { return NULL; } if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) { DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)", (long) i, (long) step, (long) size)); return e; } } DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)", (long) i, (long) step, (long) size)); i = (i + step) % size; /* looping should never happen */ DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); } DUK_UNREACHABLE(); }
DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; duk_hstring *prev; duk_uint32_t i; #if defined(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)); #if defined(DUK_USE_STRTAB_PTRCOMP) if (heap->strtable16 == NULL) { #else if (heap->strtable == NULL) { #endif goto done; } for (i = 0; i < heap->st_size; i++) { #if defined(DUK_USE_STRTAB_PTRCOMP) h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif prev = NULL; while (h != NULL) { duk_hstring *next; next = h->hdr.h_next; if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); count_keep++; prev = h; } else { #if defined(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 /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); /* remove the string from the string table */ duk_heap_strtable_unlink_prev(heap, (duk_hstring *) h, (duk_hstring *) prev); /* free inner references (these exist e.g. when external * strings are enabled) and the struct itself. */ duk_free_hstring(heap, (duk_hstring *) h); /* don't update 'prev'; it should be last string kept */ } h = next; } } done: #if defined(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; } /* * Sweep heap */ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_count_keep) { duk_heaphdr *prev; /* last element that was left in the heap */ duk_heaphdr *curr; duk_heaphdr *next; #if defined(DUK_USE_DEBUG) duk_size_t count_free = 0; duk_size_t count_finalize = 0; duk_size_t count_rescue = 0; #endif duk_size_t count_keep = 0; DUK_UNREF(flags); DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap)); prev = NULL; curr = heap->heap_allocated; heap->heap_allocated = NULL; while (curr) { /* Strings and ROM objects are never placed on the heap allocated list. */ DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_STRING); DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); next = DUK_HEAPHDR_GET_NEXT(heap, curr); if (DUK_HEAPHDR_HAS_REACHABLE(curr)) { /* * Reachable object, keep */ DUK_DDD(DUK_DDDPRINT("sweep, reachable: %p", (void *) curr)); if (DUK_HEAPHDR_HAS_FINALIZABLE(curr)) { /* * If object has been marked finalizable, move it to the * "to be finalized" work list. It will be collected on * the next mark-and-sweep if it is still unreachable * after running the finalizer. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DDD(DUK_DDDPRINT("object has finalizer, move to finalization work list: %p", (void *) curr)); #if defined(DUK_USE_DOUBLE_LINKED_HEAP) if (heap->finalize_list) { DUK_HEAPHDR_SET_PREV(heap, heap->finalize_list, curr); } DUK_HEAPHDR_SET_PREV(heap, curr, NULL); #endif DUK_HEAPHDR_SET_NEXT(heap, curr, heap->finalize_list); DUK_ASSERT_HEAPHDR_LINKS(heap, curr); heap->finalize_list = curr; #if defined(DUK_USE_DEBUG) count_finalize++; #endif } else { /* * Object will be kept; queue object back to heap_allocated (to tail) */ if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { /* * Object's finalizer was executed on last round, and * object has been happily rescued. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DD(DUK_DDPRINT("object rescued during mark-and-sweep finalization: %p", (void *) curr)); #if defined(DUK_USE_DEBUG) count_rescue++; #endif } else { /* * Plain, boring reachable object. */ DUK_DD(DUK_DDPRINT("keep object: %!iO", curr)); count_keep++; } if (!heap->heap_allocated) { heap->heap_allocated = curr; } if (prev) { DUK_HEAPHDR_SET_NEXT(heap, prev, curr); } #if defined(DUK_USE_DOUBLE_LINKED_HEAP) DUK_HEAPHDR_SET_PREV(heap, curr, prev); #endif DUK_ASSERT_HEAPHDR_LINKS(heap, prev); DUK_ASSERT_HEAPHDR_LINKS(heap, curr); prev = curr; } DUK_HEAPHDR_CLEAR_REACHABLE(curr); DUK_HEAPHDR_CLEAR_FINALIZED(curr); DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); curr = next; } else { /* * Unreachable object, free */ DUK_DDD(DUK_DDDPRINT("sweep, not reachable: %p", (void *) curr)); #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen because we refcount * finalize all unreachable objects which should cancel out * refcounts (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) == 0); #endif DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { DUK_DDD(DUK_DDDPRINT("finalized object not rescued: %p", (void *) curr)); } /* Note: object cannot be a finalizable unreachable object, as * they have been marked temporarily reachable for this round, * and are handled above. */ #if defined(DUK_USE_DEBUG) count_free++; #endif /* weak refs should be handled here, but no weak refs for * any non-string objects exist right now. */ /* free object and all auxiliary (non-heap) allocs */ duk_heap_free_heaphdr_raw(heap, curr); curr = next; } } if (prev) { DUK_HEAPHDR_SET_NEXT(heap, prev, NULL); } DUK_ASSERT_HEAPHDR_LINKS(heap, prev); #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep sweep objects (non-string): %ld freed, %ld kept, %ld rescued, %ld queued for finalization", (long) count_free, (long) count_keep, (long) count_rescue, (long) count_finalize)); #endif *out_count_keep = count_keep; }
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; }