Пример #1
0
void duk_fb_put_bytes(duk_fixedbuffer *fb, duk_uint8_t *buffer, duk_uint32_t length) {
	duk_uint32_t avail;

	avail = (fb->offset >= fb->length ? (duk_uint32_t) 0 : (duk_uint32_t) (fb->length - fb->offset));
	if (length > avail) {
		DUK_MEMCPY(fb->buffer + fb->offset, buffer, avail);
		fb->offset += avail;
		fb->truncated = 1;
	} else {
		DUK_MEMCPY(fb->buffer + fb->offset, buffer, length);
		fb->offset += length;
	}
}
Пример #2
0
static duk_hstring *duk__alloc_init_hstring(duk_heap *heap,
                                            duk_uint8_t *str,
                                            duk_uint32_t blen,
                                            duk_uint32_t strhash) {
	duk_hstring *res = NULL;
	duk_uint8_t *data;
	duk_size_t alloc_size;
	duk_uarridx_t dummy;

	/* NUL terminate for convenient C access */

	alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1);
	res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
	if (!res) {
		goto error;
	}

	DUK_MEMZERO(res, sizeof(duk_hstring));
#ifdef DUK_USE_EXPLICIT_NULL_INIT
	DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
	DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0);

	if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) {
		DUK_HSTRING_SET_ARRIDX(res);
	}

	/* All strings beginning with 0xff are treated as "internal",
	 * even strings interned by the user.  This allows user code to
	 * create internal properties too, and makes behavior consistent
	 * in case user code happens to use a string also used by Duktape
	 * (such as string has already been interned and has the 'internal'
	 * flag set).
	 */
	if (blen > 0 && str[0] == (duk_uint8_t) 0xff) {
		DUK_HSTRING_SET_INTERNAL(res);
	}

	res->hash = strhash;
	res->blen = blen;
	res->clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen);  /* clen <= blen */

	data = (duk_uint8_t *) (res + 1);
	DUK_MEMCPY(data, str, blen);
	data[blen] = (duk_uint8_t) 0;

	DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld",
	                     (unsigned long) DUK_HSTRING_GET_HASH(res),
	                     (long) DUK_HSTRING_GET_BYTELEN(res),
	                     (long) DUK_HSTRING_GET_CHARLEN(res),
	                     (long) DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0));

	return res;

 error:
	DUK_FREE(heap, res);
	return NULL;
}
Пример #3
0
DUK_LOCAL duk_uint8_t *duk__load_buffer_raw(duk_context *ctx, duk_uint8_t *p) {
	duk_uint32_t len;
	duk_uint8_t *buf;

	len = DUK_RAW_READ_U32_BE(p);
	buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len);
	DUK_ASSERT(buf != NULL);
	DUK_MEMCPY((void *) buf, (const void *) p, (size_t) len);
	p += len;
	return p;
}
DUK_INTERNAL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffer, duk_size_t length) {
	duk_size_t avail;
	duk_size_t copylen;

	avail = (fb->offset >= fb->length ? (duk_size_t) 0 : (duk_size_t) (fb->length - fb->offset));
	if (length > avail) {
		copylen = avail;
		fb->truncated = 1;
	} else {
		copylen = length;
	}
	DUK_MEMCPY(fb->buffer + fb->offset, buffer, copylen);
	fb->offset += copylen;
}
Пример #5
0
DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) {
	duk_size_t len;
	duk_uint32_t tmp32;

	DUK_ASSERT(h != NULL);

	len = DUK_HSTRING_GET_BYTELEN(h);
	DUK_ASSERT(len <= 0xffffffffUL);  /* string limits */
	tmp32 = (duk_uint32_t) len;
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	DUK_MEMCPY((void *) p,
	           (const void *) DUK_HSTRING_GET_DATA(h),
	           len);
	p += len;
	return p;
}
Пример #6
0
static duk_hstring *duk__alloc_init_hstring(duk_heap *heap,
                                            duk_uint8_t *str,
                                            duk_uint32_t blen,
                                            duk_uint32_t strhash) {
	duk_hstring *res = NULL;
	duk_uint8_t *data;
	duk_uint32_t alloc_size;
	duk_uint32_t dummy;

	/* NUL terminate for convenient C access */

	alloc_size = sizeof(duk_hstring) + blen + 1;
	res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
	if (!res) {
		goto error;
	}

	DUK_MEMZERO(res, sizeof(duk_hstring));
#ifdef DUK_USE_EXPLICIT_NULL_INIT
	DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
	DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0);

	if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) {
		DUK_HSTRING_SET_ARRIDX(res);
	}

	res->hash = strhash;
	res->blen = blen;
	res->clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen);  /* clen <= blen */

	data = (duk_uint8_t *) (res + 1);
	DUK_MEMCPY(data, str, blen);
	data[blen] = (duk_uint8_t) 0;

	DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08x, blen=%d, clen=%d, arridx=%d",
	                     DUK_HSTRING_GET_HASH(res),
	                     DUK_HSTRING_GET_BYTELEN(res),
	                     DUK_HSTRING_GET_CHARLEN(res),
	                     DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0));

	return res;

 error:
	DUK_FREE(heap, res);
	return NULL;
}
Пример #7
0
DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, duk_hbuffer *h) {
	duk_size_t len;
	duk_uint32_t tmp32;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(h != NULL);
	DUK_UNREF(thr);

	len = DUK_HBUFFER_GET_SIZE(h);
	DUK_ASSERT(len <= 0xffffffffUL);  /* buffer limits */
	tmp32 = (duk_uint32_t) len;
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	DUK_MEMCPY((void *) p,
	           (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
	           len);
	p += len;
	return p;
}
Пример #8
0
void duk_hbuffer_insert_bytes(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t offset, duk_uint8_t *data, size_t length) {
	char *p;

	/* XXX: allow inserts with offset > curr_size? i.e., insert zeroes automatically? */

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(buf != NULL);
	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf));
	DUK_ASSERT_DISABLE(offset >= 0);  /* unsigned, so always true */
	DUK_ASSERT(offset <= DUK_HBUFFER_GET_SIZE(buf));  /* equality is OK (= append) */
	DUK_ASSERT(data != NULL);
	DUK_ASSERT_DISABLE(length >= 0);  /* unsigned, so always true */

	if (length == 0) {
		return;
	}

	if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) {
		duk_hbuffer_resize(thr,
		                   buf,
		                   DUK_HBUFFER_GET_SIZE(buf),
		                   duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length));
	}
	DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length);

	p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf);
	if (offset < DUK_HBUFFER_GET_SIZE(buf)) {
		/* not an append */

		DUK_ASSERT(DUK_HBUFFER_GET_SIZE(buf) - offset > 0);  /* not a zero byte memmove */
		DUK_MEMMOVE((void *) (p + offset + length),
		            (void *) (p + offset),
		            DUK_HBUFFER_GET_SIZE(buf) - offset);
	}

	DUK_MEMCPY((void *) (p + offset),
	           data,
	           length);

	buf->size += length;
}
Пример #9
0
void duk_to_fixed_buffer(duk_context *ctx, duk_idx_t index) {
	duk_hbuffer_dynamic *h_src;
	duk_uint8_t *data;
	duk_size_t size;

	index = duk_require_normalize_index(ctx, index);

	h_src = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index);
	DUK_ASSERT(h_src != NULL);
	if (!DUK_HBUFFER_HAS_DYNAMIC(h_src)) {
		return;
	}

	size = DUK_HBUFFER_GET_SIZE(h_src);
	data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, size);
	if (size > 0U) {
		DUK_ASSERT(data != NULL);
		DUK_MEMCPY(data, DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(h_src), size);
	}

	duk_replace(ctx, index);
}
Пример #10
0
/* Get local time offset (in seconds) for a certain (UTC) instant 'd'. */
DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) {
	time_t t, t1, t2;
	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
	struct tm tms[2];
#ifdef DUK_USE_DATE_TZO_GMTIME
	struct tm *tm_ptr;
#endif

	/* For NaN/inf, the return value doesn't matter. */
	if (!DUK_ISFINITE(d)) {
		return 0;
	}

	/* If not within Ecmascript range, some integer time calculations
	 * won't work correctly (and some asserts will fail), so bail out
	 * if so.  This fixes test-bug-date-insane-setyear.js.  There is
	 * a +/- 24h leeway in this range check to avoid a test262 corner
	 * case documented in test-bug-date-timeval-edges.js.
	 */
	if (!duk_bi_date_timeval_in_leeway_range(d)) {
		DUK_DD(DUK_DDPRINT("timeval not within valid range, skip tzoffset computation to avoid integer overflows"));
		return 0;
	}

	/*
	 *  This is a bit tricky to implement portably.  The result depends
	 *  on the timestamp (specifically, DST depends on the timestamp).
	 *  If e.g. UNIX APIs are used, they'll have portability issues with
	 *  very small and very large years.
	 *
	 *  Current approach:
	 *
	 *  - Stay within portable UNIX limits by using equivalent year mapping.
	 *    Avoid year 1970 and 2038 as some conversions start to fail, at
	 *    least on some platforms.  Avoiding 1970 means that there are
	 *    currently DST discrepancies for 1970.
	 *
	 *  - Create a UTC and local time breakdowns from 't'.  Then create
	 *    a time_t using gmtime() and localtime() and compute the time
	 *    difference between the two.
	 *
	 *  Equivalent year mapping (E5 Section 15.9.1.8):
	 *
	 *    If the host environment provides functionality for determining
	 *    daylight saving time, the implementation of ECMAScript is free
	 *    to map the year in question to an equivalent year (same
	 *    leap-year-ness and same starting week day for the year) for which
	 *    the host environment provides daylight saving time information.
	 *    The only restriction is that all equivalent years should produce
	 *    the same result.
	 *
	 *  This approach is quite reasonable but not entirely correct, e.g.
	 *  the specification also states (E5 Section 15.9.1.8):
	 *
	 *    The implementation of ECMAScript should not try to determine
	 *    whether the exact time was subject to daylight saving time, but
	 *    just whether daylight saving time would have been in effect if
	 *    the _current daylight saving time algorithm_ had been used at the
	 *    time.  This avoids complications such as taking into account the
	 *    years that the locale observed daylight saving time year round.
	 *
	 *  Since we rely on the platform APIs for conversions between local
	 *  time and UTC, we can't guarantee the above.  Rather, if the platform
	 *  has historical DST rules they will be applied.  This seems to be the
	 *  general preferred direction in Ecmascript standardization (or at least
	 *  implementations) anyway, and even the equivalent year mapping should
	 *  be disabled if the platform is known to handle DST properly for the
	 *  full Ecmascript range.
	 *
	 *  The following has useful discussion and links:
	 *
	 *    https://bugzilla.mozilla.org/show_bug.cgi?id=351066
	 */

	duk_bi_date_timeval_to_parts(d, parts, dparts, DUK_DATE_FLAG_EQUIVYEAR /*flags*/);
	DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] >= 1970 && parts[DUK_DATE_IDX_YEAR] <= 2038);

	d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
	DUK_ASSERT(d >= 0 && d < 2147483648.0 * 1000.0);  /* unsigned 31-bit range */
	t = (time_t) (d / 1000.0);
	DUK_DDD(DUK_DDDPRINT("timeval: %lf -> time_t %ld", (double) d, (long) t));

	t1 = t;

	DUK_MEMZERO((void *) tms, sizeof(struct tm) * 2);

