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;
}
Example #10
0
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();
}
Example #11
0
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;
}
Example #12
0
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;
}