1 #include "jsi.h"
2 #include "jsvalue.h"
3 #include "jsbuiltin.h"
4
js_getlength(js_State * J,int idx)5 int js_getlength(js_State *J, int idx)
6 {
7 int len;
8 js_getproperty(J, idx, "length");
9 len = js_tointeger(J, -1);
10 js_pop(J, 1);
11 return len;
12 }
13
js_setlength(js_State * J,int idx,int len)14 void js_setlength(js_State *J, int idx, int len)
15 {
16 js_pushnumber(J, len);
17 js_setproperty(J, idx < 0 ? idx - 1 : idx, "length");
18 }
19
js_hasindex(js_State * J,int idx,int i)20 int js_hasindex(js_State *J, int idx, int i)
21 {
22 char buf[32];
23 return js_hasproperty(J, idx, js_itoa(buf, i));
24 }
25
js_getindex(js_State * J,int idx,int i)26 void js_getindex(js_State *J, int idx, int i)
27 {
28 char buf[32];
29 js_getproperty(J, idx, js_itoa(buf, i));
30 }
31
js_setindex(js_State * J,int idx,int i)32 void js_setindex(js_State *J, int idx, int i)
33 {
34 char buf[32];
35 js_setproperty(J, idx, js_itoa(buf, i));
36 }
37
js_delindex(js_State * J,int idx,int i)38 void js_delindex(js_State *J, int idx, int i)
39 {
40 char buf[32];
41 js_delproperty(J, idx, js_itoa(buf, i));
42 }
43
jsB_new_Array(js_State * J)44 static void jsB_new_Array(js_State *J)
45 {
46 int i, top = js_gettop(J);
47
48 js_newarray(J);
49
50 if (top == 2) {
51 if (js_isnumber(J, 1)) {
52 js_copy(J, 1);
53 js_setproperty(J, -2, "length");
54 } else {
55 js_copy(J, 1);
56 js_setindex(J, -2, 0);
57 }
58 } else {
59 for (i = 1; i < top; ++i) {
60 js_copy(J, i);
61 js_setindex(J, -2, i - 1);
62 }
63 }
64 }
65
Ap_concat(js_State * J)66 static void Ap_concat(js_State *J)
67 {
68 int i, top = js_gettop(J);
69 int n, k, len;
70
71 js_newarray(J);
72 n = 0;
73
74 for (i = 0; i < top; ++i) {
75 js_copy(J, i);
76 if (js_isarray(J, -1)) {
77 len = js_getlength(J, -1);
78 for (k = 0; k < len; ++k)
79 if (js_hasindex(J, -1, k))
80 js_setindex(J, -3, n++);
81 js_pop(J, 1);
82 } else {
83 js_setindex(J, -2, n++);
84 }
85 }
86 }
87
Ap_join(js_State * J)88 static void Ap_join(js_State *J)
89 {
90 char * volatile out = NULL;
91 const char *sep;
92 const char *r;
93 int seplen;
94 int k, n, len;
95
96 len = js_getlength(J, 0);
97
98 if (js_isdefined(J, 1)) {
99 sep = js_tostring(J, 1);
100 seplen = strlen(sep);
101 } else {
102 sep = ",";
103 seplen = 1;
104 }
105
106 if (len == 0) {
107 js_pushliteral(J, "");
108 return;
109 }
110
111 if (js_try(J)) {
112 js_free(J, out);
113 js_throw(J);
114 }
115
116 n = 1;
117 for (k = 0; k < len; ++k) {
118 js_getindex(J, 0, k);
119 if (js_isundefined(J, -1) || js_isnull(J, -1))
120 r = "";
121 else
122 r = js_tostring(J, -1);
123 n += strlen(r);
124
125 if (k == 0) {
126 out = js_malloc(J, n);
127 strcpy(out, r);
128 } else {
129 n += seplen;
130 out = js_realloc(J, out, n);
131 strcat(out, sep);
132 strcat(out, r);
133 }
134
135 js_pop(J, 1);
136 }
137
138 js_pushstring(J, out);
139 js_endtry(J);
140 js_free(J, out);
141 }
142
Ap_pop(js_State * J)143 static void Ap_pop(js_State *J)
144 {
145 int n;
146
147 n = js_getlength(J, 0);
148
149 if (n > 0) {
150 js_getindex(J, 0, n - 1);
151 js_delindex(J, 0, n - 1);
152 js_setlength(J, 0, n - 1);
153 } else {
154 js_setlength(J, 0, 0);
155 js_pushundefined(J);
156 }
157 }
158
Ap_push(js_State * J)159 static void Ap_push(js_State *J)
160 {
161 int i, top = js_gettop(J);
162 int n;
163
164 n = js_getlength(J, 0);
165
166 for (i = 1; i < top; ++i, ++n) {
167 js_copy(J, i);
168 js_setindex(J, 0, n);
169 }
170
171 js_setlength(J, 0, n);
172
173 js_pushnumber(J, n);
174 }
175
Ap_reverse(js_State * J)176 static void Ap_reverse(js_State *J)
177 {
178 int len, middle, lower;
179
180 len = js_getlength(J, 0);
181 middle = len / 2;
182 lower = 0;
183
184 while (lower != middle) {
185 int upper = len - lower - 1;
186 int haslower = js_hasindex(J, 0, lower);
187 int hasupper = js_hasindex(J, 0, upper);
188 if (haslower && hasupper) {
189 js_setindex(J, 0, lower);
190 js_setindex(J, 0, upper);
191 } else if (hasupper) {
192 js_setindex(J, 0, lower);
193 js_delindex(J, 0, upper);
194 } else if (haslower) {
195 js_setindex(J, 0, upper);
196 js_delindex(J, 0, lower);
197 }
198 ++lower;
199 }
200
201 js_copy(J, 0);
202 }
203
Ap_shift(js_State * J)204 static void Ap_shift(js_State *J)
205 {
206 int k, len;
207
208 len = js_getlength(J, 0);
209
210 if (len == 0) {
211 js_setlength(J, 0, 0);
212 js_pushundefined(J);
213 return;
214 }
215
216 js_getindex(J, 0, 0);
217
218 for (k = 1; k < len; ++k) {
219 if (js_hasindex(J, 0, k))
220 js_setindex(J, 0, k - 1);
221 else
222 js_delindex(J, 0, k - 1);
223 }
224
225 js_delindex(J, 0, len - 1);
226 js_setlength(J, 0, len - 1);
227 }
228
Ap_slice(js_State * J)229 static void Ap_slice(js_State *J)
230 {
231 int len, s, e, n;
232 double sv, ev;
233
234 js_newarray(J);
235
236 len = js_getlength(J, 0);
237 sv = js_tointeger(J, 1);
238 ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
239
240 if (sv < 0) sv = sv + len;
241 if (ev < 0) ev = ev + len;
242
243 s = sv < 0 ? 0 : sv > len ? len : sv;
244 e = ev < 0 ? 0 : ev > len ? len : ev;
245
246 for (n = 0; s < e; ++s, ++n)
247 if (js_hasindex(J, 0, s))
248 js_setindex(J, -2, n);
249 }
250
251 struct sortslot {
252 js_Value v;
253 js_State *J;
254 };
255
sortcmp(const void * avoid,const void * bvoid)256 static int sortcmp(const void *avoid, const void *bvoid)
257 {
258 const struct sortslot *aslot = avoid, *bslot = bvoid;
259 const js_Value *a = &aslot->v, *b = &bslot->v;
260 js_State *J = aslot->J;
261 const char *sx, *sy;
262 double v;
263 int c;
264
265 int unx = (a->type == JS_TUNDEFINED);
266 int uny = (b->type == JS_TUNDEFINED);
267 if (unx) return !uny;
268 if (uny) return -1;
269
270 if (js_iscallable(J, 1)) {
271 js_copy(J, 1); /* copy function */
272 js_pushundefined(J);
273 js_pushvalue(J, *a);
274 js_pushvalue(J, *b);
275 js_call(J, 2);
276 v = js_tonumber(J, -1);
277 c = (v == 0) ? 0 : (v < 0) ? -1 : 1;
278 js_pop(J, 1);
279 } else {
280 js_pushvalue(J, *a);
281 js_pushvalue(J, *b);
282 sx = js_tostring(J, -2);
283 sy = js_tostring(J, -1);
284 c = strcmp(sx, sy);
285 js_pop(J, 2);
286 }
287 return c;
288 }
289
Ap_sort(js_State * J)290 static void Ap_sort(js_State *J)
291 {
292 struct sortslot *array = NULL;
293 int i, n, len;
294
295 len = js_getlength(J, 0);
296 if (len <= 0) {
297 js_copy(J, 0);
298 return;
299 }
300
301 if (len >= INT_MAX / (int)sizeof(*array))
302 js_rangeerror(J, "array is too large to sort");
303
304 array = js_malloc(J, len * sizeof *array);
305
306 /* Holding objects where the GC cannot see them is illegal, but if we
307 * don't allow the GC to run we can use qsort() on a temporary array of
308 * js_Values for fast sorting.
309 */
310 ++J->gcpause;
311
312 if (js_try(J)) {
313 --J->gcpause;
314 js_free(J, array);
315 js_throw(J);
316 }
317
318 n = 0;
319 for (i = 0; i < len; ++i) {
320 if (js_hasindex(J, 0, i)) {
321 array[n].v = *js_tovalue(J, -1);
322 array[n].J = J;
323 js_pop(J, 1);
324 ++n;
325 }
326 }
327
328 qsort(array, n, sizeof *array, sortcmp);
329
330 for (i = 0; i < n; ++i) {
331 js_pushvalue(J, array[i].v);
332 js_setindex(J, 0, i);
333 }
334 for (i = n; i < len; ++i) {
335 js_delindex(J, 0, i);
336 }
337
338 --J->gcpause;
339
340 js_endtry(J);
341 js_free(J, array);
342
343 js_copy(J, 0);
344 }
345
Ap_splice(js_State * J)346 static void Ap_splice(js_State *J)
347 {
348 int top = js_gettop(J);
349 int len, start, del, add, k;
350 double f;
351
352 js_newarray(J);
353
354 len = js_getlength(J, 0);
355
356 f = js_tointeger(J, 1);
357 if (f < 0) f = f + len;
358 start = f < 0 ? 0 : f > len ? len : f;
359
360 f = js_tointeger(J, 2);
361 del = f < 0 ? 0 : f > len - start ? len - start : f;
362
363 /* copy deleted items to return array */
364 for (k = 0; k < del; ++k)
365 if (js_hasindex(J, 0, start + k))
366 js_setindex(J, -2, k);
367 js_setlength(J, -1, del);
368
369 /* shift the tail to resize the hole left by deleted items */
370 add = top - 3;
371 if (add < del) {
372 for (k = start; k < len - del; ++k) {
373 if (js_hasindex(J, 0, k + del))
374 js_setindex(J, 0, k + add);
375 else
376 js_delindex(J, 0, k + add);
377 }
378 for (k = len; k > len - del + add; --k)
379 js_delindex(J, 0, k - 1);
380 } else if (add > del) {
381 for (k = len - del; k > start; --k) {
382 if (js_hasindex(J, 0, k + del - 1))
383 js_setindex(J, 0, k + add - 1);
384 else
385 js_delindex(J, 0, k + add - 1);
386 }
387 }
388
389 /* copy new items into the hole */
390 for (k = 0; k < add; ++k) {
391 js_copy(J, 3 + k);
392 js_setindex(J, 0, start + k);
393 }
394
395 js_setlength(J, 0, len - del + add);
396 }
397
Ap_unshift(js_State * J)398 static void Ap_unshift(js_State *J)
399 {
400 int i, top = js_gettop(J);
401 int k, len;
402
403 len = js_getlength(J, 0);
404
405 for (k = len; k > 0; --k) {
406 int from = k - 1;
407 int to = k + top - 2;
408 if (js_hasindex(J, 0, from))
409 js_setindex(J, 0, to);
410 else
411 js_delindex(J, 0, to);
412 }
413
414 for (i = 1; i < top; ++i) {
415 js_copy(J, i);
416 js_setindex(J, 0, i - 1);
417 }
418
419 js_setlength(J, 0, len + top - 1);
420
421 js_pushnumber(J, len + top - 1);
422 }
423
Ap_toString(js_State * J)424 static void Ap_toString(js_State *J)
425 {
426 int top = js_gettop(J);
427 js_pop(J, top - 1);
428 Ap_join(J);
429 }
430
Ap_indexOf(js_State * J)431 static void Ap_indexOf(js_State *J)
432 {
433 int k, len, from;
434
435 len = js_getlength(J, 0);
436 from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
437 if (from < 0) from = len + from;
438 if (from < 0) from = 0;
439
440 js_copy(J, 1);
441 for (k = from; k < len; ++k) {
442 if (js_hasindex(J, 0, k)) {
443 if (js_strictequal(J)) {
444 js_pushnumber(J, k);
445 return;
446 }
447 js_pop(J, 1);
448 }
449 }
450
451 js_pushnumber(J, -1);
452 }
453
Ap_lastIndexOf(js_State * J)454 static void Ap_lastIndexOf(js_State *J)
455 {
456 int k, len, from;
457
458 len = js_getlength(J, 0);
459 from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1;
460 if (from > len - 1) from = len - 1;
461 if (from < 0) from = len + from;
462
463 js_copy(J, 1);
464 for (k = from; k >= 0; --k) {
465 if (js_hasindex(J, 0, k)) {
466 if (js_strictequal(J)) {
467 js_pushnumber(J, k);
468 return;
469 }
470 js_pop(J, 1);
471 }
472 }
473
474 js_pushnumber(J, -1);
475 }
476
Ap_every(js_State * J)477 static void Ap_every(js_State *J)
478 {
479 int hasthis = js_gettop(J) >= 3;
480 int k, len;
481
482 if (!js_iscallable(J, 1))
483 js_typeerror(J, "callback is not a function");
484
485 len = js_getlength(J, 0);
486 for (k = 0; k < len; ++k) {
487 if (js_hasindex(J, 0, k)) {
488 js_copy(J, 1);
489 if (hasthis)
490 js_copy(J, 2);
491 else
492 js_pushundefined(J);
493 js_copy(J, -3);
494 js_pushnumber(J, k);
495 js_copy(J, 0);
496 js_call(J, 3);
497 if (!js_toboolean(J, -1))
498 return;
499 js_pop(J, 2);
500 }
501 }
502
503 js_pushboolean(J, 1);
504 }
505
Ap_some(js_State * J)506 static void Ap_some(js_State *J)
507 {
508 int hasthis = js_gettop(J) >= 3;
509 int k, len;
510
511 if (!js_iscallable(J, 1))
512 js_typeerror(J, "callback is not a function");
513
514 len = js_getlength(J, 0);
515 for (k = 0; k < len; ++k) {
516 if (js_hasindex(J, 0, k)) {
517 js_copy(J, 1);
518 if (hasthis)
519 js_copy(J, 2);
520 else
521 js_pushundefined(J);
522 js_copy(J, -3);
523 js_pushnumber(J, k);
524 js_copy(J, 0);
525 js_call(J, 3);
526 if (js_toboolean(J, -1))
527 return;
528 js_pop(J, 2);
529 }
530 }
531
532 js_pushboolean(J, 0);
533 }
534
Ap_forEach(js_State * J)535 static void Ap_forEach(js_State *J)
536 {
537 int hasthis = js_gettop(J) >= 3;
538 int k, len;
539
540 if (!js_iscallable(J, 1))
541 js_typeerror(J, "callback is not a function");
542
543 len = js_getlength(J, 0);
544 for (k = 0; k < len; ++k) {
545 if (js_hasindex(J, 0, k)) {
546 js_copy(J, 1);
547 if (hasthis)
548 js_copy(J, 2);
549 else
550 js_pushundefined(J);
551 js_copy(J, -3);
552 js_pushnumber(J, k);
553 js_copy(J, 0);
554 js_call(J, 3);
555 js_pop(J, 2);
556 }
557 }
558
559 js_pushundefined(J);
560 }
561
Ap_map(js_State * J)562 static void Ap_map(js_State *J)
563 {
564 int hasthis = js_gettop(J) >= 3;
565 int k, len;
566
567 if (!js_iscallable(J, 1))
568 js_typeerror(J, "callback is not a function");
569
570 js_newarray(J);
571
572 len = js_getlength(J, 0);
573 for (k = 0; k < len; ++k) {
574 if (js_hasindex(J, 0, k)) {
575 js_copy(J, 1);
576 if (hasthis)
577 js_copy(J, 2);
578 else
579 js_pushundefined(J);
580 js_copy(J, -3);
581 js_pushnumber(J, k);
582 js_copy(J, 0);
583 js_call(J, 3);
584 js_setindex(J, -3, k);
585 js_pop(J, 1);
586 }
587 }
588 }
589
Ap_filter(js_State * J)590 static void Ap_filter(js_State *J)
591 {
592 int hasthis = js_gettop(J) >= 3;
593 int k, to, len;
594
595 if (!js_iscallable(J, 1))
596 js_typeerror(J, "callback is not a function");
597
598 js_newarray(J);
599 to = 0;
600
601 len = js_getlength(J, 0);
602 for (k = 0; k < len; ++k) {
603 if (js_hasindex(J, 0, k)) {
604 js_copy(J, 1);
605 if (hasthis)
606 js_copy(J, 2);
607 else
608 js_pushundefined(J);
609 js_copy(J, -3);
610 js_pushnumber(J, k);
611 js_copy(J, 0);
612 js_call(J, 3);
613 if (js_toboolean(J, -1)) {
614 js_pop(J, 1);
615 js_setindex(J, -2, to++);
616 } else {
617 js_pop(J, 2);
618 }
619 }
620 }
621 }
622
Ap_reduce(js_State * J)623 static void Ap_reduce(js_State *J)
624 {
625 int hasinitial = js_gettop(J) >= 3;
626 int k, len;
627
628 if (!js_iscallable(J, 1))
629 js_typeerror(J, "callback is not a function");
630
631 len = js_getlength(J, 0);
632 k = 0;
633
634 if (len == 0 && !hasinitial)
635 js_typeerror(J, "no initial value");
636
637 /* initial value of accumulator */
638 if (hasinitial)
639 js_copy(J, 2);
640 else {
641 while (k < len)
642 if (js_hasindex(J, 0, k++))
643 break;
644 if (k == len)
645 js_typeerror(J, "no initial value");
646 }
647
648 while (k < len) {
649 if (js_hasindex(J, 0, k)) {
650 js_copy(J, 1);
651 js_pushundefined(J);
652 js_rot(J, 4); /* accumulator on top */
653 js_rot(J, 4); /* property on top */
654 js_pushnumber(J, k);
655 js_copy(J, 0);
656 js_call(J, 4); /* calculate new accumulator */
657 }
658 ++k;
659 }
660
661 /* return accumulator */
662 }
663
Ap_reduceRight(js_State * J)664 static void Ap_reduceRight(js_State *J)
665 {
666 int hasinitial = js_gettop(J) >= 3;
667 int k, len;
668
669 if (!js_iscallable(J, 1))
670 js_typeerror(J, "callback is not a function");
671
672 len = js_getlength(J, 0);
673 k = len - 1;
674
675 if (len == 0 && !hasinitial)
676 js_typeerror(J, "no initial value");
677
678 /* initial value of accumulator */
679 if (hasinitial)
680 js_copy(J, 2);
681 else {
682 while (k >= 0)
683 if (js_hasindex(J, 0, k--))
684 break;
685 if (k < 0)
686 js_typeerror(J, "no initial value");
687 }
688
689 while (k >= 0) {
690 if (js_hasindex(J, 0, k)) {
691 js_copy(J, 1);
692 js_pushundefined(J);
693 js_rot(J, 4); /* accumulator on top */
694 js_rot(J, 4); /* property on top */
695 js_pushnumber(J, k);
696 js_copy(J, 0);
697 js_call(J, 4); /* calculate new accumulator */
698 }
699 --k;
700 }
701
702 /* return accumulator */
703 }
704
A_isArray(js_State * J)705 static void A_isArray(js_State *J)
706 {
707 if (js_isobject(J, 1)) {
708 js_Object *T = js_toobject(J, 1);
709 js_pushboolean(J, T->type == JS_CARRAY);
710 } else {
711 js_pushboolean(J, 0);
712 }
713 }
714
jsB_initarray(js_State * J)715 void jsB_initarray(js_State *J)
716 {
717 js_pushobject(J, J->Array_prototype);
718 {
719 jsB_propf(J, "Array.prototype.toString", Ap_toString, 0);
720 jsB_propf(J, "Array.prototype.concat", Ap_concat, 0); /* 1 */
721 jsB_propf(J, "Array.prototype.join", Ap_join, 1);
722 jsB_propf(J, "Array.prototype.pop", Ap_pop, 0);
723 jsB_propf(J, "Array.prototype.push", Ap_push, 0); /* 1 */
724 jsB_propf(J, "Array.prototype.reverse", Ap_reverse, 0);
725 jsB_propf(J, "Array.prototype.shift", Ap_shift, 0);
726 jsB_propf(J, "Array.prototype.slice", Ap_slice, 2);
727 jsB_propf(J, "Array.prototype.sort", Ap_sort, 1);
728 jsB_propf(J, "Array.prototype.splice", Ap_splice, 0); /* 2 */
729 jsB_propf(J, "Array.prototype.unshift", Ap_unshift, 0); /* 1 */
730
731 /* ES5 */
732 jsB_propf(J, "Array.prototype.indexOf", Ap_indexOf, 1);
733 jsB_propf(J, "Array.prototype.lastIndexOf", Ap_lastIndexOf, 1);
734 jsB_propf(J, "Array.prototype.every", Ap_every, 1);
735 jsB_propf(J, "Array.prototype.some", Ap_some, 1);
736 jsB_propf(J, "Array.prototype.forEach", Ap_forEach, 1);
737 jsB_propf(J, "Array.prototype.map", Ap_map, 1);
738 jsB_propf(J, "Array.prototype.filter", Ap_filter, 1);
739 jsB_propf(J, "Array.prototype.reduce", Ap_reduce, 1);
740 jsB_propf(J, "Array.prototype.reduceRight", Ap_reduceRight, 1);
741 }
742 js_newcconstructor(J, jsB_new_Array, jsB_new_Array, "Array", 0); /* 1 */
743 {
744 /* ES5 */
745 jsB_propf(J, "Array.isArray", A_isArray, 1);
746 }
747 js_defglobal(J, "Array", JS_DONTENUM);
748 }
749