Exemple #1
0
int duk_bi_array_prototype_slice(duk_context *ctx) {
	unsigned int len;
	int start, end;
	int idx;
	int i;
	duk_uint32_t res_length = 0;

	len = duk__push_this_obj_len_u32(ctx);
	duk_push_array(ctx);

	/* stack[0] = start
	 * stack[1] = end
	 * stack[2] = ToObject(this)
	 * stack[3] = ToUint32(length)
	 * stack[4] = result array
	 */

	start = duk_to_int_clamped(ctx, 0, -len, len);  /* FIXME: does not support full 32-bit range */
	if (start < 0) {
		start = len + start;
	}
	/* FIXME: could duk_is_undefined() provide defaulting undefined to 'len'
	 * (the upper limit)?
	 */
	if (duk_is_undefined(ctx, 1)) {
		end = len;
	} else {
		end = duk_to_int_clamped(ctx, 1, -len, len);
		if (end < 0) {
			end = len + end;
		}
	}
	DUK_ASSERT(start >= 0 && (duk_uint32_t) start <= len);
	DUK_ASSERT(end >= 0 && (duk_uint32_t) end <= len);

	idx = 0;
	for (i = start; i < end; i++) {
		DUK_ASSERT_TOP(ctx, 5);
		if (duk_get_prop_index(ctx, 2, i)) {
			duk_def_prop_index(ctx, 4, idx, DUK_PROPDESC_FLAGS_WEC);
			res_length = idx + 1;
		} else {
			duk_pop(ctx);
		}
		idx++;
		DUK_ASSERT_TOP(ctx, 5);
	}

	duk_push_int(ctx, res_length);  /* FIXME */
	duk_def_prop_stridx(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

	DUK_ASSERT_TOP(ctx, 5);
	return 1;
}
Exemple #2
0
int duk_bi_array_constructor(duk_context *ctx) {
	int nargs;
	double d;
	duk_uint32_t len;
	int i;

	nargs = duk_get_top(ctx);
	duk_push_array(ctx);

	if (nargs == 1 && duk_is_number(ctx, 0)) {
		/* FIXME: expensive check (also shared elsewhere - so add a shared internal API call?) */
		d = duk_get_number(ctx, 0);
		len = duk_to_uint32(ctx, 0);
		if (((double) len) != d) {
			return DUK_RET_RANGE_ERROR;
		}

		/* FIXME: if 'len' is low, may want to ensure array part is kept:
		 * the caller is likely to want a dense array.
		 */
		duk_dup(ctx, 0);
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);  /* [ ToUint32(len) array ToUint32(len) ] -> [ ToUint32(len) array ] */
		return 1;
	}

	/* FIXME: optimize by creating array into correct size directly, and
	 * operating on the array part directly; values can be memcpy()'d from
	 * value stack directly as long as refcounts are increased.
	 */
	for (i = 0; i < nargs; i++) {
		duk_dup(ctx, i);
		duk_def_prop_index(ctx, -2, i, DUK_PROPDESC_FLAGS_WEC);
	}

	duk_push_number(ctx, (double) nargs);  /* FIXME: push_u32 */
	duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
	return 1;
}
Exemple #3
0
static void add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, duk_hobject *obj, int err_index, const char *filename, int line, int noblame_fileline) {
	duk_context *ctx = (duk_context *) thr;
	int depth;
	int i, i_min;
	int arr_idx;
	double d;

	DUK_UNREF(obj);  /* FIXME: remove entire argument (it's accessed through the stack)? */

	DUK_ASSERT(thr != NULL);
	DUK_ASSERT(thr_callstack != NULL);
	DUK_ASSERT(obj != NULL);
	DUK_ASSERT(err_index >= 0);
	DUK_ASSERT(ctx != NULL);

	/*
	 *  The traceback format is pretty arcane in an attempt to keep it compact
	 *  and cheap to create.  It may change arbitrarily from version to version.
	 *  It should be decoded/accessed through version specific accessors only.
	 *
	 *  See doc/error-objects.txt.
	 */

	DUK_DDDPRINT("adding traceback to object: %!O", (duk_heaphdr *) obj);

	duk_push_array(ctx);  /* XXX: specify array size, as we know it */
	arr_idx = 0;

	/* filename/line from C macros (__FILE__, __LINE__) are added as an
	 * entry with a special format: (string, number).  The number contains
	 * the line and flags.
	 */

	/* FIXME: optimize: allocate an array part to the necessary size (upwards
	 * estimate) and fill in the values directly into the array part; finally
	 * update 'length'.
	 */

	/* FIXME: using duk_put_prop_index() would cause obscure error cases when Array.prototype
	 * has write-protected array index named properties.  This was seen as DoubleErrors
	 * in e.g. some test262 test cases.  Using duk_def_prop_index() is better but currently
	 * there is no fast path variant for that; the current implementation interns the array
	 * index as a string.  This can be fixed directly, or perhaps the traceback can be fixed
	 * altogether to fill in the tracedata directly into the array part.
	 */

	if (filename) {
		duk_push_string(ctx, filename);
		duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC);
		arr_idx++;

		d = (noblame_fileline ? ((double) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
		    (double) line;
		duk_push_number(ctx, d);
		duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC);
		arr_idx++;
	}

	/* traceback depth doesn't take into account the filename/line
	 * special handling above (intentional)
	 */
	depth = DUK_USE_TRACEBACK_DEPTH;
	i_min = (thr_callstack->callstack_top > (size_t) depth ? (int) (thr_callstack->callstack_top - depth) : 0);
	DUK_ASSERT(i_min >= 0);

	for (i = thr_callstack->callstack_top - 1; i >= i_min; i--) {
		int pc;

		/*
		 *  Note: each API operation potentially resizes the callstack,
		 *  so be careful to re-lookup after every operation.  Currently
		 *  these is no issue because we don't store a temporary 'act'
		 *  pointer at all.  (This would be a non-issue if we operated
		 *  directly on the array part.)
		 */

		/* [... arr] */

		DUK_ASSERT(thr_callstack->callstack[i].func != NULL);
		DUK_ASSERT(thr_callstack->callstack[i].pc >= 0);

		/* add function */
		duk_push_hobject(ctx, thr_callstack->callstack[i].func);  /* -> [... arr func] */
		duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC);
		arr_idx++;

		/* add a number containing: pc, activation flags */

		/* Add a number containing: pc, activation flag
		 *
		 * PC points to next instruction, find offending PC.  Note that
		 * PC == 0 for native code.
		 */
		pc = thr_callstack->callstack[i].pc;
		if (pc > 0) {
			pc--;
		}
		DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
		d = ((double) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (double) pc;
		duk_push_number(ctx, d);  /* -> [... arr num] */
		duk_def_prop_index(ctx, -2, arr_idx, DUK_PROPDESC_FLAGS_WEC);
		arr_idx++;
	}

	/* FIXME: set with duk_hobject_set_length() when tracedata is filled directly */
	duk_push_int(ctx, (int) arr_idx);
	duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);

	/* [... arr] */
	duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_TRACEDATA, DUK_PROPDESC_FLAGS_WEC);  /* -> [...] */
}
Exemple #4
0
int duk_bi_array_prototype_splice(duk_context *ctx) {
	int nargs;
	int have_delcount;
	int item_count;
	int len;
	int act_start;
	int del_count;
	int i;

	DUK_UNREF(have_delcount);

	nargs = duk_get_top(ctx);
	if (nargs < 2) {
		duk_set_top(ctx, 2);
		nargs = 2;
		have_delcount = 0;
	} else {
		have_delcount = 1;
	}

	len = duk__push_this_obj_len_u32(ctx);

	act_start = duk_to_int_clamped(ctx, 0, -len, len);
	if (act_start < 0) {
		act_start = len + act_start;
	}
	DUK_ASSERT(act_start >= 0 && act_start <= len);

#ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT
	if (have_delcount) {
#endif
		del_count = duk_to_int_clamped(ctx, 1, 0, len - act_start);
#ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT
	} else {
		/* E5.1 standard behavior when deleteCount is not given would be
		 * to treat it just like if 'undefined' was given, which coerces
		 * ultimately to 0.  Real world behavior is to splice to the end
		 * of array, see test-bi-array-proto-splice-no-delcount.js.
		 */
		del_count = len - act_start;
	}
#endif

	DUK_ASSERT(del_count >= 0 && del_count <= len - act_start);
	DUK_ASSERT(del_count + act_start <= len);

	duk_push_array(ctx);

	/* stack[0] = start
	 * stack[1] = deleteCount
	 * stack[2...nargs-1] = items
	 * stack[nargs] = ToObject(this)               -3
	 * stack[nargs+1] = ToUint32(length)           -2
	 * stack[nargs+2] = result array               -1
	 */

	DUK_ASSERT_TOP(ctx, nargs + 3);

	/* Step 9: copy elements-to-be-deleted into the result array */

	for (i = 0; i < del_count; i++) {
		if (duk_get_prop_index(ctx, -3, act_start + i)) {
			duk_def_prop_index(ctx, -2, i, DUK_PROPDESC_FLAGS_WEC);  /* throw flag irrelevant (false in std alg) */
		} else {
			duk_pop(ctx);
		}
	}
	duk_push_int(ctx, del_count);  /* FIXME: typing */
	duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

	/* Steps 12 and 13: reorganize elements to make room for itemCount elements */

	DUK_ASSERT(nargs >= 2);
	item_count = nargs - 2;
	if (item_count < del_count) {
		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 1
		 * -> [ A B F G H ]          (conceptual intermediate step)
		 * -> [ A B . F G H ]        (placeholder marked)
		 *    [ A B C F G H ]        (actual result at this point, C will be replaced)
		 */

		DUK_ASSERT_TOP(ctx, nargs + 3);

		for (i = act_start; i < len - del_count; i++) {
			if (duk_get_prop_index(ctx, -3, i + del_count)) {
				duk_put_prop_index(ctx, -4, i + item_count);  /* FIXME: Throw */
			} else {
				duk_pop(ctx);
				duk_del_prop_index(ctx, -3, i + item_count);  /* FIXME: Throw */
			}
		}

		DUK_ASSERT_TOP(ctx, nargs + 3);

		/* loop iterator init and limit changed from standard algorithm */
		for (i = len - 1; i >= len - del_count + item_count; i--) {
			duk_del_prop_index(ctx, -3, i);  /* FIXME: Throw */
		}

		DUK_ASSERT_TOP(ctx, nargs + 3);
	} else if (item_count > del_count) {
		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 4
		 * -> [ A B F G H ]          (conceptual intermediate step)
		 * -> [ A B . . . . F G H ]  (placeholder marked)
		 *    [ A B C D E F F G H ]  (actual result at this point)
		 */

		DUK_ASSERT_TOP(ctx, nargs + 3);

		/* loop iterator init and limit changed from standard algorithm */
		for (i = len - del_count - 1; i >= act_start; i--) {
			if (duk_get_prop_index(ctx, -3, i + del_count)) {
				duk_put_prop_index(ctx, -4, i + item_count);  /* FIXME: Throw */
			} else {
				duk_pop(ctx);
				duk_del_prop_index(ctx, -3, i + item_count);  /* FIXME: Throw */
			}
		}

		DUK_ASSERT_TOP(ctx, nargs + 3);
	} else {
		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 3
		 * -> [ A B F G H ]          (conceptual intermediate step)
		 * -> [ A B . . . F G H ]    (placeholder marked)
		 *    [ A B C D E F G H ]    (actual result at this point)
		 */
	}
	DUK_ASSERT_TOP(ctx, nargs + 3);

	/* Step 15: insert itemCount elements into the hole made above */

	for (i = 0; i < item_count; i++) {
		duk_dup(ctx, i + 2);  /* args start at index 2 */
		duk_put_prop_index(ctx, -4, act_start + i);  /* FIXME: Throw */
	}

	/* Step 16: update length; note that the final length may be above 32 bit range */

	duk_push_number(ctx, ((double) len) - ((double) del_count) + ((double) item_count));
	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);

	/* result array is already at the top of stack */
	DUK_ASSERT_TOP(ctx, nargs + 3);
	return 1;
}
Exemple #5
0
int duk_bi_array_prototype_concat(duk_context *ctx) {
	int i, n;
	int j, len;
	int idx, idx_last;
	duk_hobject *h;

	/* XXX: the insert here is a bit expensive if there are a lot of items.
	 * It could also be special cased in the outermost for loop quite easily
	 * (as the element is dup()'d anyway).
	 */

	(void) duk_push_this_coercible_to_object(ctx);
	duk_insert(ctx, 0);
	n = duk_get_top(ctx);
	duk_push_array(ctx);  /* -> [ ToObject(this) item1 ... itemN arr ] */

	/* NOTE: The Array special behaviors are NOT invoked by duk_def_prop_index()
	 * (which differs from the official algorithm).  If no error is thrown, this
	 * doesn't matter as the length is updated at the end.  However, if an error
	 * is thrown, the length will be unset.  That shouldn't matter because the
	 * caller won't get a reference to the intermediate value.
	 */

	idx = 0;
	idx_last = 0;
	for (i = 0; i < n; i++) {
		DUK_ASSERT_TOP(ctx, n + 1);

		/* [ ToObject(this) item1 ... itemN arr ] */

		duk_dup(ctx, i);
		h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY);
		if (!h) {
			duk_def_prop_index(ctx, -2, idx++, DUK_PROPDESC_FLAGS_WEC);
			idx_last = idx;
			continue;
		}

		/* [ ToObject(this) item1 ... itemN arr item(i) ] */

		/* FIXME: an array can have length higher than 32 bits; this is not handled
		 * correctly now (also len is signed so length above 2**31-1 will have trouble.
		 */
		len = duk_get_length(ctx, -1);
		for (j = 0; j < len; j++) {
			if (duk_get_prop_index(ctx, -1, j)) {
				/* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */
				duk_def_prop_index(ctx, -3, idx++, DUK_PROPDESC_FLAGS_WEC);
				idx_last = idx;
			} else {
				/* XXX: according to E5.1 Section 15.4.4.4 nonexistent trailing
				 * elements do not affect 'length' but test262 disagrees.  Work
				 * as E5.1 mandates for now and don't touch idx_last.
				 */
				idx++;
				duk_pop(ctx);
			}
		}
		duk_pop(ctx);
	}

	duk_push_number(ctx, (double) idx_last);
	duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);

	DUK_ASSERT_TOP(ctx, n + 1);
	return 1;
}
Exemple #6
0
int duk_bi_array_prototype_iter_shared(duk_context *ctx) {
	int len;
	int i;
	int k;
	int bval;
	int iter_type = duk_get_magic(ctx);
	duk_uint32_t res_length = 0;

	/* each call this helper serves has nargs==2 */
	DUK_ASSERT_TOP(ctx, 2);

	len = duk__push_this_obj_len_u32(ctx);
	if (!duk_is_callable(ctx, 0)) {
		goto type_error;
	}
	/* if thisArg not supplied, behave as if undefined was supplied */

	if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) {
		duk_push_array(ctx);
	} else {
		duk_push_undefined(ctx);
	}

	/* stack[0] = callback
	 * stack[1] = thisArg
	 * stack[2] = object
	 * stack[3] = ToUint32(length)  (unused, but avoid unnecessary pop)
	 * stack[4] = result array (or undefined)
	 */

	k = 0;  /* result index for filter() */
	for (i = 0; i < len; i++) {
		DUK_ASSERT_TOP(ctx, 5);

		if (!duk_get_prop_index(ctx, 2, i)) {
			duk_pop(ctx);
			continue;
		}

		/* The original value needs to be preserved for filter(), hence
		 * this funny order.  We can't re-get the value because of side
		 * effects.
		 */

		duk_dup(ctx, 0);
		duk_dup(ctx, 1);
		duk_dup(ctx, -3);
		duk_push_int(ctx, i);
		duk_dup(ctx, 2);  /* [ ... val callback thisArg val i obj ] */
		duk_call_method(ctx, 3); /* -> [ ... val retval ] */

		switch (iter_type) {
		case DUK__ITER_EVERY:
			bval = duk_to_boolean(ctx, -1);
			if (!bval) {
				/* stack top contains 'false' */
				return 1;
			}
			break;
		case DUK__ITER_SOME:
			bval = duk_to_boolean(ctx, -1);
			if (bval) {
				/* stack top contains 'true' */
				return 1;
			}
			break;
		case DUK__ITER_FOREACH:
			/* nop */
			break;
		case DUK__ITER_MAP:
			duk_dup(ctx, -1);
			duk_def_prop_index(ctx, 4, i, DUK_PROPDESC_FLAGS_WEC);  /* retval to result[i] */
			res_length = i + 1;
			break;
		case DUK__ITER_FILTER:
			bval = duk_to_boolean(ctx, -1);
			if (bval) {
				duk_dup(ctx, -2);  /* orig value */
				duk_def_prop_index(ctx, 4, k, DUK_PROPDESC_FLAGS_WEC);
				k++;
				res_length = k;
			}
			break;
		default:
			DUK_UNREACHABLE();
			break;
		}
		duk_pop_2(ctx);

		DUK_ASSERT_TOP(ctx, 5);
	}

	switch (iter_type) {
	case DUK__ITER_EVERY:
		duk_push_true(ctx);
		break;
	case DUK__ITER_SOME:
		duk_push_false(ctx);
		break;
	case DUK__ITER_FOREACH:
		duk_push_undefined(ctx);
		break;
	case DUK__ITER_MAP:
	case DUK__ITER_FILTER:
		DUK_ASSERT_TOP(ctx, 5);
		DUK_ASSERT(duk_is_array(ctx, -1));  /* topmost element is the result array already */
		duk_push_number(ctx, (double) res_length);  /* FIXME */
		duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
		break;
	default:
		DUK_UNREACHABLE();
		break;
	}

	return 1;

 type_error:
	return DUK_RET_TYPE_ERROR;
}