ref: 47e88ac80569849e458b22b67341e19acd9af5a7
dir: /jsrun.c/
#include "js.h"
#include "jsobject.h"
#include "jscompile.h"
#include "jsrun.h"
#include "jsstate.h"
static js_Value stack[256];
static int top = 0;
static int bot = 0;
static inline double tointeger(double n)
{
double sign = n < 0 ? -1 : 1;
if (isnan(n)) return 0;
if (n == 0 || isinf(n)) return n;
return sign * floor(abs(n));
}
static inline int toint32(double n)
{
double two32 = 4294967296.0;
double two31 = 2147483648.0;
if (!isfinite(n) || n == 0)
return 0;
n = fmod(n, two32);
n = n >= 0 ? floor(n) : ceil(n) + two32;
if (n >= two31)
return n - two32;
else
return n;
}
static inline unsigned int touint32(double n)
{
return toint32(n);
}
static void js_pushvalue(js_State *J, js_Value v)
{
stack[top] = v;
++top;
}
void js_pushundefined(js_State *J)
{
stack[top].type = JS_TUNDEFINED;
++top;
}
void js_pushnull(js_State *J)
{
stack[top].type = JS_TNULL;
++top;
}
void js_pushboolean(js_State *J, int v)
{
stack[top].type = JS_TBOOLEAN;
stack[top].u.boolean = !!v;
++top;
}
void js_pushnumber(js_State *J, double v)
{
stack[top].type = JS_TNUMBER;
stack[top].u.number = v;
++top;
}
void js_pushstring(js_State *J, const char *v)
{
stack[top].type = JS_TSTRING;
stack[top].u.string = v;
++top;
}
void js_pushobject(js_State *J, js_Object *v)
{
stack[top].type = JS_TOBJECT;
stack[top].u.object = v;
++top;
}
static int stackidx(js_State *J, int idx)
{
if (idx < 0)
return top + idx;
return bot + idx;
}
int js_isundefined(js_State *J, int idx)
{
idx = stackidx(J, idx);
return stack[idx].type == JS_TUNDEFINED;
}
int js_isstring(js_State *J, int idx)
{
idx = stackidx(J, idx);
return stack[idx].type == JS_TSTRING;
}
int js_iscfunction(js_State *J, int idx)
{
idx = stackidx(J, idx);
return stack[idx].type == JS_TCFUNCTION;
}
js_Value js_tovalue(js_State *J, int idx)
{
idx = stackidx(J, idx);
return stack[idx];
}
int js_toboolean(js_State *J, int idx)
{
const char *s;
double n;
idx = stackidx(J, idx);
switch (stack[idx].type) {
case JS_TUNDEFINED: return 0;
case JS_TNULL: return 0;
case JS_TBOOLEAN: return stack[idx].u.boolean;
case JS_TNUMBER: n = stack[idx].u.number; return n != 0 || !isnan(n);
case JS_TSTRING: s = stack[idx].u.string; return s[0] != 0;
default: return 1;
}
}
double js_tonumber(js_State *J, int idx)
{
idx = stackidx(J, idx);
switch (stack[idx].type) {
case JS_TUNDEFINED: return NAN;
case JS_TNULL: return 0;
case JS_TBOOLEAN: return stack[idx].u.boolean;
case JS_TNUMBER: return stack[idx].u.number;
case JS_TSTRING: return strtod(stack[idx].u.string, NULL);
default: return 0;
}
}
double js_tointeger(js_State *J, int idx)
{
return toint32(js_tonumber(J, idx));
}
const char *js_tostring(js_State *J, int idx)
{
char buf[20];
idx = stackidx(J, idx);
switch (stack[idx].type) {
case JS_TUNDEFINED: return "undefined";
case JS_TNULL: return "null";
case JS_TBOOLEAN: return stack[idx].u.boolean ? "true" : "false";
case JS_TNUMBER: sprintf(buf, "%.9g", stack[idx].u.number); return js_intern(J, buf);
case JS_TSTRING: return stack[idx].u.string;
default: return "undefined";
}
}
js_Object *js_toobject(js_State *J, int idx)
{
idx = stackidx(J, idx);
switch (stack[idx].type) {
case JS_TUNDEFINED: jsR_error(J, "TypeError");
case JS_TNULL: jsR_error(J, "TypeError");
case JS_TBOOLEAN: jsR_error(J, "new Boolean()");
case JS_TNUMBER: jsR_error(J, "new Number()");
case JS_TSTRING: jsR_error(J, "new String()");
case JS_TOBJECT: return stack[idx].u.object;
default: jsR_error(J, "TypeError");
}
return NULL;
}
void js_pop(js_State *J, int n)
{
top -= n;
}
void js_dup(js_State *J)
{
stack[top] = stack[top-1];
++top;
}
void js_dup2(js_State *J)
{
stack[top] = stack[top-2];
stack[top+1] = stack[top-1];
top += 2;
}
void js_rot2(js_State *J)
{
/* A B -> B A */
js_Value tmp = stack[top-1]; /* A B (B) */
stack[top-1] = stack[top-2]; /* A A */
stack[top-2] = tmp; /* B A */
}
void js_rot3(js_State *J)
{
/* A B C -> C A B */
js_Value tmp = stack[top-1]; /* A B C (C) */
stack[top-1] = stack[top-2]; /* A B B */
stack[top-2] = stack[top-3]; /* A A B */
stack[top-3] = tmp; /* C A B */
}
void js_rot3pop2(js_State *J)
{
/* A B C -> C */
stack[top-3] = stack[top-1];
top -= 2;
}
void js_dup1rot4(js_State *J)
{
/* A B C -> C A B C */
stack[top] = stack[top-1]; /* A B C C */
stack[top-1] = stack[top-2]; /* A B B C */
stack[top-2] = stack[top-3]; /* A A B C */
stack[top-3] = stack[top]; /* C A B C */
++top;
}
void js_trap(js_State *J)
{
}
static void runfun(js_State *J, js_Function *F, js_Object *E)
{
js_Function **FT = F->funtab;
double *NT = F->numtab;
const char **ST = F->strtab;
short *pcstart = F->code;
short *pc = F->code;
int opcode, offset;
const char *str;
js_Object *obj;
js_Property *ref;
double x, y;
int b;
while (1) {
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_DUP1ROT4: js_dup1rot4(J); break;
case OP_NUMBER_0: js_pushnumber(J, 0); break;
case OP_NUMBER_1: js_pushnumber(J, 1); break;
case OP_NUMBER_X: js_pushnumber(J, *pc++); break;
case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break;
case OP_STRING: js_pushstring(J, ST[*pc++]); break;
// case OP_CLOSURE: 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_pushnull(J); break;
case OP_NEWOBJECT: js_pushobject(J, js_newobject(J)); break;
case OP_NEWARRAY: js_pushobject(J, js_newobject(J)); break;
case OP_FUNDEC:
ref = js_setproperty(J, E, ST[*pc++]);
// if (ref)
// ref->value = js_toclosure(J, -1);
js_pop(J, 1);
break;
case OP_VARDEC: pc++; break;
ref = js_setproperty(J, E, ST[*pc++]);
break;
case OP_GETVAR:
ref = js_getproperty(J, E, ST[*pc++]);
if (ref)
js_pushvalue(J, ref->value);
else
js_pushundefined(J);
break;
case OP_SETVAR:
ref = js_setproperty(J, E, ST[*pc++]);
if (ref)
ref->value = js_tovalue(J, -1);
break;
// OP_DELVAR
case OP_IN:
str = js_tostring(J, -2);
obj = js_toobject(J, -1);
ref = js_getproperty(J, obj, str);
js_pop(J, 2);
js_pushboolean(J, ref != NULL);
break;
case OP_GETPROP:
obj = js_toobject(J, -2);
str = js_tostring(J, -1);
ref = js_getproperty(J, obj, str);
js_pop(J, 2);
if (ref)
js_pushvalue(J, ref->value);
else
js_pushundefined(J);
break;
case OP_SETPROP:
obj = js_toobject(J, -3);
str = js_tostring(J, -2);
ref = js_setproperty(J, obj, str);
if (ref)
ref->value = js_tovalue(J, -1);
js_rot3pop2(J);
break;
// OP_DELPROP
case OP_NEXTPROP:
obj = js_toobject(J, -2);
if (js_isundefined(J, -1))
ref = js_firstproperty(J, obj);
else
ref = js_nextproperty(J, obj, js_tostring(J, -1));
if (ref) {
js_pop(J, 1);
js_pushstring(J, ref->name);
js_pushboolean(J, 1);
} else {
js_pop(J, 2);
js_pushboolean(J, 0);
}
break;
/* Unary expressions */
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:
x = js_tonumber(J, -1);
js_pop(J, 1);
js_pushnumber(J, ~toint32(x));
break;
case OP_LOGNOT:
b = js_toboolean(J, -1);
js_pop(J, 1);
js_pushnumber(J, !b);
break;
/* Binary expressions */
case OP_ADD:
// TODO: check string concatenation
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, x + y);
break;
case OP_SUB:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, x - y);
break;
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;
case OP_SHL:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, toint32(x) << (touint32(y) & 0x1F));
break;
case OP_SHR:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, toint32(x) >> (touint32(y) & 0x1F)); break;
break;
case OP_USHR:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, touint32(x) >> (touint32(y) & 0x1F)); break;
break;
case OP_BITAND:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, toint32(x) & toint32(y));
break;
case OP_BITXOR:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, toint32(x) ^ toint32(y));
break;
case OP_BITOR:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushnumber(J, toint32(x) | toint32(y));
break;
/* Relational expressions */
/* TODO: string comparisons */
case OP_LT:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushboolean(J, x < y);
break;
case OP_GT:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushboolean(J, x > y);
break;
case OP_LE:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushboolean(J, x <= y);
break;
case OP_GE:
x = js_tonumber(J, -2);
y = js_tonumber(J, -1);
js_pop(J, 2);
js_pushboolean(J, x >= y);
break;
// case OP_EQ:
// case OP_NE:
// case OP_STRICTEQ:
// case OP_STRICTNE:
/* Branching */
case OP_DEBUGGER:
js_trap(J);
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;
default:
fprintf(stderr, "illegal instruction: %d (pc=%d)\n", opcode, (int)(pc - F->code - 1));
return;
}
}
}
void jsR_error(js_State *J, const char *message)
{
fprintf(stderr, "runtime error: %s\n", message);
longjmp(J->jb, 1);
}
void jsR_runfunction(js_State *J, js_Function *F)
{
js_Object *E = js_newobject(J);
if (setjmp(J->jb)) {
js_dumpobject(J, E);
return;
}
runfun(J, F, E);
js_dumpobject(J, E);
}