#if defined(DUK_USE_DATE_TZO_GMTIME_R)
	(void) gmtime_r(&t, &tms[0]);
	(void) localtime_r(&t, &tms[1]);
#elif defined(DUK_USE_DATE_TZO_GMTIME)
	tm_ptr = gmtime(&t);
	DUK_MEMCPY((void *) &tms[0], tm_ptr, sizeof(struct tm));
	tm_ptr = localtime(&t);
	DUK_MEMCPY((void *) &tms[1], tm_ptr, sizeof(struct tm));
#else
#error internal error
#endif
	DUK_DDD(DUK_DDDPRINT("gmtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld,"
	                     "wday:%ld,yday:%ld,isdst:%ld}",
	                     (long) tms[0].tm_sec, (long) tms[0].tm_min, (long) tms[0].tm_hour,
	                     (long) tms[0].tm_mday, (long) tms[0].tm_mon, (long) tms[0].tm_year,
	                     (long) tms[0].tm_wday, (long) tms[0].tm_yday, (long) tms[0].tm_isdst));
	DUK_DDD(DUK_DDDPRINT("localtime result: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld,"
	                     "wday:%ld,yday:%ld,isdst:%ld}",
	                     (long) tms[1].tm_sec, (long) tms[1].tm_min, (long) tms[1].tm_hour,
	                     (long) tms[1].tm_mday, (long) tms[1].tm_mon, (long) tms[1].tm_year,
	                     (long) tms[1].tm_wday, (long) tms[1].tm_yday, (long) tms[1].tm_isdst));

	/* tm_isdst is both an input and an output to mktime(), use 0 to
	 * avoid DST handling in mktime():
	 * - https://github.com/svaarala/duktape/issues/406
	 * - http://stackoverflow.com/questions/8558919/mktime-and-tm-isdst
	 */
	tms[0].tm_isdst = 0;
	tms[1].tm_isdst = 0;
	t1 = mktime(&tms[0]);  /* UTC */
	t2 = mktime(&tms[1]);  /* local */
	if (t1 == (time_t) -1 || t2 == (time_t) -1) {
		/* This check used to be for (t < 0) but on some platforms
		 * time_t is unsigned and apparently the proper way to detect
		 * an mktime() error return is the cast above.  See e.g.:
		 * http://pubs.opengroup.org/onlinepubs/009695299/functions/mktime.html
		 */
		goto error;
	}
	DUK_DDD(DUK_DDDPRINT("t1=%ld (utc), t2=%ld (local)", (long) t1, (long) t2));

	/* Compute final offset in seconds, positive if local time ahead of
	 * UTC (returned value is UTC-to-local offset).
	 *
	 * difftime() returns a double, so coercion to int generates quite
	 * a lot of code.  Direct subtraction is not portable, however.
	 * XXX: allow direct subtraction on known platforms.
	 */
