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