Beispiel #1
0
static void duk__free_hobject_inner(duk_heap *heap, duk_hobject *h) {
	DUK_ASSERT(heap != NULL);
	DUK_ASSERT(h != NULL);

	DUK_FREE(heap, h->p);

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) h;
		DUK_UNREF(f);
		/* Currently nothing to free; 'data' is a heap object */
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
		duk_hnativefunction *f = (duk_hnativefunction *) h;
		DUK_UNREF(f);
		/* Currently nothing to free */
	} else if (DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		DUK_FREE(heap, t->valstack);
		DUK_FREE(heap, t->callstack);
		DUK_FREE(heap, t->catchstack);
		/* Don't free h->resumer because it exists in the heap.
		 * Callstack entries also contain function pointers which
		 * are not freed for the same reason.
		 */

		/* XXX: with 'caller' property the callstack would need
		 * to be unwound to update the 'caller' properties of
		 * functions in the callstack.
		 */
	}
}
Beispiel #2
0
int wrapped_compile_execute(duk_context *ctx) {
	int comp_flags;

	comp_flags = 0;
	duk_compile(ctx, comp_flags);

#if 0
	/* FIXME: something similar with public API */
	if (interactive_mode) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);

		if (f && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) f)) {
			fprintf(stdout, "[bytecode length %d opcodes, registers %d, constants %d, inner functions %d]\n",
				(int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(f),
				(int) f->nregs,
				(int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(f),
				(int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(f));
			fflush(stdout);
		} else {
			fprintf(stdout, "[invalid compile result]\n");
			fflush(stdout);
		}
	}
#endif

	duk_push_global_object(ctx);  /* 'this' binding */
	duk_call_method(ctx, 0);

	if (interactive_mode) {
		/*
		 *  In interactive mode, write to stdout so output won't interleave as easily.
		 *
		 *  NOTE: the ToString() coercion may fail in some cases; for instance,
		 *  if you evaluate:
		 *
		 *    ( {valueOf: function() {return {}}, toString: function() {return {}}});
		 *
		 *  The error is:
		 *
		 *    TypeError: failed to coerce with [[DefaultValue]]
		 *            duk_api.c:1420
		 *
		 *  These errors are caught and printed out as errors although
		 *  the errors are not generated by user code as such.  Changing
		 *  duk_to_string() to duk_safe_to_string() would avoid these
		 *  errors.
		 */

		fprintf(stdout, "= %s\n", duk_to_string(ctx, -1));
		fflush(stdout);
	} else {
		/* In non-interactive mode, success results are not written at all.
		 * It is important that the result value is not string coerced,
		 * as the string coercion may cause an error in some cases.
		 */
	}

	duk_pop(ctx);
	return 0;
}
Beispiel #3
0
/* for thread dumping */
static char duk__get_act_summary_char(duk_activation *act) {
	if (act->func) {
		if (DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) {
			return 'c';
		} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(act->func)) {
			return 'n';
		} else {
			/* should not happen */
			return '?';
		}
	} else {
		/* should not happen */
		return '?';
	}
}
Beispiel #4
0
/* for thread dumping */
static char duk__get_tval_summary_char(duk_tval *tv) {
	switch (DUK_TVAL_GET_TAG(tv)) {
	case DUK_TAG_UNDEFINED:
		if (DUK_TVAL_IS_UNDEFINED_UNUSED(tv)) {
			return '.';
		}
		return 'u';
	case DUK_TAG_NULL:
		return 'n';
	case DUK_TAG_BOOLEAN:
		return 'b';
	case DUK_TAG_STRING:
		return 's';
	case DUK_TAG_OBJECT: {
		duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);

		if (DUK_HOBJECT_IS_ARRAY(h)) {
			return 'A';
		} else if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
			return 'C';
		} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
			return 'N';
		} else if (DUK_HOBJECT_IS_THREAD(h)) {
			return 'T';
		}
		return 'O';
	}
	case DUK_TAG_BUFFER: {
		return 'B';
	}
	case DUK_TAG_POINTER: {
		return 'P';
	}
	default:
		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
		return 'd';
	}

	DUK_UNREACHABLE();
}
Beispiel #5
0
static void free_hobject_inner(duk_heap *heap, duk_hobject *h) {
	DUK_ASSERT(heap != NULL);
	DUK_ASSERT(h != NULL);

	DUK_FREE(heap, h->p);

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) h;
		DUK_UNREF(f);
		/* Currently nothing to free; 'data' is a heap object */
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
		duk_hnativefunction *f = (duk_hnativefunction *) h;
		DUK_UNREF(f);
		/* Currently nothing to free */
	} else if (DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		DUK_FREE(heap, t->valstack);
		DUK_FREE(heap, t->callstack);
		DUK_FREE(heap, t->catchstack);
		/* don't free h->resumer, because it exists in the heap */
	}
}
Beispiel #6
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;
}
Beispiel #7
0
static void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
	duk_uint_fast32_t i;

	DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h));

	DUK_ASSERT(h);

	/* XXX: use advancing pointers instead of index macros -> faster and smaller? */

	for (i = 0; i < h->e_used; i++) {
		duk_hstring *key = DUK_HOBJECT_E_GET_KEY(h, i);
		if (!key) {
			continue;
		}
		duk__mark_heaphdr(heap, (duk_heaphdr *) key);
		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(h, i)) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->a.get);
			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->a.set);
		} else {
			duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->v);
		}
	}

	for (i = 0; i < h->a_size; i++) {
		duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(h, i));
	}

	/* hash part is a 'weak reference' and does not contribute */

	duk__mark_heaphdr(heap, (duk_heaphdr *) h->prototype);

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) h;
		duk_tval *tv, *tv_end;
		duk_hobject **funcs, **funcs_end;

		/* 'data' is reachable through every compiled function which
		 * contains a reference.
		 */

		duk__mark_heaphdr(heap, (duk_heaphdr *) f->data);

		tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(f);
		tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(f);
		while (tv < tv_end) {
			duk__mark_tval(heap, tv);
			tv++;
		}

		funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(f);
		funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(f);
		while (funcs < funcs_end) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) *funcs);
			funcs++;
		}
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
		duk_hnativefunction *f = (duk_hnativefunction *) h;
		DUK_UNREF(f);
		/* nothing to mark */
	} else if (DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		duk_tval *tv;

		tv = t->valstack;
		while (tv < t->valstack_end) {
			duk__mark_tval(heap, tv);
			tv++;
		}

		for (i = 0; i < t->callstack_top; i++) {
			duk_activation *act = &t->callstack[i];
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->func);
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env);
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller);
#endif
		}