#if 0
	return (duk_int_t) (t2 - t1);
#endif
	return (duk_int_t) difftime(t2, t1);

 error:
	/* XXX: return something more useful, so that caller can throw? */
	DUK_D(DUK_DPRINT("mktime() failed, d=%lf", (double) d));
	return 0;
}
Пример #11
0
void duk_hbuffer_insert_slice(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t dst_offset, size_t src_offset, size_t length) {
	char *p;
	size_t src_end_offset;  /* source end (exclusive) in initial buffer */
	size_t len;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(buf != NULL);
	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf));
	DUK_ASSERT_DISABLE(dst_offset >= 0);                           /* always true */
	DUK_ASSERT(dst_offset <= DUK_HBUFFER_GET_SIZE(buf));           /* allow equality */
	DUK_ASSERT_DISABLE(src_offset >= 0);                           /* always true */
	DUK_ASSERT(src_offset <= DUK_HBUFFER_GET_SIZE(buf));           /* allow equality */
	DUK_ASSERT_DISABLE(length >= 0);                               /* always true */
	DUK_ASSERT(src_offset + length <= DUK_HBUFFER_GET_SIZE(buf));  /* allow equality */

	if (length == 0) {
		return;
	}

	if (DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) < length) {
		duk_hbuffer_resize(thr,
		                   buf,
		                   DUK_HBUFFER_GET_SIZE(buf),
		                   duk__add_spare(DUK_HBUFFER_GET_SIZE(buf) + length));
	}
	DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_SPARE_SIZE(buf) >= length);

	p = (char *) DUK_HBUFFER_DYNAMIC_GET_CURR_DATA_PTR(buf);

	/*
	 *  src_offset and dst_offset refer to the state of the buffer
	 *  before any changes are made.  This must be taken into account
	 *  when moving data around; in particular, the source data may
	 *  "straddle" the dst_offset, so the insert may need to be handled
	 *  in two pieces.
	 */

	src_end_offset = src_offset + length;

	/* create a hole for the insert */
	len = DUK_HBUFFER_GET_SIZE(buf) - dst_offset;
	if (len > 0) {
		DUK_MEMMOVE(p + dst_offset + length,
		            p + dst_offset,
		            len);
	}

	if (src_offset < dst_offset) {
		if (src_end_offset <= dst_offset) {
			/* entire source is before 'dst_offset' */
			DUK_MEMCPY(p + dst_offset,
			           p + src_offset,
			           length);
		} else {
			/* part of the source is before 'dst_offset'; straddles */
			len = dst_offset - src_offset;
			DUK_ASSERT(len >= 1 && len < length);
			DUK_ASSERT(length - len >= 1);
			DUK_MEMCPY(p + dst_offset,
			           p + src_offset,
			           len);
			DUK_MEMCPY(p + dst_offset + len,
			           p + src_offset + length + len,  /* take above memmove() into account */
			           length - len);
		}
	} else {
		/* entire source is after 'dst_offset' */
		DUK_MEMCPY(p + dst_offset,
		           p + src_offset + length,  /* take above memmove() into account */
		           length);
	}

	buf->size += length;
}
Пример #12
0
int duk_bi_buffer_constructor(duk_context *ctx) {
	duk_size_t buf_size;
	duk_small_int_t buf_dynamic;
	duk_uint8_t *buf_data;
	const duk_uint8_t *src_data;
	duk_hobject *h_obj;

	/*
	 *  Constructor arguments are currently somewhat compatible with
	 *  (keep it that way if possible):
	 *
	 *    http://nodejs.org/api/buffer.html
	 *
	 */

	buf_dynamic = duk_get_boolean(ctx, 1);  /* default to false */

	switch (duk_get_type(ctx, 0)) {
	case DUK_TYPE_NUMBER:
		/* new buffer of specified size */
		buf_size = (duk_size_t) duk_to_int(ctx, 0);
		(void) duk_push_buffer(ctx, buf_size, buf_dynamic);
		break;
	case DUK_TYPE_BUFFER:
		/* return input buffer, converted to a Buffer object if called as a
		 * constructor (no change if called as a function).
		 */
		duk_set_top(ctx, 1);
		break;
	case DUK_TYPE_STRING:
		/* new buffer with string contents */
		src_data = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &buf_size);
		DUK_ASSERT(src_data != NULL);  /* even for zero-length string */
		buf_data = (duk_uint8_t *) duk_push_buffer(ctx, buf_size, buf_dynamic);
		DUK_MEMCPY((void *) buf_data, (const void *) src_data, (size_t) buf_size);
		break;
	case DUK_TYPE_OBJECT:
		/* Buffer object: get the plain buffer inside.  If called as as
		 * constructor, a new Buffer object pointing to the same plain
		 * buffer is created below.
		 */
		h_obj = duk_get_hobject(ctx, 0);
		DUK_ASSERT(h_obj != NULL);
		if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) != DUK_HOBJECT_CLASS_BUFFER) {
			return DUK_RET_TYPE_ERROR;
		}
		duk_get_prop_stridx(ctx, 0, DUK_STRIDX_INT_VALUE);
		DUK_ASSERT(duk_is_buffer(ctx, -1));
		break;
	case DUK_TYPE_NONE:
	default:
		return DUK_RET_TYPE_ERROR;
	}

	/* stack is unbalanced, but: [ <something> buf ] */

	if (duk_is_constructor_call(ctx)) {
		duk_push_object_helper(ctx,
		                       DUK_HOBJECT_FLAG_EXTENSIBLE |
		                       DUK_HOBJECT_FLAG_EXOTIC_BUFFEROBJ |
		                       DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
		                       DUK_BIDX_BUFFER_PROTOTYPE);

		/* Buffer object internal value is immutable */
		duk_dup(ctx, -2);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
	}
	/* Note: unbalanced stack on purpose */

	return 1;
}
Пример #13
0
static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
	duk_hthread *thr;
	duk_tval *tv, *tv_end;
	duk_instr_t *ins, *ins_end;
	duk_hobject **fn, **fn_end;
	duk_hstring *h_str;
	duk_uint32_t count_instr;
	duk_uint32_t tmp32;
	duk_uint16_t tmp16;
	duk_double_t d;

	thr = (duk_hthread *) ctx;
	DUK_UNREF(ctx);
	DUK_UNREF(thr);

	DUK_DD(DUK_DDPRINT("dumping function %p to %p: "
	                   "consts=[%p,%p[ (%ld bytes, %ld items), "
	                   "funcs=[%p,%p[ (%ld bytes, %ld items), "
	                   "code=[%p,%p[ (%ld bytes, %ld items)",
	                   (void *) func,
	                   (void *) p,
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func),
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func),
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func),
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func),
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func),
	                   (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(thr->heap, func),
	                   (long) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func)));

	DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL);  /* ensures no overflow */
	count_instr = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func);
	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p);

	/* Fixed header info. */
	tmp32 = count_instr;
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func);
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func);
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	tmp16 = func->nregs;
	DUK_RAW_WRITE_U16_BE(p, tmp16);
	tmp16 = func->nargs;
	DUK_RAW_WRITE_U16_BE(p, tmp16);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
	tmp32 = func->start_line;
	DUK_RAW_WRITE_U32_BE(p, tmp32);
	tmp32 = func->end_line;
	DUK_RAW_WRITE_U32_BE(p, tmp32);
