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