#if 0  /* nothing now */
		for (i = 0; i < t->catchstack_top; i++) {
			duk_catcher *cat = &t->catchstack[i];
		}
#endif

		duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer);

		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]);
		}
	}
}
Beispiel #8
0
/* Raw helper to extract internal information / statistics about a value.
 * The return values are version specific and must not expose anything
 * that would lead to security issues (e.g. exposing compiled function
 * 'data' buffer might be an issue).  Currently only counts and sizes and
 * such are given so there should not be a security impact.
 */
duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) {
	duk_tval *tv;
	duk_heaphdr *h;
	duk_int_t i, n;

	tv = duk_get_tval(ctx, 0);
	DUK_ASSERT(tv != NULL);  /* because arg count is 1 */

	duk_push_array(ctx);  /* -> [ val arr ] */

	/* type tag (public) */
	duk_push_int(ctx, duk_get_type(ctx, 0));

	/* address */
	if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
		h = DUK_TVAL_GET_HEAPHDR(tv);
		duk_push_pointer(ctx, (void *) h);
	} else {
		goto done;
	}
	DUK_ASSERT(h != NULL);

	/* refcount */
#ifdef DUK_USE_REFERENCE_COUNTING
	duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h));
#else
	duk_push_undefined(ctx);
#endif

	/* heaphdr size and additional allocation size, followed by
	 * type specific stuff (with varying value count)
	 */
	switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) {
	case DUK_HTYPE_STRING: {
		duk_hstring *h_str = (duk_hstring *) h;
		duk_push_int(ctx, (int) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1));
		break;
	}
	case DUK_HTYPE_OBJECT: {
		duk_hobject *h_obj = (duk_hobject *) h;
		duk_int_t hdr_size;
		if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
			hdr_size = (duk_int_t) sizeof(duk_hcompiledfunction);
		} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) {
			hdr_size = (duk_int_t) sizeof(duk_hnativefunction);
		} else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
			hdr_size = (duk_int_t) sizeof(duk_hthread);
		} else {
			hdr_size = (duk_int_t) sizeof(duk_hobject);
		}
		duk_push_int(ctx, (int) hdr_size);
		duk_push_int(ctx, (int) DUK_HOBJECT_E_ALLOC_SIZE(h_obj));
		duk_push_int(ctx, (int) h_obj->e_size);
		duk_push_int(ctx, (int) h_obj->e_used);
		duk_push_int(ctx, (int) h_obj->a_size);
		duk_push_int(ctx, (int) h_obj->h_size);
		if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
			duk_hbuffer *h_data = ((duk_hcompiledfunction *) h_obj)->data;
			if (h_data) {
				duk_push_int(ctx, DUK_HBUFFER_GET_SIZE(h_data));
			} else {
				duk_push_int(ctx, 0);
			}
		}
		break;
	}
	case DUK_HTYPE_BUFFER: {
		duk_hbuffer *h_buf = (duk_hbuffer *) h;
		if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) {
			/* XXX: when usable_size == 0, dynamic buf ptr may now be NULL, in which case
			 * the second allocation does not exist.
			 */
			duk_hbuffer_dynamic *h_dyn = (duk_hbuffer_dynamic *) h;
			duk_push_int(ctx, (int) (sizeof(duk_hbuffer_dynamic)));
			duk_push_int(ctx, (int) (DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_dyn)));
		} else {
			duk_push_int(ctx, (int) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1));
		}
		break;

	}
	}

 done:
	/* set values into ret array */
	/* FIXME: primitive to make array from valstack slice */
	n = duk_get_top(ctx);
	for (i = 2; i < n; i++) {
		duk_dup(ctx, i);
		duk_put_prop_index(ctx, 1, i - 2);
	}
	duk_dup(ctx, 1);
	return 1;
}
Beispiel #9
0
void duk_debug_dump_hobject(duk_hobject *obj) {
	duk_uint_fast32_t i;
	const char *str_empty = "";
	const char *str_excl = "!";

	DUK_D(DUK_DPRINT("=== hobject %p ===", (void *) obj));
	if (!obj) {
		return;
	}

	DUK_D(DUK_DPRINT("  %sextensible", DUK_HOBJECT_HAS_EXTENSIBLE(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sconstructable", DUK_HOBJECT_HAS_CONSTRUCTABLE(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sbound", DUK_HOBJECT_HAS_BOUND(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %scompiledfunction", DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snativefunction", DUK_HOBJECT_HAS_NATIVEFUNCTION(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sthread", DUK_HOBJECT_HAS_THREAD(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sarray_part", DUK_HOBJECT_HAS_ARRAY_PART(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sstrict", DUK_HOBJECT_HAS_STRICT(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snewenv", DUK_HOBJECT_HAS_NEWENV(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snamebinding", DUK_HOBJECT_HAS_NAMEBINDING(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %screateargs", DUK_HOBJECT_HAS_CREATEARGS(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %senvrecclosed", DUK_HOBJECT_HAS_ENVRECCLOSED(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_array", DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_stringobj", DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_arguments", DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_dukfunc", DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_bufferobj", DUK_HOBJECT_HAS_EXOTIC_BUFFEROBJ(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_proxyobj", DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj) ? str_empty : str_excl));

	DUK_D(DUK_DPRINT("  class: number %d -> %s",
	                 (int) DUK_HOBJECT_GET_CLASS_NUMBER(obj),
	                 duk__class_names[(DUK_HOBJECT_GET_CLASS_NUMBER(obj)) & ((1 << DUK_HOBJECT_FLAG_CLASS_BITS) - 1)]));

	DUK_D(DUK_DPRINT("  prototype: %p -> %!O",
	                 (void *) obj->prototype,
	                 (duk_heaphdr *) obj->prototype));

	DUK_D(DUK_DPRINT("  props: p=%p, e_size=%d, e_used=%d, a_size=%d, h_size=%d",
	                 (void *) obj->p,
	                 (int) obj->e_size,
	                 (int) obj->e_used,
	                 (int) obj->a_size,
	                 (int) obj->h_size));

	/*
	 *  Object (struct layout) specific dumping.  Inline code here
	 *  instead of helpers, to ensure debug line prefix is identical.
	 */

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(obj)) {
		duk_hcompiledfunction *h = (duk_hcompiledfunction *) obj;

		DUK_D(DUK_DPRINT("  hcompiledfunction"));
		DUK_D(DUK_DPRINT("  data: %!O", h->data));
		DUK_D(DUK_DPRINT("  nregs: %d", (int) h->nregs));
		DUK_D(DUK_DPRINT("  nargs: %d", (int) h->nargs));

		if (h->data && DUK_HBUFFER_HAS_DYNAMIC(h->data) && DUK_HBUFFER_GET_DATA_PTR(h->data)) {
			DUK_D(DUK_DPRINT("  consts: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(h)));
			DUK_D(DUK_DPRINT("  funcs: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(h)));
			DUK_D(DUK_DPRINT("  bytecode: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(h)));
		} else {
			DUK_D(DUK_DPRINT("  consts: ???"));
			DUK_D(DUK_DPRINT("  funcs: ???"));
			DUK_D(DUK_DPRINT("  bytecode: ???"));
		}
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(obj)) {
		duk_hnativefunction *h = (duk_hnativefunction *) obj;

		DUK_D(DUK_DPRINT("  hnativefunction"));
		/* XXX: h->func, cannot print function pointers portably */
		DUK_D(DUK_DPRINT("  nargs: %d", (int) h->nargs));
	} else if (DUK_HOBJECT_IS_THREAD(obj)) {
		duk_hthread *thr = (duk_hthread *) obj;
		duk_tval *p;

		DUK_D(DUK_DPRINT("  hthread"));
		DUK_D(DUK_DPRINT("  strict: %d", (int) thr->strict));
		DUK_D(DUK_DPRINT("  state: %d", (int) thr->state));

		DUK_D(DUK_DPRINT("  valstack_max: %d, callstack_max:%d, catchstack_max: %d",
		                 thr->valstack_max, thr->callstack_max, thr->catchstack_max));

		DUK_D(DUK_DPRINT("  callstack: ptr %p, size %d, top %d, preventcount %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->callstack,
		                 thr->callstack_size,
		                 thr->callstack_top,
		                 thr->callstack_preventcount,
		                 thr->callstack_top,
		                 thr->callstack_top * sizeof(duk_activation),
		                 thr->callstack_size,
		                 thr->callstack_size * sizeof(duk_activation)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		for (i = 0; i <= thr->callstack_size; i++) {
			if (i == thr->callstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (!thr->callstack) {
				DUK_DEBUG_SUMMARY_CHAR('@');
			} else if (i < thr->callstack_size) {
				if (i < thr->callstack_top) {
					/* tailcalling is nice to see immediately; other flags (e.g. strict)
					 * not that important.
					 */
					if (thr->callstack[i].flags & DUK_ACT_FLAG_TAILCALLED) {
						DUK_DEBUG_SUMMARY_CHAR('/');
					}
					DUK_DEBUG_SUMMARY_CHAR(duk__get_act_summary_char(&thr->callstack[i]));
				} else {
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  valstack: ptr %p, end %p (%d), bottom %p (%d), top %p (%d), used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->valstack,
		                 (void *) thr->valstack_end,
		                 (int) (thr->valstack_end - thr->valstack),
		                 (void *) thr->valstack_bottom,
		                 (int) (thr->valstack_bottom - thr->valstack),
		                 (void *) thr->valstack_top,
		                 (int) (thr->valstack_top - thr->valstack),
		                 (int) (thr->valstack_top - thr->valstack),
		                 (int) (thr->valstack_top - thr->valstack) * sizeof(duk_tval),
		                 (int) (thr->valstack_end - thr->valstack),
		                 (int) (thr->valstack_end - thr->valstack) * sizeof(duk_tval)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		p = thr->valstack;
		while (p <= thr->valstack_end) {
			i = (duk_uint_fast32_t) (p - thr->valstack);
			if (thr->callstack &&
			    thr->callstack_top > 0 &&
			    i == (duk_size_t) (thr->callstack + thr->callstack_top - 1)->idx_bottom) {
				DUK_DEBUG_SUMMARY_CHAR('>');
			}
			if (p == thr->valstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (p < thr->valstack_end) {
				if (p < thr->valstack_top) {
					DUK_DEBUG_SUMMARY_CHAR(duk__get_tval_summary_char(p));
				} else {
					/* XXX: safe printer for these?  would be nice, because
					 * we could visualize whether the values are in proper
					 * state.
					 */
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
			p++;
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  catchstack: ptr %p, size %d, top %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->catchstack,
		                 thr->catchstack_size,
		                 thr->catchstack_top,
		                 thr->catchstack_top,
		                 thr->catchstack_top * sizeof(duk_catcher),
		                 thr->catchstack_size,
		                 thr->catchstack_size * sizeof(duk_catcher)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		for (i = 0; i <= thr->catchstack_size; i++) {
			if (i == thr->catchstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (!thr->catchstack) {
				DUK_DEBUG_SUMMARY_CHAR('@');
			} else if (i < thr->catchstack_size) {
				if (i < thr->catchstack_top) {
					DUK_DEBUG_SUMMARY_CHAR(duk__get_cat_summary_char(&thr->catchstack[i]));
				} else {
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  resumer: ptr %p",
		                 (void *) thr->resumer));

#if 0  /* worth dumping? */
		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
			DUK_D(DUK_DPRINT("  builtins[%d] -> %!@O", i, thr->builtins[i]));
		}
#endif
	}

	if (obj->p) {
		DUK_D(DUK_DPRINT("  props alloc size: %d",
		                 (int) DUK_HOBJECT_P_COMPUTE_SIZE(obj->e_size, obj->a_size, obj->h_size)));
	} else {
		DUK_D(DUK_DPRINT("  props alloc size: n/a"));
	}

	DUK_D(DUK_DPRINT("  prop entries:"));
	for (i = 0; i < obj->e_size; i++) {
		duk_hstring *k;
		duk_propvalue *v;

		k = DUK_HOBJECT_E_GET_KEY(obj, i);
		v = DUK_HOBJECT_E_GET_VALUE_PTR(obj, i);

		if (i >= obj->e_used) {
			DUK_D(DUK_DPRINT("    [%d]: UNUSED", i));
			continue;
		}

		if (!k) {
			DUK_D(DUK_DPRINT("    [%d]: NULL", i));
			continue;
		}

		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i)) {
			DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %!O -> get:%p set:%p; get %!O; set %!O",
			                 i,
			                 DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i),
			                 k,
			                 (void *) v->a.get,
			                 (void *) v->a.set,
			                 (duk_heaphdr *) v->a.get,
			                 (duk_heaphdr *) v->a.set));
		} else {
			DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %!O -> %!T",
			                 i,
			                 DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i),
			                 k,
			                 &v->v));
		}
	}

	DUK_D(DUK_DPRINT("  array entries:"));
	for (i = 0; i < obj->a_size; i++) {
		DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %d -> %!T",
		                 i,
		                 1,  /* implicit attributes */
		                 1,
		                 1,
		                 0,
		                 i,
		                 DUK_HOBJECT_A_GET_VALUE_PTR(obj, i)));
	}

	DUK_D(DUK_DPRINT("  hash entries:"));
	for (i = 0; i < obj->h_size; i++) {
		duk_uint32_t t = DUK_HOBJECT_H_GET_INDEX(obj, i);
		if (t == DUK_HOBJECT_HASHIDX_UNUSED) {
			DUK_D(DUK_DPRINT("    [%d]: unused", i));
		} else if (t == DUK_HOBJECT_HASHIDX_DELETED) {
			DUK_D(DUK_DPRINT("    [%d]: deleted", i));
		} else {
			DUK_D(DUK_DPRINT("    [%d]: %d",
			                 i,
			                 (int) t));
		}
	}
}
static void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_int_t noblame_fileline, duk_hobject *obj) {
	duk_context *ctx = (duk_context *) thr;
#ifdef DUK_USE_ASSERTIONS
	duk_int_t entry_top;
#endif

#ifdef DUK_USE_ASSERTIONS
	entry_top = duk_get_top(ctx);
#endif
	DUK_ASSERT(obj != NULL);

	DUK_UNREF(obj);  /* unreferenced w/o tracebacks */
	DUK_UNREF(ctx);  /* unreferenced w/ tracebacks */

#ifdef DUK_USE_TRACEBACKS
	/*
	 *  If tracebacks are enabled, the 'tracedata' property is the only
	 *  thing we need: 'fileName' and 'lineNumber' are virtual properties
	 *  which use 'tracedata'.
	 */

	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) {
		DUK_DDD(DUK_DDDPRINT("error value already has a 'tracedata' property, not modifying it"));
	} else {
		duk__add_traceback(thr, thr_callstack, filename, line, noblame_fileline);
	}
#else
	/*
	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
	 *  as plain own properties.  Since Error.prototype has accessors of
	 *  the same name, we need to define own properties directly (cannot
	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
	 *  overwritten in case they already exist.
	 */

	if (filename && !noblame_fileline) {
		/* XXX: file/line is disabled in minimal builds, so disable this too
		 * when appropriate.
		 */
		duk_push_string(ctx, filename);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
		duk_push_int(ctx, line);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
	} else if (thr_callstack->callstack_top > 0) {
		duk_activation *act;
		duk_hobject *func;

		act = thr_callstack->callstack + thr_callstack->callstack_top - 1;
		DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
		func = act->func;
		if (func) {
			duk_uint32_t pc;
			duk_uint32_t line;

			/* PC points to next instruction, find offending PC.  Note that
			 * PC == 0 for native code.
			 */
			pc = act->pc;
			if (pc > 0) {
				pc--;
			}
			DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
			DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
			act = NULL;  /* invalidated by pushes, so get out of the way */

			duk_push_hobject(ctx, func);

			/* [ ... error func ] */

			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
			duk_def_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
#if 0
				duk_push_number(ctx, pc);
				duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE);
#endif
				line = duk_hobject_pc2line_query(ctx, -1, (duk_uint_fast32_t) pc);
				if (line > 0) {
					duk_push_u32(ctx, (duk_uint32_t) line); /* -> [ ... error func line ] */
					duk_def_prop_stridx(ctx, -3, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
				}
			} else {
				/* Native function, no relevant lineNumber. */
			}

			duk_pop(ctx);
		}
	}
#endif  /* DUK_USE_TRACEBACKS */

#ifdef DUK_USE_ASSERTIONS
	DUK_ASSERT(duk_get_top(ctx) == entry_top);
#endif
}
Beispiel #11
0
DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
	duk_context *ctx;
#if defined(DUK_USE_ASSERTIONS)
	duk_int_t entry_top;
#endif

	ctx = (duk_context *) thr;
#if defined(DUK_USE_ASSERTIONS)
	entry_top = duk_get_top(ctx);
#endif

	/*
	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
	 *  as plain own properties.  Since Error.prototype has accessors of
	 *  the same name, we need to define own properties directly (cannot
	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
	 *  overwritten in case they already exist.
	 */

	if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
		/* Compiler SyntaxError (or other error) gets the primary blame.
		 * Currently no flag to prevent blaming.
		 */
		duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
		duk_push_hstring(ctx, thr->compile_ctx->h_filename);
	} else if (c_filename && !noblame_fileline) {
		/* C call site gets blamed next, unless flagged not to do so.
		 * XXX: file/line is disabled in minimal builds, so disable this
		 * too when appropriate.
		 */
		duk_push_int(ctx, c_line);
		duk_push_string(ctx, c_filename);
	} else {
		/* Finally, blame the innermost callstack entry which has a
		 * .fileName property.
		 */
		duk_small_uint_t depth;
		duk_int_t i, i_min;
		duk_uint32_t ecma_line;

		depth = DUK_USE_TRACEBACK_DEPTH;
		i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
		DUK_ASSERT(i_min >= 0);

		DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
		for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
			duk_activation *act;
			duk_hobject *func;
			duk_uint32_t pc;

			act = thr_callstack->callstack + i;
			DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);

			func = DUK_ACT_GET_FUNC(act);
			if (func == NULL) {
				/* Lightfunc, not blamed now. */
				continue;
			}

			/* PC points to next instruction, find offending PC,
			 * PC == 0 for native code.
			 */
			pc = duk_hthread_get_act_prev_pc(thr, act);  /* thr argument only used for thr->heap, so specific thread doesn't matter */
			DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
			DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
			act = NULL;  /* invalidated by pushes, so get out of the way */

			duk_push_hobject(ctx, func);

			/* [ ... error func ] */

			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
			if (!duk_is_string(ctx, -1)) {
				duk_pop_2(ctx);
				continue;
			}

			/* [ ... error func fileName ] */

			ecma_line = 0;
#if defined(DUK_USE_PC2LINE)
			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
				ecma_line = duk_hobject_pc2line_query(ctx, -2, (duk_uint_fast32_t) pc);
			} else {
				/* Native function, no relevant lineNumber. */
			}
#endif  /* DUK_USE_PC2LINE */
			duk_push_u32(ctx, ecma_line);

			/* [ ... error func fileName lineNumber ] */

			duk_replace(ctx, -3);

			/* [ ... error lineNumber fileName ] */
			goto define_props;
		}

		/* No activation matches, use undefined for both .fileName and
		 * .lineNumber (matches what we do with a _Tracedata based
		 * no-match lookup.
		 */
		duk_push_undefined(ctx);
		duk_push_undefined(ctx);
	}

 define_props:
	/* [ ... error lineNumber fileName ] */
#if defined(DUK_USE_ASSERTIONS)
	DUK_ASSERT(duk_get_top(ctx) == entry_top + 2);
#endif
	duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
}
DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
	duk_uint_fast32_t i;

	DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h));

	DUK_ASSERT(h);

	/* XXX: use advancing pointers instead of index macros -> faster and smaller? */

	for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
		duk_hstring *key = DUK_HOBJECT_E_GET_KEY(heap, h, i);
		if (!key) {
			continue;
		}
		duk__mark_heaphdr(heap, (duk_heaphdr *) key);
		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get);
			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set);
		} else {
			duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v);
		}
	}

	for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
		duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i));
	}

	/* hash part is a 'weak reference' and does not contribute */

	duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h));

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) h;
		duk_tval *tv, *tv_end;
		duk_hobject **fn, **fn_end;

		/* 'data' is reachable through every compiled function which
		 * contains a reference.
		 */

		duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f));

		tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f);
		tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f);
		while (tv < tv_end) {
			duk__mark_tval(heap, tv);
			tv++;
		}

		fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f);
		fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f);
		while (fn < fn_end) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
			fn++;
		}
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
		duk_hnativefunction *f = (duk_hnativefunction *) h;
		DUK_UNREF(f);
		/* nothing to mark */
	} else if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
		duk_hbufferobject *b = (duk_hbufferobject *) h;
		duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf);
	} else if (DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		duk_tval *tv;

		tv = t->valstack;
		while (tv < t->valstack_top) {
			duk__mark_tval(heap, tv);
			tv++;
		}

		for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
			duk_activation *act = t->callstack + i;
			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env);
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
			duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller);
