1#include "jsi.h"
2#include "jsvalue.h"
3#include "jsbuiltin.h"
4
5int 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
14void 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
20int 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
26void 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
32void 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
38void 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
44static 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
66static 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
88static 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
143static 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
159static 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
176static 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
204static 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
229static 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
251struct sortslot {
252 js_Value v;
253 js_State *J;
254};
255
256static 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 int c;
263
264 int unx = (a->type == JS_TUNDEFINED);
265 int uny = (b->type == JS_TUNDEFINED);
266 if (unx) return !uny;
267 if (uny) return -1;
268
269 if (js_iscallable(J, 1)) {
270 js_copy(J, 1); /* copy function */
271 js_pushundefined(J);
272 js_pushvalue(J, *a);
273 js_pushvalue(J, *b);
274 js_call(J, 2);
275 c = js_tonumber(J, -1);
276 js_pop(J, 1);
277 } else {
278 js_pushvalue(J, *a);
279 js_pushvalue(J, *b);
280 sx = js_tostring(J, -2);
281 sy = js_tostring(J, -1);
282 c = strcmp(sx, sy);
283 js_pop(J, 2);
284 }
285 return c;
286}
287
288static void Ap_sort(js_State *J)
289{
290 struct sortslot *array = NULL;
291 int i, n, len;
292
293 len = js_getlength(J, 0);
294 if (len <= 0) {
295 js_copy(J, 0);
296 return;
297 }
298
299 if (len >= INT_MAX / (int)sizeof(*array))
300 js_rangeerror(J, "array is too large to sort");
301
302 array = js_malloc(J, len * sizeof *array);
303
304 /* Holding objects where the GC cannot see them is illegal, but if we
305 * don't allow the GC to run we can use qsort() on a temporary array of
306 * js_Values for fast sorting.
307 */
308 ++J->gcpause;
309
310 if (js_try(J)) {
311 --J->gcpause;
312 js_free(J, array);
313 js_throw(J);
314 }
315
316 n = 0;
317 for (i = 0; i < len; ++i) {
318 if (js_hasindex(J, 0, i)) {
319 array[n].v = *js_tovalue(J, -1);
320 array[n].J = J;
321 js_pop(J, 1);
322 ++n;
323 }
324 }
325
326 qsort(array, n, sizeof *array, sortcmp);
327
328 for (i = 0; i < n; ++i) {
329 js_pushvalue(J, array[i].v);
330 js_setindex(J, 0, i);
331 }
332 for (i = n; i < len; ++i) {
333 js_delindex(J, 0, i);
334 }
335
336 --J->gcpause;
337
338 js_endtry(J);
339 js_free(J, array);
340
341 js_copy(J, 0);
342}
343
344static void Ap_splice(js_State *J)
345{
346 int top = js_gettop(J);
347 int len, start, del, add, k;
348 double f;
349
350 js_newarray(J);
351
352 len = js_getlength(J, 0);
353
354 f = js_tointeger(J, 1);
355 if (f < 0) f = f + len;
356 start = f < 0 ? 0 : f > len ? len : f;
357
358 f = js_tointeger(J, 2);
359 del = f < 0 ? 0 : f > len - start ? len - start : f;
360
361 /* copy deleted items to return array */
362 for (k = 0; k < del; ++k)
363 if (js_hasindex(J, 0, start + k))
364 js_setindex(J, -2, k);
365 js_setlength(J, -1, del);
366
367 /* shift the tail to resize the hole left by deleted items */
368 add = top - 3;
369 if (add < del) {
370 for (k = start; k < len - del; ++k) {
371 if (js_hasindex(J, 0, k + del))
372 js_setindex(J, 0, k + add);
373 else
374 js_delindex(J, 0, k + add);
375 }
376 for (k = len; k > len - del + add; --k)
377 js_delindex(J, 0, k - 1);
378 } else if (add > del) {
379 for (k = len - del; k > start; --k) {
380 if (js_hasindex(J, 0, k + del - 1))
381 js_setindex(J, 0, k + add - 1);
382 else
383 js_delindex(J, 0, k + add - 1);
384 }
385 }
386
387 /* copy new items into the hole */
388 for (k = 0; k < add; ++k) {
389 js_copy(J, 3 + k);
390 js_setindex(J, 0, start + k);
391 }
392
393 js_setlength(J, 0, len - del + add);
394}
395
396static void Ap_unshift(js_State *J)
397{
398 int i, top = js_gettop(J);
399 int k, len;
400
401 len = js_getlength(J, 0);
402
403 for (k = len; k > 0; --k) {
404 int from = k - 1;
405 int to = k + top - 2;
406 if (js_hasindex(J, 0, from))
407 js_setindex(J, 0, to);
408 else
409 js_delindex(J, 0, to);
410 }
411
412 for (i = 1; i < top; ++i) {
413 js_copy(J, i);
414 js_setindex(J, 0, i - 1);
415 }
416
417 js_setlength(J, 0, len + top - 1);
418
419 js_pushnumber(J, len + top - 1);
420}
421
422static void Ap_toString(js_State *J)
423{
424 int top = js_gettop(J);
425 js_pop(J, top - 1);
426 Ap_join(J);
427}
428
429static void Ap_indexOf(js_State *J)
430{
431 int k, len, from;
432
433 len = js_getlength(J, 0);
434 from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
435 if (from < 0) from = len + from;
436 if (from < 0) from = 0;
437
438 js_copy(J, 1);
439 for (k = from; k < len; ++k) {
440 if (js_hasindex(J, 0, k)) {
441 if (js_strictequal(J)) {
442 js_pushnumber(J, k);
443 return;
444 }
445 js_pop(J, 1);
446 }
447 }
448
449 js_pushnumber(J, -1);
450}
451
452static void Ap_lastIndexOf(js_State *J)
453{
454 int k, len, from;
455
456 len = js_getlength(J, 0);
457 from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1;
458 if (from > len - 1) from = len - 1;
459 if (from < 0) from = len + from;
460
461 js_copy(J, 1);
462 for (k = from; k >= 0; --k) {
463 if (js_hasindex(J, 0, k)) {
464 if (js_strictequal(J)) {
465 js_pushnumber(J, k);
466 return;
467 }
468 js_pop(J, 1);
469 }
470 }
471
472 js_pushnumber(J, -1);
473}
474
475static void Ap_every(js_State *J)
476{
477 int hasthis = js_gettop(J) >= 3;
478 int k, len;
479
480 if (!js_iscallable(J, 1))
481 js_typeerror(J, "callback is not a function");
482
483 len = js_getlength(J, 0);
484 for (k = 0; k < len; ++k) {
485 if (js_hasindex(J, 0, k)) {
486 js_copy(J, 1);
487 if (hasthis)
488 js_copy(J, 2);
489 else
490 js_pushundefined(J);
491 js_copy(J, -3);
492 js_pushnumber(J, k);
493 js_copy(J, 0);
494 js_call(J, 3);
495 if (!js_toboolean(J, -1))
496 return;
497 js_pop(J, 2);
498 }
499 }
500
501 js_pushboolean(J, 1);
502}
503
504static void Ap_some(js_State *J)
505{
506 int hasthis = js_gettop(J) >= 3;
507 int k, len;
508
509 if (!js_iscallable(J, 1))
510 js_typeerror(J, "callback is not a function");
511
512 len = js_getlength(J, 0);
513 for (k = 0; k < len; ++k) {
514 if (js_hasindex(J, 0, k)) {
515 js_copy(J, 1);
516 if (hasthis)
517 js_copy(J, 2);
518 else
519 js_pushundefined(J);
520 js_copy(J, -3);
521 js_pushnumber(J, k);
522 js_copy(J, 0);
523 js_call(J, 3);
524 if (js_toboolean(J, -1))
525 return;
526 js_pop(J, 2);
527 }
528 }
529
530 js_pushboolean(J, 0);
531}
532
533static void Ap_forEach(js_State *J)
534{
535 int hasthis = js_gettop(J) >= 3;
536 int k, len;
537
538 if (!js_iscallable(J, 1))
539 js_typeerror(J, "callback is not a function");
540
541 len = js_getlength(J, 0);
542 for (k = 0; k < len; ++k) {
543 if (js_hasindex(J, 0, k)) {
544 js_copy(J, 1);
545 if (hasthis)
546 js_copy(J, 2);
547 else
548 js_pushundefined(J);
549 js_copy(J, -3);
550 js_pushnumber(J, k);
551 js_copy(J, 0);
552 js_call(J, 3);
553 js_pop(J, 2);
554 }
555 }
556
557 js_pushundefined(J);
558}
559
560static void Ap_map(js_State *J)
561{
562 int hasthis = js_gettop(J) >= 3;
563 int k, len;
564
565 if (!js_iscallable(J, 1))
566 js_typeerror(J, "callback is not a function");
567
568 js_newarray(J);
569
570 len = js_getlength(J, 0);
571 for (k = 0; k < len; ++k) {
572 if (js_hasindex(J, 0, k)) {
573 js_copy(J, 1);
574 if (hasthis)
575 js_copy(J, 2);
576 else
577 js_pushundefined(J);
578 js_copy(J, -3);
579 js_pushnumber(J, k);
580 js_copy(J, 0);
581 js_call(J, 3);
582 js_setindex(J, -3, k);
583 js_pop(J, 1);
584 }
585 }
586}
587
588static void Ap_filter(js_State *J)
589{
590 int hasthis = js_gettop(J) >= 3;
591 int k, to, len;
592
593 if (!js_iscallable(J, 1))
594 js_typeerror(J, "callback is not a function");
595
596 js_newarray(J);
597 to = 0;
598
599 len = js_getlength(J, 0);
600 for (k = 0; k < len; ++k) {
601 if (js_hasindex(J, 0, k)) {
602 js_copy(J, 1);
603 if (hasthis)
604 js_copy(J, 2);
605 else
606 js_pushundefined(J);
607 js_copy(J, -3);
608 js_pushnumber(J, k);
609 js_copy(J, 0);
610 js_call(J, 3);
611 if (js_toboolean(J, -1)) {
612 js_pop(J, 1);
613 js_setindex(J, -2, to++);
614 } else {
615 js_pop(J, 2);
616 }
617 }
618 }
619}
620
621static void Ap_reduce(js_State *J)
622{
623 int hasinitial = js_gettop(J) >= 3;
624 int k, len;
625
626 if (!js_iscallable(J, 1))
627 js_typeerror(J, "callback is not a function");
628
629 len = js_getlength(J, 0);
630 k = 0;
631
632 if (len == 0 && !hasinitial)
633 js_typeerror(J, "no initial value");
634
635 /* initial value of accumulator */
636 if (hasinitial)
637 js_copy(J, 2);
638 else {
639 while (k < len)
640 if (js_hasindex(J, 0, k++))
641 break;
642 if (k == len)
643 js_typeerror(J, "no initial value");
644 }
645
646 while (k < len) {
647 if (js_hasindex(J, 0, k)) {
648 js_copy(J, 1);
649 js_pushundefined(J);
650 js_rot(J, 4); /* accumulator on top */
651 js_rot(J, 4); /* property on top */
652 js_pushnumber(J, k);
653 js_copy(J, 0);
654 js_call(J, 4); /* calculate new accumulator */
655 }
656 ++k;
657 }
658
659 /* return accumulator */
660}
661
662static void Ap_reduceRight(js_State *J)
663{
664 int hasinitial = js_gettop(J) >= 3;
665 int k, len;
666
667 if (!js_iscallable(J, 1))
668 js_typeerror(J, "callback is not a function");
669
670 len = js_getlength(J, 0);
671 k = len - 1;
672
673 if (len == 0 && !hasinitial)
674 js_typeerror(J, "no initial value");
675
676 /* initial value of accumulator */
677 if (hasinitial)
678 js_copy(J, 2);
679 else {
680 while (k >= 0)
681 if (js_hasindex(J, 0, k--))
682 break;
683 if (k < 0)
684 js_typeerror(J, "no initial value");
685 }
686
687 while (k >= 0) {
688 if (js_hasindex(J, 0, k)) {
689 js_copy(J, 1);
690 js_pushundefined(J);
691 js_rot(J, 4); /* accumulator on top */
692 js_rot(J, 4); /* property on top */
693 js_pushnumber(J, k);
694 js_copy(J, 0);
695 js_call(J, 4); /* calculate new accumulator */
696 }
697 --k;
698 }
699
700 /* return accumulator */
701}
702
703static void A_isArray(js_State *J)
704{
705 if (js_isobject(J, 1)) {
706 js_Object *T = js_toobject(J, 1);
707 js_pushboolean(J, T->type == JS_CARRAY);
708 } else {
709 js_pushboolean(J, 0);
710 }
711}
712
713void jsB_initarray(js_State *J)
714{
715 js_pushobject(J, J->Array_prototype);
716 {
717 jsB_propf(J, "Array.prototype.toString", Ap_toString, 0);
718 jsB_propf(J, "Array.prototype.concat", Ap_concat, 0); /* 1 */
719 jsB_propf(J, "Array.prototype.join", Ap_join, 1);
720 jsB_propf(J, "Array.prototype.pop", Ap_pop, 0);
721 jsB_propf(J, "Array.prototype.push", Ap_push, 0); /* 1 */
722 jsB_propf(J, "Array.prototype.reverse", Ap_reverse, 0);
723 jsB_propf(J, "Array.prototype.shift", Ap_shift, 0);
724 jsB_propf(J, "Array.prototype.slice", Ap_slice, 2);
725 jsB_propf(J, "Array.prototype.sort", Ap_sort, 1);
726 jsB_propf(J, "Array.prototype.splice", Ap_splice, 0); /* 2 */
727 jsB_propf(J, "Array.prototype.unshift", Ap_unshift, 0); /* 1 */
728
729 /* ES5 */
730 jsB_propf(J, "Array.prototype.indexOf", Ap_indexOf, 1);
731 jsB_propf(J, "Array.prototype.lastIndexOf", Ap_lastIndexOf, 1);
732 jsB_propf(J, "Array.prototype.every", Ap_every, 1);
733 jsB_propf(J, "Array.prototype.some", Ap_some, 1);
734 jsB_propf(J, "Array.prototype.forEach", Ap_forEach, 1);
735 jsB_propf(J, "Array.prototype.map", Ap_map, 1);
736 jsB_propf(J, "Array.prototype.filter", Ap_filter, 1);
737 jsB_propf(J, "Array.prototype.reduce", Ap_reduce, 1);
738 jsB_propf(J, "Array.prototype.reduceRight", Ap_reduceRight, 1);
739 }
740 js_newcconstructor(J, jsB_new_Array, jsB_new_Array, "Array", 0); /* 1 */
741 {
742 /* ES5 */
743 jsB_propf(J, "Array.isArray", A_isArray, 1);
744 }
745 js_defglobal(J, "Array", JS_DONTENUM);
746}
747