js_State *js_newstate(js_Alloc alloc, void *actx) { js_State *J; if (!alloc) alloc = js_defaultalloc; J = alloc(actx, NULL, sizeof *J); if (!J) return NULL; memset(J, 0, sizeof(*J)); J->actx = actx; J->alloc = alloc; J->panic = js_defaultpanic; J->stack = alloc(actx, NULL, JS_STACKSIZE * sizeof *J->stack); if (!J->stack) { alloc(actx, NULL, 0); return NULL; } J->gcmark = 1; J->nextref = 0; J->R = jsV_newobject(J, JS_COBJECT, NULL); J->G = jsV_newobject(J, JS_COBJECT, NULL); J->E = jsR_newenvironment(J, J->G, NULL); J->GE = J->E; jsB_init(J); return J; }
js_State *js_newstate(js_Alloc alloc, void *actx, int flags) { js_State *J; assert(sizeof(js_Value) == 16); assert(offsetof(js_Value, type) == 15); if (!alloc) alloc = js_defaultalloc; J = alloc(actx, NULL, sizeof *J); if (!J) return NULL; memset(J, 0, sizeof(*J)); J->actx = actx; J->alloc = alloc; if (flags & JS_STRICT) J->strict = 1; J->trace[0].name = "?"; J->trace[0].file = "[C]"; J->trace[0].line = 0; J->panic = js_defaultpanic; J->stack = alloc(actx, NULL, JS_STACKSIZE * sizeof *J->stack); if (!J->stack) { alloc(actx, NULL, 0); return NULL; } J->gcmark = 1; J->nextref = 0; J->R = jsV_newobject(J, JS_COBJECT, NULL); J->G = jsV_newobject(J, JS_COBJECT, NULL); J->E = jsR_newenvironment(J, J->G, NULL); J->GE = J->E; jsB_init(J); return J; }
static void jsR_callfunction(js_State *J, unsigned int n, js_Function *F, js_Environment *scope) { js_Value v; unsigned int i; scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope); jsR_savescope(J, scope); if (F->arguments) { js_newobject(J); if (!J->strict) { js_currentfunction(J); js_defproperty(J, -2, "callee", JS_DONTENUM); } js_pushnumber(J, n); js_defproperty(J, -2, "length", JS_DONTENUM); for (i = 0; i < n; ++i) { js_copy(J, i + 1); js_setindex(J, -2, i); } js_initvar(J, "arguments", -1); js_pop(J, 1); } for (i = 0; i < F->numparams; ++i) { if (i < n) js_initvar(J, F->vartab[i], i + 1); else { js_pushundefined(J); js_initvar(J, F->vartab[i], -1); js_pop(J, 1); } } js_pop(J, n); jsR_run(J, F); v = *stackidx(J, -1); TOP = --BOT; /* clear stack */ js_pushvalue(J, v); jsR_restorescope(J); }
static void jsR_run(js_State *J, js_Function *F) { js_Function **FT = F->funtab; double *NT = F->numtab; const char **ST = F->strtab; js_Instruction *pcstart = F->code; js_Instruction *pc = F->code; enum js_OpCode opcode; int offset; const char *str; js_Object *obj; double x, y; unsigned int ux, uy; int ix, iy, okay; int b; while (1) { if (J->gccounter > JS_GCLIMIT) { J->gccounter = 0; js_gc(J, 0); } opcode = *pc++; switch (opcode) { case OP_POP: js_pop(J, 1); break; case OP_DUP: js_dup(J); break; case OP_DUP2: js_dup2(J); break; case OP_ROT2: js_rot2(J); break; case OP_ROT3: js_rot3(J); break; case OP_ROT4: js_rot4(J); break; case OP_NUMBER_0: js_pushnumber(J, 0); break; case OP_NUMBER_1: js_pushnumber(J, 1); break; case OP_NUMBER_POS: js_pushnumber(J, *pc++); break; case OP_NUMBER_NEG: js_pushnumber(J, -(*pc++)); break; case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break; case OP_STRING: js_pushliteral(J, ST[*pc++]); break; case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break; case OP_NEWOBJECT: js_newobject(J); break; case OP_NEWARRAY: js_newarray(J); break; case OP_NEWREGEXP: js_newregexp(J, ST[pc[0]], pc[1]); pc += 2; break; case OP_UNDEF: js_pushundefined(J); break; case OP_NULL: js_pushnull(J); break; case OP_TRUE: js_pushboolean(J, 1); break; case OP_FALSE: js_pushboolean(J, 0); break; case OP_THIS: js_copy(J, 0); break; case OP_GLOBAL: js_pushobject(J, J->G); break; case OP_CURRENT: js_currentfunction(J); break; case OP_INITLOCAL: STACK[BOT + *pc++] = STACK[--TOP]; break; case OP_GETLOCAL: CHECKSTACK(1); STACK[TOP++] = STACK[BOT + *pc++]; break; case OP_SETLOCAL: STACK[BOT + *pc++] = STACK[TOP-1]; break; case OP_DELLOCAL: ++pc; js_pushboolean(J, 0); break; case OP_INITVAR: js_initvar(J, ST[*pc++], -1); js_pop(J, 1); break; case OP_DEFVAR: js_defvar(J, ST[*pc++]); break; case OP_GETVAR: str = ST[*pc++]; if (!js_hasvar(J, str)) js_referenceerror(J, "'%s' is not defined", str); break; case OP_HASVAR: if (!js_hasvar(J, ST[*pc++])) js_pushundefined(J); break; case OP_SETVAR: js_setvar(J, ST[*pc++]); break; case OP_DELVAR: b = js_delvar(J, ST[*pc++]); js_pushboolean(J, b); break; case OP_IN: str = js_tostring(J, -2); if (!js_isobject(J, -1)) js_typeerror(J, "operand to 'in' is not an object"); b = js_hasproperty(J, -1, str); js_pop(J, 2 + b); js_pushboolean(J, b); break; case OP_INITPROP: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_pop(J, 2); break; case OP_INITGETTER: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL); js_pop(J, 2); break; case OP_INITSETTER: obj = js_toobject(J, -3); str = js_tostring(J, -2); jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1)); js_pop(J, 2); break; case OP_GETPROP: str = js_tostring(J, -1); obj = js_toobject(J, -2); jsR_getproperty(J, obj, str); js_rot3pop2(J); break; case OP_GETPROP_S: str = ST[*pc++]; obj = js_toobject(J, -1); jsR_getproperty(J, obj, str); js_rot2pop1(J); break; case OP_SETPROP: str = js_tostring(J, -2); obj = js_toobject(J, -3); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot3pop2(J); break; case OP_SETPROP_S: str = ST[*pc++]; obj = js_toobject(J, -2); jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot2pop1(J); break; case OP_DELPROP: str = js_tostring(J, -1); obj = js_toobject(J, -2); b = jsR_delproperty(J, obj, str); js_pop(J, 2); js_pushboolean(J, b); break; case OP_DELPROP_S: str = ST[*pc++]; obj = js_toobject(J, -1); b = jsR_delproperty(J, obj, str); js_pop(J, 1); js_pushboolean(J, b); break; case OP_ITERATOR: if (!js_isundefined(J, -1) && !js_isnull(J, -1)) { obj = jsV_newiterator(J, js_toobject(J, -1), 0); js_pop(J, 1); js_pushobject(J, obj); } break; case OP_NEXTITER: obj = js_toobject(J, -1); str = jsV_nextiterator(J, obj); if (str) { js_pushliteral(J, str); js_pushboolean(J, 1); } else { js_pop(J, 1); js_pushboolean(J, 0); } break; /* Function calls */ case OP_EVAL: js_eval(J); break; case OP_CALL: js_call(J, *pc++); break; case OP_NEW: js_construct(J, *pc++); break; /* Unary operators */ case OP_TYPEOF: str = js_typeof(J, -1); js_pop(J, 1); js_pushliteral(J, str); break; case OP_POS: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x); break; case OP_NEG: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, -x); break; case OP_BITNOT: ix = js_toint32(J, -1); js_pop(J, 1); js_pushnumber(J, ~ix); break; case OP_LOGNOT: b = js_toboolean(J, -1); js_pop(J, 1); js_pushboolean(J, !b); break; case OP_INC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x + 1); break; case OP_DEC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x - 1); break; case OP_POSTINC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x + 1); js_pushnumber(J, x); break; case OP_POSTDEC: x = js_tonumber(J, -1); js_pop(J, 1); js_pushnumber(J, x - 1); js_pushnumber(J, x); break; /* Multiplicative operators */ case OP_MUL: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x * y); break; case OP_DIV: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x / y); break; case OP_MOD: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, fmod(x, y)); break; /* Additive operators */ case OP_ADD: js_concat(J); break; case OP_SUB: x = js_tonumber(J, -2); y = js_tonumber(J, -1); js_pop(J, 2); js_pushnumber(J, x - y); break; /* Shift operators */ case OP_SHL: ix = js_toint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix << (uy & 0x1F)); break; case OP_SHR: ix = js_toint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix >> (uy & 0x1F)); break; case OP_USHR: ux = js_touint32(J, -2); uy = js_touint32(J, -1); js_pop(J, 2); js_pushnumber(J, ux >> (uy & 0x1F)); break; /* Relational operators */ case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break; case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break; case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break; case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break; case OP_INSTANCEOF: b = js_instanceof(J); js_pop(J, 2); js_pushboolean(J, b); break; /* Equality */ case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break; case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break; case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break; case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break; case OP_JCASE: offset = *pc++; b = js_strictequal(J); if (b) { js_pop(J, 2); pc = pcstart + offset; } else { js_pop(J, 1); } break; /* Binary bitwise operators */ case OP_BITAND: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix & iy); break; case OP_BITXOR: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix ^ iy); break; case OP_BITOR: ix = js_toint32(J, -2); iy = js_toint32(J, -1); js_pop(J, 2); js_pushnumber(J, ix | iy); break; /* Try and Catch */ case OP_THROW: js_throw(J); case OP_TRY: offset = *pc++; if (js_trypc(J, pc)) { pc = J->trybuf[J->trytop].pc; } else { pc = pcstart + offset; } break; case OP_ENDTRY: js_endtry(J); break; case OP_CATCH: str = ST[*pc++]; obj = jsV_newobject(J, JS_COBJECT, NULL); js_pushobject(J, obj); js_rot2(J); js_setproperty(J, -2, str); J->E = jsR_newenvironment(J, obj, J->E); js_pop(J, 1); break; case OP_ENDCATCH: J->E = J->E->outer; break; /* With */ case OP_WITH: obj = js_toobject(J, -1); J->E = jsR_newenvironment(J, obj, J->E); js_pop(J, 1); break; case OP_ENDWITH: J->E = J->E->outer; break; /* Branching */ case OP_DEBUGGER: js_trap(J, (int)(pc - pcstart) - 1); break; case OP_JUMP: pc = pcstart + *pc; break; case OP_JTRUE: offset = *pc++; b = js_toboolean(J, -1); js_pop(J, 1); if (b) pc = pcstart + offset; break; case OP_JFALSE: offset = *pc++; b = js_toboolean(J, -1); js_pop(J, 1); if (!b) pc = pcstart + offset; break; case OP_RETURN: return; case OP_LINE: J->trace[J->tracetop].line = *pc++; break; } } }