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