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