Пример #1
0
int duk_bi_array_prototype_push(duk_context *ctx) {
	/* Note: 'this' is not necessarily an Array object.  The push()
	 * algorithm is supposed to work for other kinds of objects too,
	 * so the algorithm has e.g. an explicit update for the 'length'
	 * property which is normally "magical" in arrays.
	 */

	double len;
	int i, n;

	n = duk_get_top(ctx);
	len = (double) duk__push_this_obj_len_u32(ctx);

	/* [ arg1 ... argN obj length ] */

	/* Note: we keep track of length with a double instead of a 32-bit
	 * (unsigned) int because the length can go beyond 32 bits and the
	 * final length value is NOT wrapped to 32 bits on this call.
	 */

	for (i = 0; i < n; i++) {
		duk_push_number(ctx, len);
		duk_dup(ctx, i);
		duk_put_prop(ctx, -4);  /* FIXME: "Throw" */
		len += 1.0;
	}

	duk_push_number(ctx, len);
	duk_dup_top(ctx);
	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);

	/* [ arg1 ... argN obj length new_length ] */
	return 1;
}
Пример #2
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) {
	duk_idx_t nargs;
	duk_uint32_t len;
	duk_uint32_t i;

	nargs = duk_get_top(ctx);
	len = duk__push_this_obj_len_u32(ctx);

	/* stack[0...nargs-1] = unshift args (vararg)
	 * stack[nargs] = ToObject(this)
	 * stack[nargs+1] = ToUint32(length)
	 */

	DUK_ASSERT_TOP(ctx, nargs + 2);

	/* Note: unshift() may operate on indices above unsigned 32-bit range
	 * and the final length may be >= 2**32.  However, we restrict the
	 * final result to 32-bit range for practicality.
	 */

	if (len + (duk_uint32_t) nargs < len) {
		DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw"));
		return DUK_RET_RANGE_ERROR;
	}

	i = len;
	while (i > 0) {
		DUK_ASSERT_TOP(ctx, nargs + 2);
		i--;
		/* k+argCount-1; note that may be above 32-bit range */

		if (duk_get_prop_index(ctx, -2, (duk_uarridx_t) i)) {
			/* fromPresent = true */
			/* [ ... ToObject(this) ToUint32(length) val ] */
			duk_put_prop_index(ctx, -3, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
		} else {
			/* fromPresent = false */
			/* [ ... ToObject(this) ToUint32(length) val ] */
			duk_pop(ctx);
			duk_del_prop_index(ctx, -2, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
		}
		DUK_ASSERT_TOP(ctx, nargs + 2);
	}

	for (i = 0; i < (duk_uint32_t) nargs; i++) {
		DUK_ASSERT_TOP(ctx, nargs + 2);
		duk_dup(ctx, i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
		duk_put_prop_index(ctx, -3, (duk_uarridx_t) i);
		DUK_ASSERT_TOP(ctx, nargs + 2);
	}

	DUK_ASSERT_TOP(ctx, nargs + 2);
	duk_push_u32(ctx, len + nargs);
	duk_dup_top(ctx);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);
	return 1;
}
Пример #3
0
DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) {
	/* Range limited to [0, 0x7fffffff] range, i.e. range that can be
	 * represented with duk_int32_t.  Use this when the method doesn't
	 * handle the full 32-bit unsigned range correctly.
	 */
	duk_uint32_t ret = duk__push_this_obj_len_u32(ctx);
	if (DUK_UNLIKELY(ret >= 0x80000000UL)) {
		DUK_ERROR((duk_hthread *) ctx, DUK_ERR_INTERNAL_ERROR, DUK_STR_ARRAY_LENGTH_OVER_2G);
	}
	return ret;
}
Пример #4
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;
}
Пример #5
0
int duk_bi_array_prototype_unshift(duk_context *ctx) {
	unsigned int nargs;
	unsigned int len;
	unsigned int i;
	double final_len;

	/* FIXME: duk_get_top return type */
	nargs = (unsigned int) duk_get_top(ctx);
	len = duk__push_this_obj_len_u32(ctx);

	/* stack[0...nargs-1] = unshift args (vararg)
	 * stack[nargs] = ToObject(this)
	 * stack[nargs+1] = ToUint32(length)
	 */

	DUK_ASSERT_TOP(ctx, nargs + 2);

	/* Note: unshift() may operate on indices above unsigned 32-bit range
	 * and the final length may be >= 2**32.  Hence we use 'double' vars
	 * here, when appropriate.
	 */

	i = len;
	while (i > 0) {
		DUK_ASSERT_TOP(ctx, nargs + 2);
		i--;
		duk_push_number(ctx, ((double) i) + ((double) nargs));  /* k+argCount-1; note that may be above 32-bit range */
		if (duk_get_prop_index(ctx, -3, i)) {
			/* fromPresent = true */
			/* [ ... ToObject(this) ToUint32(length) to val ] */
			duk_put_prop(ctx, -4);  /* -> [ ... ToObject(this) ToUint32(length) ] */  /* FIXME: Throw */
		} else {
			/* fromPresent = false */
			/* [ ... ToObject(this) ToUint32(length) to val ] */
			duk_pop(ctx);
			duk_del_prop(ctx, -3);  /* -> [ ... ToObject(this) ToUint32(length) ] */  /* FIXME: Throw */
		}
		DUK_ASSERT_TOP(ctx, nargs + 2);
	}

	for (i = 0; i < nargs; i++) {
		DUK_ASSERT_TOP(ctx, nargs + 2);
		duk_dup(ctx, i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
		duk_put_prop_index(ctx, -3, i);  /* FIXME: Throw */
		DUK_ASSERT_TOP(ctx, nargs + 2);
	}

	DUK_ASSERT_TOP(ctx, nargs + 2);
	final_len = ((double) len) + ((double) nargs);
	duk_push_number(ctx, final_len);
	duk_dup_top(ctx);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);  /* FIXME: Throw */
	return 1;
}
Пример #6
0
int duk_bi_array_prototype_sort(duk_context *ctx) {
	unsigned int len;

	len = duk__push_this_obj_len_u32(ctx);

	/* stack[0] = compareFn
	 * stack[1] = ToObject(this)
	 * stack[2] = ToUint32(length)
	 */

	duk__array_qsort(ctx, 0, len - 1);

	DUK_ASSERT_TOP(ctx, 3);
	duk_pop(ctx);
	return 1;  /* return ToObject(this) */
}
Пример #7
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) {
	duk_uint32_t len;
	duk_uint32_t middle;
	duk_uint32_t lower, upper;
	duk_bool_t have_lower, have_upper;

	len = duk__push_this_obj_len_u32(ctx);
	middle = len / 2;

	/* If len <= 1, middle will be 0 and for-loop bails out
	 * immediately (0 < 0 -> false).
	 */

	for (lower = 0; lower < middle; lower++) {
		DUK_ASSERT(len >= 2);
		DUK_ASSERT_TOP(ctx, 2);

		DUK_ASSERT(len >= lower + 1);
		upper = len - lower - 1;

		have_lower = duk_get_prop_index(ctx, -2, (duk_uarridx_t) lower);
		have_upper = duk_get_prop_index(ctx, -3, (duk_uarridx_t) upper);

		/* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */

		if (have_upper) {
			duk_put_prop_index(ctx, -4, (duk_uarridx_t) lower);
		} else {
			duk_del_prop_index(ctx, -4, (duk_uarridx_t) lower);
			duk_pop(ctx);
		}

		if (have_lower) {
			duk_put_prop_index(ctx, -3, (duk_uarridx_t) upper);
		} else {
			duk_del_prop_index(ctx, -3, (duk_uarridx_t) upper);
			duk_pop(ctx);
		}

		DUK_ASSERT_TOP(ctx, 2);
	}

	DUK_ASSERT_TOP(ctx, 2);
	duk_pop(ctx);  /* -> [ ToObject(this) ] */
	return 1;
}
Пример #8
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx) {
	duk_uint32_t len;
	duk_uint32_t idx;

	DUK_ASSERT_TOP(ctx, 0);
	len = duk__push_this_obj_len_u32(ctx);
	if (len == 0) {
		duk_push_int(ctx, 0);
		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
		return 0;
	}
	idx = len - 1;

	duk_get_prop_index(ctx, 0, (duk_uarridx_t) idx);
	duk_del_prop_index(ctx, 0, (duk_uarridx_t) idx);
	duk_push_u32(ctx, idx);
	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
	return 1;
}
Пример #9
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) {
	/* Note: 'this' is not necessarily an Array object.  The push()
	 * algorithm is supposed to work for other kinds of objects too,
	 * so the algorithm has e.g. an explicit update for the 'length'
	 * property which is normally "magical" in arrays.
	 */

	duk_uint32_t len;
	duk_idx_t i, n;

	n = duk_get_top(ctx);
	len = duk__push_this_obj_len_u32(ctx);

	/* [ arg1 ... argN obj length ] */

	/* Technically Array.prototype.push() can create an Array with length
	 * longer than 2^32-1, i.e. outside the 32-bit range.  The final length
	 * is *not* wrapped to 32 bits in the specification.
	 *
	 * This implementation tracks length with a uint32 because it's much
	 * more practical.
	 *
	 * See: test-bi-array-push-maxlen.js.
	 */

	if (len + (duk_uint32_t) n < len) {
		DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
		return DUK_RET_RANGE_ERROR;
	}

	for (i = 0; i < n; i++) {
		duk_dup(ctx, i);
		duk_put_prop_index(ctx, -3, len + i);
	}
	len += n;

	duk_push_u32(ctx, len);
	duk_dup_top(ctx);
	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);

	/* [ arg1 ... argN obj length new_length ] */
	return 1;
}
Пример #10
0
int duk_bi_array_prototype_pop(duk_context *ctx) {
	unsigned int len;
	unsigned int idx;

	DUK_ASSERT_TOP(ctx, 0);
	len = duk__push_this_obj_len_u32(ctx);
	if (len == 0) {
		duk_push_int(ctx, 0);
		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);  /* FIXME: Throw */
		return 0;
	}
	idx = len - 1;

	duk_get_prop_index(ctx, 0, idx);
	duk_del_prop_index(ctx, 0, idx);  /* FIXME: Throw */
	duk_push_int(ctx, idx);  /* FIXME: unsigned */
	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);  /* FIXME: Throw */
	return 1;
}
Пример #11
0
int duk_bi_array_prototype_reverse(duk_context *ctx) {
	unsigned int len;
	unsigned int middle;
	unsigned int lower, upper;
	int have_lower, have_upper;

	len = duk__push_this_obj_len_u32(ctx);
	middle = len / 2;

	for (lower = 0; lower < middle; lower++) {
		DUK_ASSERT_TOP(ctx, 2);

		upper = len - lower - 1;

		have_lower = duk_get_prop_index(ctx, -2, lower);
		have_upper = duk_get_prop_index(ctx, -3, upper);

		/* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */

		if (have_upper) {
			duk_put_prop_index(ctx, -4, lower);  /* FIXME: Throw */
		} else {
			duk_del_prop_index(ctx, -4, lower);
			duk_pop(ctx);
		}

		if (have_lower) {
			duk_put_prop_index(ctx, -3, upper);
		} else {
			duk_del_prop_index(ctx, -3, upper);
			duk_pop(ctx);
		}

		DUK_ASSERT_TOP(ctx, 2);
	}

	DUK_ASSERT_TOP(ctx, 2);
	duk_pop(ctx);  /* -> [ ToObject(this) ] */
	return 1;
}
Пример #12
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) {
	duk_uint32_t len;
	duk_uint32_t i;

	len = duk__push_this_obj_len_u32(ctx);
	if (len == 0) {
		duk_push_int(ctx, 0);
		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
		return 0;
	}

	duk_get_prop_index(ctx, 0, 0);

	/* stack[0] = object (this)
	 * stack[1] = ToUint32(length)
	 * stack[2] = elem at index 0 (retval)
	 */

	for (i = 1; i < len; i++) {
		DUK_ASSERT_TOP(ctx, 3);
		if (duk_get_prop_index(ctx, 0, (duk_uarridx_t) i)) {
			/* fromPresent = true */
			duk_put_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
		} else {
			/* fromPresent = false */
			duk_del_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
			duk_pop(ctx);
		}
	}
	duk_del_prop_index(ctx, 0, (duk_uarridx_t) (len - 1));

	duk_push_u32(ctx, (duk_uint32_t) (len - 1));
	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);

	DUK_ASSERT_TOP(ctx, 3);
	return 1;
}
Пример #13
0
int duk_bi_array_prototype_shift(duk_context *ctx) {
	unsigned int len;
	unsigned int i;

	len = duk__push_this_obj_len_u32(ctx);
	if (len == 0) {
		duk_push_int(ctx, 0);
		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);  /* FIXME: Throw */
		return 0;
	}

	duk_get_prop_index(ctx, 0, 0);

	/* stack[0] = object (this)
	 * stack[1] = ToUint32(length)
	 * stack[2] = elem at index 0 (retval)
	 */

	for (i = 1; i < len; i++) {
		DUK_ASSERT_TOP(ctx, 3);
		if (duk_get_prop_index(ctx, 0, i)) {
			/* fromPresent = true */
			duk_put_prop_index(ctx, 0, i - 1);  /* FIXME: Throw */
		} else {
			/* fromPresent = false */
			duk_del_prop_index(ctx, 0, i - 1);
			duk_pop(ctx);
		}
	}
	duk_del_prop_index(ctx, 0, len - 1);  /* FIXME: Throw */

	duk_push_number(ctx, (double) (len - 1));  /* FIXME: push uint */
	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);

	DUK_ASSERT_TOP(ctx, 3);
	return 1;
}
Пример #14
0
int duk_bi_array_prototype_indexof_shared(duk_context *ctx) {
	/* FIXME: types, ensure loop below works when fixed (i must be able to go negative right now) */
	int nargs;
	int i, len;
	int fromIndex;
	int idx_step = duk_get_magic(ctx);  /* idx_step is +1 for indexOf, -1 for lastIndexOf */

	/* lastIndexOf() needs to be a vararg function because we must distinguish
	 * between an undefined fromIndex and a "not given" fromIndex; indexOf() is
	 * made vararg for symmetry although it doesn't strictly need to be.
	 */

	nargs = duk_get_top(ctx);
	duk_set_top(ctx, 2);

	len = duk__push_this_obj_len_u32(ctx);
	if (len == 0) {
		goto not_found;
	}

	/* Index clamping is a bit tricky, we must ensure that we'll only iterate
	 * through elements that exist and that the specific requirements from E5.1
	 * Sections 15.4.4.14 and 15.4.4.15 are fulfilled; especially:
	 *
	 *   - indexOf: clamp to [-len,len], negative handling -> [0,len],
	 *     if clamped result is len, for-loop bails out immediately
	 *
	 *   - lastIndexOf: clamp to [-len-1, len-1], negative handling -> [-1, len-1],
	 *     if clamped result is -1, for-loop bails out immediately
	 *
	 * If fromIndex is not given, ToInteger(undefined) = 0, which is correct
	 * for indexOf() but incorrect for lastIndexOf().  Hence special handling,
	 * and why lastIndexOf() needs to be a vararg function.
	 */

	if (nargs >= 2) {
		/* indexOf: clamp fromIndex to [-len, len]
		 * (if fromIndex == len, for-loop terminates directly)
		 *
		 * lastIndexOf: clamp fromIndex to [-len - 1, len - 1]
		 * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly)
		 */
		fromIndex = duk_to_int_clamped(ctx,
		                               1,
		                               (idx_step > 0 ? -len : -len - 1),
		                               (idx_step > 0 ? len : len - 1));
		if (fromIndex < 0) {
			/* for lastIndexOf, result may be -1 (mark immediate termination) */
			fromIndex = len + fromIndex;
		}
	} else {
		/* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but
		 * handle both indexOf and lastIndexOf specially here.
		 */
		if (idx_step > 0) {
			fromIndex = 0;
		} else {
			fromIndex = len - 1;
		}
	}

	/* stack[0] = searchElement
	 * stack[1] = fromIndex
	 * stack[2] = object
	 * stack[3] = length (not needed, but not popped above)
	 */

	for (i = fromIndex;
	     i >= 0 && i < len;
	     i += idx_step) {
		DUK_ASSERT_TOP(ctx, 4);

		if (duk_get_prop_index(ctx, 2, i)) {
			DUK_ASSERT_TOP(ctx, 5);
			if (duk_strict_equals(ctx, 0, 4)) {
				duk_push_int(ctx, i);
				return 1;
			}
		}

		duk_pop(ctx);
	}

 not_found:
	duk_push_int(ctx, -1);
	return 1;
}
Пример #15
0
int duk_bi_array_prototype_reduce_shared(duk_context *ctx) {
	int nargs;
	int have_acc;
	int i, len;
	int idx_step = duk_get_magic(ctx);  /* idx_step is +1 for reduce, -1 for reduceRight */

	/* We're a varargs function because we need to detect whether
	 * initialValue was given or not.
	 */
	nargs = duk_get_top(ctx);
	DUK_DDD(DUK_DDDPRINT("nargs=%d", nargs));

	duk_set_top(ctx, 2);
	len = duk__push_this_obj_len_u32(ctx);
	if (!duk_is_callable(ctx, 0)) {
		goto type_error;
	}

	/* stack[0] = callback fn
	 * stack[1] = initialValue
	 * stack[2] = object (coerced this)
	 * stack[3] = length (not needed, but not popped above)
	 * stack[4] = accumulator
	 */

	have_acc = 0;
	if (nargs >= 2) {
		duk_dup(ctx, 1);
		have_acc = 1;
	}
	DUK_DDD(DUK_DDDPRINT("have_acc=%d, acc=%!T", have_acc, duk_get_tval(ctx, 3)));

	for (i = (idx_step >= 0 ? 0 : len - 1);
	     i >= 0 && i < len;
	     i += idx_step) {
		DUK_DDD(DUK_DDDPRINT("i=%d, len=%d, have_acc=%d, top=%d, acc=%!T",
		                     i, len, have_acc, duk_get_top(ctx), duk_get_tval(ctx, 4)));

		DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) ||
		           (!have_acc && duk_get_top(ctx) == 4));

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

		if (!have_acc) {
			DUK_ASSERT_TOP(ctx, 4);
			duk_get_prop_index(ctx, 2, i);
			have_acc = 1;
			DUK_ASSERT_TOP(ctx, 5);
		} else {
			DUK_ASSERT_TOP(ctx, 5);
			duk_dup(ctx, 0);
			duk_dup(ctx, 4);
			duk_get_prop_index(ctx, 2, i);
			duk_push_int(ctx, i);  /* FIXME: type */
			duk_dup(ctx, 2);
			DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T",
			                     duk_get_tval(ctx, -5), duk_get_tval(ctx, -4), duk_get_tval(ctx, -3),
			                     duk_get_tval(ctx, -2), duk_get_tval(ctx, -1)));
			duk_call(ctx, 4);
			DUK_DDD(DUK_DDDPRINT("-> result: %!T", duk_get_tval(ctx, -1)));
			duk_replace(ctx, 4);
			DUK_ASSERT_TOP(ctx, 5);
		}
	}

	if (!have_acc) {
		goto type_error;
	}

	DUK_ASSERT_TOP(ctx, 5);
	return 1;

 type_error:
	return DUK_RET_TYPE_ERROR;
}
Пример #16
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) {
	duk_uint32_t len, count;
	duk_uint32_t idx;
	duk_small_int_t to_locale_string = duk_get_current_magic(ctx);
	duk_idx_t valstack_required;

	/* For join(), nargs is 1.  For toLocaleString(), nargs is 0 and
	 * setting the top essentially pushes an undefined to the stack,
	 * thus defaulting to a comma separator.
	 */
	duk_set_top(ctx, 1);
	if (duk_is_undefined(ctx, 0)) {
		duk_pop(ctx);
		duk_push_hstring_stridx(ctx, DUK_STRIDX_COMMA);
	} else {
		duk_to_string(ctx, 0);
	}

	len = duk__push_this_obj_len_u32(ctx);

	/* [ sep ToObject(this) len ] */

	DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu",
	                     (duk_tval *) duk_get_tval(ctx, 0),
	                     (duk_tval *) duk_get_tval(ctx, 1),
	                     (unsigned long) len));

	/* The extra (+4) is tight. */
	valstack_required = (len >= DUK__ARRAY_MID_JOIN_LIMIT ?
	                     DUK__ARRAY_MID_JOIN_LIMIT : len) + 4;
	duk_require_stack(ctx, valstack_required);

	duk_dup(ctx, 0);

	/* [ sep ToObject(this) len sep ] */

	count = 0;
	idx = 0;
	for (;;) {
		if (count >= DUK__ARRAY_MID_JOIN_LIMIT ||   /* intermediate join to avoid valstack overflow */
		    idx >= len) { /* end of loop (careful with len==0) */
			/* [ sep ToObject(this) len sep str0 ... str(count-1) ] */
			DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld",
			                     (long) count, (long) idx, (long) len));
			duk_join(ctx, (duk_idx_t) count);  /* -> [ sep ToObject(this) len str ] */
			duk_dup(ctx, 0);                   /* -> [ sep ToObject(this) len str sep ] */
			duk_insert(ctx, -2);               /* -> [ sep ToObject(this) len sep str ] */
			count = 1;
		}
		if (idx >= len) {
			/* if true, the stack already contains the final result */
			break;
		}

		duk_get_prop_index(ctx, 1, (duk_uarridx_t) idx);
		if (duk_is_null_or_undefined(ctx, -1)) {
			duk_pop(ctx);
			duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
		} else {
			if (to_locale_string) {
				duk_to_object(ctx, -1);
				duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_LOCALE_STRING);
				duk_insert(ctx, -2);  /* -> [ ... toLocaleString ToObject(val) ] */
				duk_call_method(ctx, 0);
				duk_to_string(ctx, -1);
			} else {
				duk_to_string(ctx, -1);
			}
		}

		count++;
		idx++;
	}

	/* [ sep ToObject(this) len sep result ] */

	return 1;
}
Пример #17
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) {
	duk_idx_t nargs;
	duk_bool_t have_acc;
	duk_uint32_t i, len;
	duk_small_int_t idx_step = duk_get_current_magic(ctx);  /* idx_step is +1 for reduce, -1 for reduceRight */

	/* We're a varargs function because we need to detect whether
	 * initialValue was given or not.
	 */
	nargs = duk_get_top(ctx);
	DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs));

	duk_set_top(ctx, 2);
	len = duk__push_this_obj_len_u32(ctx);
	if (!duk_is_callable(ctx, 0)) {
		goto type_error;
	}

	/* stack[0] = callback fn
	 * stack[1] = initialValue
	 * stack[2] = object (coerced this)
	 * stack[3] = length (not needed, but not popped above)
	 * stack[4] = accumulator
	 */

	have_acc = 0;
	if (nargs >= 2) {
		duk_dup(ctx, 1);
		have_acc = 1;
	}
	DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T",
	                     (long) have_acc, (duk_tval *) duk_get_tval(ctx, 3)));

	/* For len == 0, i is initialized to len - 1 which underflows.
	 * The condition (i < len) will then exit the for-loop on the
	 * first round which is correct.  Similarly, loop termination
	 * happens by i underflowing.
	 */

	for (i = (idx_step >= 0 ? 0 : len - 1);
	     i < len;  /* i >= 0 would always be true */
	     i += idx_step) {
		DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T",
		                     (long) i, (long) len, (long) have_acc,
		                     (long) duk_get_top(ctx),
		                     (duk_tval *) duk_get_tval(ctx, 4)));

		DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) ||
		           (!have_acc && duk_get_top(ctx) == 4));

		if (!duk_has_prop_index(ctx, 2, (duk_uarridx_t) i)) {
			continue;
		}

		if (!have_acc) {
			DUK_ASSERT_TOP(ctx, 4);
			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
			have_acc = 1;
			DUK_ASSERT_TOP(ctx, 5);
		} else {
			DUK_ASSERT_TOP(ctx, 5);
			duk_dup(ctx, 0);
			duk_dup(ctx, 4);
			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
			duk_push_u32(ctx, i);
			duk_dup(ctx, 2);
			DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T",
			                     (duk_tval *) duk_get_tval(ctx, -5), (duk_tval *) duk_get_tval(ctx, -4),
			                     (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2),
			                     (duk_tval *) duk_get_tval(ctx, -1)));
			duk_call(ctx, 4);
			DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
			duk_replace(ctx, 4);
			DUK_ASSERT_TOP(ctx, 5);
		}
	}

	if (!have_acc) {
		goto type_error;
	}

	DUK_ASSERT_TOP(ctx, 5);
	return 1;

 type_error:
	return DUK_RET_TYPE_ERROR;
}
Пример #18
0
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) {
	duk_uint32_t len;
	duk_uint32_t i;
	duk_uarridx_t k;
	duk_bool_t bval;
	duk_small_int_t iter_type = duk_get_current_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);
	duk_require_callable(ctx, 0);
	/* 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, (duk_uarridx_t) i)) {
#if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER)
			/* Real world behavior for map(): trailing non-existent
			 * elements don't invoke the user callback, but are still
			 * counted towards result 'length'.
			 */
			if (iter_type == DUK__ITER_MAP) {
				res_length = i + 1;
			}
#else
			/* Standard behavior for map(): trailing non-existent
			 * elements don't invoke the user callback and are not
			 * counted towards result 'length'.
			 */
#endif
			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_u32(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_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) i);  /* 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_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) k);
				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_u32(ctx, res_length);
		duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
		break;
	default:
		DUK_UNREACHABLE();
		break;
	}

	return 1;
}
Пример #19
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;
}
Пример #20
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;
}