コード例 #1
0
ファイル: duk_heap_alloc.c プロジェクト: CarterTsai/duktape
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;
}
コード例 #2
0
ファイル: duk_heap_alloc.c プロジェクト: oleavr/duktape
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;
}