comparison mupdf-source/thirdparty/mujs/jsrun.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 #include "jsi.h"
2 #include "utf.h"
3
4 #include <assert.h>
5
6 static void jsR_run(js_State *J, js_Function *F);
7
8 /* Push values on stack */
9
10 #define STACK (J->stack)
11 #define TOP (J->top)
12 #define BOT (J->bot)
13
14 static void js_trystackoverflow(js_State *J)
15 {
16 STACK[TOP].t.type = JS_TLITSTR;
17 STACK[TOP].u.litstr = "exception stack overflow";
18 ++TOP;
19 js_throw(J);
20 }
21
22 static void js_stackoverflow(js_State *J)
23 {
24 STACK[TOP].t.type = JS_TLITSTR;
25 STACK[TOP].u.litstr = "stack overflow";
26 ++TOP;
27 js_throw(J);
28 }
29
30 static void js_outofmemory(js_State *J)
31 {
32 STACK[TOP].t.type = JS_TLITSTR;
33 STACK[TOP].u.litstr = "out of memory";
34 ++TOP;
35 js_throw(J);
36 }
37
38 void *js_malloc(js_State *J, int size)
39 {
40 void *ptr = J->alloc(J->actx, NULL, size);
41 if (!ptr)
42 js_outofmemory(J);
43 return ptr;
44 }
45
46 void *js_realloc(js_State *J, void *ptr, int size)
47 {
48 ptr = J->alloc(J->actx, ptr, size);
49 if (!ptr)
50 js_outofmemory(J);
51 return ptr;
52 }
53
54 char *js_strdup(js_State *J, const char *s)
55 {
56 int n = strlen(s) + 1;
57 char *p = js_malloc(J, n);
58 memcpy(p, s, n);
59 return p;
60 }
61
62 void js_free(js_State *J, void *ptr)
63 {
64 J->alloc(J->actx, ptr, 0);
65 }
66
67 js_String *jsV_newmemstring(js_State *J, const char *s, int n)
68 {
69 js_String *v = js_malloc(J, soffsetof(js_String, p) + n + 1);
70 memcpy(v->p, s, n);
71 v->p[n] = 0;
72 v->gcmark = 0;
73 v->gcnext = J->gcstr;
74 J->gcstr = v;
75 ++J->gccounter;
76 return v;
77 }
78
79 #define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J)
80
81 void js_pushvalue(js_State *J, js_Value v)
82 {
83 CHECKSTACK(1);
84 STACK[TOP] = v;
85 ++TOP;
86 }
87
88 void js_pushundefined(js_State *J)
89 {
90 CHECKSTACK(1);
91 STACK[TOP].t.type = JS_TUNDEFINED;
92 ++TOP;
93 }
94
95 void js_pushnull(js_State *J)
96 {
97 CHECKSTACK(1);
98 STACK[TOP].t.type = JS_TNULL;
99 ++TOP;
100 }
101
102 void js_pushboolean(js_State *J, int v)
103 {
104 CHECKSTACK(1);
105 STACK[TOP].t.type = JS_TBOOLEAN;
106 STACK[TOP].u.boolean = !!v;
107 ++TOP;
108 }
109
110 void js_pushnumber(js_State *J, double v)
111 {
112 CHECKSTACK(1);
113 STACK[TOP].t.type = JS_TNUMBER;
114 STACK[TOP].u.number = v;
115 ++TOP;
116 }
117
118 void js_pushstring(js_State *J, const char *v)
119 {
120 size_t n = strlen(v);
121 if (n > JS_STRLIMIT)
122 js_rangeerror(J, "invalid string length");
123 CHECKSTACK(1);
124 if (n <= soffsetof(js_Value, t.type)) {
125 char *s = STACK[TOP].u.shrstr;
126 while (n--) *s++ = *v++;
127 *s = 0;
128 STACK[TOP].t.type = JS_TSHRSTR;
129 } else {
130 STACK[TOP].t.type = JS_TMEMSTR;
131 STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
132 }
133 ++TOP;
134 }
135
136 void js_pushlstring(js_State *J, const char *v, int n)
137 {
138 if (n > JS_STRLIMIT)
139 js_rangeerror(J, "invalid string length");
140 CHECKSTACK(1);
141 if (n <= soffsetof(js_Value, t.type)) {
142 char *s = STACK[TOP].u.shrstr;
143 while (n--) *s++ = *v++;
144 *s = 0;
145 STACK[TOP].t.type = JS_TSHRSTR;
146 } else {
147 STACK[TOP].t.type = JS_TMEMSTR;
148 STACK[TOP].u.memstr = jsV_newmemstring(J, v, n);
149 }
150 ++TOP;
151 }
152
153 void js_pushliteral(js_State *J, const char *v)
154 {
155 CHECKSTACK(1);
156 STACK[TOP].t.type = JS_TLITSTR;
157 STACK[TOP].u.litstr = v;
158 ++TOP;
159 }
160
161 void js_pushobject(js_State *J, js_Object *v)
162 {
163 CHECKSTACK(1);
164 STACK[TOP].t.type = JS_TOBJECT;
165 STACK[TOP].u.object = v;
166 ++TOP;
167 }
168
169 void js_pushglobal(js_State *J)
170 {
171 js_pushobject(J, J->G);
172 }
173
174 void js_currentfunction(js_State *J)
175 {
176 CHECKSTACK(1);
177 if (BOT > 0)
178 STACK[TOP] = STACK[BOT-1];
179 else
180 STACK[TOP].t.type = JS_TUNDEFINED;
181 ++TOP;
182 }
183
184 void *js_currentfunctiondata(js_State *J)
185 {
186 if (BOT > 0)
187 return STACK[BOT-1].u.object->u.c.data;
188 return NULL;
189 }
190
191 /* Read values from stack */
192
193 static js_Value *stackidx(js_State *J, int idx)
194 {
195 static js_Value undefined = { { {0}, JS_TUNDEFINED } };
196 idx = idx < 0 ? TOP + idx : BOT + idx;
197 if (idx < 0 || idx >= TOP)
198 return &undefined;
199 return STACK + idx;
200 }
201
202 js_Value *js_tovalue(js_State *J, int idx)
203 {
204 return stackidx(J, idx);
205 }
206
207 int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TUNDEFINED; }
208 int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TUNDEFINED; }
209 int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNULL; }
210 int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TBOOLEAN; }
211 int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNUMBER; }
212 int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->t.type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; }
213 int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TOBJECT; }
214 int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TOBJECT; }
215 int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->t.type != JS_TUNDEFINED && v->t.type != JS_TNULL; }
216
217 int js_iscallable(js_State *J, int idx)
218 {
219 js_Value *v = stackidx(J, idx);
220 if (v->t.type == JS_TOBJECT)
221 return v->u.object->type == JS_CFUNCTION ||
222 v->u.object->type == JS_CSCRIPT ||
223 v->u.object->type == JS_CCFUNCTION;
224 return 0;
225 }
226
227 int js_isarray(js_State *J, int idx)
228 {
229 js_Value *v = stackidx(J, idx);
230 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CARRAY;
231 }
232
233 int js_isregexp(js_State *J, int idx)
234 {
235 js_Value *v = stackidx(J, idx);
236 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP;
237 }
238
239 int js_isuserdata(js_State *J, int idx, const char *tag)
240 {
241 js_Value *v = stackidx(J, idx);
242 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
243 return !strcmp(tag, v->u.object->u.user.tag);
244 return 0;
245 }
246
247 int js_iserror(js_State *J, int idx)
248 {
249 js_Value *v = stackidx(J, idx);
250 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CERROR;
251 }
252
253 const char *js_typeof(js_State *J, int idx)
254 {
255 js_Value *v = stackidx(J, idx);
256 switch (v->t.type) {
257 default:
258 case JS_TSHRSTR: return "string";
259 case JS_TUNDEFINED: return "undefined";
260 case JS_TNULL: return "object";
261 case JS_TBOOLEAN: return "boolean";
262 case JS_TNUMBER: return "number";
263 case JS_TLITSTR: return "string";
264 case JS_TMEMSTR: return "string";
265 case JS_TOBJECT:
266 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
267 return "function";
268 return "object";
269 }
270 }
271
272 int js_type(js_State *J, int idx)
273 {
274 js_Value *v = stackidx(J, idx);
275 switch (v->t.type) {
276 default:
277 case JS_TSHRSTR: return JS_ISSTRING;
278 case JS_TUNDEFINED: return JS_ISUNDEFINED;
279 case JS_TNULL: return JS_ISNULL;
280 case JS_TBOOLEAN: return JS_ISBOOLEAN;
281 case JS_TNUMBER: return JS_ISNUMBER;
282 case JS_TLITSTR: return JS_ISSTRING;
283 case JS_TMEMSTR: return JS_ISSTRING;
284 case JS_TOBJECT:
285 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
286 return JS_ISFUNCTION;
287 return JS_ISOBJECT;
288 }
289 }
290
291 int js_toboolean(js_State *J, int idx)
292 {
293 return jsV_toboolean(J, stackidx(J, idx));
294 }
295
296 double js_tonumber(js_State *J, int idx)
297 {
298 return jsV_tonumber(J, stackidx(J, idx));
299 }
300
301 int js_tointeger(js_State *J, int idx)
302 {
303 return jsV_numbertointeger(jsV_tonumber(J, stackidx(J, idx)));
304 }
305
306 int js_toint32(js_State *J, int idx)
307 {
308 return jsV_numbertoint32(jsV_tonumber(J, stackidx(J, idx)));
309 }
310
311 unsigned int js_touint32(js_State *J, int idx)
312 {
313 return jsV_numbertouint32(jsV_tonumber(J, stackidx(J, idx)));
314 }
315
316 short js_toint16(js_State *J, int idx)
317 {
318 return jsV_numbertoint16(jsV_tonumber(J, stackidx(J, idx)));
319 }
320
321 unsigned short js_touint16(js_State *J, int idx)
322 {
323 return jsV_numbertouint16(jsV_tonumber(J, stackidx(J, idx)));
324 }
325
326 const char *js_tostring(js_State *J, int idx)
327 {
328 return jsV_tostring(J, stackidx(J, idx));
329 }
330
331 js_Object *js_toobject(js_State *J, int idx)
332 {
333 return jsV_toobject(J, stackidx(J, idx));
334 }
335
336 void js_toprimitive(js_State *J, int idx, int hint)
337 {
338 jsV_toprimitive(J, stackidx(J, idx), hint);
339 }
340
341 js_Regexp *js_toregexp(js_State *J, int idx)
342 {
343 js_Value *v = stackidx(J, idx);
344 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP)
345 return &v->u.object->u.r;
346 js_typeerror(J, "not a regexp");
347 }
348
349 void *js_touserdata(js_State *J, int idx, const char *tag)
350 {
351 js_Value *v = stackidx(J, idx);
352 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA)
353 if (!strcmp(tag, v->u.object->u.user.tag))
354 return v->u.object->u.user.data;
355 js_typeerror(J, "not a %s", tag);
356 }
357
358 static js_Object *jsR_tofunction(js_State *J, int idx)
359 {
360 js_Value *v = stackidx(J, idx);
361 if (v->t.type == JS_TUNDEFINED || v->t.type == JS_TNULL)
362 return NULL;
363 if (v->t.type == JS_TOBJECT)
364 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION)
365 return v->u.object;
366 js_typeerror(J, "not a function");
367 }
368
369 /* Stack manipulation */
370
371 int js_gettop(js_State *J)
372 {
373 return TOP - BOT;
374 }
375
376 void js_pop(js_State *J, int n)
377 {
378 TOP -= n;
379 if (TOP < BOT) {
380 TOP = BOT;
381 js_error(J, "stack underflow!");
382 }
383 }
384
385 void js_remove(js_State *J, int idx)
386 {
387 idx = idx < 0 ? TOP + idx : BOT + idx;
388 if (idx < BOT || idx >= TOP)
389 js_error(J, "stack error!");
390 for (;idx < TOP - 1; ++idx)
391 STACK[idx] = STACK[idx+1];
392 --TOP;
393 }
394
395 void js_insert(js_State *J, int idx)
396 {
397 js_error(J, "not implemented yet");
398 }
399
400 void js_replace(js_State* J, int idx)
401 {
402 idx = idx < 0 ? TOP + idx : BOT + idx;
403 if (idx < BOT || idx >= TOP)
404 js_error(J, "stack error!");
405 STACK[idx] = STACK[--TOP];
406 }
407
408 void js_copy(js_State *J, int idx)
409 {
410 CHECKSTACK(1);
411 STACK[TOP] = *stackidx(J, idx);
412 ++TOP;
413 }
414
415 void js_dup(js_State *J)
416 {
417 CHECKSTACK(1);
418 STACK[TOP] = STACK[TOP-1];
419 ++TOP;
420 }
421
422 void js_dup2(js_State *J)
423 {
424 CHECKSTACK(2);
425 STACK[TOP] = STACK[TOP-2];
426 STACK[TOP+1] = STACK[TOP-1];
427 TOP += 2;
428 }
429
430 void js_rot2(js_State *J)
431 {
432 /* A B -> B A */
433 js_Value tmp = STACK[TOP-1]; /* A B (B) */
434 STACK[TOP-1] = STACK[TOP-2]; /* A A */
435 STACK[TOP-2] = tmp; /* B A */
436 }
437
438 void js_rot3(js_State *J)
439 {
440 /* A B C -> C A B */
441 js_Value tmp = STACK[TOP-1]; /* A B C (C) */
442 STACK[TOP-1] = STACK[TOP-2]; /* A B B */
443 STACK[TOP-2] = STACK[TOP-3]; /* A A B */
444 STACK[TOP-3] = tmp; /* C A B */
445 }
446
447 void js_rot4(js_State *J)
448 {
449 /* A B C D -> D A B C */
450 js_Value tmp = STACK[TOP-1]; /* A B C D (D) */
451 STACK[TOP-1] = STACK[TOP-2]; /* A B C C */
452 STACK[TOP-2] = STACK[TOP-3]; /* A B B C */
453 STACK[TOP-3] = STACK[TOP-4]; /* A A B C */
454 STACK[TOP-4] = tmp; /* D A B C */
455 }
456
457 void js_rot2pop1(js_State *J)
458 {
459 /* A B -> B */
460 STACK[TOP-2] = STACK[TOP-1];
461 --TOP;
462 }
463
464 void js_rot3pop2(js_State *J)
465 {
466 /* A B C -> C */
467 STACK[TOP-3] = STACK[TOP-1];
468 TOP -= 2;
469 }
470
471 void js_rot(js_State *J, int n)
472 {
473 int i;
474 js_Value tmp = STACK[TOP-1];
475 for (i = 1; i < n; ++i)
476 STACK[TOP-i] = STACK[TOP-i-1];
477 STACK[TOP-i] = tmp;
478 }
479
480 /* Property access that takes care of attributes and getters/setters */
481
482 int js_isarrayindex(js_State *J, const char *p, int *idx)
483 {
484 int n = 0;
485
486 /* check for empty string */
487 if (p[0] == 0)
488 return 0;
489
490 /* check for '0' and integers with leading zero */
491 if (p[0] == '0')
492 return (p[1] == 0) ? *idx = 0, 1 : 0;
493
494 while (*p) {
495 int c = *p++;
496 if (c >= '0' && c <= '9') {
497 if (n >= INT_MAX / 10)
498 return 0;
499 n = n * 10 + (c - '0');
500 } else {
501 return 0;
502 }
503 }
504 return *idx = n, 1;
505 }
506
507 static void js_pushrune(js_State *J, Rune rune)
508 {
509 char buf[UTFmax + 1];
510 if (rune >= 0) {
511 buf[runetochar(buf, &rune)] = 0;
512 js_pushstring(J, buf);
513 } else {
514 js_pushundefined(J);
515 }
516 }
517
518 void jsR_unflattenarray(js_State *J, js_Object *obj) {
519 if (obj->type == JS_CARRAY && obj->u.a.simple) {
520 js_Property *ref;
521 int i;
522 char name[32];
523 if (js_try(J)) {
524 obj->properties = NULL;
525 js_throw(J);
526 }
527 for (i = 0; i < obj->u.a.flat_length; ++i) {
528 js_itoa(name, i);
529 ref = jsV_setproperty(J, obj, name);
530 ref->value = obj->u.a.array[i];
531 }
532 js_free(J, obj->u.a.array);
533 obj->u.a.simple = 0;
534 obj->u.a.flat_length = 0;
535 obj->u.a.flat_capacity = 0;
536 obj->u.a.array = NULL;
537 js_endtry(J);
538 }
539 }
540
541 static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name)
542 {
543 js_Property *ref;
544 int k;
545
546 if (obj->type == JS_CARRAY) {
547 if (!strcmp(name, "length")) {
548 js_pushnumber(J, obj->u.a.length);
549 return 1;
550 }
551 if (obj->u.a.simple) {
552 if (js_isarrayindex(J, name, &k)) {
553 if (k >= 0 && k < obj->u.a.flat_length) {
554 js_pushvalue(J, obj->u.a.array[k]);
555 return 1;
556 }
557 return 0;
558 }
559 }
560 }
561
562 else if (obj->type == JS_CSTRING) {
563 if (!strcmp(name, "length")) {
564 js_pushnumber(J, obj->u.s.length);
565 return 1;
566 }
567 if (js_isarrayindex(J, name, &k)) {
568 if (k >= 0 && k < obj->u.s.length) {
569 js_pushrune(J, js_runeat(J, obj->u.s.string, k));
570 return 1;
571 }
572 }
573 }
574
575 else if (obj->type == JS_CREGEXP) {
576 if (!strcmp(name, "source")) {
577 js_pushstring(J, obj->u.r.source);
578 return 1;
579 }
580 if (!strcmp(name, "global")) {
581 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_G);
582 return 1;
583 }
584 if (!strcmp(name, "ignoreCase")) {
585 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_I);
586 return 1;
587 }
588 if (!strcmp(name, "multiline")) {
589 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_M);
590 return 1;
591 }
592 if (!strcmp(name, "lastIndex")) {
593 js_pushnumber(J, obj->u.r.last);
594 return 1;
595 }
596 }
597
598 else if (obj->type == JS_CUSERDATA) {
599 if (obj->u.user.has && obj->u.user.has(J, obj->u.user.data, name))
600 return 1;
601 }
602
603 ref = jsV_getproperty(J, obj, name);
604 if (ref) {
605 if (ref->getter) {
606 js_pushobject(J, ref->getter);
607 js_pushobject(J, obj);
608 js_call(J, 0);
609 } else {
610 js_pushvalue(J, ref->value);
611 }
612 return 1;
613 }
614
615 return 0;
616 }
617
618 static void jsR_getproperty(js_State *J, js_Object *obj, const char *name)
619 {
620 if (!jsR_hasproperty(J, obj, name))
621 js_pushundefined(J);
622 }
623
624 static int jsR_hasindex(js_State *J, js_Object *obj, int k)
625 {
626 char buf[32];
627 if (obj->type == JS_CARRAY && obj->u.a.simple) {
628 if (k >= 0 && k < obj->u.a.flat_length) {
629 js_pushvalue(J, obj->u.a.array[k]);
630 return 1;
631 }
632 return 0;
633 }
634 return jsR_hasproperty(J, obj, js_itoa(buf, k));
635 }
636
637 static void jsR_getindex(js_State *J, js_Object *obj, int k)
638 {
639 if (!jsR_hasindex(J, obj, k))
640 js_pushundefined(J);
641 }
642
643 static void jsR_setarrayindex(js_State *J, js_Object *obj, int k, js_Value *value)
644 {
645 int newlen = k + 1;
646 assert(obj->u.a.simple);
647 assert(k >= 0);
648 if (newlen > JS_ARRAYLIMIT)
649 js_rangeerror(J, "array too large");
650 if (newlen > obj->u.a.flat_length) {
651 assert(newlen == obj->u.a.flat_length + 1);
652 if (newlen > obj->u.a.flat_capacity) {
653 int newcap = obj->u.a.flat_capacity;
654 if (newcap == 0)
655 newcap = 8;
656 while (newcap < newlen)
657 newcap <<= 1;
658 obj->u.a.array = js_realloc(J, obj->u.a.array, newcap * sizeof(js_Value));
659 obj->u.a.flat_capacity = newcap;
660 }
661 obj->u.a.flat_length = newlen;
662 }
663 if (newlen > obj->u.a.length)
664 obj->u.a.length = newlen;
665 obj->u.a.array[k] = *value;
666 }
667
668 static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int transient)
669 {
670 js_Value *value = stackidx(J, -1);
671 js_Property *ref;
672 int k;
673 int own;
674
675 if (obj->type == JS_CARRAY) {
676 if (!strcmp(name, "length")) {
677 double rawlen = jsV_tonumber(J, value);
678 int newlen = jsV_numbertointeger(rawlen);
679 if (newlen != rawlen || newlen < 0)
680 js_rangeerror(J, "invalid array length");
681 if (newlen > JS_ARRAYLIMIT)
682 js_rangeerror(J, "array too large");
683 if (obj->u.a.simple) {
684 obj->u.a.length = newlen;
685 if (newlen <= obj->u.a.flat_length)
686 obj->u.a.flat_length = newlen;
687 } else {
688 jsV_resizearray(J, obj, newlen);
689 }
690 return;
691 }
692
693 if (js_isarrayindex(J, name, &k)) {
694 if (obj->u.a.simple) {
695 if (k >= 0 && k <= obj->u.a.flat_length) {
696 jsR_setarrayindex(J, obj, k, value);
697 } else {
698 jsR_unflattenarray(J, obj);
699 if (obj->u.a.length < k + 1)
700 obj->u.a.length = k + 1;
701 }
702 } else {
703 if (obj->u.a.length < k + 1)
704 obj->u.a.length = k + 1;
705 }
706 }
707 }
708
709 else if (obj->type == JS_CSTRING) {
710 if (!strcmp(name, "length"))
711 goto readonly;
712 if (js_isarrayindex(J, name, &k))
713 if (k >= 0 && k < obj->u.s.length)
714 goto readonly;
715 }
716
717 else if (obj->type == JS_CREGEXP) {
718 if (!strcmp(name, "source")) goto readonly;
719 if (!strcmp(name, "global")) goto readonly;
720 if (!strcmp(name, "ignoreCase")) goto readonly;
721 if (!strcmp(name, "multiline")) goto readonly;
722 if (!strcmp(name, "lastIndex")) {
723 obj->u.r.last = jsV_tointeger(J, value);
724 return;
725 }
726 }
727
728 else if (obj->type == JS_CUSERDATA) {
729 if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name))
730 return;
731 }
732
733 /* First try to find a setter in prototype chain */
734 ref = jsV_getpropertyx(J, obj, name, &own);
735 if (ref) {
736 if (ref->setter) {
737 js_pushobject(J, ref->setter);
738 js_pushobject(J, obj);
739 js_pushvalue(J, *value);
740 js_call(J, 1);
741 js_pop(J, 1);
742 return;
743 } else {
744 if (J->strict)
745 if (ref->getter)
746 js_typeerror(J, "setting property '%s' that only has a getter", name);
747 if (ref->atts & JS_READONLY)
748 goto readonly;
749 }
750 }
751
752 /* Property not found on this object, so create one */
753 if (!ref || !own) {
754 if (transient) {
755 if (J->strict)
756 js_typeerror(J, "cannot create property '%s' on transient object", name);
757 return;
758 }
759 ref = jsV_setproperty(J, obj, name);
760 }
761
762 if (ref) {
763 if (!(ref->atts & JS_READONLY))
764 ref->value = *value;
765 else
766 goto readonly;
767 }
768
769 return;
770
771 readonly:
772 if (J->strict)
773 js_typeerror(J, "'%s' is read-only", name);
774 }
775
776 static void jsR_setindex(js_State *J, js_Object *obj, int k, int transient)
777 {
778 char buf[32];
779 if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.flat_length) {
780 jsR_setarrayindex(J, obj, k, stackidx(J, -1));
781 } else {
782 jsR_setproperty(J, obj, js_itoa(buf, k), transient);
783 }
784 }
785
786 static void jsR_defproperty(js_State *J, js_Object *obj, const char *name,
787 int atts, js_Value *value, js_Object *getter, js_Object *setter,
788 int throw)
789 {
790 js_Property *ref;
791 int k;
792
793 if (obj->type == JS_CARRAY) {
794 if (!strcmp(name, "length"))
795 goto readonly;
796 if (obj->u.a.simple)
797 jsR_unflattenarray(J, obj);
798 }
799
800 else if (obj->type == JS_CSTRING) {
801 if (!strcmp(name, "length"))
802 goto readonly;
803 if (js_isarrayindex(J, name, &k))
804 if (k >= 0 && k < obj->u.s.length)
805 goto readonly;
806 }
807
808 else if (obj->type == JS_CREGEXP) {
809 if (!strcmp(name, "source")) goto readonly;
810 if (!strcmp(name, "global")) goto readonly;
811 if (!strcmp(name, "ignoreCase")) goto readonly;
812 if (!strcmp(name, "multiline")) goto readonly;
813 if (!strcmp(name, "lastIndex")) goto readonly;
814 }
815
816 else if (obj->type == JS_CUSERDATA) {
817 if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name))
818 return;
819 }
820
821 ref = jsV_setproperty(J, obj, name);
822 if (ref) {
823 if (value) {
824 if (!(ref->atts & JS_READONLY))
825 ref->value = *value;
826 else if (J->strict)
827 js_typeerror(J, "'%s' is read-only", name);
828 }
829 if (getter) {
830 if (!(ref->atts & JS_DONTCONF))
831 ref->getter = getter;
832 else if (J->strict)
833 js_typeerror(J, "'%s' is non-configurable", name);
834 }
835 if (setter) {
836 if (!(ref->atts & JS_DONTCONF))
837 ref->setter = setter;
838 else if (J->strict)
839 js_typeerror(J, "'%s' is non-configurable", name);
840 }
841 ref->atts |= atts;
842 }
843
844 return;
845
846 readonly:
847 if (J->strict || throw)
848 js_typeerror(J, "'%s' is read-only or non-configurable", name);
849 }
850
851 static int jsR_delproperty(js_State *J, js_Object *obj, const char *name)
852 {
853 js_Property *ref;
854 int k;
855
856 if (obj->type == JS_CARRAY) {
857 if (!strcmp(name, "length"))
858 goto dontconf;
859 if (obj->u.a.simple)
860 jsR_unflattenarray(J, obj);
861 }
862
863 else if (obj->type == JS_CSTRING) {
864 if (!strcmp(name, "length"))
865 goto dontconf;
866 if (js_isarrayindex(J, name, &k))
867 if (k >= 0 && k < obj->u.s.length)
868 goto dontconf;
869 }
870
871 else if (obj->type == JS_CREGEXP) {
872 if (!strcmp(name, "source")) goto dontconf;
873 if (!strcmp(name, "global")) goto dontconf;
874 if (!strcmp(name, "ignoreCase")) goto dontconf;
875 if (!strcmp(name, "multiline")) goto dontconf;
876 if (!strcmp(name, "lastIndex")) goto dontconf;
877 }
878
879 else if (obj->type == JS_CUSERDATA) {
880 if (obj->u.user.delete && obj->u.user.delete(J, obj->u.user.data, name))
881 return 1;
882 }
883
884 ref = jsV_getownproperty(J, obj, name);
885 if (ref) {
886 if (ref->atts & JS_DONTCONF)
887 goto dontconf;
888 jsV_delproperty(J, obj, name);
889 }
890 return 1;
891
892 dontconf:
893 if (J->strict)
894 js_typeerror(J, "'%s' is non-configurable", name);
895 return 0;
896 }
897
898 static void jsR_delindex(js_State *J, js_Object *obj, int k)
899 {
900 char buf[32];
901 /* Allow deleting last element of a simple array without unflattening */
902 if (obj->type == JS_CARRAY && obj->u.a.simple && k == obj->u.a.flat_length - 1)
903 obj->u.a.flat_length = k;
904 else
905 jsR_delproperty(J, obj, js_itoa(buf, k));
906 }
907
908 /* Registry, global and object property accessors */
909
910 const char *js_ref(js_State *J)
911 {
912 js_Value *v = stackidx(J, -1);
913 const char *s;
914 char buf[32];
915 switch (v->t.type) {
916 case JS_TUNDEFINED: s = "_Undefined"; break;
917 case JS_TNULL: s = "_Null"; break;
918 case JS_TBOOLEAN:
919 s = v->u.boolean ? "_True" : "_False";
920 break;
921 case JS_TOBJECT:
922 sprintf(buf, "%p", (void*)v->u.object);
923 s = js_intern(J, buf);
924 break;
925 default:
926 sprintf(buf, "%d", J->nextref++);
927 s = js_intern(J, buf);
928 break;
929 }
930 js_setregistry(J, s);
931 return s;
932 }
933
934 void js_unref(js_State *J, const char *ref)
935 {
936 js_delregistry(J, ref);
937 }
938
939 void js_getregistry(js_State *J, const char *name)
940 {
941 jsR_getproperty(J, J->R, name);
942 }
943
944 void js_setregistry(js_State *J, const char *name)
945 {
946 jsR_setproperty(J, J->R, name, 0);
947 js_pop(J, 1);
948 }
949
950 void js_delregistry(js_State *J, const char *name)
951 {
952 jsR_delproperty(J, J->R, name);
953 }
954
955 void js_getglobal(js_State *J, const char *name)
956 {
957 jsR_getproperty(J, J->G, name);
958 }
959
960 void js_setglobal(js_State *J, const char *name)
961 {
962 jsR_setproperty(J, J->G, name, 0);
963 js_pop(J, 1);
964 }
965
966 void js_defglobal(js_State *J, const char *name, int atts)
967 {
968 jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL, 0);
969 js_pop(J, 1);
970 }
971
972 void js_delglobal(js_State *J, const char *name)
973 {
974 jsR_delproperty(J, J->G, name);
975 }
976
977 void js_getproperty(js_State *J, int idx, const char *name)
978 {
979 jsR_getproperty(J, js_toobject(J, idx), name);
980 }
981
982 void js_setproperty(js_State *J, int idx, const char *name)
983 {
984 jsR_setproperty(J, js_toobject(J, idx), name, !js_isobject(J, idx));
985 js_pop(J, 1);
986 }
987
988 void js_defproperty(js_State *J, int idx, const char *name, int atts)
989 {
990 jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL, 1);
991 js_pop(J, 1);
992 }
993
994 void js_delproperty(js_State *J, int idx, const char *name)
995 {
996 jsR_delproperty(J, js_toobject(J, idx), name);
997 }
998
999 void js_defaccessor(js_State *J, int idx, const char *name, int atts)
1000 {
1001 jsR_defproperty(J, js_toobject(J, idx), name, atts, NULL, jsR_tofunction(J, -2), jsR_tofunction(J, -1), 1);
1002 js_pop(J, 2);
1003 }
1004
1005 int js_hasproperty(js_State *J, int idx, const char *name)
1006 {
1007 return jsR_hasproperty(J, js_toobject(J, idx), name);
1008 }
1009
1010 void js_getindex(js_State *J, int idx, int i)
1011 {
1012 jsR_getindex(J, js_toobject(J, idx), i);
1013 }
1014
1015 int js_hasindex(js_State *J, int idx, int i)
1016 {
1017 return jsR_hasindex(J, js_toobject(J, idx), i);
1018 }
1019
1020 void js_setindex(js_State *J, int idx, int i)
1021 {
1022 jsR_setindex(J, js_toobject(J, idx), i, !js_isobject(J, idx));
1023 js_pop(J, 1);
1024 }
1025
1026 void js_delindex(js_State *J, int idx, int i)
1027 {
1028 jsR_delindex(J, js_toobject(J, idx), i);
1029 }
1030
1031 /* Iterator */
1032
1033 void js_pushiterator(js_State *J, int idx, int own)
1034 {
1035 js_pushobject(J, jsV_newiterator(J, js_toobject(J, idx), own));
1036 }
1037
1038 const char *js_nextiterator(js_State *J, int idx)
1039 {
1040 return jsV_nextiterator(J, js_toobject(J, idx));
1041 }
1042
1043 /* Environment records */
1044
1045 js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
1046 {
1047 js_Environment *E = js_malloc(J, sizeof *E);
1048 E->gcmark = 0;
1049 E->gcnext = J->gcenv;
1050 J->gcenv = E;
1051 ++J->gccounter;
1052
1053 E->outer = outer;
1054 E->variables = vars;
1055 return E;
1056 }
1057
1058 static void js_initvar(js_State *J, const char *name, int idx)
1059 {
1060 jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL, 0);
1061 }
1062
1063 static int js_hasvar(js_State *J, const char *name)
1064 {
1065 js_Environment *E = J->E;
1066 do {
1067 js_Property *ref = jsV_getproperty(J, E->variables, name);
1068 if (ref) {
1069 if (ref->getter) {
1070 js_pushobject(J, ref->getter);
1071 js_pushobject(J, E->variables);
1072 js_call(J, 0);
1073 } else {
1074 js_pushvalue(J, ref->value);
1075 }
1076 return 1;
1077 }
1078 E = E->outer;
1079 } while (E);
1080 return 0;
1081 }
1082
1083 static void js_setvar(js_State *J, const char *name)
1084 {
1085 js_Environment *E = J->E;
1086 do {
1087 js_Property *ref = jsV_getproperty(J, E->variables, name);
1088 if (ref) {
1089 if (ref->setter) {
1090 js_pushobject(J, ref->setter);
1091 js_pushobject(J, E->variables);
1092 js_copy(J, -3);
1093 js_call(J, 1);
1094 js_pop(J, 1);
1095 return;
1096 }
1097 if (!(ref->atts & JS_READONLY))
1098 ref->value = *stackidx(J, -1);
1099 else if (J->strict)
1100 js_typeerror(J, "'%s' is read-only", name);
1101 return;
1102 }
1103 E = E->outer;
1104 } while (E);
1105 if (J->strict)
1106 js_referenceerror(J, "assignment to undeclared variable '%s'", name);
1107 jsR_setproperty(J, J->G, name, 0);
1108 }
1109
1110 static int js_delvar(js_State *J, const char *name)
1111 {
1112 js_Environment *E = J->E;
1113 do {
1114 js_Property *ref = jsV_getownproperty(J, E->variables, name);
1115 if (ref) {
1116 if (ref->atts & JS_DONTCONF) {
1117 if (J->strict)
1118 js_typeerror(J, "'%s' is non-configurable", name);
1119 return 0;
1120 }
1121 jsV_delproperty(J, E->variables, name);
1122 return 1;
1123 }
1124 E = E->outer;
1125 } while (E);
1126 return jsR_delproperty(J, J->G, name);
1127 }
1128
1129 /* Function calls */
1130
1131 static void jsR_savescope(js_State *J, js_Environment *newE)
1132 {
1133 if (J->envtop + 1 >= JS_ENVLIMIT)
1134 js_stackoverflow(J);
1135 J->envstack[J->envtop++] = J->E;
1136 J->E = newE;
1137 }
1138
1139 static void jsR_restorescope(js_State *J)
1140 {
1141 J->E = J->envstack[--J->envtop];
1142 }
1143
1144 static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
1145 {
1146 js_Value v;
1147 int i;
1148
1149 jsR_savescope(J, scope);
1150
1151 if (n > F->numparams) {
1152 js_pop(J, n - F->numparams);
1153 n = F->numparams;
1154 }
1155
1156 for (i = n; i < F->varlen; ++i)
1157 js_pushundefined(J);
1158
1159 jsR_run(J, F);
1160 v = *stackidx(J, -1);
1161 TOP = --BOT; /* clear stack */
1162 js_pushvalue(J, v);
1163
1164 jsR_restorescope(J);
1165 }
1166
1167 static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
1168 {
1169 js_Value v;
1170 int i;
1171
1172 scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
1173
1174 jsR_savescope(J, scope);
1175
1176 if (F->arguments) {
1177 js_newarguments(J);
1178 if (!J->strict) {
1179 js_currentfunction(J);
1180 js_defproperty(J, -2, "callee", JS_DONTENUM);
1181 }
1182 js_pushnumber(J, n);
1183 js_defproperty(J, -2, "length", JS_DONTENUM);
1184 for (i = 0; i < n; ++i) {
1185 js_copy(J, i + 1);
1186 js_setindex(J, -2, i);
1187 }
1188 js_initvar(J, "arguments", -1);
1189 js_pop(J, 1);
1190 }
1191
1192 for (i = 0; i < n && i < F->numparams; ++i)
1193 js_initvar(J, F->vartab[i], i + 1);
1194 js_pop(J, n);
1195
1196 for (; i < F->varlen; ++i) {
1197 js_pushundefined(J);
1198 js_initvar(J, F->vartab[i], -1);
1199 js_pop(J, 1);
1200 }
1201
1202 jsR_run(J, F);
1203 v = *stackidx(J, -1);
1204 TOP = --BOT; /* clear stack */
1205 js_pushvalue(J, v);
1206
1207 jsR_restorescope(J);
1208 }
1209
1210 static void jsR_callscript(js_State *J, int n, js_Function *F, js_Environment *scope)
1211 {
1212 js_Value v;
1213 int i;
1214
1215 if (scope)
1216 jsR_savescope(J, scope);
1217
1218 /* scripts take no arguments */
1219 js_pop(J, n);
1220
1221 for (i = 0; i < F->varlen; ++i) {
1222 /* Bug 701886: don't redefine existing vars in eval/scripts */
1223 if (!js_hasvar(J, F->vartab[i])) {
1224 js_pushundefined(J);
1225 js_initvar(J, F->vartab[i], -1);
1226 js_pop(J, 1);
1227 }
1228 }
1229
1230 jsR_run(J, F);
1231 v = *stackidx(J, -1);
1232 TOP = --BOT; /* clear stack */
1233 js_pushvalue(J, v);
1234
1235 if (scope)
1236 jsR_restorescope(J);
1237 }
1238
1239 static void jsR_callcfunction(js_State *J, int n, int min, js_CFunction F)
1240 {
1241 int save_top;
1242 int i;
1243 js_Value v;
1244
1245 for (i = n; i < min; ++i)
1246 js_pushundefined(J);
1247
1248 save_top = TOP;
1249 F(J);
1250 if (TOP > save_top) {
1251 v = *stackidx(J, -1);
1252 TOP = --BOT; /* clear stack */
1253 js_pushvalue(J, v);
1254 } else {
1255 TOP = --BOT; /* clear stack */
1256 js_pushundefined(J);
1257 }
1258 }
1259
1260 static void jsR_pushtrace(js_State *J, const char *name, const char *file, int line)
1261 {
1262 if (J->tracetop + 1 == JS_ENVLIMIT)
1263 js_error(J, "call stack overflow");
1264 ++J->tracetop;
1265 J->trace[J->tracetop].name = name;
1266 J->trace[J->tracetop].file = file;
1267 J->trace[J->tracetop].line = line;
1268 }
1269
1270 void js_call(js_State *J, int n)
1271 {
1272 js_Object *obj;
1273 int savebot;
1274
1275 if (n < 0)
1276 js_rangeerror(J, "number of arguments cannot be negative");
1277
1278 if (!js_iscallable(J, -n-2))
1279 js_typeerror(J, "%s is not callable", js_typeof(J, -n-2));
1280
1281 obj = js_toobject(J, -n-2);
1282
1283 savebot = BOT;
1284 BOT = TOP - n - 1;
1285
1286 if (obj->type == JS_CFUNCTION) {
1287 jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line);
1288 if (obj->u.f.function->lightweight)
1289 jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope);
1290 else
1291 jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
1292 --J->tracetop;
1293 } else if (obj->type == JS_CSCRIPT) {
1294 jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line);
1295 jsR_callscript(J, n, obj->u.f.function, obj->u.f.scope);
1296 --J->tracetop;
1297 } else if (obj->type == JS_CCFUNCTION) {
1298 jsR_pushtrace(J, obj->u.c.name, "native", 0);
1299 jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function);
1300 --J->tracetop;
1301 }
1302
1303 BOT = savebot;
1304 }
1305
1306 void js_construct(js_State *J, int n)
1307 {
1308 js_Object *obj;
1309 js_Object *prototype;
1310 js_Object *newobj;
1311
1312 if (!js_iscallable(J, -n-1))
1313 js_typeerror(J, "%s is not callable", js_typeof(J, -n-1));
1314
1315 obj = js_toobject(J, -n-1);
1316
1317 /* built-in constructors create their own objects, give them a 'null' this */
1318 if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) {
1319 int savebot = BOT;
1320 js_pushnull(J);
1321 if (n > 0)
1322 js_rot(J, n + 1);
1323 BOT = TOP - n - 1;
1324
1325 jsR_pushtrace(J, obj->u.c.name, "native", 0);
1326 jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor);
1327 --J->tracetop;
1328
1329 BOT = savebot;
1330 return;
1331 }
1332
1333 /* extract the function object's prototype property */
1334 js_getproperty(J, -n - 1, "prototype");
1335 if (js_isobject(J, -1))
1336 prototype = js_toobject(J, -1);
1337 else
1338 prototype = J->Object_prototype;
1339 js_pop(J, 1);
1340
1341 /* create a new object with above prototype, and shift it into the 'this' slot */
1342 newobj = jsV_newobject(J, JS_COBJECT, prototype);
1343 js_pushobject(J, newobj);
1344 if (n > 0)
1345 js_rot(J, n + 1);
1346
1347 /* and save a copy to return */
1348 js_pushobject(J, newobj);
1349 js_rot(J, n + 3);
1350
1351 /* call the function */
1352 js_call(J, n);
1353
1354 /* if result is not an object, return the original object we created */
1355 if (!js_isobject(J, -1)) {
1356 js_pop(J, 1);
1357 } else {
1358 js_rot2pop1(J);
1359 }
1360 }
1361
1362 void js_eval(js_State *J)
1363 {
1364 if (!js_isstring(J, -1))
1365 return;
1366 js_loadeval(J, "(eval)", js_tostring(J, -1));
1367 js_rot2pop1(J);
1368 js_copy(J, 0); /* copy 'this' */
1369 js_call(J, 0);
1370 }
1371
1372 int js_pconstruct(js_State *J, int n)
1373 {
1374 int savetop = TOP - n - 2;
1375 if (js_try(J)) {
1376 /* clean up the stack to only hold the error object */
1377 STACK[savetop] = STACK[TOP-1];
1378 TOP = savetop + 1;
1379 return 1;
1380 }
1381 js_construct(J, n);
1382 js_endtry(J);
1383 return 0;
1384 }
1385
1386 int js_pcall(js_State *J, int n)
1387 {
1388 int savetop = TOP - n - 2;
1389 if (js_try(J)) {
1390 /* clean up the stack to only hold the error object */
1391 STACK[savetop] = STACK[TOP-1];
1392 TOP = savetop + 1;
1393 return 1;
1394 }
1395 js_call(J, n);
1396 js_endtry(J);
1397 return 0;
1398 }
1399
1400 /* Exceptions */
1401
1402 void *js_savetrypc(js_State *J, js_Instruction *pc)
1403 {
1404 if (J->trytop == JS_TRYLIMIT)
1405 js_trystackoverflow(J);
1406 J->trybuf[J->trytop].E = J->E;
1407 J->trybuf[J->trytop].envtop = J->envtop;
1408 J->trybuf[J->trytop].tracetop = J->tracetop;
1409 J->trybuf[J->trytop].top = J->top;
1410 J->trybuf[J->trytop].bot = J->bot;
1411 J->trybuf[J->trytop].strict = J->strict;
1412 J->trybuf[J->trytop].pc = pc;
1413 return J->trybuf[J->trytop++].buf;
1414 }
1415
1416 void *js_savetry(js_State *J)
1417 {
1418 if (J->trytop == JS_TRYLIMIT)
1419 js_trystackoverflow(J);
1420 J->trybuf[J->trytop].E = J->E;
1421 J->trybuf[J->trytop].envtop = J->envtop;
1422 J->trybuf[J->trytop].tracetop = J->tracetop;
1423 J->trybuf[J->trytop].top = J->top;
1424 J->trybuf[J->trytop].bot = J->bot;
1425 J->trybuf[J->trytop].strict = J->strict;
1426 J->trybuf[J->trytop].pc = NULL;
1427 return J->trybuf[J->trytop++].buf;
1428 }
1429
1430 void js_endtry(js_State *J)
1431 {
1432 if (J->trytop == 0)
1433 js_error(J, "endtry: exception stack underflow");
1434 --J->trytop;
1435 }
1436
1437 void js_throw(js_State *J)
1438 {
1439 if (J->trytop > 0) {
1440 js_Value v = *stackidx(J, -1);
1441 --J->trytop;
1442 J->E = J->trybuf[J->trytop].E;
1443 J->envtop = J->trybuf[J->trytop].envtop;
1444 J->tracetop = J->trybuf[J->trytop].tracetop;
1445 J->top = J->trybuf[J->trytop].top;
1446 J->bot = J->trybuf[J->trytop].bot;
1447 J->strict = J->trybuf[J->trytop].strict;
1448 js_pushvalue(J, v);
1449 longjmp(J->trybuf[J->trytop].buf, 1);
1450 }
1451 if (J->panic)
1452 J->panic(J);
1453 abort();
1454 }
1455
1456 /* Main interpreter loop */
1457
1458 static void js_dumpvalue(js_State *J, js_Value v)
1459 {
1460 switch (v.t.type) {
1461 case JS_TUNDEFINED: printf("undefined"); break;
1462 case JS_TNULL: printf("null"); break;
1463 case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
1464 case JS_TNUMBER: printf("%.9g", v.u.number); break;
1465 case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break;
1466 case JS_TLITSTR: printf("'%s'", v.u.litstr); break;
1467 case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break;
1468 case JS_TOBJECT:
1469 if (v.u.object == J->G) {
1470 printf("[Global]");
1471 break;
1472 }
1473 switch (v.u.object->type) {
1474 case JS_COBJECT: printf("[Object %p]", (void*)v.u.object); break;
1475 case JS_CARRAY: printf("[Array %p]", (void*)v.u.object); break;
1476 case JS_CFUNCTION:
1477 printf("[Function %p, %s, %s:%d]",
1478 (void*)v.u.object,
1479 v.u.object->u.f.function->name,
1480 v.u.object->u.f.function->filename,
1481 v.u.object->u.f.function->line);
1482 break;
1483 case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break;
1484 case JS_CCFUNCTION: printf("[CFunction %s]", v.u.object->u.c.name); break;
1485 case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break;
1486 case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break;
1487 case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break;
1488 case JS_CERROR: printf("[Error]"); break;
1489 case JS_CARGUMENTS: printf("[Arguments %p]", (void*)v.u.object); break;
1490 case JS_CITERATOR: printf("[Iterator %p]", (void*)v.u.object); break;
1491 case JS_CUSERDATA:
1492 printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data);
1493 break;
1494 default: printf("[Object %p]", (void*)v.u.object); break;
1495 }
1496 break;
1497 }
1498 }
1499
1500 static void js_stacktrace(js_State *J)
1501 {
1502 int n;
1503 printf("stack trace:\n");
1504 for (n = J->tracetop; n >= 0; --n) {
1505 const char *name = J->trace[n].name;
1506 const char *file = J->trace[n].file;
1507 int line = J->trace[n].line;
1508 if (line > 0) {
1509 if (name[0])
1510 printf("\tat %s (%s:%d)\n", name, file, line);
1511 else
1512 printf("\tat %s:%d\n", file, line);
1513 } else
1514 printf("\tat %s (%s)\n", name, file);
1515 }
1516 }
1517
1518 static void js_dumpstack(js_State *J)
1519 {
1520 int i;
1521 printf("stack {\n");
1522 for (i = 0; i < TOP; ++i) {
1523 putchar(i == BOT ? '>' : ' ');
1524 printf("%4d: ", i);
1525 js_dumpvalue(J, STACK[i]);
1526 putchar('\n');
1527 }
1528 printf("}\n");
1529 }
1530
1531 void js_trap(js_State *J, int pc)
1532 {
1533 js_dumpstack(J);
1534 js_stacktrace(J);
1535 }
1536
1537 static int jsR_isindex(js_State *J, int idx, int *k)
1538 {
1539 js_Value *v = stackidx(J, idx);
1540 if (v->t.type == JS_TNUMBER) {
1541 *k = v->u.number;
1542 return *k == v->u.number && *k >= 0;
1543 }
1544 return 0;
1545 }
1546
1547 static void jsR_run(js_State *J, js_Function *F)
1548 {
1549 js_Function **FT = F->funtab;
1550 const char **VT = F->vartab-1;
1551 int lightweight = F->lightweight;
1552 js_Instruction *pcstart = F->code;
1553 js_Instruction *pc = F->code;
1554 enum js_OpCode opcode;
1555 int offset;
1556 int savestrict;
1557
1558 const char *str;
1559 js_Object *obj;
1560 double x, y;
1561 unsigned int ux, uy;
1562 int ix, iy, okay;
1563 int b;
1564 int transient;
1565
1566 savestrict = J->strict;
1567 J->strict = F->strict;
1568
1569 #define READSTRING() \
1570 memcpy(&str, pc, sizeof(str)); \
1571 pc += sizeof(str) / sizeof(*pc)
1572
1573 while (1) {
1574 if (J->gccounter > J->gcthresh)
1575 js_gc(J, 0);
1576
1577 J->trace[J->tracetop].line = *pc++;
1578
1579 opcode = *pc++;
1580
1581 switch (opcode) {
1582 case OP_POP: js_pop(J, 1); break;
1583 case OP_DUP: js_dup(J); break;
1584 case OP_DUP2: js_dup2(J); break;
1585 case OP_ROT2: js_rot2(J); break;
1586 case OP_ROT3: js_rot3(J); break;
1587 case OP_ROT4: js_rot4(J); break;
1588
1589 case OP_INTEGER:
1590 js_pushnumber(J, *pc++ - 32768);
1591 break;
1592
1593 case OP_NUMBER:
1594 memcpy(&x, pc, sizeof(x));
1595 pc += sizeof(x) / sizeof(*pc);
1596 js_pushnumber(J, x);
1597 break;
1598
1599 case OP_STRING:
1600 READSTRING();
1601 js_pushliteral(J, str);
1602 break;
1603
1604 case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break;
1605 case OP_NEWOBJECT: js_newobject(J); break;
1606 case OP_NEWARRAY: js_newarray(J); break;
1607 case OP_NEWREGEXP:
1608 READSTRING();
1609 js_newregexp(J, str, *pc++);
1610 break;
1611
1612 case OP_UNDEF: js_pushundefined(J); break;
1613 case OP_NULL: js_pushnull(J); break;
1614 case OP_TRUE: js_pushboolean(J, 1); break;
1615 case OP_FALSE: js_pushboolean(J, 0); break;
1616
1617 case OP_THIS:
1618 if (J->strict) {
1619 js_copy(J, 0);
1620 } else {
1621 if (js_iscoercible(J, 0))
1622 js_copy(J, 0);
1623 else
1624 js_pushglobal(J);
1625 }
1626 break;
1627
1628 case OP_CURRENT:
1629 js_currentfunction(J);
1630 break;
1631
1632 case OP_GETLOCAL:
1633 if (lightweight) {
1634 CHECKSTACK(1);
1635 STACK[TOP++] = STACK[BOT + *pc++];
1636 } else {
1637 str = VT[*pc++];
1638 if (!js_hasvar(J, str))
1639 js_referenceerror(J, "'%s' is not defined", str);
1640 }
1641 break;
1642
1643 case OP_SETLOCAL:
1644 if (lightweight) {
1645 STACK[BOT + *pc++] = STACK[TOP-1];
1646 } else {
1647 js_setvar(J, VT[*pc++]);
1648 }
1649 break;
1650
1651 case OP_DELLOCAL:
1652 if (lightweight) {
1653 ++pc;
1654 js_pushboolean(J, 0);
1655 } else {
1656 b = js_delvar(J, VT[*pc++]);
1657 js_pushboolean(J, b);
1658 }
1659 break;
1660
1661 case OP_GETVAR:
1662 READSTRING();
1663 if (!js_hasvar(J, str))
1664 js_referenceerror(J, "'%s' is not defined", str);
1665 break;
1666
1667 case OP_HASVAR:
1668 READSTRING();
1669 if (!js_hasvar(J, str))
1670 js_pushundefined(J);
1671 break;
1672
1673 case OP_SETVAR:
1674 READSTRING();
1675 js_setvar(J, str);
1676 break;
1677
1678 case OP_DELVAR:
1679 READSTRING();
1680 b = js_delvar(J, str);
1681 js_pushboolean(J, b);
1682 break;
1683
1684 case OP_IN:
1685 str = js_tostring(J, -2);
1686 if (!js_isobject(J, -1))
1687 js_typeerror(J, "operand to 'in' is not an object");
1688 b = js_hasproperty(J, -1, str);
1689 js_pop(J, 2 + b);
1690 js_pushboolean(J, b);
1691 break;
1692
1693 case OP_SKIPARRAY:
1694 js_setlength(J, -1, js_getlength(J, -1) + 1);
1695 break;
1696 case OP_INITARRAY:
1697 js_setindex(J, -2, js_getlength(J, -2));
1698 break;
1699
1700 case OP_INITPROP:
1701 obj = js_toobject(J, -3);
1702 str = js_tostring(J, -2);
1703 jsR_setproperty(J, obj, str, 0);
1704 js_pop(J, 2);
1705 break;
1706
1707 case OP_INITGETTER:
1708 obj = js_toobject(J, -3);
1709 str = js_tostring(J, -2);
1710 jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL, 0);
1711 js_pop(J, 2);
1712 break;
1713
1714 case OP_INITSETTER:
1715 obj = js_toobject(J, -3);
1716 str = js_tostring(J, -2);
1717 jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1), 0);
1718 js_pop(J, 2);
1719 break;
1720
1721 case OP_GETPROP:
1722 if (jsR_isindex(J, -1, &ix)) {
1723 obj = js_toobject(J, -2);
1724 jsR_getindex(J, obj, ix);
1725 } else {
1726 str = js_tostring(J, -1);
1727 obj = js_toobject(J, -2);
1728 jsR_getproperty(J, obj, str);
1729 }
1730 js_rot3pop2(J);
1731 break;
1732
1733 case OP_GETPROP_S:
1734 READSTRING();
1735 obj = js_toobject(J, -1);
1736 jsR_getproperty(J, obj, str);
1737 js_rot2pop1(J);
1738 break;
1739
1740 case OP_SETPROP:
1741 if (jsR_isindex(J, -2, &ix)) {
1742 obj = js_toobject(J, -3);
1743 transient = !js_isobject(J, -3);
1744 jsR_setindex(J, obj, ix, transient);
1745 } else {
1746 str = js_tostring(J, -2);
1747 obj = js_toobject(J, -3);
1748 transient = !js_isobject(J, -3);
1749 jsR_setproperty(J, obj, str, transient);
1750 }
1751 js_rot3pop2(J);
1752 break;
1753
1754 case OP_SETPROP_S:
1755 READSTRING();
1756 obj = js_toobject(J, -2);
1757 transient = !js_isobject(J, -2);
1758 jsR_setproperty(J, obj, str, transient);
1759 js_rot2pop1(J);
1760 break;
1761
1762 case OP_DELPROP:
1763 str = js_tostring(J, -1);
1764 obj = js_toobject(J, -2);
1765 b = jsR_delproperty(J, obj, str);
1766 js_pop(J, 2);
1767 js_pushboolean(J, b);
1768 break;
1769
1770 case OP_DELPROP_S:
1771 READSTRING();
1772 obj = js_toobject(J, -1);
1773 b = jsR_delproperty(J, obj, str);
1774 js_pop(J, 1);
1775 js_pushboolean(J, b);
1776 break;
1777
1778 case OP_ITERATOR:
1779 if (js_iscoercible(J, -1)) {
1780 obj = jsV_newiterator(J, js_toobject(J, -1), 0);
1781 js_pop(J, 1);
1782 js_pushobject(J, obj);
1783 }
1784 break;
1785
1786 case OP_NEXTITER:
1787 if (js_isobject(J, -1)) {
1788 obj = js_toobject(J, -1);
1789 str = jsV_nextiterator(J, obj);
1790 if (str) {
1791 js_pushstring(J, str);
1792 js_pushboolean(J, 1);
1793 } else {
1794 js_pop(J, 1);
1795 js_pushboolean(J, 0);
1796 }
1797 } else {
1798 js_pop(J, 1);
1799 js_pushboolean(J, 0);
1800 }
1801 break;
1802
1803 /* Function calls */
1804
1805 case OP_EVAL:
1806 js_eval(J);
1807 break;
1808
1809 case OP_CALL:
1810 js_call(J, *pc++);
1811 break;
1812
1813 case OP_NEW:
1814 js_construct(J, *pc++);
1815 break;
1816
1817 /* Unary operators */
1818
1819 case OP_TYPEOF:
1820 str = js_typeof(J, -1);
1821 js_pop(J, 1);
1822 js_pushliteral(J, str);
1823 break;
1824
1825 case OP_POS:
1826 x = js_tonumber(J, -1);
1827 js_pop(J, 1);
1828 js_pushnumber(J, x);
1829 break;
1830
1831 case OP_NEG:
1832 x = js_tonumber(J, -1);
1833 js_pop(J, 1);
1834 js_pushnumber(J, -x);
1835 break;
1836
1837 case OP_BITNOT:
1838 ix = js_toint32(J, -1);
1839 js_pop(J, 1);
1840 js_pushnumber(J, ~ix);
1841 break;
1842
1843 case OP_LOGNOT:
1844 b = js_toboolean(J, -1);
1845 js_pop(J, 1);
1846 js_pushboolean(J, !b);
1847 break;
1848
1849 case OP_INC:
1850 x = js_tonumber(J, -1);
1851 js_pop(J, 1);
1852 js_pushnumber(J, x + 1);
1853 break;
1854
1855 case OP_DEC:
1856 x = js_tonumber(J, -1);
1857 js_pop(J, 1);
1858 js_pushnumber(J, x - 1);
1859 break;
1860
1861 case OP_POSTINC:
1862 x = js_tonumber(J, -1);
1863 js_pop(J, 1);
1864 js_pushnumber(J, x + 1);
1865 js_pushnumber(J, x);
1866 break;
1867
1868 case OP_POSTDEC:
1869 x = js_tonumber(J, -1);
1870 js_pop(J, 1);
1871 js_pushnumber(J, x - 1);
1872 js_pushnumber(J, x);
1873 break;
1874
1875 /* Multiplicative operators */
1876
1877 case OP_MUL:
1878 x = js_tonumber(J, -2);
1879 y = js_tonumber(J, -1);
1880 js_pop(J, 2);
1881 js_pushnumber(J, x * y);
1882 break;
1883
1884 case OP_DIV:
1885 x = js_tonumber(J, -2);
1886 y = js_tonumber(J, -1);
1887 js_pop(J, 2);
1888 js_pushnumber(J, x / y);
1889 break;
1890
1891 case OP_MOD:
1892 x = js_tonumber(J, -2);
1893 y = js_tonumber(J, -1);
1894 js_pop(J, 2);
1895 js_pushnumber(J, fmod(x, y));
1896 break;
1897
1898 /* Additive operators */
1899
1900 case OP_ADD:
1901 js_concat(J);
1902 break;
1903
1904 case OP_SUB:
1905 x = js_tonumber(J, -2);
1906 y = js_tonumber(J, -1);
1907 js_pop(J, 2);
1908 js_pushnumber(J, x - y);
1909 break;
1910
1911 /* Shift operators */
1912
1913 case OP_SHL:
1914 ix = js_toint32(J, -2);
1915 uy = js_touint32(J, -1);
1916 js_pop(J, 2);
1917 js_pushnumber(J, ix << (uy & 0x1F));
1918 break;
1919
1920 case OP_SHR:
1921 ix = js_toint32(J, -2);
1922 uy = js_touint32(J, -1);
1923 js_pop(J, 2);
1924 js_pushnumber(J, ix >> (uy & 0x1F));
1925 break;
1926
1927 case OP_USHR:
1928 ux = js_touint32(J, -2);
1929 uy = js_touint32(J, -1);
1930 js_pop(J, 2);
1931 js_pushnumber(J, ux >> (uy & 0x1F));
1932 break;
1933
1934 /* Relational operators */
1935
1936 case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break;
1937 case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break;
1938 case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break;
1939 case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break;
1940
1941 case OP_INSTANCEOF:
1942 b = js_instanceof(J);
1943 js_pop(J, 2);
1944 js_pushboolean(J, b);
1945 break;
1946
1947 /* Equality */
1948
1949 case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break;
1950 case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
1951 case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break;
1952 case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break;
1953
1954 case OP_JCASE:
1955 offset = *pc++;
1956 b = js_strictequal(J);
1957 if (b) {
1958 js_pop(J, 2);
1959 pc = pcstart + offset;
1960 } else {
1961 js_pop(J, 1);
1962 }
1963 break;
1964
1965 /* Binary bitwise operators */
1966
1967 case OP_BITAND:
1968 ix = js_toint32(J, -2);
1969 iy = js_toint32(J, -1);
1970 js_pop(J, 2);
1971 js_pushnumber(J, ix & iy);
1972 break;
1973
1974 case OP_BITXOR:
1975 ix = js_toint32(J, -2);
1976 iy = js_toint32(J, -1);
1977 js_pop(J, 2);
1978 js_pushnumber(J, ix ^ iy);
1979 break;
1980
1981 case OP_BITOR:
1982 ix = js_toint32(J, -2);
1983 iy = js_toint32(J, -1);
1984 js_pop(J, 2);
1985 js_pushnumber(J, ix | iy);
1986 break;
1987
1988 /* Try and Catch */
1989
1990 case OP_THROW:
1991 js_throw(J);
1992
1993 case OP_TRY:
1994 offset = *pc++;
1995 if (js_trypc(J, pc)) {
1996 pc = J->trybuf[J->trytop].pc;
1997 } else {
1998 pc = pcstart + offset;
1999 }
2000 break;
2001
2002 case OP_ENDTRY:
2003 js_endtry(J);
2004 break;
2005
2006 case OP_CATCH:
2007 READSTRING();
2008 obj = jsV_newobject(J, JS_COBJECT, NULL);
2009 js_pushobject(J, obj);
2010 js_rot2(J);
2011 js_setproperty(J, -2, str);
2012 J->E = jsR_newenvironment(J, obj, J->E);
2013 js_pop(J, 1);
2014 break;
2015
2016 case OP_ENDCATCH:
2017 J->E = J->E->outer;
2018 break;
2019
2020 /* With */
2021
2022 case OP_WITH:
2023 obj = js_toobject(J, -1);
2024 J->E = jsR_newenvironment(J, obj, J->E);
2025 js_pop(J, 1);
2026 break;
2027
2028 case OP_ENDWITH:
2029 J->E = J->E->outer;
2030 break;
2031
2032 /* Branching */
2033
2034 case OP_DEBUGGER:
2035 js_trap(J, (int)(pc - pcstart) - 1);
2036 break;
2037
2038 case OP_JUMP:
2039 pc = pcstart + *pc;
2040 break;
2041
2042 case OP_JTRUE:
2043 offset = *pc++;
2044 b = js_toboolean(J, -1);
2045 js_pop(J, 1);
2046 if (b)
2047 pc = pcstart + offset;
2048 break;
2049
2050 case OP_JFALSE:
2051 offset = *pc++;
2052 b = js_toboolean(J, -1);
2053 js_pop(J, 1);
2054 if (!b)
2055 pc = pcstart + offset;
2056 break;
2057
2058 case OP_RETURN:
2059 J->strict = savestrict;
2060 return;
2061 }
2062 }
2063 }