#endif
		}

#if 0  /* nothing now */
		for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) {
			duk_catcher *cat = t->catchstack + i;
		}
#endif

		duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer);

		/* XXX: duk_small_uint_t would be enough for this loop */
		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
			duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]);
		}
	}
}
DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hstring *h_sourcecode;
	duk_idx_t nargs;
	duk_idx_t i;
	duk_small_uint_t comp_flags;
	duk_hcompiledfunction *func;
	duk_hobject *outer_lex_env;
	duk_hobject *outer_var_env;

	/* normal and constructor calls have identical semantics */

	nargs = duk_get_top(ctx);
	for (i = 0; i < nargs; i++) {
		duk_to_string(ctx, i);
	}

	if (nargs == 0) {
		duk_push_string(ctx, "");
		duk_push_string(ctx, "");
	} else if (nargs == 1) {
		/* XXX: cover this with the generic >1 case? */
		duk_push_string(ctx, "");
	} else {
		duk_insert(ctx, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
		duk_push_string(ctx, ",");
		duk_insert(ctx, 1);
		duk_join(ctx, nargs - 1);
	}

	/* [ body formals ], formals is comma separated list that needs to be parsed */

	DUK_ASSERT_TOP(ctx, 2);

	/* XXX: this placeholder is not always correct, but use for now.
	 * It will fail in corner cases; see test-dev-func-cons-args.js.
	 */
	duk_push_string(ctx, "function(");
	duk_dup(ctx, 1);
	duk_push_string(ctx, "){");
	duk_dup(ctx, 0);
	duk_push_string(ctx, "}");
	duk_concat(ctx, 5);

	/* [ body formals source ] */

	DUK_ASSERT_TOP(ctx, 3);

	/* strictness is not inherited, intentional */
	comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR;

	duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
	h_sourcecode = duk_require_hstring(ctx, -2);
	duk_js_compile(thr,
	               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode),
	               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode),
	               comp_flags);
	func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
	DUK_ASSERT(func != NULL);
	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func));

	/* [ body formals source template ] */

	/* only outer_lex_env matters, as functions always get a new
	 * variable declaration environment.
	 */

	outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
	outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];

	duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/);

	/* [ body formals source template closure ] */

	return 1;
}
Beispiel #14
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;
}
Beispiel #15
0
static void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) {
	duk_uint_fast32_t i;

	DUK_ASSERT(h);
	DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT);

	/* XXX: better to get base and walk forwards? */

	for (i = 0; i < (duk_uint_fast32_t) h->e_next; i++) {
		duk_hstring *key = DUK_HOBJECT_E_GET_KEY(h, i);
		if (!key) {
			continue;
		}
		duk_heap_heaphdr_decref(thr, (duk_heaphdr *) key);
		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(h, i)) {
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(h, i));
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(h, i));
		} else {
			duk_heap_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(h, i));
		}
	}

	for (i = 0; i < (duk_uint_fast32_t) h->a_size; i++) {
		duk_heap_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(h, i));
	}

	/* hash part is a 'weak reference' and does not contribute */

	duk_heap_heaphdr_decref(thr, (duk_heaphdr *) h->prototype);

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
		duk_hcompiledfunction *f = (duk_hcompiledfunction *) h;
		duk_tval *tv, *tv_end;
		duk_hobject **funcs, **funcs_end;

		DUK_ASSERT(f->data != NULL);  /* compiled functions must be created 'atomically' */

		tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(f);
		tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(f);
		while (tv < tv_end) {
			duk_heap_tval_decref(thr, tv);
			tv++;
		}

		funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(f);
		funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(f);
		while (funcs < funcs_end) {
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
			funcs++;
		}

		duk_heap_heaphdr_decref(thr, (duk_heaphdr *) f->data);
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
		duk_hnativefunction *f = (duk_hnativefunction *) h;
		DUK_UNREF(f);
		/* nothing to finalize */
	} else if (DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		duk_tval *tv;

		tv = t->valstack;
		while (tv < t->valstack_end) {
			duk_heap_tval_decref(thr, tv);
			tv++;
		}

		for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
			duk_activation *act = t->callstack + i;
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->func);
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->var_env);
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->prev_caller);
#endif
		}