#else
	DUK_RAW_WRITE_U32_BE(p, 0);
	DUK_RAW_WRITE_U32_BE(p, 0);
#endif
	tmp32 = ((duk_heaphdr *) func)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK;
	DUK_RAW_WRITE_U32_BE(p, tmp32);

	/* Bytecode instructions: endian conversion needed unless
	 * platform is big endian.
	 */
	ins = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func);
	ins_end = DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func);
	DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr);
#if defined(DUK_USE_INTEGER_BE)
	DUK_MEMCPY((void *) p, (const void *) ins, (size_t) (ins_end - ins));
	p += (size_t) (ins_end - ins);
#else
	while (ins != ins_end) {
		tmp32 = (duk_uint32_t) (*ins);
		DUK_RAW_WRITE_U32_BE(p, tmp32);
		ins++;
	}
#endif

	/* Constants: variable size encoding. */
	tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func);
	tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func);
	while (tv != tv_end) {
		/* constants are strings or numbers now */
		DUK_ASSERT(DUK_TVAL_IS_STRING(tv) ||
		           DUK_TVAL_IS_NUMBER(tv));

		if (DUK_TVAL_IS_STRING(tv)) {
			h_str = DUK_TVAL_GET_STRING(tv);
			DUK_ASSERT(h_str != NULL);
			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p),
			*p++ = DUK__SER_STRING;
			p = duk__dump_hstring_raw(p, h_str);
		} else {
			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p);
			*p++ = DUK__SER_NUMBER;
			d = DUK_TVAL_GET_NUMBER(tv);
			DUK_RAW_WRITE_DOUBLE_BE(p, d);
		}
		tv++;
	}

	/* Inner functions recursively. */
	fn = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func);
	fn_end = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func);
	while (fn != fn_end) {
		/* XXX: This causes recursion up to inner function depth
		 * which is normally not an issue, e.g. mark-and-sweep uses
		 * a recursion limiter to avoid C stack issues.  Avoiding
		 * this would mean some sort of a work list or just refusing
		 * to serialize deep functions.
		 */
		DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(*fn));
		p = duk__dump_func(ctx, (duk_hcompiledfunction *) *fn, bw_ctx, p);
		fn++;
	}

	/* Object extra properties.
	 *
	 * There are some difference between function templates and functions.
	 * For example, function templates don't have .length and nargs is
	 * normally used to instantiate the functions.
	 */

	p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs);
	p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME);
	p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME);
	p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE);
	p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func);
	p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func);

	DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p));

	return p;
}
Пример #14
0
DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) {
	duk_context *ctx;
	duk_hobject *h1;
#if defined(DUK_USE_ROM_GLOBAL_CLONE)
	duk_hobject *h2;
	duk_uint8_t *props;
	duk_size_t alloc_size;
#endif

	ctx = (duk_context *) thr;

	/* XXX: refactor into internal helper, duk_clone_hobject() */

#if defined(DUK_USE_ROM_GLOBAL_INHERIT)
	/* Inherit from ROM-based global object: less RAM usage, less transparent. */
	duk_push_object_helper(ctx,
	                       DUK_HOBJECT_FLAG_EXTENSIBLE |
	                       DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
	                       DUK_BIDX_GLOBAL);
	h1 = duk_get_hobject(ctx, -1);
	DUK_ASSERT(h1 != NULL);
#elif defined(DUK_USE_ROM_GLOBAL_CLONE)
	/* Clone the properties of the ROM-based global object to create a
	 * fully RAM-based global object.  Uses more memory than the inherit
	 * model but more compliant.
	 */
	duk_push_object_helper(ctx,
	                       DUK_HOBJECT_FLAG_EXTENSIBLE |
	                       DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
	                       DUK_BIDX_OBJECT_PROTOTYPE);
	h1 = duk_get_hobject(ctx, -1);
	DUK_ASSERT(h1 != NULL);
	h2 = thr->builtins[DUK_BIDX_GLOBAL];
	DUK_ASSERT(h2 != NULL);

	/* Copy the property table verbatim; this handles attributes etc.
	 * For ROM objects it's not necessary (or possible) to update
	 * refcounts so leave them as is.
	 */
	alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h2);
	DUK_ASSERT(alloc_size > 0);
	props = DUK_ALLOC(thr->heap, alloc_size);
	if (!props) {
		DUK_ERROR_ALLOC_FAILED(thr);
		return;
	}
	DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h2) != NULL);
	DUK_MEMCPY((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h2), alloc_size);

	/* XXX: keep property attributes or tweak them here?
	 * Properties will now be non-configurable even when they're
	 * normally configurable for the global object.
	 */

	DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h1) == NULL);
	DUK_HOBJECT_SET_PROPS(thr->heap, h1, props);
	DUK_HOBJECT_SET_ESIZE(h1, DUK_HOBJECT_GET_ESIZE(h2));
	DUK_HOBJECT_SET_ENEXT(h1, DUK_HOBJECT_GET_ENEXT(h2));
	DUK_HOBJECT_SET_ASIZE(h1, DUK_HOBJECT_GET_ASIZE(h2));
	DUK_HOBJECT_SET_HSIZE(h1, DUK_HOBJECT_GET_HSIZE(h2));
