Beispiel #1
0
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
	}
}
Beispiel #5
0
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
	}
}
Beispiel #6
0
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;
}
Beispiel #7
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;
}
Beispiel #9
0
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;
}