#if 0  /* nothing now */
		for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) {
			duk_catcher *cat = t->catchstack + i;
		}
#endif

		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
			duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->builtins[i]);
		}

		duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->resumer);
	}
}
Beispiel #16
0
void duk_err_augment_error(duk_hthread *thr, duk_hthread *thr_callstack, int err_index, const char *filename, int line, int noblame_fileline) {
	duk_context *ctx = (duk_context *) thr;
	duk_hobject *obj;

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(thr_callstack != NULL);
	DUK_ASSERT(ctx != NULL);

	err_index = duk_require_normalize_index(ctx, err_index);

	/*
	 *  Criteria for augmenting:
	 *
	 *   - augmentation enabled in build (naturally)
	 *   - error value is an extensible object
	 *   - error value internal prototype chain contains the built-in
	 *     Error prototype object (i.e. 'val instanceof Error')
	 */

	obj = duk_require_hobject(ctx, err_index);
	if (!obj) {	
		DUK_DDDPRINT("error value not an object, not augmented");
		return;
	}
	if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
		DUK_DDDPRINT("error value not extensible, not augmented");
		return;
	}
	if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE])) {
		DUK_DDDPRINT("error value not inherited from Error, not augmented");
		return;
	}

	/* Yes, augment error. */

#ifdef DUK_USE_TRACEBACKS
	/*
	 *  If tracebacks are enabled, the 'tracedata' property is the only
	 *  thing we need: 'fileName' and 'lineNumber' are virtual properties
	 *  which use 'tracedata'.
	 */

	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) {
		DUK_DDDPRINT("error value already has a 'traceback' property, not modifying it");
	} else {
		add_traceback(thr, thr_callstack, obj, err_index, filename, line, noblame_fileline);
	}