#else
#error internal error in defines
#endif

	duk_hobject_compact_props(thr, h1);
	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
	DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL]));  /* no need to decref */
	thr->builtins[DUK_BIDX_GLOBAL] = h1;
	DUK_HOBJECT_INCREF(thr, h1);
	DUK_D(DUK_DPRINT("duplicated global object: %!O", h1));


	/* Create a fresh object environment for the global scope.  This is
	 * needed so that the global scope points to the newly created RAM-based
	 * global object.
	 */
	duk_push_object_helper(ctx,
	                       DUK_HOBJECT_FLAG_EXTENSIBLE |
	                       DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV),
	                       -1);  /* no prototype */
	h1 = duk_get_hobject(ctx, -1);
	DUK_ASSERT(h1 != NULL);
	duk_dup(ctx, -2);
	duk_dup(ctx, -1);  /* -> [ ... new_global new_globalenv new_global new_global ] */
	duk_xdef_prop_stridx(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
	duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE);  /* always provideThis=true */

	duk_hobject_compact_props(thr, h1);
	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL);
	DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV]));  /* no need to decref */
	thr->builtins[DUK_BIDX_GLOBAL_ENV] = h1;
	DUK_HOBJECT_INCREF(thr, h1);
	DUK_D(DUK_DPRINT("duplicated global env: %!O", h1));

	duk_pop_2(ctx);
}
Пример #15
0
static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t *p_end) {
	duk_hthread *thr;
	duk_hcompiledfunction *h_fun;
	duk_hbuffer *h_data;
	duk_size_t data_size;
	duk_uint32_t count_instr, count_const, count_funcs;
	duk_uint32_t n;
	duk_uint32_t tmp32;
	duk_small_uint_t const_type;
	duk_uint8_t *fun_data;
	duk_uint8_t *q;
	duk_idx_t idx_base;
	duk_tval *tv;
	duk_uarridx_t arr_idx;

	/* XXX: There's some overlap with duk_js_closure() here, but
	 * seems difficult to share code.  Ensure that the final function
	 * looks the same as created by duk_js_closure().
	 */

	DUK_ASSERT(ctx != NULL);
	thr = (duk_hthread *) ctx;

	DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (void *) p, (void *) p_end));

	DUK__ASSERT_LEFT(3 * 4);
	count_instr = DUK_RAW_READ_U32_BE(p);
	count_const = DUK_RAW_READ_U32_BE(p);
	count_funcs = DUK_RAW_READ_U32_BE(p);

	data_size = sizeof(duk_tval) * count_const +
	            sizeof(duk_hobject *) * count_funcs +
	            sizeof(duk_instr_t) * count_instr;

	DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld",
	                   (long) count_instr, (long) count_const,
	                   (long) count_const, (long) data_size));

	/* Value stack is used to ensure reachability of constants and
	 * inner functions being loaded.  Require enough space to handle
	 * large functions correctly.
	 */
	duk_require_stack(ctx, 2 + count_const + count_funcs);
	idx_base = duk_get_top(ctx);

	/* Push function object, init flags etc.  This must match
	 * duk_js_push_closure() quite carefully.
	 */
	duk_push_compiledfunction(ctx);
	h_fun = duk_get_hcompiledfunction(ctx, -1);
	DUK_ASSERT(h_fun != NULL);
	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) h_fun));
	DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, h_fun) == NULL);
	DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr->heap, h_fun) == NULL);
	DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr->heap, h_fun) == NULL);

	h_fun->nregs = DUK_RAW_READ_U16_BE(p);
	h_fun->nargs = DUK_RAW_READ_U16_BE(p);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
	h_fun->start_line = DUK_RAW_READ_U32_BE(p);
	h_fun->end_line = DUK_RAW_READ_U32_BE(p);
#else
	p += 8;  /* skip line info */
