comparison mupdf-source/thirdparty/mujs/jsarray.c @ 3:2c135c81b16c

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