#else
	/*
	 *  If tracebacks are disabled, 'fileName' and 'lineNumber' are added
	 *  as plain own properties.  Since Error.prototype has accessors of
	 *  the same name, we need to define own properties directly (cannot
	 *  just use e.g. duk_put_prop_stridx).  Existing properties are not
	 *  overwritten in case they already exist.
	 */

	if (filename && !noblame_fileline) {
		/* FIXME: file/line is disabled in minimal builds, so disable this too
		 * when appropriate.
		 */
		duk_push_string(ctx, filename);
		duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
		duk_push_int(ctx, line);
		duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
	} else if (thr_callstack->callstack_top > 0) {
		duk_activation *act;
		duk_hobject *func;
		duk_hbuffer *pc2line;

		act = thr_callstack->callstack + thr_callstack->callstack_top - 1;
		DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
		func = act->func;
		if (func) {
			int pc;
			duk_uint32_t line;

			/* PC points to next instruction, find offending PC.  Note that
			 * PC == 0 for native code.
			 */
			pc = act->pc;
			if (pc > 0) {
				pc--;
			}
			DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
			act = NULL;  /* invalidated by pushes, so get out of the way */

			duk_push_hobject(ctx, func);

			duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME);
			duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
			if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
#if 0
				duk_push_number(ctx, pc);
				duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE);