#endif

	/* duk_hcompiledfunction flags; quite version specific */
	tmp32 = DUK_RAW_READ_U32_BE(p);
	DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32);

	/* standard prototype */
	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);

	/* assert just a few critical flags */
	DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&h_fun->obj));
	DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(&h_fun->obj));
	DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(&h_fun->obj));
	DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj));
	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj));
	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj));
	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj));

	/* Create function 'data' buffer but don't attach it yet. */
	fun_data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, data_size);
	DUK_ASSERT(fun_data != NULL);

	/* Load bytecode instructions. */
	DUK_ASSERT(sizeof(duk_instr_t) == 4);
	DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t));
#if defined(DUK_USE_INTEGER_BE)
	q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
	DUK_MEMCPY((void *) q,
	           (const void *) p,
	           sizeof(duk_instr_t) * count_instr);
	p += sizeof(duk_instr_t) * count_instr;
#else
	q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
	for (n = count_instr; n > 0; n--) {
		*((duk_instr_t *) (void *) q) = DUK_RAW_READ_U32_BE(p);
		q += sizeof(duk_instr_t);
	}
#endif

	/* Load constants onto value stack but don't yet copy to buffer. */
	for (n = count_const; n > 0; n--) {
		DUK__ASSERT_LEFT(1);
		const_type = DUK_RAW_READ_U8(p);
		switch (const_type) {
		case DUK__SER_STRING: {
			p = duk__load_string_raw(ctx, p);
			break;
		}
		case DUK__SER_NUMBER: {
			/* Important to do a fastint check so that constants are
			 * properly read back as fastints.
			 */
			duk_tval tv_tmp;
			duk_double_t val;
			DUK__ASSERT_LEFT(8);
			val = DUK_RAW_READ_DOUBLE_BE(p);
			DUK_TVAL_SET_NUMBER_CHKFAST(&tv_tmp, val);
			duk_push_tval(ctx, &tv_tmp);
			break;
		}
		default: {
			goto format_error;
		}
		}
	}

	/* Load inner functions to value stack, but don't yet copy to buffer. */
	for (n = count_funcs; n > 0; n--) {
		p = duk__load_func(ctx, p, p_end);
		if (p == NULL) {
			goto format_error;
		}
	}

	/* With constants and inner functions on value stack, we can now
	 * atomically finish the function 'data' buffer, bump refcounts,
	 * etc.
	 *
	 * Here we take advantage of the value stack being just a duk_tval
	 * array: we can just memcpy() the constants as long as we incref
	 * them afterwards.
	 */

	h_data = (duk_hbuffer *) duk_get_hbuffer(ctx, idx_base + 1);
	DUK_ASSERT(h_data != NULL);
	DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data));
	DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_fun, h_data);
	DUK_HBUFFER_INCREF(thr, h_data);

	tv = duk_get_tval(ctx, idx_base + 2);  /* may be NULL if no constants or inner funcs */
	DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv != NULL);

	q = fun_data;
	if (count_const > 0) {
		/* Explicit zero size check to avoid NULL 'tv'. */
		DUK_MEMCPY((void *) q, (const void *) tv, sizeof(duk_tval) * count_const);
		for (n = count_const; n > 0; n--) {
			DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q);  /* no side effects */
			q += sizeof(duk_tval);
		}
		tv += count_const;
	}

	DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q);
	for (n = count_funcs; n > 0; n--) {
		duk_hobject *h_obj;

		DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
		h_obj = DUK_TVAL_GET_OBJECT(tv);
		DUK_ASSERT(h_obj != NULL);
		tv++;
		DUK_HOBJECT_INCREF(thr, h_obj);

		*((duk_hobject **) (void *) q) = h_obj;
		q += sizeof(duk_hobject *);
	}

	DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q);

	/* The function object is now reachable and refcounts are fine,
	 * so we can pop off all the temporaries.
	 */
	DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(ctx, idx_base)));
	duk_set_top(ctx, idx_base + 1);

	/* Setup function properties. */
	tmp32 = DUK_RAW_READ_U32_BE(p);
	duk_push_u32(ctx, tmp32);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);

	p = duk__load_string_raw(ctx, p);
	if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) {
		/* Original function instance/template had NAMEBINDING.
		 * Must create a lexical environment on loading to allow
		 * recursive functions like 'function foo() { foo(); }'.
		 */
		duk_hobject *proto;

		proto = thr->builtins[DUK_BIDX_GLOBAL_ENV];
		(void) duk_push_object_helper_proto(ctx,
		                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
		                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
		                                    proto);
		duk_dup(ctx, -2);                                 /* -> [ func funcname env funcname ] */
		duk_dup(ctx, idx_base);                           /* -> [ func funcname env funcname func ] */
		duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE);  /* -> [ func funcname env ] */
		duk_xdef_prop_stridx(ctx, idx_base, DUK_STRIDX_INT_LEXENV, DUK_PROPDESC_FLAGS_WC);
		/* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it
		 * will be ignored anyway
		 */
	}
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);

	p = duk__load_string_raw(ctx, p);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC);

	duk_push_object(ctx);
	duk_dup(ctx, -2);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* func.prototype.constructor = func */
	duk_compact(ctx, -1);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);

	p = duk__load_buffer_raw(ctx, p);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);

	duk_push_object(ctx);  /* _Varmap */
	for (;;) {
		/* XXX: awkward */
		p = duk__load_string_raw(ctx, p);
		if (duk_get_length(ctx, -1) == 0) {
			duk_pop(ctx);
			break;
		}
		tmp32 = DUK_RAW_READ_U32_BE(p);
		duk_push_u32(ctx, tmp32);
		duk_put_prop(ctx, -3);
	}
	duk_compact(ctx, -1);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);

	duk_push_array(ctx);  /* _Formals */
	for (arr_idx = 0; ; arr_idx++) {
		/* XXX: awkward */
		p = duk__load_string_raw(ctx, p);
		if (duk_get_length(ctx, -1) == 0) {
			duk_pop(ctx);
			break;
		}
		duk_put_prop_index(ctx, -2, arr_idx);
	}
	duk_compact(ctx, -1);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);

	/* Return with final function pushed on stack top. */
	DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(ctx, -1)));
	DUK_ASSERT_TOP(ctx, idx_base + 1);
	return p;

 format_error:
	return NULL;
}
Пример #16
0
/* Get local time offset (in seconds) for a certain (UTC) instant 'd'. */
static int get_local_tzoffset_gmtime(double d) {
	time_t t, t1, t2;
	int parts[NUM_PARTS];
	double dparts[NUM_PARTS];
	struct tm tms[2];
#ifdef DUK_USE_DATE_TZO_GMTIME
	struct tm *tm_ptr;
#endif

	/* For NaN/inf, the return value doesn't matter. */
	if (!DUK_ISFINITE(d)) {
		return 0;
	}

	/*
	 *  This is a bit tricky to implement portably.  The result depends
	 *  on the timestamp (specifically, DST depends on the timestamp).
	 *  If e.g. UNIX APIs are used, they'll have portability issues with
	 *  very small and very large years.
	 *
	 *  Current approach:
	 *
	 *  - Clamp year to stay within portable UNIX limits.  Avoid 2038 as
	 *    some conversions start to fail.  Avoid 1970, as some conversions
	 *    in January 1970 start to fail (verified in practice).
	 *
	 *  - Create a UTC time breakdown from 't', and then pretend it is a
	 *    local time breakdown and build a UTC time from it.  The timestamp
	 *    will effectively shift backwards by time the time offset (e.g. -2h
	 *    or -3h for EET/EEST).  Convert with mktime() twice to get the DST
	 *    flag for the final conversion.
	 *
	 *  FIXME: this is probably not entirely correct nor clear, but is
	 *  good enough for now.
	 */

	timeval_to_parts(d, parts, dparts, 0 /*flags*/);

	/*
	 *  FIXME: must choose 'equivalent year', E5 Section 15.9.1.8, instead
	 *  of just clamping.
	 */
	if (parts[IDX_YEAR] < 1971) {
		dparts[IDX_YEAR] = 1971.0;
	} else if (parts[IDX_YEAR] > 2037) {
		dparts[IDX_YEAR] = 2037.0;
	}

	d = get_timeval_from_dparts(dparts, 0 /*flags*/);
	DUK_ASSERT(d >= 0 && d < 2147483648.0 * 1000.0);  /* unsigned 31-bit range */
	t = (size_t) (d / 1000.0);
	DUK_DDDPRINT("timeval: %lf -> time_t %d", d, (int) t);

	t1 = t;

	DUK_MEMSET((void *) tms, 0, sizeof(struct tm) * 2);

#if defined(DUK_USE_DATE_TZO_GMTIME_R)
	(void) gmtime_r(&t, &tms[0]);
#elif defined(DUK_USE_DATE_TZO_GMTIME)
	tm_ptr = gmtime(&t);
	DUK_MEMCPY((void *) &tms[0], tm_ptr, sizeof(struct tm));
#else
#error internal error
#endif
	DUK_MEMCPY((void *) &tms[1], &tms[0], sizeof(struct tm));

	DUK_DDDPRINT("before mktime: tm={sec:%d,min:%d,hour:%d,mday:%d,mon:%d,year:%d,"
	             "wday:%d,yday:%d,isdst:%d}",
	             (int) tms[0].tm_sec, (int) tms[0].tm_min, (int) tms[0].tm_hour,
	             (int) tms[0].tm_mday, (int) tms[0].tm_mon, (int) tms[0].tm_year,
	             (int) tms[0].tm_wday, (int) tms[0].tm_yday, (int) tms[0].tm_isdst);

	(void) mktime(&tms[0]);
	tms[1].tm_isdst = tms[0].tm_isdst;
	t2 = mktime(&tms[1]);
	DUK_ASSERT(t2 >= 0);
	if (t2 < 0) {
		goto error;
	}

	DUK_DDDPRINT("after mktime: tm={sec:%d,min:%d,hour:%d,mday:%d,mon:%d,year:%d,"
	             "wday:%d,yday:%d,isdst:%d}",
	             (int) tms[1].tm_sec, (int) tms[1].tm_min, (int) tms[1].tm_hour,
	             (int) tms[1].tm_mday, (int) tms[1].tm_mon, (int) tms[1].tm_year,
	             (int) tms[1].tm_wday, (int) tms[1].tm_yday, (int) tms[1].tm_isdst);
	DUK_DDDPRINT("t2=%d", (int) t2);

	/* Positive if local time ahead of UTC. */

	/* difftime() returns a double, so coercion to int generates quite
	 * a lot of code.  Direct subtraction is not portable, however.
	 *
	 * FIXME: allow direct subtraction on known platforms.
	 */
#if 0
	return t1 - t2;
#endif
	return (int) difftime(t1, t2);

 error:
	/* FIXME: return something more useful, so that caller can throw? */
	DUK_DPRINT("mktime() failed, d=%lf", d);
	return 0;
}
Пример #17
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);

	/* 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_bi_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
	 */

	if (tot_len <= DUK_BI_LOGGER_SHORT_MSG_LIMIT) {
		duk_hbuffer_dynamic *h_buf;

		DUK_DDD(DUK_DDDPRINT("reuse existing small log message buffer, tot_len %ld", (long) tot_len));

		/* We can assert for all buffer properties because user code
		 * never has access to heap->log_buffer.
		 */

		DUK_ASSERT(thr != NULL);
		DUK_ASSERT(thr->heap != NULL);
		h_buf = thr->heap->log_buffer;
		DUK_ASSERT(h_buf != NULL);
		DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) h_buf));
		DUK_ASSERT(DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_buf) == DUK_BI_LOGGER_SHORT_MSG_LIMIT);

		/* Set buffer 'visible size' to actual message length and
		 * push it to the stack.
		 */

		DUK_HBUFFER_SET_SIZE((duk_hbuffer *) h_buf, tot_len);
		duk_push_hbuffer(ctx, (duk_hbuffer *) h_buf);
		buf = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf);
	} else {
		DUK_DDD(DUK_DDDPRINT("use a one-off large log message buffer, tot_len %ld", (long) tot_len));
		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;
}
Пример #18
0
DUK_LOCAL
duk_hstring *duk__alloc_init_hstring(duk_heap *heap,
                                     const duk_uint8_t *str,
                                     duk_uint32_t blen,
                                     duk_uint32_t strhash,
                                     const duk_uint8_t *extdata) {
	duk_hstring *res = NULL;
	duk_uint8_t *data;
	duk_size_t alloc_size;
	duk_uarridx_t dummy;
	duk_uint32_t clen;

#if defined(DUK_USE_STRLEN16)
	/* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */
	if (blen > 0xffffUL) {
		DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern"));
		return NULL;
	}
#endif

	if (extdata) {
		alloc_size = (duk_size_t) sizeof(duk_hstring_external);
		res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
		if (!res) {
			goto alloc_error;
		}
		DUK_MEMZERO(res, sizeof(duk_hstring_external));
#ifdef DUK_USE_EXPLICIT_NULL_INIT
		DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
		DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA);

		((duk_hstring_external *) res)->extdata = extdata;
	} else {
		/* NUL terminate for convenient C access */
		alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1);
		res = (duk_hstring *) DUK_ALLOC(heap, alloc_size);
		if (!res) {
			goto alloc_error;
		}
		DUK_MEMZERO(res, sizeof(duk_hstring));
