Esempio n. 1
0
DUK_EXTERNAL duk_double_t duk_get_now(duk_context *ctx) {
	return ((duk_double_t) DUK_USE_DATE_GET_NOW((ctx)));
}
Esempio n. 2
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;

	/* Silence a few global unused warnings here. */
	DUK_UNREF(duk_str_unsupported);

	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)
	DUK_D(DUK_DPRINT("running self tests"));
	duk_selftest_run_tests();
	DUK_D(DUK_DPRINT("self tests passed"));
#endif

	/*
	 *  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;
#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 */
	res->rnd_state = (duk_uint32_t) (duk_intptr_t) res;

#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;
	}

	DUK_ASSERT(res->heap_thread != NULL);
	res->rnd_state ^= (duk_uint32_t) DUK_USE_DATE_GET_NOW((duk_context *) res->heap_thread);

	/*
	 *  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;
}
Esempio n. 3
0
/* Log frontend shared helper, magic value indicates log level.  Provides
 * frontend functions: trace(), debug(), info(), warn(), error(), fatal().
 * This needs to have small footprint, reasonable performance, minimal
 * memory churn, etc.
 */
DUK_INTERNAL duk_ret_t duk_bi_logger_prototype_log_shared(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_double_t now;
	duk_small_int_t entry_lev = duk_get_current_magic(ctx);
	duk_small_int_t logger_lev;
	duk_int_t nargs;
	duk_int_t i;
	duk_size_t tot_len;
	const duk_uint8_t *arg_str;
	duk_size_t arg_len;
	duk_uint8_t *buf, *p;
	const duk_uint8_t *q;
	duk_uint8_t date_buf[DUK_BI_DATE_ISO8601_BUFSIZE];
	duk_size_t date_len;
	duk_small_int_t rc;

	DUK_ASSERT(entry_lev >= 0 && entry_lev <= 5);
	DUK_UNREF(thr);

	/* XXX: sanitize to printable (and maybe ASCII) */
	/* XXX: better multiline */

	/*
	 *  Logger arguments are:
	 *
	 *    magic: log level (0-5)
	 *    this: logger
	 *    stack: plain log args
	 *
	 *  We want to minimize memory churn so a two-pass approach
	 *  is used: first pass formats arguments and computes final
	 *  string length, second pass copies strings either into a
	 *  pre-allocated and reused buffer (short messages) or into a
	 *  newly allocated fixed buffer.  If the backend function plays
	 *  nice, it won't coerce the buffer to a string (and thus
	 *  intern it).
	 */

	nargs = duk_get_top(ctx);

	/* [ arg1 ... argN this ] */

	/*
	 *  Log level check
	 */

	duk_push_this(ctx);

	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LC_L);
	logger_lev = (duk_small_int_t) duk_get_int(ctx, -1);
	if (entry_lev < logger_lev) {
		return 0;
	}
	/* log level could be popped but that's not necessary */

	now = DUK_USE_DATE_GET_NOW(ctx);
	duk_bi_date_format_timeval(now, date_buf);
	date_len = DUK_STRLEN((const char *) date_buf);

	duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LC_N);
	duk_to_string(ctx, -1);
	DUK_ASSERT(duk_is_string(ctx, -1));

	/* [ arg1 ... argN this loggerLevel loggerName ] */

	/*
	 *  Pass 1
	 */

	/* Line format: <time> <entryLev> <loggerName>: <msg> */

	tot_len = 0;
	tot_len += 3 +  /* separators: space, space, colon */
	           3 +  /* level string */
	           date_len +  /* time */
	           duk_get_length(ctx, -1);  /* loggerName */

	for (i = 0; i < nargs; i++) {
		/* When formatting an argument to a string, errors may happen from multiple
		 * causes.  In general we want to catch obvious errors like a toLogString()
		 * throwing an error, but we don't currently try to catch every possible
		 * error.  In particular, internal errors (like out of memory or stack) are
		 * not caught.  Also, we expect Error toString() to not throw an error.
		 */
		if (duk_is_object(ctx, i)) {
			/* duk_pcall_prop() may itself throw an error, but we're content
			 * in catching the obvious errors (like toLogString() throwing an
			 * error).
			 */
			duk_push_hstring_stridx(ctx, DUK_STRIDX_FMT);
			duk_dup(ctx, i);
			/* [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ] */
			/* call: this.fmt(arg) */
			rc = duk_pcall_prop(ctx, -5 /*obj_index*/, 1 /*nargs*/);
			if (rc) {
				/* Keep the error as the result (coercing it might fail below,
				 * but we don't catch that now).
				 */
				;
			}
			duk_replace(ctx, i);
		}
		(void) duk_to_lstring(ctx, i, &arg_len);
		tot_len++;  /* sep (even before first one) */
		tot_len += arg_len;
	}

	/*
	 *  Pass 2
	 */

	/* XXX: There used to be a shared log buffer here, but it was removed
	 * when dynamic buffer spare was removed.  The problem with using
	 * bufwriter is that, without the spare, the buffer gets passed on
	 * as an argument to the raw() call so it'd need to be resized
	 * (reallocated) anyway.  If raw() call convention is changed, this
	 * could be made more efficient.
	 */

	buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, tot_len);
	DUK_ASSERT(buf != NULL);
	p = buf;

	DUK_MEMCPY((void *) p, (void *) date_buf, date_len);
	p += date_len;
	*p++ = (duk_uint8_t) DUK_ASC_SPACE;

	q = duk__log_level_strings + (entry_lev * 3);
	DUK_MEMCPY((void *) p, (void *) q, (duk_size_t) 3);
	p += 3;

	*p++ = (duk_uint8_t) DUK_ASC_SPACE;

	arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, -2, &arg_len);
	DUK_MEMCPY((void *) p, (const void *) arg_str, arg_len);
	p += arg_len;

	*p++ = (duk_uint8_t) DUK_ASC_COLON;

	for (i = 0; i < nargs; i++) {
		*p++ = (duk_uint8_t) DUK_ASC_SPACE;

		arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &arg_len);
		DUK_ASSERT(arg_str != NULL);
		DUK_MEMCPY((void *) p, (const void *) arg_str, arg_len);
		p += arg_len;
	}
	DUK_ASSERT(buf + tot_len == p);

	/* [ arg1 ... argN this loggerLevel loggerName buffer ] */

#if defined(DUK_USE_DEBUGGER_SUPPORT) && defined(DUK_USE_DEBUGGER_FWD_LOGGING)
	/* Do debugger forwarding before raw() because the raw() function
	 * doesn't get the log level right now.
	 */
	if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
		const char *log_buf;
		duk_size_t sz_buf;
		log_buf = (const char *) duk_get_buffer(ctx, -1, &sz_buf);
		DUK_ASSERT(log_buf != NULL);
		duk_debug_write_notify(thr, DUK_DBG_CMD_LOG);
		duk_debug_write_int(thr, (duk_int32_t) entry_lev);
		duk_debug_write_string(thr, (const char *) log_buf, sz_buf);
		duk_debug_write_eom(thr);
	}
#endif

	/* Call this.raw(msg); look up through the instance allows user to override
	 * the raw() function in the instance or in the prototype for maximum
	 * flexibility.
	 */
	duk_push_hstring_stridx(ctx, DUK_STRIDX_RAW);
	duk_dup(ctx, -2);
	/* [ arg1 ... argN this loggerLevel loggerName buffer 'raw' buffer ] */
	duk_call_prop(ctx, -6, 1);  /* this.raw(buffer) */

	return 0;
}