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