#ifdef DUK_USE_EXPLICIT_NULL_INIT
		DUK_HEAPHDR_STRING_INIT_NULLS(&res->hdr);
#endif
		DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0);

		data = (duk_uint8_t *) (res + 1);
		DUK_MEMCPY(data, str, blen);
		data[blen] = (duk_uint8_t) 0;
	}

	if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) {
		DUK_HSTRING_SET_ARRIDX(res);
	}

	/* All strings beginning with 0xff are treated as "internal",
	 * even strings interned by the user.  This allows user code to
	 * create internal properties too, and makes behavior consistent
	 * in case user code happens to use a string also used by Duktape
	 * (such as string has already been interned and has the 'internal'
	 * flag set).
	 */
	if (blen > 0 && str[0] == (duk_uint8_t) 0xff) {
		DUK_HSTRING_SET_INTERNAL(res);
	}

	DUK_HSTRING_SET_HASH(res, strhash);
	DUK_HSTRING_SET_BYTELEN(res, blen);
	clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen);
	DUK_ASSERT(clen <= blen);
	DUK_HSTRING_SET_CHARLEN(res, clen);

	DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%ld",
	                     (unsigned long) DUK_HSTRING_GET_HASH(res),
	                     (long) DUK_HSTRING_GET_BYTELEN(res),
	                     (long) DUK_HSTRING_GET_CHARLEN(res),
	                     (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0),
	                     (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0)));

	return res;

 alloc_error:
	DUK_FREE(heap, res);
	return NULL;
}
Пример #19
0
DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) {
	duk_hstring *h_input;
	duk_size_t input_blen;
	duk_size_t result_len;
	duk_int_t count_signed;
	duk_uint_t count;
	const duk_uint8_t *src;
	duk_uint8_t *buf;
	duk_uint8_t *p;
	duk_double_t d;
#if !defined(DUK_USE_PREFER_SIZE)
	duk_size_t copy_size;
	duk_uint8_t *p_end;
#endif

	DUK_ASSERT_TOP(ctx, 1);
	h_input = duk_push_this_coercible_to_string(ctx);
	DUK_ASSERT(h_input != NULL);
	input_blen = DUK_HSTRING_GET_BYTELEN(h_input);

	/* Count is ToNumber() coerced; +Infinity must be always rejected
	 * (even if input string is zero length), as well as negative values
	 * and -Infinity.  -Infinity doesn't require an explicit check
	 * because duk_get_int() clamps it to DUK_INT_MIN which gets rejected
	 * as a negative value (regardless of input string length).
	 */
	d = duk_to_number(ctx, 0);
	if (duk_double_is_posinf(d)) {
		goto fail_range;
	}
	count_signed = duk_get_int(ctx, 0);
	if (count_signed < 0) {
		goto fail_range;
	}
	count = (duk_uint_t) count_signed;

	/* Overflow check for result length. */
	result_len = count * input_blen;
	if (count != 0 && result_len / count != input_blen) {
		goto fail_range;
	}

	/* Temporary fixed buffer, later converted to string. */
	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, result_len);
	src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);

