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