#endif

				duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_PC2LINE);
				if (duk_is_buffer(ctx, -1)) {
					pc2line = duk_get_hbuffer(ctx, -1);
					DUK_ASSERT(pc2line != NULL);
					DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(pc2line));
					line = duk_hobject_pc2line_query((duk_hbuffer_fixed *) pc2line, (duk_uint_fast32_t) pc);
					duk_push_number(ctx, (double) line);  /* FIXME: u32 */
					duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE);
				}
				duk_pop(ctx);
			} else {
				/* Native function, no relevant lineNumber. */
			}

			duk_pop(ctx);
		}
	}
#endif  /* DUK_USE_TRACEBACKS */
}
Beispiel #17
0
int duk_bi_function_constructor(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	int num_args;
	int i;
	int comp_flags;
	duk_hcompiledfunction *func;
	duk_hobject *outer_lex_env;
	duk_hobject *outer_var_env;

	/* normal and constructor calls have identical semantics */

	num_args = duk_get_top(ctx);

	for (i = 0; i < num_args; i++) {
		duk_to_string(ctx, i);
	}

	if (num_args == 0) {
		duk_push_string(ctx, "");
		duk_push_string(ctx, "");
	} else if (num_args == 1) {
		duk_push_string(ctx, "");
	} else {
		duk_insert(ctx, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
		duk_push_string(ctx, ",");
		duk_insert(ctx, 1);
		duk_join(ctx, num_args - 1);
	}

	/* [ body formals ], formals is comma separated list that needs to be parsed */

	DUK_ASSERT_TOP(ctx, 2);

	/* FIXME: this placeholder is not always correct, but use for now.
	 * It will fail in corner cases; see test-dev-func-cons-args.js.
	 */
	duk_push_string(ctx, "function(");
	duk_dup(ctx, 1);
	duk_push_string(ctx, "){");
	duk_dup(ctx, 0);
	duk_push_string(ctx, "}");
	duk_concat(ctx, 5);

	DUK_ASSERT_TOP(ctx, 3);

	/* FIXME: uses internal API */

	/* strictness is not inherited, intentional */
	comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR;

	duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */
	duk_js_compile(thr, comp_flags);
	func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
	DUK_ASSERT(func != NULL);
	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func));

	/* only outer_lex_env matters, as functions always get a new
	 * variable declaration environment.
	 */

	outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
	outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];

	duk_js_push_closure(thr, func, outer_var_env, outer_lex_env);

	return 1;
}
/* Raw helper to extract internal information / statistics about a value.
 * The return values are version specific and must not expose anything
 * that would lead to security issues (e.g. exposing compiled function
 * 'data' buffer might be an issue).  Currently only counts and sizes and
 * such are given so there should not be a security impact.
 */
DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_tval *tv;
	duk_heaphdr *h;
	duk_int_t i, n;

	DUK_UNREF(thr);

	/* result array */
	duk_push_array(ctx);  /* -> [ val arr ] */

	/* type tag (public) */
	duk_push_int(ctx, duk_get_type(ctx, 0));

	/* address */
	tv = duk_get_tval(ctx, 0);
	DUK_ASSERT(tv != NULL);  /* because arg count is 1 */
	if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
		h = DUK_TVAL_GET_HEAPHDR(tv);
		duk_push_pointer(ctx, (void *) h);
	} else {
		/* internal type tag */
		duk_push_int(ctx, (duk_int_t) DUK_TVAL_GET_TAG(tv));
		goto done;
	}
	DUK_ASSERT(h != NULL);

	/* refcount */
#ifdef DUK_USE_REFERENCE_COUNTING
	duk_push_size_t(ctx, DUK_HEAPHDR_GET_REFCOUNT(h));
#else
	duk_push_undefined(ctx);
#endif

	/* heaphdr size and additional allocation size, followed by
	 * type specific stuff (with varying value count)
	 */
	switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) {
	case DUK_HTYPE_STRING: {
		duk_hstring *h_str = (duk_hstring *) h;
		duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1));
		break;
	}
	case DUK_HTYPE_OBJECT: {
		duk_hobject *h_obj = (duk_hobject *) h;
		duk_small_uint_t hdr_size;
		if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
			hdr_size = (duk_small_uint_t) sizeof(duk_hcompiledfunction);
		} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) {
			hdr_size = (duk_small_uint_t) sizeof(duk_hnativefunction);
		} else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
			hdr_size = (duk_small_uint_t) sizeof(duk_hthread);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
		} else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) {
			hdr_size = (duk_small_uint_t) sizeof(duk_hbufferobject);
#endif
		} else {
			hdr_size = (duk_small_uint_t) sizeof(duk_hobject);
		}
		duk_push_uint(ctx, (duk_uint_t) hdr_size);
		duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj));
		duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj));
		/* Note: e_next indicates the number of gc-reachable entries
		 * in the entry part, and also indicates the index where the
		 * next new property would be inserted.  It does *not* indicate
		 * the number of non-NULL keys present in the object.  That
		 * value could be counted separately but requires a pass through
		 * the key list.
		 */
		duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj));
		duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj));
		duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj));
		if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) {
			duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, (duk_hcompiledfunction *) h_obj);
			if (h_data) {
				duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_data));
			} else {
				duk_push_uint(ctx, 0);
			}
		}
		break;
	}
	case DUK_HTYPE_BUFFER: {
		duk_hbuffer *h_buf = (duk_hbuffer *) h;
		if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) {
			if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) {
				duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_external)));
			} else {
				/* When alloc_size == 0 the second allocation may not
				 * actually exist.
				 */
				duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_dynamic)));
			}
			duk_push_uint(ctx, (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf)));
		} else {
			duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1));
		}
		break;

	}
	}

 done:
	/* set values into ret array */
	/* XXX: primitive to make array from valstack slice */
	n = duk_get_top(ctx);
	for (i = 2; i < n; i++) {
		duk_dup(ctx, i);
		duk_put_prop_index(ctx, 1, i - 2);
	}
	duk_dup(ctx, 1);
	return 1;
}
Beispiel #19
0
duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_hthread *thr_resume;
	duk_tval tv_tmp;
	duk_tval *tv;
	duk_hobject *func;
	duk_small_int_t is_error;

	DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T",
	             duk_get_tval(ctx, 0),
	             duk_get_tval(ctx, 1),
	             duk_get_tval(ctx, 2));

	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
	DUK_ASSERT(thr->heap->curr_thread == thr);

	thr_resume = duk_require_hthread(ctx, 0);
	is_error = (duk_small_int_t) duk_to_boolean(ctx, 2);
	duk_set_top(ctx, 2);

	/* [ thread value ] */

	/*
	 *  Thread state and calling context checks
	 */

	if (thr->callstack_top < 2) {
		DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)");
		goto state_error;
	}
	DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL);  /* us */
	DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func));
	DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL);  /* caller */

	if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
		DUK_DDPRINT("resume state invalid: caller must be Ecmascript code");
		goto state_error;
	}

	/* Note: there is no requirement that: 'thr->callstack_preventcount == 1'
	 * like for yield.
	 */

	if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE &&
	    thr_resume->state != DUK_HTHREAD_STATE_YIELDED) {
		DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED");
		goto state_error;
	}

	DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE ||
	           thr_resume->state == DUK_HTHREAD_STATE_YIELDED);

	/* Further state-dependent pre-checks */

	if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
		/* no pre-checks now, assume a previous yield() has left things in
		 * tip-top shape (longjmp handler will assert for these).
		 */
	} else {
		DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE);

		if ((thr_resume->callstack_top != 0) ||
		    (thr_resume->valstack_top - thr_resume->valstack != 1)) {
			goto state_invalid_initial;
		}
		tv = &thr_resume->valstack_top[-1];
		DUK_ASSERT(tv >= thr_resume->valstack && tv < thr_resume->valstack_top);
		if (!DUK_TVAL_IS_OBJECT(tv)) {
			goto state_invalid_initial;
		}
		func = DUK_TVAL_GET_OBJECT(tv);
		DUK_ASSERT(func != NULL);
		if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
			/* Note: cannot be a bound function either right now,
			 * this would be easy to relax though.
			 */
			goto state_invalid_initial;
		}

	}

	/*
	 *  The error object has been augmented with a traceback and other
	 *  info from its creation point -- usually another thread.  The
	 *  error handler is called here right before throwing, but it also
	 *  runs in the resumer's thread.  It might be nice to get a traceback
	 *  from the resumee but this is not the case now.
	 */

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
	if (is_error) {
		DUK_ASSERT_TOP(ctx, 2);  /* value (error) is at stack top */
		duk_err_augment_error_throw(thr);  /* in resumer's context */
	}