#if defined(DUK_USE_PREFER_SIZE)
	p = buf;
	while (count-- > 0) {
		DUK_MEMCPY((void *) p, (const void *) src, input_blen);  /* copy size may be zero */
		p += input_blen;
	}
#else  /* DUK_USE_PREFER_SIZE */
	/* Take advantage of already copied pieces to speed up the process
	 * especially for small repeated strings.
	 */
	p = buf;
	p_end = p + result_len;
	copy_size = input_blen;
	for (;;) {
		duk_size_t remain = (duk_size_t) (p_end - p);
		DUK_DDD(DUK_DDDPRINT("remain=%ld, copy_size=%ld, input_blen=%ld, result_len=%ld",
		                     (long) remain, (long) copy_size, (long) input_blen,
		                     (long) result_len));
		if (remain <= copy_size) {
			/* If result_len is zero, this case is taken and does
			 * a zero size copy.
			 */
			DUK_MEMCPY((void *) p, (const void *) src, remain);
			break;
		} else {
			DUK_MEMCPY((void *) p, (const void *) src, copy_size);
			p += copy_size;
			copy_size *= 2;
		}

		src = (const duk_uint8_t *) buf;  /* Use buf as source for larger copies. */
	}
#endif  /* DUK_USE_PREFER_SIZE */

	/* XXX: It would be useful to be able to create a duk_hstring with
	 * a certain byte size whose data area wasn't initialized and which
	 * wasn't in the string table yet.  This would allow a string to be
	 * constructed directly without a buffer temporary and when it was
	 * finished, it could be injected into the string table.  Currently
	 * this isn't possible because duk_hstrings are only tracked by the
	 * intern table (they are not in heap_allocated).
	 */

	duk_buffer_to_string(ctx, -1);
	return 1;

 fail_range:
	DUK_DCERROR_RANGE_INVALID_ARGS((duk_hthread *) ctx);
}