1 /*
2  *  Array built-ins
3  *
4  *  Note that most Array built-ins are intentionally generic and work even
5  *  when the 'this' binding is not an Array instance.  To ensure this,
6  *  Array algorithms do not assume "magical" Array behavior for the "length"
7  *  property, for instance.
8  *
9  *  XXX: the "Throw" flag should be set for (almost?) all [[Put]] and
10  *  [[Delete]] operations, but it's currently false throughout.  Go through
11  *  all put/delete cases and check throw flag use.  Need a new API primitive
12  *  which allows throws flag to be specified.
13  *
14  *  XXX: array lengths above 2G won't work reliably.  There are many places
15  *  where one needs a full signed 32-bit range ([-0xffffffff, 0xffffffff],
16  *  i.e. -33- bits).  Although array 'length' cannot be written to be outside
17  *  the unsigned 32-bit range (E5.1 Section 15.4.5.1 throws a RangeError if so)
18  *  some intermediate values may be above 0xffffffff and this may not be always
19  *  correctly handled now (duk_uint32_t is not enough for all algorithms).
20  *
21  *  For instance, push() can legitimately write entries beyond length 0xffffffff
22  *  and cause a RangeError only at the end.  To do this properly, the current
23  *  push() implementation tracks the array index using a 'double' instead of a
24  *  duk_uint32_t (which is somewhat awkward).  See test-bi-array-push-maxlen.js.
25  *
26  *  On using "put" vs. "def" prop
27  *  =============================
28  *
29  *  Code below must be careful to use the appropriate primitive as it matters
30  *  for compliance.  When using "put" there may be inherited properties in
31  *  Array.prototype which cause side effects when values are written.  When
32  *  using "define" there are no such side effects, and many test262 test cases
33  *  check for this (for real world code, such side effects are very rare).
34  *  Both "put" and "define" are used in the E5.1 specification; as a rule,
35  *  "put" is used when modifying an existing array (or a non-array 'this'
36  *  binding) and "define" for setting values into a fresh result array.
37  *
38  *  Also note that Array instance 'length' should be writable, but not
39  *  enumerable and definitely not configurable: even Duktape code internally
40  *  assumes that an Array instance will always have a 'length' property.
41  *  Preventing deletion of the property is critical.
42  */
43 
44 #include "duk_internal.h"
45 
46 /* Perform an intermediate join when this many elements have been pushed
47  * on the value stack.
48  */
49 #define  DUK__ARRAY_MID_JOIN_LIMIT  4096
50 
51 /* Shared entry code for many Array built-ins.  Note that length is left
52  * on stack (it could be popped, but that's not necessary).
53  */
duk__push_this_obj_len_u32(duk_context * ctx)54 DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_context *ctx) {
55 	duk_uint32_t len;
56 
57 	(void) duk_push_this_coercible_to_object(ctx);
58 	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH);
59 	len = duk_to_uint32(ctx, -1);
60 
61 	/* -> [ ... ToObject(this) ToUint32(length) ] */
62 	return len;
63 }
64 
duk__push_this_obj_len_u32_limited(duk_context * ctx)65 DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) {
66 	/* Range limited to [0, 0x7fffffff] range, i.e. range that can be
67 	 * represented with duk_int32_t.  Use this when the method doesn't
68 	 * handle the full 32-bit unsigned range correctly.
69 	 */
70 	duk_uint32_t ret = duk__push_this_obj_len_u32(ctx);
71 	if (DUK_UNLIKELY(ret >= 0x80000000UL)) {
72 		DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_ARRAY_LENGTH_OVER_2G);
73 	}
74 	return ret;
75 }
76 
77 /*
78  *  Constructor
79  */
80 
duk_bi_array_constructor(duk_context * ctx)81 DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) {
82 	duk_idx_t nargs;
83 	duk_double_t d;
84 	duk_uint32_t len;
85 	duk_idx_t i;
86 
87 	nargs = duk_get_top(ctx);
88 	duk_push_array(ctx);
89 
90 	if (nargs == 1 && duk_is_number(ctx, 0)) {
91 		/* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */
92 		d = duk_get_number(ctx, 0);
93 		len = duk_to_uint32(ctx, 0);
94 		if (((duk_double_t) len) != d) {
95 			return DUK_RET_RANGE_ERROR;
96 		}
97 
98 		/* XXX: if 'len' is low, may want to ensure array part is kept:
99 		 * the caller is likely to want a dense array.
100 		 */
101 		duk_push_u32(ctx, len);
102 		duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);  /* [ ToUint32(len) array ToUint32(len) ] -> [ ToUint32(len) array ] */
103 		return 1;
104 	}
105 
106 	/* XXX: optimize by creating array into correct size directly, and
107 	 * operating on the array part directly; values can be memcpy()'d from
108 	 * value stack directly as long as refcounts are increased.
109 	 */
110 	for (i = 0; i < nargs; i++) {
111 		duk_dup(ctx, i);
112 		duk_xdef_prop_index_wec(ctx, -2, (duk_uarridx_t) i);
113 	}
114 
115 	duk_push_u32(ctx, (duk_uint32_t) nargs);
116 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
117 	return 1;
118 }
119 
120 /*
121  *  isArray()
122  */
123 
duk_bi_array_constructor_is_array(duk_context * ctx)124 DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx) {
125 	duk_hobject *h;
126 
127 	h = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_ARRAY);
128 	duk_push_boolean(ctx, (h != NULL));
129 	return 1;
130 }
131 
132 /*
133  *  toString()
134  */
135 
duk_bi_array_prototype_to_string(duk_context * ctx)136 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) {
137 	(void) duk_push_this_coercible_to_object(ctx);
138 	duk_get_prop_stridx(ctx, -1, DUK_STRIDX_JOIN);
139 
140 	/* [ ... this func ] */
141 	if (!duk_is_callable(ctx, -1)) {
142 		/* Fall back to the initial (original) Object.toString().  We don't
143 		 * currently have pointers to the built-in functions, only the top
144 		 * level global objects (like "Array") so this is now done in a bit
145 		 * of a hacky manner.  It would be cleaner to push the (original)
146 		 * function and use duk_call_method().
147 		 */
148 
149 		/* XXX: 'this' will be ToObject() coerced twice, which is incorrect
150 		 * but should have no visible side effects.
151 		 */
152 		DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString"));
153 		duk_set_top(ctx, 0);
154 		return duk_bi_object_prototype_to_string(ctx);  /* has access to 'this' binding */
155 	}
156 
157 	/* [ ... this func ] */
158 
159 	duk_insert(ctx, -2);
160 
161 	/* [ ... func this ] */
162 
163 	DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT",
164 	                     (duk_tval *) duk_get_tval(ctx, -2),
165 	                     (duk_tval *) duk_get_tval(ctx, -1)));
166 	duk_call_method(ctx, 0);
167 
168 	return 1;
169 }
170 
171 /*
172  *  concat()
173  */
174 
duk_bi_array_prototype_concat(duk_context * ctx)175 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) {
176 	duk_idx_t i, n;
177 	duk_uarridx_t idx, idx_last;
178 	duk_uarridx_t j, len;
179 	duk_hobject *h;
180 
181 	/* XXX: the insert here is a bit expensive if there are a lot of items.
182 	 * It could also be special cased in the outermost for loop quite easily
183 	 * (as the element is dup()'d anyway).
184 	 */
185 
186 	(void) duk_push_this_coercible_to_object(ctx);
187 	duk_insert(ctx, 0);
188 	n = duk_get_top(ctx);
189 	duk_push_array(ctx);  /* -> [ ToObject(this) item1 ... itemN arr ] */
190 
191 	/* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index()
192 	 * (which differs from the official algorithm).  If no error is thrown, this
193 	 * doesn't matter as the length is updated at the end.  However, if an error
194 	 * is thrown, the length will be unset.  That shouldn't matter because the
195 	 * caller won't get a reference to the intermediate value.
196 	 */
197 
198 	idx = 0;
199 	idx_last = 0;
200 	for (i = 0; i < n; i++) {
201 		DUK_ASSERT_TOP(ctx, n + 1);
202 
203 		/* [ ToObject(this) item1 ... itemN arr ] */
204 
205 		duk_dup(ctx, i);
206 		h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY);
207 		if (!h) {
208 			duk_xdef_prop_index_wec(ctx, -2, idx++);
209 			idx_last = idx;
210 			continue;
211 		}
212 
213 		/* [ ToObject(this) item1 ... itemN arr item(i) ] */
214 
215 		/* XXX: an array can have length higher than 32 bits; this is not handled
216 		 * correctly now.
217 		 */
218 		len = (duk_uarridx_t) duk_get_length(ctx, -1);
219 		for (j = 0; j < len; j++) {
220 			if (duk_get_prop_index(ctx, -1, j)) {
221 				/* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */
222 				duk_xdef_prop_index_wec(ctx, -3, idx++);
223 				idx_last = idx;
224 			} else {
225 				idx++;
226 				duk_pop(ctx);
227 #if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER)
228 				/* According to E5.1 Section 15.4.4.4 nonexistent trailing
229 				 * elements do not affect 'length' of the result.  Test262
230 				 * and other engines disagree, so update idx_last here too.
231 				 */
232 				idx_last = idx;
233 #else
234 				/* Strict standard behavior, ignore trailing elements for
235 				 * result 'length'.
236 				 */
237 #endif
238 			}
239 		}
240 		duk_pop(ctx);
241 	}
242 
243 	/* The E5.1 Section 15.4.4.4 algorithm doesn't set the length explicitly
244 	 * in the end, but because we're operating with an internal value which
245 	 * is known to be an array, this should be equivalent.
246 	 */
247 	duk_push_uarridx(ctx, idx_last);
248 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
249 
250 	DUK_ASSERT_TOP(ctx, n + 1);
251 	return 1;
252 }
253 
254 /*
255  *  join(), toLocaleString()
256  *
257  *  Note: checking valstack is necessary, but only in the per-element loop.
258  *
259  *  Note: the trivial approach of pushing all the elements on the value stack
260  *  and then calling duk_join() fails when the array contains a large number
261  *  of elements.  This problem can't be offloaded to duk_join() because the
262  *  elements to join must be handled here and have special handling.  Current
263  *  approach is to do intermediate joins with very large number of elements.
264  *  There is no fancy handling; the prefix gets re-joined multiple times.
265  */
266 
duk_bi_array_prototype_join_shared(duk_context * ctx)267 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) {
268 	duk_uint32_t len, count;
269 	duk_uint32_t idx;
270 	duk_small_int_t to_locale_string = duk_get_current_magic(ctx);
271 	duk_idx_t valstack_required;
272 
273 	/* For join(), nargs is 1.  For toLocaleString(), nargs is 0 and
274 	 * setting the top essentially pushes an undefined to the stack,
275 	 * thus defaulting to a comma separator.
276 	 */
277 	duk_set_top(ctx, 1);
278 	if (duk_is_undefined(ctx, 0)) {
279 		duk_pop(ctx);
280 		duk_push_hstring_stridx(ctx, DUK_STRIDX_COMMA);
281 	} else {
282 		duk_to_string(ctx, 0);
283 	}
284 
285 	len = duk__push_this_obj_len_u32(ctx);
286 
287 	/* [ sep ToObject(this) len ] */
288 
289 	DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu",
290 	                     (duk_tval *) duk_get_tval(ctx, 0),
291 	                     (duk_tval *) duk_get_tval(ctx, 1),
292 	                     (unsigned long) len));
293 
294 	/* The extra (+4) is tight. */
295 	valstack_required = (len >= DUK__ARRAY_MID_JOIN_LIMIT ?
296 	                     DUK__ARRAY_MID_JOIN_LIMIT : len) + 4;
297 	duk_require_stack(ctx, valstack_required);
298 
299 	duk_dup(ctx, 0);
300 
301 	/* [ sep ToObject(this) len sep ] */
302 
303 	count = 0;
304 	idx = 0;
305 	for (;;) {
306 		if (count >= DUK__ARRAY_MID_JOIN_LIMIT ||   /* intermediate join to avoid valstack overflow */
307 		    idx >= len) { /* end of loop (careful with len==0) */
308 			/* [ sep ToObject(this) len sep str0 ... str(count-1) ] */
309 			DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld",
310 			                     (long) count, (long) idx, (long) len));
311 			duk_join(ctx, (duk_idx_t) count);  /* -> [ sep ToObject(this) len str ] */
312 			duk_dup(ctx, 0);                   /* -> [ sep ToObject(this) len str sep ] */
313 			duk_insert(ctx, -2);               /* -> [ sep ToObject(this) len sep str ] */
314 			count = 1;
315 		}
316 		if (idx >= len) {
317 			/* if true, the stack already contains the final result */
318 			break;
319 		}
320 
321 		duk_get_prop_index(ctx, 1, (duk_uarridx_t) idx);
322 		if (duk_is_null_or_undefined(ctx, -1)) {
323 			duk_pop(ctx);
324 			duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
325 		} else {
326 			if (to_locale_string) {
327 				duk_to_object(ctx, -1);
328 				duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_LOCALE_STRING);
329 				duk_insert(ctx, -2);  /* -> [ ... toLocaleString ToObject(val) ] */
330 				duk_call_method(ctx, 0);
331 				duk_to_string(ctx, -1);
332 			} else {
333 				duk_to_string(ctx, -1);
334 			}
335 		}
336 
337 		count++;
338 		idx++;
339 	}
340 
341 	/* [ sep ToObject(this) len sep result ] */
342 
343 	return 1;
344 }
345 
346 /*
347  *  pop(), push()
348  */
349 
duk_bi_array_prototype_pop(duk_context * ctx)350 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx) {
351 	duk_uint32_t len;
352 	duk_uint32_t idx;
353 
354 	DUK_ASSERT_TOP(ctx, 0);
355 	len = duk__push_this_obj_len_u32(ctx);
356 	if (len == 0) {
357 		duk_push_int(ctx, 0);
358 		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
359 		return 0;
360 	}
361 	idx = len - 1;
362 
363 	duk_get_prop_index(ctx, 0, (duk_uarridx_t) idx);
364 	duk_del_prop_index(ctx, 0, (duk_uarridx_t) idx);
365 	duk_push_u32(ctx, idx);
366 	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
367 	return 1;
368 }
369 
duk_bi_array_prototype_push(duk_context * ctx)370 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) {
371 	/* Note: 'this' is not necessarily an Array object.  The push()
372 	 * algorithm is supposed to work for other kinds of objects too,
373 	 * so the algorithm has e.g. an explicit update for the 'length'
374 	 * property which is normally "magical" in arrays.
375 	 */
376 
377 	duk_uint32_t len;
378 	duk_idx_t i, n;
379 
380 	n = duk_get_top(ctx);
381 	len = duk__push_this_obj_len_u32(ctx);
382 
383 	/* [ arg1 ... argN obj length ] */
384 
385 	/* Technically Array.prototype.push() can create an Array with length
386 	 * longer than 2^32-1, i.e. outside the 32-bit range.  The final length
387 	 * is *not* wrapped to 32 bits in the specification.
388 	 *
389 	 * This implementation tracks length with a uint32 because it's much
390 	 * more practical.
391 	 *
392 	 * See: test-bi-array-push-maxlen.js.
393 	 */
394 
395 	if (len + (duk_uint32_t) n < len) {
396 		DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
397 		return DUK_RET_RANGE_ERROR;
398 	}
399 
400 	for (i = 0; i < n; i++) {
401 		duk_dup(ctx, i);
402 		duk_put_prop_index(ctx, -3, len + i);
403 	}
404 	len += n;
405 
406 	duk_push_u32(ctx, len);
407 	duk_dup_top(ctx);
408 	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);
409 
410 	/* [ arg1 ... argN obj length new_length ] */
411 	return 1;
412 }
413 
414 /*
415  *  sort()
416  *
417  *  Currently qsort with random pivot.  This is now really, really slow,
418  *  because there is no fast path for array parts.
419  *
420  *  Signed indices are used because qsort() leaves and degenerate cases
421  *  may use a negative offset.
422  */
423 
duk__array_sort_compare(duk_context * ctx,duk_int_t idx1,duk_int_t idx2)424 DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t idx1, duk_int_t idx2) {
425 	duk_bool_t have1, have2;
426 	duk_bool_t undef1, undef2;
427 	duk_small_int_t ret;
428 	duk_idx_t idx_obj = 1;  /* fixed offsets in valstack */
429 	duk_idx_t idx_fn = 0;
430 	duk_hstring *h1, *h2;
431 
432 	/* Fast exit if indices are identical.  This is valid for a non-existent property,
433 	 * for an undefined value, and almost always for ToString() coerced comparison of
434 	 * arbitrary values (corner cases where this is not the case include e.g. a an
435 	 * object with varying ToString() coercion).
436 	 *
437 	 * The specification does not prohibit "caching" of values read from the array, so
438 	 * assuming equality for comparing an index with itself falls into the category of
439 	 * "caching".
440 	 *
441 	 * Also, compareFn may be inconsistent, so skipping a call to compareFn here may
442 	 * have an effect on the final result.  The specification does not require any
443 	 * specific behavior for inconsistent compare functions, so again, this fast path
444 	 * is OK.
445 	 */
446 
447 	if (idx1 == idx2) {
448 		DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld -> indices identical, quick exit",
449 		                     (long) idx1, (long) idx2));
450 		return 0;
451 	}
452 
453 	have1 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx1);
454 	have2 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx2);
455 
456 	DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T",
457 	                     (long) idx1, (long) idx2, (long) have1, (long) have2,
458 	                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
459 
460 	if (have1) {
461 		if (have2) {
462 			;
463 		} else {
464 			ret = -1;
465 			goto pop_ret;
466 		}
467 	} else {
468 		if (have2) {
469 			ret = 1;
470 			goto pop_ret;
471 		} else {
472 			ret = 0;
473 			goto pop_ret;
474 		}
475 	}
476 
477 	undef1 = duk_is_undefined(ctx, -2);
478 	undef2 = duk_is_undefined(ctx, -1);
479 	if (undef1) {
480 		if (undef2) {
481 			ret = 0;
482 			goto pop_ret;
483 		} else {
484 			ret = 1;
485 			goto pop_ret;
486 		}
487 	} else {
488 		if (undef2) {
489 			ret = -1;
490 			goto pop_ret;
491 		} else {
492 			;
493 		}
494 	}
495 
496 	if (!duk_is_undefined(ctx, idx_fn)) {
497 		duk_double_t d;
498 
499 		/* no need to check callable; duk_call() will do that */
500 		duk_dup(ctx, idx_fn);    /* -> [ ... x y fn ] */
501 		duk_insert(ctx, -3);     /* -> [ ... fn x y ] */
502 		duk_call(ctx, 2);        /* -> [ ... res ] */
503 
504 		/* The specification is a bit vague what to do if the return
505 		 * value is not a number.  Other implementations seem to
506 		 * tolerate non-numbers but e.g. V8 won't apparently do a
507 		 * ToNumber().
508 		 */
509 
510 		/* XXX: best behavior for real world compatibility? */
511 
512 		d = duk_to_number(ctx, -1);
513 		if (d < 0.0) {
514 			ret = -1;
515 		} else if (d > 0.0) {
516 			ret = 1;
517 		} else {
518 			ret = 0;
519 		}
520 
521 		duk_pop(ctx);
522 		DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret));
523 		return ret;
524 	}
525 
526 	/* string compare is the default (a bit oddly) */
527 
528 	h1 = duk_to_hstring(ctx, -2);
529 	h2 = duk_to_hstring(ctx, -1);
530 	DUK_ASSERT(h1 != NULL);
531 	DUK_ASSERT(h2 != NULL);
532 
533 	ret = duk_js_string_compare(h1, h2);  /* retval is directly usable */
534 	goto pop_ret;
535 
536  pop_ret:
537 	duk_pop_2(ctx);
538 	DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret));
539 	return ret;
540 }
541 
duk__array_sort_swap(duk_context * ctx,duk_int_t l,duk_int_t r)542 DUK_LOCAL void duk__array_sort_swap(duk_context *ctx, duk_int_t l, duk_int_t r) {
543 	duk_bool_t have_l, have_r;
544 	duk_idx_t idx_obj = 1;  /* fixed offset in valstack */
545 
546 	if (l == r) {
547 		return;
548 	}
549 
550 	/* swap elements; deal with non-existent elements correctly */
551 	have_l = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
552 	have_r = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
553 
554 	if (have_r) {
555 		/* right exists, [[Put]] regardless whether or not left exists */
556 		duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
557 	} else {
558 		duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
559 		duk_pop(ctx);
560 	}
561 
562 	if (have_l) {
563 		duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
564 	} else {
565 		duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
566 		duk_pop(ctx);
567 	}
568 }
569 
570 #if defined(DUK_USE_DDDPRINT)
571 /* Debug print which visualizes the qsort partitioning process. */
duk__debuglog_qsort_state(duk_context * ctx,duk_int_t lo,duk_int_t hi,duk_int_t pivot)572 DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int_t hi, duk_int_t pivot) {
573 	char buf[4096];
574 	char *ptr = buf;
575 	duk_int_t i, n;
576 	n = (duk_int_t) duk_get_length(ctx, 1);
577 	if (n > 4000) {
578 		n = 4000;
579 	}
580 	*ptr++ = '[';
581 	for (i = 0; i < n; i++) {
582 		if (i == pivot) {
583 			*ptr++ = '|';
584 		} else if (i == lo) {
585 			*ptr++ = '<';
586 		} else if (i == hi) {
587 			*ptr++ = '>';
588 		} else if (i >= lo && i <= hi) {
589 			*ptr++ = '-';
590 		} else {
591 			*ptr++ = ' ';
592 		}
593 	}
594 	*ptr++ = ']';
595 	*ptr++ = '\0';
596 
597 	DUK_DDD(DUK_DDDPRINT("%s   (lo=%ld, hi=%ld, pivot=%ld)",
598 	                     (const char *) buf, (long) lo, (long) hi, (long) pivot));
599 }
600 #endif
601 
duk__array_qsort(duk_context * ctx,duk_int_t lo,duk_int_t hi)602 DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) {
603 	duk_hthread *thr = (duk_hthread *) ctx;
604 	duk_int_t p, l, r;
605 
606 	/* The lo/hi indices may be crossed and hi < 0 is possible at entry. */
607 
608 	DUK_DDD(DUK_DDDPRINT("duk__array_qsort: lo=%ld, hi=%ld, obj=%!T",
609 	                     (long) lo, (long) hi, (duk_tval *) duk_get_tval(ctx, 1)));
610 
611 	DUK_ASSERT_TOP(ctx, 3);
612 
613 	/* In some cases it may be that lo > hi, or hi < 0; these
614 	 * degenerate cases happen e.g. for empty arrays, and in
615 	 * recursion leaves.
616 	 */
617 
618 	/* trivial cases */
619 	if (hi - lo < 1) {
620 		DUK_DDD(DUK_DDDPRINT("degenerate case, return immediately"));
621 		return;
622 	}
623 	DUK_ASSERT(hi > lo);
624 	DUK_ASSERT(hi - lo + 1 >= 2);
625 
626 	/* randomized pivot selection */
627 	p = lo + (duk_util_tinyrandom_get_bits(thr, 30) % (hi - lo + 1));  /* rnd in [lo,hi] */
628 	DUK_ASSERT(p >= lo && p <= hi);
629 	DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld",
630 	                     (long) lo, (long) hi, (long) p));
631 
632 	/* move pivot out of the way */
633 	duk__array_sort_swap(ctx, p, lo);
634 	p = lo;
635 	DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
636 
637 	l = lo + 1;
638 	r = hi;
639 	for (;;) {
640 		/* find elements to swap */
641 		for (;;) {
642 			DUK_DDD(DUK_DDDPRINT("left scan: l=%ld, r=%ld, p=%ld",
643 			                     (long) l, (long) r, (long) p));
644 			if (l >= hi) {
645 				break;
646 			}
647 			if (duk__array_sort_compare(ctx, l, p) >= 0) {  /* !(l < p) */
648 				break;
649 			}
650 			l++;
651 		}
652 		for (;;) {
653 			DUK_DDD(DUK_DDDPRINT("right scan: l=%ld, r=%ld, p=%ld",
654 			                     (long) l, (long) r, (long) p));
655 			if (r <= lo) {
656 				break;
657 			}
658 			if (duk__array_sort_compare(ctx, p, r) >= 0) {  /* !(p < r) */
659 				break;
660 			}
661 			r--;
662 		}
663 		if (l >= r) {
664 			goto done;
665 		}
666 		DUK_ASSERT(l < r);
667 
668 		DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r));
669 
670 		duk__array_sort_swap(ctx, l, r);
671 
672 		DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
673 		l++;
674 		r--;
675 	}
676  done:
677 	/* Note that 'l' and 'r' may cross, i.e. r < l */
678 	DUK_ASSERT(l >= lo && l <= hi);
679 	DUK_ASSERT(r >= lo && r <= hi);
680 
681 	/* XXX: there's no explicit recursion bound here now.  For the average
682 	 * qsort recursion depth O(log n) that's not really necessary: e.g. for
683 	 * 2**32 recursion depth would be about 32 which is OK.  However, qsort
684 	 * worst case recursion depth is O(n) which may be a problem.
685 	 */
686 
687 	/* move pivot to its final place */
688 	DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
689 	duk__array_sort_swap(ctx, lo, r);
690 
691 #if defined(DUK_USE_DDDPRINT)
692 	duk__debuglog_qsort_state(ctx, lo, hi, r);
693 #endif
694 
695 	DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(ctx, 1)));
696 	duk__array_qsort(ctx, lo, r - 1);
697 	duk__array_qsort(ctx, r + 1, hi);
698 }
699 
duk_bi_array_prototype_sort(duk_context * ctx)700 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) {
701 	duk_uint32_t len;
702 
703 	/* XXX: len >= 0x80000000 won't work below because a signed type
704 	 * is needed by qsort.
705 	 */
706 	len = duk__push_this_obj_len_u32_limited(ctx);
707 
708 	/* stack[0] = compareFn
709 	 * stack[1] = ToObject(this)
710 	 * stack[2] = ToUint32(length)
711 	 */
712 
713 	if (len > 0) {
714 		/* avoid degenerate cases, so that (len - 1) won't underflow */
715 		duk__array_qsort(ctx, (duk_int_t) 0, (duk_int_t) (len - 1));
716 	}
717 
718 	DUK_ASSERT_TOP(ctx, 3);
719 	duk_pop(ctx);
720 	return 1;  /* return ToObject(this) */
721 }
722 
723 /*
724  *  splice()
725  */
726 
727 /* XXX: this compiles to over 500 bytes now, even without special handling
728  * for an array part.  Uses signed ints so does not handle full array range correctly.
729  */
730 
731 /* XXX: can shift() / unshift() use the same helper?
732  *   shift() is (close to?) <--> splice(0, 1)
733  *   unshift is (close to?) <--> splice(0, 0, [items])?
734  */
735 
duk_bi_array_prototype_splice(duk_context * ctx)736 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) {
737 	duk_idx_t nargs;
738 	duk_uint32_t len;
739 	duk_bool_t have_delcount;
740 	duk_int_t item_count;
741 	duk_int_t act_start;
742 	duk_int_t del_count;
743 	duk_int_t i, n;
744 
745 	DUK_UNREF(have_delcount);
746 
747 	nargs = duk_get_top(ctx);
748 	if (nargs < 2) {
749 		duk_set_top(ctx, 2);
750 		nargs = 2;
751 		have_delcount = 0;
752 	} else {
753 		have_delcount = 1;
754 	}
755 
756 	/* XXX: len >= 0x80000000 won't work below because we need to be
757 	 * able to represent -len.
758 	 */
759 	len = duk__push_this_obj_len_u32_limited(ctx);
760 
761 	act_start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len);
762 	if (act_start < 0) {
763 		act_start = len + act_start;
764 	}
765 	DUK_ASSERT(act_start >= 0 && act_start <= (duk_int_t) len);
766 
767 #ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT
768 	if (have_delcount) {
769 #endif
770 		del_count = duk_to_int_clamped(ctx, 1, 0, len - act_start);
771 #ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT
772 	} else {
773 		/* E5.1 standard behavior when deleteCount is not given would be
774 		 * to treat it just like if 'undefined' was given, which coerces
775 		 * ultimately to 0.  Real world behavior is to splice to the end
776 		 * of array, see test-bi-array-proto-splice-no-delcount.js.
777 		 */
778 		del_count = len - act_start;
779 	}
780 #endif
781 
782 	DUK_ASSERT(nargs >= 2);
783 	item_count = (duk_int_t) (nargs - 2);
784 
785 	DUK_ASSERT(del_count >= 0 && del_count <= (duk_int_t) len - act_start);
786 	DUK_ASSERT(del_count + act_start <= (duk_int_t) len);
787 
788 	/* For now, restrict result array into 32-bit length range. */
789 	if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) {
790 		DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw"));
791 		return DUK_RET_RANGE_ERROR;
792 	}
793 
794 	duk_push_array(ctx);
795 
796 	/* stack[0] = start
797 	 * stack[1] = deleteCount
798 	 * stack[2...nargs-1] = items
799 	 * stack[nargs] = ToObject(this)               -3
800 	 * stack[nargs+1] = ToUint32(length)           -2
801 	 * stack[nargs+2] = result array               -1
802 	 */
803 
804 	DUK_ASSERT_TOP(ctx, nargs + 3);
805 
806 	/* Step 9: copy elements-to-be-deleted into the result array */
807 
808 	for (i = 0; i < del_count; i++) {
809 		if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (act_start + i))) {
810 			duk_xdef_prop_index_wec(ctx, -2, i);  /* throw flag irrelevant (false in std alg) */
811 		} else {
812 			duk_pop(ctx);
813 		}
814 	}
815 	duk_push_u32(ctx, (duk_uint32_t) del_count);
816 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
817 
818 	/* Steps 12 and 13: reorganize elements to make room for itemCount elements */
819 
820 	if (item_count < del_count) {
821 		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 1
822 		 * -> [ A B F G H ]          (conceptual intermediate step)
823 		 * -> [ A B . F G H ]        (placeholder marked)
824 		 *    [ A B C F G H ]        (actual result at this point, C will be replaced)
825 		 */
826 
827 		DUK_ASSERT_TOP(ctx, nargs + 3);
828 
829 		n = len - del_count;
830 		for (i = act_start; i < n; i++) {
831 			if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) {
832 				duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count));
833 			} else {
834 				duk_pop(ctx);
835 				duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count));
836 			}
837 		}
838 
839 		DUK_ASSERT_TOP(ctx, nargs + 3);
840 
841 		/* loop iterator init and limit changed from standard algorithm */
842 		n = len - del_count + item_count;
843 		for (i = len - 1; i >= n; i--) {
844 			duk_del_prop_index(ctx, -3, (duk_uarridx_t) i);
845 		}
846 
847 		DUK_ASSERT_TOP(ctx, nargs + 3);
848 	} else if (item_count > del_count) {
849 		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 4
850 		 * -> [ A B F G H ]          (conceptual intermediate step)
851 		 * -> [ A B . . . . F G H ]  (placeholder marked)
852 		 *    [ A B C D E F F G H ]  (actual result at this point)
853 		 */
854 
855 		DUK_ASSERT_TOP(ctx, nargs + 3);
856 
857 		/* loop iterator init and limit changed from standard algorithm */
858 		for (i = len - del_count - 1; i >= act_start; i--) {
859 			if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) {
860 				duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count));
861 			} else {
862 				duk_pop(ctx);
863 				duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count));
864 			}
865 		}
866 
867 		DUK_ASSERT_TOP(ctx, nargs + 3);
868 	} else {
869 		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 3
870 		 * -> [ A B F G H ]          (conceptual intermediate step)
871 		 * -> [ A B . . . F G H ]    (placeholder marked)
872 		 *    [ A B C D E F G H ]    (actual result at this point)
873 		 */
874 	}
875 	DUK_ASSERT_TOP(ctx, nargs + 3);
876 
877 	/* Step 15: insert itemCount elements into the hole made above */
878 
879 	for (i = 0; i < item_count; i++) {
880 		duk_dup(ctx, i + 2);  /* args start at index 2 */
881 		duk_put_prop_index(ctx, -4, (duk_uarridx_t) (act_start + i));
882 	}
883 
884 	/* Step 16: update length; note that the final length may be above 32 bit range
885 	 * (but we checked above that this isn't the case here)
886 	 */
887 
888 	duk_push_u32(ctx, len - del_count + item_count);
889 	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);
890 
891 	/* result array is already at the top of stack */
892 	DUK_ASSERT_TOP(ctx, nargs + 3);
893 	return 1;
894 }
895 
896 /*
897  *  reverse()
898  */
899 
duk_bi_array_prototype_reverse(duk_context * ctx)900 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) {
901 	duk_uint32_t len;
902 	duk_uint32_t middle;
903 	duk_uint32_t lower, upper;
904 	duk_bool_t have_lower, have_upper;
905 
906 	len = duk__push_this_obj_len_u32(ctx);
907 	middle = len / 2;
908 
909 	/* If len <= 1, middle will be 0 and for-loop bails out
910 	 * immediately (0 < 0 -> false).
911 	 */
912 
913 	for (lower = 0; lower < middle; lower++) {
914 		DUK_ASSERT(len >= 2);
915 		DUK_ASSERT_TOP(ctx, 2);
916 
917 		DUK_ASSERT(len >= lower + 1);
918 		upper = len - lower - 1;
919 
920 		have_lower = duk_get_prop_index(ctx, -2, (duk_uarridx_t) lower);
921 		have_upper = duk_get_prop_index(ctx, -3, (duk_uarridx_t) upper);
922 
923 		/* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */
924 
925 		if (have_upper) {
926 			duk_put_prop_index(ctx, -4, (duk_uarridx_t) lower);
927 		} else {
928 			duk_del_prop_index(ctx, -4, (duk_uarridx_t) lower);
929 			duk_pop(ctx);
930 		}
931 
932 		if (have_lower) {
933 			duk_put_prop_index(ctx, -3, (duk_uarridx_t) upper);
934 		} else {
935 			duk_del_prop_index(ctx, -3, (duk_uarridx_t) upper);
936 			duk_pop(ctx);
937 		}
938 
939 		DUK_ASSERT_TOP(ctx, 2);
940 	}
941 
942 	DUK_ASSERT_TOP(ctx, 2);
943 	duk_pop(ctx);  /* -> [ ToObject(this) ] */
944 	return 1;
945 }
946 
947 /*
948  *  slice()
949  */
950 
duk_bi_array_prototype_slice(duk_context * ctx)951 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) {
952 	duk_uint32_t len;
953 	duk_int_t start, end;
954 	duk_int_t i;
955 	duk_uarridx_t idx;
956 	duk_uint32_t res_length = 0;
957 
958 	/* XXX: len >= 0x80000000 won't work below because we need to be
959 	 * able to represent -len.
960 	 */
961 	len = duk__push_this_obj_len_u32_limited(ctx);
962 	duk_push_array(ctx);
963 
964 	/* stack[0] = start
965 	 * stack[1] = end
966 	 * stack[2] = ToObject(this)
967 	 * stack[3] = ToUint32(length)
968 	 * stack[4] = result array
969 	 */
970 
971 	start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len);
972 	if (start < 0) {
973 		start = len + start;
974 	}
975 	/* XXX: could duk_is_undefined() provide defaulting undefined to 'len'
976 	 * (the upper limit)?
977 	 */
978 	if (duk_is_undefined(ctx, 1)) {
979 		end = len;
980 	} else {
981 		end = duk_to_int_clamped(ctx, 1, -((duk_int_t) len), (duk_int_t) len);
982 		if (end < 0) {
983 			end = len + end;
984 		}
985 	}
986 	DUK_ASSERT(start >= 0 && (duk_uint32_t) start <= len);
987 	DUK_ASSERT(end >= 0 && (duk_uint32_t) end <= len);
988 
989 	idx = 0;
990 	for (i = start; i < end; i++) {
991 		DUK_ASSERT_TOP(ctx, 5);
992 		if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
993 			duk_xdef_prop_index_wec(ctx, 4, idx);
994 			res_length = idx + 1;
995 		} else {
996 			duk_pop(ctx);
997 		}
998 		idx++;
999 		DUK_ASSERT_TOP(ctx, 5);
1000 	}
1001 
1002 	duk_push_u32(ctx, res_length);
1003 	duk_xdef_prop_stridx(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
1004 
1005 	DUK_ASSERT_TOP(ctx, 5);
1006 	return 1;
1007 }
1008 
1009 /*
1010  *  shift()
1011  */
1012 
duk_bi_array_prototype_shift(duk_context * ctx)1013 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) {
1014 	duk_uint32_t len;
1015 	duk_uint32_t i;
1016 
1017 	len = duk__push_this_obj_len_u32(ctx);
1018 	if (len == 0) {
1019 		duk_push_int(ctx, 0);
1020 		duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
1021 		return 0;
1022 	}
1023 
1024 	duk_get_prop_index(ctx, 0, 0);
1025 
1026 	/* stack[0] = object (this)
1027 	 * stack[1] = ToUint32(length)
1028 	 * stack[2] = elem at index 0 (retval)
1029 	 */
1030 
1031 	for (i = 1; i < len; i++) {
1032 		DUK_ASSERT_TOP(ctx, 3);
1033 		if (duk_get_prop_index(ctx, 0, (duk_uarridx_t) i)) {
1034 			/* fromPresent = true */
1035 			duk_put_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
1036 		} else {
1037 			/* fromPresent = false */
1038 			duk_del_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
1039 			duk_pop(ctx);
1040 		}
1041 	}
1042 	duk_del_prop_index(ctx, 0, (duk_uarridx_t) (len - 1));
1043 
1044 	duk_push_u32(ctx, (duk_uint32_t) (len - 1));
1045 	duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH);
1046 
1047 	DUK_ASSERT_TOP(ctx, 3);
1048 	return 1;
1049 }
1050 
1051 /*
1052  *  unshift()
1053  */
1054 
duk_bi_array_prototype_unshift(duk_context * ctx)1055 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) {
1056 	duk_idx_t nargs;
1057 	duk_uint32_t len;
1058 	duk_uint32_t i;
1059 
1060 	nargs = duk_get_top(ctx);
1061 	len = duk__push_this_obj_len_u32(ctx);
1062 
1063 	/* stack[0...nargs-1] = unshift args (vararg)
1064 	 * stack[nargs] = ToObject(this)
1065 	 * stack[nargs+1] = ToUint32(length)
1066 	 */
1067 
1068 	DUK_ASSERT_TOP(ctx, nargs + 2);
1069 
1070 	/* Note: unshift() may operate on indices above unsigned 32-bit range
1071 	 * and the final length may be >= 2**32.  However, we restrict the
1072 	 * final result to 32-bit range for practicality.
1073 	 */
1074 
1075 	if (len + (duk_uint32_t) nargs < len) {
1076 		DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw"));
1077 		return DUK_RET_RANGE_ERROR;
1078 	}
1079 
1080 	i = len;
1081 	while (i > 0) {
1082 		DUK_ASSERT_TOP(ctx, nargs + 2);
1083 		i--;
1084 		/* k+argCount-1; note that may be above 32-bit range */
1085 
1086 		if (duk_get_prop_index(ctx, -2, (duk_uarridx_t) i)) {
1087 			/* fromPresent = true */
1088 			/* [ ... ToObject(this) ToUint32(length) val ] */
1089 			duk_put_prop_index(ctx, -3, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
1090 		} else {
1091 			/* fromPresent = false */
1092 			/* [ ... ToObject(this) ToUint32(length) val ] */
1093 			duk_pop(ctx);
1094 			duk_del_prop_index(ctx, -2, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
1095 		}
1096 		DUK_ASSERT_TOP(ctx, nargs + 2);
1097 	}
1098 
1099 	for (i = 0; i < (duk_uint32_t) nargs; i++) {
1100 		DUK_ASSERT_TOP(ctx, nargs + 2);
1101 		duk_dup(ctx, i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
1102 		duk_put_prop_index(ctx, -3, (duk_uarridx_t) i);
1103 		DUK_ASSERT_TOP(ctx, nargs + 2);
1104 	}
1105 
1106 	DUK_ASSERT_TOP(ctx, nargs + 2);
1107 	duk_push_u32(ctx, len + nargs);
1108 	duk_dup_top(ctx);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
1109 	duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH);
1110 	return 1;
1111 }
1112 
1113 /*
1114  *  indexOf(), lastIndexOf()
1115  */
1116 
duk_bi_array_prototype_indexof_shared(duk_context * ctx)1117 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) {
1118 	duk_idx_t nargs;
1119 	duk_int_t i, len;
1120 	duk_int_t from_index;
1121 	duk_small_int_t idx_step = duk_get_current_magic(ctx);  /* idx_step is +1 for indexOf, -1 for lastIndexOf */
1122 
1123 	/* lastIndexOf() needs to be a vararg function because we must distinguish
1124 	 * between an undefined fromIndex and a "not given" fromIndex; indexOf() is
1125 	 * made vararg for symmetry although it doesn't strictly need to be.
1126 	 */
1127 
1128 	nargs = duk_get_top(ctx);
1129 	duk_set_top(ctx, 2);
1130 
1131 	/* XXX: must be able to represent -len */
1132 	len = (duk_int_t) duk__push_this_obj_len_u32_limited(ctx);
1133 	if (len == 0) {
1134 		goto not_found;
1135 	}
1136 
1137 	/* Index clamping is a bit tricky, we must ensure that we'll only iterate
1138 	 * through elements that exist and that the specific requirements from E5.1
1139 	 * Sections 15.4.4.14 and 15.4.4.15 are fulfilled; especially:
1140 	 *
1141 	 *   - indexOf: clamp to [-len,len], negative handling -> [0,len],
1142 	 *     if clamped result is len, for-loop bails out immediately
1143 	 *
1144 	 *   - lastIndexOf: clamp to [-len-1, len-1], negative handling -> [-1, len-1],
1145 	 *     if clamped result is -1, for-loop bails out immediately
1146 	 *
1147 	 * If fromIndex is not given, ToInteger(undefined) = 0, which is correct
1148 	 * for indexOf() but incorrect for lastIndexOf().  Hence special handling,
1149 	 * and why lastIndexOf() needs to be a vararg function.
1150 	 */
1151 
1152 	if (nargs >= 2) {
1153 		/* indexOf: clamp fromIndex to [-len, len]
1154 		 * (if fromIndex == len, for-loop terminates directly)
1155 		 *
1156 		 * lastIndexOf: clamp fromIndex to [-len - 1, len - 1]
1157 		 * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly)
1158 		 */
1159 		from_index = duk_to_int_clamped(ctx,
1160 		                                1,
1161 		                                (idx_step > 0 ? -len : -len - 1),
1162 		                                (idx_step > 0 ? len : len - 1));
1163 		if (from_index < 0) {
1164 			/* for lastIndexOf, result may be -1 (mark immediate termination) */
1165 			from_index = len + from_index;
1166 		}
1167 	} else {
1168 		/* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but
1169 		 * handle both indexOf and lastIndexOf specially here.
1170 		 */
1171 		if (idx_step > 0) {
1172 			from_index = 0;
1173 		} else {
1174 			from_index = len - 1;
1175 		}
1176 	}
1177 
1178 	/* stack[0] = searchElement
1179 	 * stack[1] = fromIndex
1180 	 * stack[2] = object
1181 	 * stack[3] = length (not needed, but not popped above)
1182 	 */
1183 
1184 	for (i = from_index; i >= 0 && i < len; i += idx_step) {
1185 		DUK_ASSERT_TOP(ctx, 4);
1186 
1187 		if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
1188 			DUK_ASSERT_TOP(ctx, 5);
1189 			if (duk_strict_equals(ctx, 0, 4)) {
1190 				duk_push_int(ctx, i);
1191 				return 1;
1192 			}
1193 		}
1194 
1195 		duk_pop(ctx);
1196 	}
1197 
1198  not_found:
1199 	duk_push_int(ctx, -1);
1200 	return 1;
1201 }
1202 
1203 /*
1204  *  every(), some(), forEach(), map(), filter()
1205  */
1206 
1207 #define DUK__ITER_EVERY    0
1208 #define DUK__ITER_SOME     1
1209 #define DUK__ITER_FOREACH  2
1210 #define DUK__ITER_MAP      3
1211 #define DUK__ITER_FILTER   4
1212 
1213 /* XXX: This helper is a bit awkward because the handling for the different iteration
1214  * callers is quite different.  This now compiles to a bit less than 500 bytes, so with
1215  * 5 callers the net result is about 100 bytes / caller.
1216  */
1217 
duk_bi_array_prototype_iter_shared(duk_context * ctx)1218 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) {
1219 	duk_uint32_t len;
1220 	duk_uint32_t i;
1221 	duk_uarridx_t k;
1222 	duk_bool_t bval;
1223 	duk_small_int_t iter_type = duk_get_current_magic(ctx);
1224 	duk_uint32_t res_length = 0;
1225 
1226 	/* each call this helper serves has nargs==2 */
1227 	DUK_ASSERT_TOP(ctx, 2);
1228 
1229 	len = duk__push_this_obj_len_u32(ctx);
1230 	duk_require_callable(ctx, 0);
1231 	/* if thisArg not supplied, behave as if undefined was supplied */
1232 
1233 	if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) {
1234 		duk_push_array(ctx);
1235 	} else {
1236 		duk_push_undefined(ctx);
1237 	}
1238 
1239 	/* stack[0] = callback
1240 	 * stack[1] = thisArg
1241 	 * stack[2] = object
1242 	 * stack[3] = ToUint32(length)  (unused, but avoid unnecessary pop)
1243 	 * stack[4] = result array (or undefined)
1244 	 */
1245 
1246 	k = 0;  /* result index for filter() */
1247 	for (i = 0; i < len; i++) {
1248 		DUK_ASSERT_TOP(ctx, 5);
1249 
1250 		if (!duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
1251 #if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER)
1252 			/* Real world behavior for map(): trailing non-existent
1253 			 * elements don't invoke the user callback, but are still
1254 			 * counted towards result 'length'.
1255 			 */
1256 			if (iter_type == DUK__ITER_MAP) {
1257 				res_length = i + 1;
1258 			}
1259 #else
1260 			/* Standard behavior for map(): trailing non-existent
1261 			 * elements don't invoke the user callback and are not
1262 			 * counted towards result 'length'.
1263 			 */
1264 #endif
1265 			duk_pop(ctx);
1266 			continue;
1267 		}
1268 
1269 		/* The original value needs to be preserved for filter(), hence
1270 		 * this funny order.  We can't re-get the value because of side
1271 		 * effects.
1272 		 */
1273 
1274 		duk_dup(ctx, 0);
1275 		duk_dup(ctx, 1);
1276 		duk_dup(ctx, -3);
1277 		duk_push_u32(ctx, i);
1278 		duk_dup(ctx, 2);  /* [ ... val callback thisArg val i obj ] */
1279 		duk_call_method(ctx, 3); /* -> [ ... val retval ] */
1280 
1281 		switch (iter_type) {
1282 		case DUK__ITER_EVERY:
1283 			bval = duk_to_boolean(ctx, -1);
1284 			if (!bval) {
1285 				/* stack top contains 'false' */
1286 				return 1;
1287 			}
1288 			break;
1289 		case DUK__ITER_SOME:
1290 			bval = duk_to_boolean(ctx, -1);
1291 			if (bval) {
1292 				/* stack top contains 'true' */
1293 				return 1;
1294 			}
1295 			break;
1296 		case DUK__ITER_FOREACH:
1297 			/* nop */
1298 			break;
1299 		case DUK__ITER_MAP:
1300 			duk_dup(ctx, -1);
1301 			duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) i);  /* retval to result[i] */
1302 			res_length = i + 1;
1303 			break;
1304 		case DUK__ITER_FILTER:
1305 			bval = duk_to_boolean(ctx, -1);
1306 			if (bval) {
1307 				duk_dup(ctx, -2);  /* orig value */
1308 				duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) k);
1309 				k++;
1310 				res_length = k;
1311 			}
1312 			break;
1313 		default:
1314 			DUK_UNREACHABLE();
1315 			break;
1316 		}
1317 		duk_pop_2(ctx);
1318 
1319 		DUK_ASSERT_TOP(ctx, 5);
1320 	}
1321 
1322 	switch (iter_type) {
1323 	case DUK__ITER_EVERY:
1324 		duk_push_true(ctx);
1325 		break;
1326 	case DUK__ITER_SOME:
1327 		duk_push_false(ctx);
1328 		break;
1329 	case DUK__ITER_FOREACH:
1330 		duk_push_undefined(ctx);
1331 		break;
1332 	case DUK__ITER_MAP:
1333 	case DUK__ITER_FILTER:
1334 		DUK_ASSERT_TOP(ctx, 5);
1335 		DUK_ASSERT(duk_is_array(ctx, -1));  /* topmost element is the result array already */
1336 		duk_push_u32(ctx, res_length);
1337 		duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
1338 		break;
1339 	default:
1340 		DUK_UNREACHABLE();
1341 		break;
1342 	}
1343 
1344 	return 1;
1345 }
1346 
1347 /*
1348  *  reduce(), reduceRight()
1349  */
1350 
duk_bi_array_prototype_reduce_shared(duk_context * ctx)1351 DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) {
1352 	duk_idx_t nargs;
1353 	duk_bool_t have_acc;
1354 	duk_uint32_t i, len;
1355 	duk_small_int_t idx_step = duk_get_current_magic(ctx);  /* idx_step is +1 for reduce, -1 for reduceRight */
1356 
1357 	/* We're a varargs function because we need to detect whether
1358 	 * initialValue was given or not.
1359 	 */
1360 	nargs = duk_get_top(ctx);
1361 	DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs));
1362 
1363 	duk_set_top(ctx, 2);
1364 	len = duk__push_this_obj_len_u32(ctx);
1365 	if (!duk_is_callable(ctx, 0)) {
1366 		goto type_error;
1367 	}
1368 
1369 	/* stack[0] = callback fn
1370 	 * stack[1] = initialValue
1371 	 * stack[2] = object (coerced this)
1372 	 * stack[3] = length (not needed, but not popped above)
1373 	 * stack[4] = accumulator
1374 	 */
1375 
1376 	have_acc = 0;
1377 	if (nargs >= 2) {
1378 		duk_dup(ctx, 1);
1379 		have_acc = 1;
1380 	}
1381 	DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T",
1382 	                     (long) have_acc, (duk_tval *) duk_get_tval(ctx, 3)));
1383 
1384 	/* For len == 0, i is initialized to len - 1 which underflows.
1385 	 * The condition (i < len) will then exit the for-loop on the
1386 	 * first round which is correct.  Similarly, loop termination
1387 	 * happens by i underflowing.
1388 	 */
1389 
1390 	for (i = (idx_step >= 0 ? 0 : len - 1);
1391 	     i < len;  /* i >= 0 would always be true */
1392 	     i += idx_step) {
1393 		DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T",
1394 		                     (long) i, (long) len, (long) have_acc,
1395 		                     (long) duk_get_top(ctx),
1396 		                     (duk_tval *) duk_get_tval(ctx, 4)));
1397 
1398 		DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) ||
1399 		           (!have_acc && duk_get_top(ctx) == 4));
1400 
1401 		if (!duk_has_prop_index(ctx, 2, (duk_uarridx_t) i)) {
1402 			continue;
1403 		}
1404 
1405 		if (!have_acc) {
1406 			DUK_ASSERT_TOP(ctx, 4);
1407 			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
1408 			have_acc = 1;
1409 			DUK_ASSERT_TOP(ctx, 5);
1410 		} else {
1411 			DUK_ASSERT_TOP(ctx, 5);
1412 			duk_dup(ctx, 0);
1413 			duk_dup(ctx, 4);
1414 			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
1415 			duk_push_u32(ctx, i);
1416 			duk_dup(ctx, 2);
1417 			DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T",
1418 			                     (duk_tval *) duk_get_tval(ctx, -5), (duk_tval *) duk_get_tval(ctx, -4),
1419 			                     (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2),
1420 			                     (duk_tval *) duk_get_tval(ctx, -1)));
1421 			duk_call(ctx, 4);
1422 			DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
1423 			duk_replace(ctx, 4);
1424 			DUK_ASSERT_TOP(ctx, 5);
1425 		}
1426 	}
1427 
1428 	if (!have_acc) {
1429 		goto type_error;
1430 	}
1431 
1432 	DUK_ASSERT_TOP(ctx, 5);
1433 	return 1;
1434 
1435  type_error:
1436 	return DUK_RET_TYPE_ERROR;
1437 }
1438 
1439 #undef DUK__ARRAY_MID_JOIN_LIMIT
1440 
1441 #undef DUK__ITER_EVERY
1442 #undef DUK__ITER_SOME
1443 #undef DUK__ITER_FOREACH
1444 #undef DUK__ITER_MAP
1445 #undef DUK__ITER_FILTER
1446