#endif

#ifdef DUK_USE_DEBUG  /* debug logging */
	if (is_error) {
		DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
		             duk_get_tval(ctx, 0),
		             duk_get_tval(ctx, 1));
	} else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
		DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
		             duk_get_tval(ctx, 0),
		             duk_get_tval(ctx, 1));
	} else {
		DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
		             duk_get_tval(ctx, 0),
		             duk_get_tval(ctx, 1));
	}
#endif

	thr->heap->lj.type = DUK_LJ_TYPE_RESUME;

	/* lj value2: thread */
	DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
	DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value2);
	DUK_TVAL_SET_TVAL(&thr->heap->lj.value2, &thr->valstack_bottom[0]);
	DUK_TVAL_INCREF(thr, &thr->heap->lj.value2);
	DUK_TVAL_DECREF(thr, &tv_tmp);

	/* lj value1: value */
	DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top);
	DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
	DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[1]);
	DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
	DUK_TVAL_DECREF(thr, &tv_tmp);

	thr->heap->lj.iserror = is_error;

	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from executor, so we know we have a jmpbuf */
	duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
	return 0;  /* never here */

 state_invalid_initial:
	DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid initial thread state/stack");
	return 0;  /* never here */

 state_error:
	DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for resume");
	return 0;  /* never here */
}
Beispiel #20
0
duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
	duk_hthread *thr = (duk_hthread *) ctx;
	duk_tval tv_tmp;
	duk_small_int_t is_error;

	DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
	             duk_get_tval(ctx, 0),
	             duk_get_tval(ctx, 1));

	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
	DUK_ASSERT(thr->heap->curr_thread == thr);

	is_error = (duk_small_int_t) duk_to_boolean(ctx, 1);
	duk_set_top(ctx, 1);

	/* [ value ] */

	/*
	 *  Thread state and calling context checks
	 */

	if (!thr->resumer) {
		DUK_DDPRINT("yield state invalid: current thread must have a resumer");
		goto state_error;
	}
	DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);

	if (thr->callstack_top < 2) {
		DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)");
		goto state_error;
	}
	DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL);  /* us */
	DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func));
	DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL);  /* caller */

	if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) {
		DUK_DDPRINT("yield state invalid: caller must be Ecmascript code");
		goto state_error;
	}

	DUK_ASSERT(thr->callstack_preventcount >= 1);  /* should never be zero, because we (Duktape.Thread.yield) are on the stack */
	if (thr->callstack_preventcount != 1) {
		/* Note: the only yield-preventing call is Duktape.Thread.yield(), hence check for 1, not 0 */
		DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack (preventcount is %d)",
		            (int) thr->callstack_preventcount);
		goto state_error;
	}

	/*
	 *  The error object has been augmented with a traceback and other
	 *  info from its creation point -- usually the current thread.
	 *  The error handler, however, is called right before throwing
	 *  and runs in the yielder's thread.
	 */

#if defined(DUK_USE_AUGMENT_ERROR_THROW)
	if (is_error) {
		DUK_ASSERT_TOP(ctx, 1);  /* value (error) is at stack top */
		duk_err_augment_error_throw(thr);  /* in yielder's context */
	}
#endif

#ifdef DUK_USE_DEBUG
	if (is_error) {
		DUK_DDDPRINT("YIELD ERROR: value=%!T",
		             duk_get_tval(ctx, 0));
	} else {
		DUK_DDDPRINT("YIELD NORMAL: value=%!T",
		             duk_get_tval(ctx, 0));
	}
#endif

	/*
	 *  Process yield
	 *
	 *  After longjmp(), processing continues in bytecode executor longjmp
	 *  handler, which will e.g. update thr->resumer to NULL.
	 */

	thr->heap->lj.type = DUK_LJ_TYPE_YIELD;

	/* lj value1: value */
	DUK_ASSERT(thr->valstack_bottom < thr->valstack_top);
	DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1);
	DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[0]);
	DUK_TVAL_INCREF(thr, &thr->heap->lj.value1);
	DUK_TVAL_DECREF(thr, &tv_tmp);

	thr->heap->lj.iserror = is_error;

	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* call is from executor, so we know we have a jmpbuf */
	duk_err_longjmp(thr);  /* execution resumes in bytecode executor */
	return 0;  /* never here */

 state_error:
	DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for yield");
	return 0;  /* never here */
}