diff --git a/jscompile.c b/jscompile.c index f900e52..ddee021 100644 --- a/jscompile.c +++ b/jscompile.c @@ -45,10 +45,10 @@ static void freefun(js_State *J, js_Function *F) { // int i; // for (i = 0; i < F->funlen; i++) -// freefun(J, F->funlist[i]); - free(F->funlist); - free(F->numlist); - free(F->strlist); +// freefun(J, F->funtab[i]); + free(F->funtab); + free(F->numtab); + free(F->strtab); free(F->code); free(F); } @@ -68,9 +68,9 @@ static int addfunction(JF, js_Function *value) { if (F->funlen >= F->funcap) { F->funcap = F->funcap ? F->funcap * 2 : 16; - F->funlist = realloc(F->funlist, F->funcap * sizeof *F->funlist); + F->funtab = realloc(F->funtab, F->funcap * sizeof *F->funtab); } - F->funlist[F->funlen] = value; + F->funtab[F->funlen] = value; return F->funlen++; } @@ -78,13 +78,13 @@ static int addnumber(JF, double value) { int i; for (i = 0; i < F->numlen; i++) - if (F->numlist[i] == value) + if (F->numtab[i] == value) return i; if (F->numlen >= F->numcap) { F->numcap = F->numcap ? F->numcap * 2 : 16; - F->numlist = realloc(F->numlist, F->numcap * sizeof *F->numlist); + F->numtab = realloc(F->numtab, F->numcap * sizeof *F->numtab); } - F->numlist[F->numlen] = value; + F->numtab[F->numlen] = value; return F->numlen++; } @@ -92,26 +92,35 @@ static int addstring(JF, const char *value) { int i; for (i = 0; i < F->strlen; i++) - if (!strcmp(F->strlist[i], value)) + if (!strcmp(F->strtab[i], value)) return i; if (F->strlen >= F->strcap) { F->strcap = F->strcap ? F->strcap * 2 : 16; - F->strlist = realloc(F->strlist, F->strcap * sizeof *F->strlist); + F->strtab = realloc(F->strtab, F->strcap * sizeof *F->strtab); } - F->strlist[F->strlen] = value; + F->strtab[F->strlen] = value; return F->strlen++; } -static void emitfunction(JF, int opcode, js_Function *fun) +static void emitfunction(JF, js_Function *fun) { - emit(J, F, opcode); + emit(J, F, OP_CLOSURE); emit(J, F, addfunction(J, F, fun)); } -static void emitnumber(JF, int opcode, double num) +static void emitnumber(JF, double num) { - emit(J, F, opcode); - emit(J, F, addnumber(J, F, num)); + if (num == 0) + emit(J, F, OP_NUMBER_0); + else if (num == 1) + emit(J, F, OP_NUMBER_1); + else if (num == (short)num) { + emit(J, F, OP_NUMBER_X); + emit(J, F, (short)num); + } else { + emit(J, F, OP_NUMBER); + emit(J, F, addnumber(J, F, num)); + } } static void emitstring(JF, int opcode, const char *str) @@ -146,13 +155,13 @@ static void label(JF, int inst) /* Expressions */ -static void unary(JF, js_Ast *exp, int opcode) +static void cunary(JF, js_Ast *exp, int opcode) { cexp(J, F, exp->a); emit(J, F, opcode); } -static void binary(JF, js_Ast *exp, int opcode) +static void cbinary(JF, js_Ast *exp, int opcode) { cexp(J, F, exp->a); cexp(J, F, exp->b); @@ -163,9 +172,12 @@ static void carray(JF, js_Ast *list) { int i = 0; while (list) { + emit(J, F, OP_DUP); + emit(J, F, OP_NUMBER_X); cexp(J, F, list->a); - emit(J, F, OP_ARRAYPUT); emit(J, F, i++); + emit(J, F, OP_SETPROP); + emit(J, F, OP_POP); list = list->b; } } @@ -176,15 +188,16 @@ static void cobject(JF, js_Ast *list) js_Ast *kv = list->a; if (kv->type == EXP_PROP_VAL) { js_Ast *prop = kv->a; - cexp(J, F, kv->b); + emit(J, F, OP_DUP); if (prop->type == AST_IDENTIFIER || prop->type == AST_STRING) - emitstring(J, F, OP_OBJECTPUT, prop->string); - else if (prop->type == AST_STRING) - emitstring(J, F, OP_OBJECTPUT, prop->string); + emitstring(J, F, OP_STRING, prop->string); else if (prop->type == AST_NUMBER) - emitnumber(J, F, OP_OBJECTPUT, prop->number); + emitnumber(J, F, prop->number); else jsC_error(J, list, "illegal property name in object initializer"); + cexp(J, F, kv->b); + emit(J, F, OP_SETPROP); + emit(J, F, OP_POP); } // TODO: set, get list = list->b; @@ -202,38 +215,114 @@ static int cargs(JF, js_Ast *list) return n; } -static void clval(JF, js_Ast *exp) +static void cassign(JF, js_Ast *lhs, js_Ast *rhs) { - switch (exp->type) { + switch (lhs->type) { case AST_IDENTIFIER: - emitstring(J, F, OP_AVAR, exp->string); + cexp(J, F, rhs); + emitstring(J, F, OP_SETVAR, lhs->string); break; case EXP_INDEX: - cexp(J, F, exp->a); - cexp(J, F, exp->b); - emit(J, F, OP_AINDEX); + cexp(J, F, lhs->a); + cexp(J, F, lhs->b); + cexp(J, F, rhs); + emit(J, F, OP_SETPROP); break; case EXP_MEMBER: - cexp(J, F, exp->a); - emitstring(J, F, OP_AMEMBER, exp->b->string); + cexp(J, F, lhs->a); + emitstring(J, F, OP_STRING, lhs->b->string); + cexp(J, F, rhs); + emit(J, F, OP_SETPROP); break; - case EXP_CALL: - /* host functions may return an assignable l-value */ - cexp(J, F, exp); + case EXP_CALL: /* host functions may return an assignable l-value */ + cexp(J, F, lhs); + cexp(J, F, rhs); + emit(J, F, OP_SETPROP); break; default: - jsC_error(J, exp, "invalid l-value in assignment"); + jsC_error(J, lhs, "invalid l-value in assignment"); break; } } -static void assignop(JF, js_Ast *exp, int opcode) +static void cassignop1(JF, js_Ast *lhs, int dup) { - clval(J, F, exp->a); - emit(J, F, OP_LOAD); - cexp(J, F, exp->b); + switch (lhs->type) { + case AST_IDENTIFIER: + emitstring(J, F, OP_GETVAR, lhs->string); + if (dup) emit(J, F, OP_DUP); + break; + case EXP_INDEX: + cexp(J, F, lhs->a); + cexp(J, F, lhs->b); + emit(J, F, OP_DUP2); + emit(J, F, OP_GETPROP); + if (dup) emit(J, F, OP_DUP1ROT4); + break; + case EXP_MEMBER: + cexp(J, F, lhs->a); + emitstring(J, F, OP_STRING, lhs->b->string); + emit(J, F, OP_DUP2); + emit(J, F, OP_GETPROP); + if (dup) emit(J, F, OP_DUP1ROT4); + break; + case EXP_CALL: /* host functions may return an assignable l-value */ + cexp(J, F, lhs); + emit(J, F, OP_DUP2); + emit(J, F, OP_GETPROP); + if (dup) emit(J, F, OP_DUP1ROT4); + break; + default: + jsC_error(J, lhs, "invalid l-value in assignment"); + break; + } +} + +static void cassignop2(JF, js_Ast *lhs) +{ + switch (lhs->type) { + case AST_IDENTIFIER: + emitstring(J, F, OP_SETVAR, lhs->string); + break; + case EXP_INDEX: + case EXP_MEMBER: + case EXP_CALL: + emit(J, F, OP_SETPROP); + break; + default: + jsC_error(J, lhs, "invalid l-value in assignment"); + break; + } +} + +static void cassignop(JF, js_Ast *lhs, js_Ast *rhs, int opcode) +{ + cassignop1(J, F, lhs, 0); + cexp(J, F, rhs); emit(J, F, opcode); - emit(J, F, OP_STORE); + cassignop2(J, F, lhs); +} + +static void cdelete(JF, js_Ast *exp) +{ + switch (exp->type) { + case AST_IDENTIFIER: + emitstring(J, F, OP_DELVAR, exp->string); + break; + case EXP_INDEX: + cexp(J, F, exp->a); + cexp(J, F, exp->b); + emit(J, F, OP_DELPROP); + break; + case EXP_MEMBER: + cexp(J, F, exp->a); + emitstring(J, F, OP_STRING, exp->b->string); + emit(J, F, OP_DELPROP); + break; + default: + jsC_error(J, exp, "invalid l-value in delete expression"); + break; + } } static void cvarinit(JF, js_Ast *list) @@ -242,9 +331,7 @@ static void cvarinit(JF, js_Ast *list) js_Ast *var = list->a; if (var->b) { cexp(J, F, var->b); - emitstring(J, F, OP_AVAR, var->a->string); - emit(J, F, OP_STORE); - emit(J, F, OP_POP); + emitstring(J, F, OP_SETVAR, var->a->string); } list = list->b; } @@ -258,12 +345,13 @@ static void ccall(JF, js_Ast *fun, js_Ast *args) cexp(J, F, fun->a); emit(J, F, OP_DUP); cexp(J, F, fun->b); - emit(J, F, OP_LOADINDEX); + emit(J, F, OP_GETPROP); break; case EXP_MEMBER: cexp(J, F, fun->a); emit(J, F, OP_DUP); - emitstring(J, F, OP_LOADMEMBER, fun->b->string); + emitstring(J, F, OP_STRING, fun->b->string); + emit(J, F, OP_GETPROP); break; default: emit(J, F, OP_THIS); @@ -281,9 +369,8 @@ static void cexp(JF, js_Ast *exp) int n; switch (exp->type) { - case AST_IDENTIFIER: emitstring(J, F, OP_LOADVAR, exp->string); break; - case AST_NUMBER: emitnumber(J, F, OP_NUMBER, exp->number); break; case AST_STRING: emitstring(J, F, OP_STRING, exp->string); break; + case AST_NUMBER: emitnumber(J, F, exp->number); break; case EXP_UNDEF: emit(J, F, OP_UNDEF); break; case EXP_NULL: emit(J, F, OP_NULL); break; case EXP_TRUE: emit(J, F, OP_TRUE); break; @@ -300,15 +387,24 @@ static void cexp(JF, js_Ast *exp) carray(J, F, exp->a); break; + case EXP_FUN: + emitfunction(J, F, newfun(J, exp->a, exp->b, exp->c)); + break; + + case AST_IDENTIFIER: + emitstring(J, F, OP_GETVAR, exp->string); + break; + case EXP_INDEX: cexp(J, F, exp->a); cexp(J, F, exp->b); - emit(J, F, OP_LOADINDEX); + emit(J, F, OP_GETPROP); break; case EXP_MEMBER: cexp(J, F, exp->a); - emitstring(J, F, OP_LOADMEMBER, exp->b->string); + emitstring(J, F, OP_STRING, exp->b->string); + emit(J, F, OP_GETPROP); break; case EXP_CALL: @@ -322,13 +418,38 @@ static void cexp(JF, js_Ast *exp) emit(J, F, n); break; - case EXP_FUN: - emitfunction(J, F, OP_CLOSURE, newfun(J, exp->a, exp->b, exp->c)); + case EXP_DELETE: + cdelete(J, F, exp->a); break; - case EXP_DELETE: - clval(J, F, exp->a); - emit(J, F, OP_DELETE); + case EXP_PREINC: + cassignop1(J, F, exp->a, 0); + emit(J, F, OP_NUMBER_1); + emit(J, F, OP_ADD); + cassignop2(J, F, exp->a); + break; + + case EXP_PREDEC: + cassignop1(J, F, exp->a, 0); + emit(J, F, OP_NUMBER_1); + emit(J, F, OP_SUB); + cassignop2(J, F, exp->a); + break; + + case EXP_POSTINC: + cassignop1(J, F, exp->a, 1); + emit(J, F, OP_NUMBER_1); + emit(J, F, OP_ADD); + cassignop2(J, F, exp->a); + emit(J, F, OP_POP); + break; + + case EXP_POSTDEC: + cassignop1(J, F, exp->a, 1); + emit(J, F, OP_NUMBER_1); + emit(J, F, OP_SUB); + cassignop2(J, F, exp->a); + emit(J, F, OP_POP); break; case EXP_VOID: @@ -337,56 +458,46 @@ static void cexp(JF, js_Ast *exp) emit(J, F, OP_UNDEF); break; - case EXP_TYPEOF: unary(J, F, exp, OP_TYPEOF); break; - case EXP_POS: unary(J, F, exp, OP_POS); break; - case EXP_NEG: unary(J, F, exp, OP_NEG); break; - case EXP_BITNOT: unary(J, F, exp, OP_BITNOT); break; - case EXP_LOGNOT: unary(J, F, exp, OP_LOGNOT); break; + case EXP_TYPEOF: cunary(J, F, exp, OP_TYPEOF); break; + case EXP_POS: cunary(J, F, exp, OP_POS); break; + case EXP_NEG: cunary(J, F, exp, OP_NEG); break; + case EXP_BITNOT: cunary(J, F, exp, OP_BITNOT); break; + case EXP_LOGNOT: cunary(J, F, exp, OP_LOGNOT); break; - case EXP_PREINC: clval(J, F, exp->a); emit(J, F, OP_PREINC); break; - case EXP_PREDEC: clval(J, F, exp->a); emit(J, F, OP_PREDEC); break; - case EXP_POSTINC: clval(J, F, exp->a); emit(J, F, OP_POSTINC); break; - case EXP_POSTDEC: clval(J, F, exp->a); emit(J, F, OP_POSTDEC); break; + case EXP_BITOR: cbinary(J, F, exp, OP_BITOR); break; + case EXP_BITXOR: cbinary(J, F, exp, OP_BITXOR); break; + case EXP_BITAND: cbinary(J, F, exp, OP_BITAND); break; + case EXP_EQ: cbinary(J, F, exp, OP_EQ); break; + case EXP_NE: cbinary(J, F, exp, OP_NE); break; + case EXP_STRICTEQ: cbinary(J, F, exp, OP_STRICTEQ); break; + case EXP_STRICTNE: cbinary(J, F, exp, OP_STRICTNE); break; + case EXP_LT: cbinary(J, F, exp, OP_LT); break; + case EXP_GT: cbinary(J, F, exp, OP_GT); break; + case EXP_LE: cbinary(J, F, exp, OP_LE); break; + case EXP_GE: cbinary(J, F, exp, OP_GE); break; + case EXP_INSTANCEOF: cbinary(J, F, exp, OP_INSTANCEOF); break; + case EXP_IN: cbinary(J, F, exp, OP_IN); break; + case EXP_SHL: cbinary(J, F, exp, OP_SHL); break; + case EXP_SHR: cbinary(J, F, exp, OP_SHR); break; + case EXP_USHR: cbinary(J, F, exp, OP_USHR); break; + case EXP_ADD: cbinary(J, F, exp, OP_ADD); break; + case EXP_SUB: cbinary(J, F, exp, OP_SUB); break; + case EXP_MUL: cbinary(J, F, exp, OP_MUL); break; + case EXP_DIV: cbinary(J, F, exp, OP_DIV); break; + case EXP_MOD: cbinary(J, F, exp, OP_MOD); break; - case EXP_BITOR: binary(J, F, exp, OP_BITOR); break; - case EXP_BITXOR: binary(J, F, exp, OP_BITXOR); break; - case EXP_BITAND: binary(J, F, exp, OP_BITAND); break; - case EXP_EQ: binary(J, F, exp, OP_EQ); break; - case EXP_NE: binary(J, F, exp, OP_NE); break; - case EXP_EQ3: binary(J, F, exp, OP_EQ3); break; - case EXP_NE3: binary(J, F, exp, OP_NE3); break; - case EXP_LT: binary(J, F, exp, OP_LT); break; - case EXP_GT: binary(J, F, exp, OP_GT); break; - case EXP_LE: binary(J, F, exp, OP_LE); break; - case EXP_GE: binary(J, F, exp, OP_GE); break; - case EXP_INSTANCEOF: binary(J, F, exp, OP_INSTANCEOF); break; - case EXP_IN: binary(J, F, exp, OP_IN); break; - case EXP_SHL: binary(J, F, exp, OP_SHL); break; - case EXP_SHR: binary(J, F, exp, OP_SHR); break; - case EXP_USHR: binary(J, F, exp, OP_USHR); break; - case EXP_ADD: binary(J, F, exp, OP_ADD); break; - case EXP_SUB: binary(J, F, exp, OP_SUB); break; - case EXP_MUL: binary(J, F, exp, OP_MUL); break; - case EXP_DIV: binary(J, F, exp, OP_DIV); break; - case EXP_MOD: binary(J, F, exp, OP_MOD); break; - - case EXP_ASS: - clval(J, F, exp->a); - cexp(J, F, exp->b); - emit(J, F, OP_STORE); - break; - - case EXP_ASS_MUL: assignop(J, F, exp, OP_MUL); break; - case EXP_ASS_DIV: assignop(J, F, exp, OP_DIV); break; - case EXP_ASS_MOD: assignop(J, F, exp, OP_MOD); break; - case EXP_ASS_ADD: assignop(J, F, exp, OP_ADD); break; - case EXP_ASS_SUB: assignop(J, F, exp, OP_SUB); break; - case EXP_ASS_SHL: assignop(J, F, exp, OP_SHL); break; - case EXP_ASS_SHR: assignop(J, F, exp, OP_SHR); break; - case EXP_ASS_USHR: assignop(J, F, exp, OP_USHR); break; - case EXP_ASS_BITAND: assignop(J, F, exp, OP_BITAND); break; - case EXP_ASS_BITXOR: assignop(J, F, exp, OP_BITXOR); break; - case EXP_ASS_BITOR: assignop(J, F, exp, OP_BITOR); break; + case EXP_ASS: cassign(J, F, exp->a, exp->b); break; + case EXP_ASS_MUL: cassignop(J, F, exp->a, exp->b, OP_MUL); break; + case EXP_ASS_DIV: cassignop(J, F, exp->a, exp->b, OP_DIV); break; + case EXP_ASS_MOD: cassignop(J, F, exp->a, exp->b, OP_MOD); break; + case EXP_ASS_ADD: cassignop(J, F, exp->a, exp->b, OP_ADD); break; + case EXP_ASS_SUB: cassignop(J, F, exp->a, exp->b, OP_SUB); break; + case EXP_ASS_SHL: cassignop(J, F, exp->a, exp->b, OP_SHL); break; + case EXP_ASS_SHR: cassignop(J, F, exp->a, exp->b, OP_SHR); break; + case EXP_ASS_USHR: cassignop(J, F, exp->a, exp->b, OP_USHR); break; + case EXP_ASS_BITAND: cassignop(J, F, exp->a, exp->b, OP_BITAND); break; + case EXP_ASS_BITXOR: cassignop(J, F, exp->a, exp->b, OP_BITXOR); break; + case EXP_ASS_BITOR: cassignop(J, F, exp->a, exp->b, OP_BITOR); break; case EXP_COMMA: cexp(J, F, exp->a); @@ -536,7 +647,7 @@ static void cfundecs(JF, js_Ast *list) while (list) { js_Ast *stm = list->a; if (stm->type == AST_FUNDEC) { - emitfunction(J, F, OP_CLOSURE, newfun(J, stm->a, stm->b, stm->c)); + emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c)); emitstring(J, F, OP_FUNDEC, stm->a->string); } list = list->b; @@ -558,7 +669,7 @@ static void cvardecs(JF, js_Ast *node) static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) { if (name) { - emitfunction(J, F, OP_CLOSURE, F); + emitfunction(J, F, F); emitstring(J, F, OP_FUNDEC, name->string); } @@ -568,10 +679,8 @@ static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) cstmlist(J, F, body); } - if (F->codelen == 0 || F->code[F->codelen - 1] != OP_RETURN) { - emit(J, F, OP_UNDEF); - emit(J, F, OP_RETURN); - } + emit(J, F, OP_UNDEF); + emit(J, F, OP_RETURN); } int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) diff --git a/jscompile.h b/jscompile.h index 3ca47ec..199ca1b 100644 --- a/jscompile.h +++ b/jscompile.h @@ -3,12 +3,18 @@ enum { - OP_POP, - OP_DUP, + OP_POP, /* A -- */ + OP_DUP, /* A -- A A */ + OP_DUP2, /* A B -- A B A B */ + OP_DUP1ROT4, /* A B C -- C A B C */ - OP_CLOSURE, - OP_NUMBER, - OP_STRING, + OP_NUMBER_0, /* -- 0 */ + OP_NUMBER_1, /* -- 1 */ + OP_NUMBER_X, /* -K- K */ + + OP_NUMBER, /* -N- */ + OP_STRING, /* -S- */ + OP_CLOSURE, /* -F- */ OP_UNDEF, OP_NULL, @@ -17,33 +23,23 @@ enum OP_THIS, OP_NEWARRAY, - OP_ARRAYPUT, OP_NEWOBJECT, - OP_OBJECTPUT, - OP_FUNDEC, /* -(name)- */ - OP_VARDEC, /* -(name)- */ + OP_FUNDEC, /* -S- */ + OP_VARDEC, /* -S- */ - OP_LOADVAR, /* -(name)- */ - OP_LOADMEMBER, /* -(name)- */ - OP_LOADINDEX, /* -- */ + OP_GETVAR, /* -S- */ + OP_SETVAR, /* -S- */ + OP_DELVAR, /* -S- */ - OP_AVAR, /* -(name)- */ - OP_AMEMBER, /* -(name)- */ - OP_AINDEX, /* -- */ - - OP_LOAD, /* -- */ - OP_STORE, /* -- */ + OP_IN, /* -- */ + OP_GETPROP, /* -- */ + OP_SETPROP, /* -- */ + OP_DELPROP, /* -- */ OP_CALL, /* -(numargs)- */ OP_NEW, /* -(numargs)- */ - OP_DELETE, /* -- */ - OP_PREINC, /* -- */ - OP_PREDEC, /* -- */ - OP_POSTINC, /* -- */ - OP_POSTDEC, /* -- */ - OP_VOID, OP_TYPEOF, OP_POS, @@ -56,14 +52,12 @@ enum OP_BITAND, OP_EQ, OP_NE, - OP_EQ3, - OP_NE3, + OP_STRICTEQ, + OP_STRICTNE, OP_LT, OP_GT, OP_LE, OP_GE, - OP_INSTANCEOF, - OP_IN, OP_SHL, OP_SHR, OP_USHR, @@ -73,17 +67,20 @@ enum OP_DIV, OP_MOD, - OP_TRY, - OP_THROW, - OP_RETURN, - OP_DEBUGGER, + OP_INSTANCEOF, + OP_THROW, + OP_TRY, + OP_CATCH, + OP_ENDCATCH, OP_WITH, OP_ENDWITH, + OP_DEBUGGER, OP_JUMP, OP_JTRUE, OP_JFALSE, + OP_RETURN, }; struct js_Function @@ -94,13 +91,13 @@ struct js_Function short *code; int codecap, codelen; - js_Function **funlist; + js_Function **funtab; int funcap, funlen; - double *numlist; + double *numtab; int numcap, numlen; - const char **strlist; + const char **strtab; int strcap, strlen; js_Function *next; /* alloc list */ diff --git a/jsdump.c b/jsdump.c index 5bc2f55..1dc4683 100644 --- a/jsdump.c +++ b/jsdump.c @@ -1,7 +1,6 @@ #include "js.h" #include "jsparse.h" #include "jscompile.h" -#include "jsvalue.h" #include "jsobject.h" #include @@ -194,8 +193,8 @@ static void pexpi(int d, int i, js_Ast *exp) case EXP_BITAND: pbin(d, i, exp, " & "); break; case EXP_EQ: pbin(d, i, exp, " == "); break; case EXP_NE: pbin(d, i, exp, " != "); break; - case EXP_EQ3: pbin(d, i, exp, " === "); break; - case EXP_NE3: pbin(d, i, exp, " !== "); break; + case EXP_STRICTEQ: pbin(d, i, exp, " === "); break; + case EXP_STRICTNE: pbin(d, i, exp, " !== "); break; case EXP_LT: pbin(d, i, exp, " < "); break; case EXP_GT: pbin(d, i, exp, " > "); break; case EXP_LE: pbin(d, i, exp, " <= "); break; @@ -598,13 +597,13 @@ void jsC_dumpfunction(js_State *J, js_Function *F) printf("function %p %s(%d)\n", F, F->name, F->numparams); for (i = 0; i < F->funlen; i++) - printf("\tfunction %p %s\n", F->funlist[i], F->funlist[i]->name); + printf("\tfunction %p %s\n", F->funtab[i], F->funtab[i]->name); for (i = 0; i < F->strlen; i++) { - ps("\tstring "); pstr(F->strlist[i]); ps("\n"); + ps("\tstring "); pstr(F->strtab[i]); ps("\n"); } // TODO: regexp for (i = 0; i < F->numlen; i++) - printf("\tnumber %.9g\n", F->numlist[i]); + printf("\tnumber %.9g\n", F->numtab[i]); while (p < end) { int c = *p++; @@ -614,29 +613,27 @@ void jsC_dumpfunction(js_State *J, js_Function *F) switch (c) { case OP_CLOSURE: - pc(' '); - ps(F->funlist[*p++]->name); + ps(" f:"); + ps(F->funtab[*p++]->name); break; case OP_NUMBER: - printf(" %.9g", F->numlist[*p++]); + printf(" %.9g", F->numtab[*p++]); break; case OP_STRING: pc(' '); - pstr(F->strlist[*p++]); + pstr(F->strtab[*p++]); break; - case OP_OBJECTPUT: case OP_FUNDEC: case OP_VARDEC: - case OP_LOADVAR: - case OP_LOADMEMBER: - case OP_AVAR: - case OP_AMEMBER: + case OP_GETVAR: + case OP_SETVAR: + case OP_DELVAR: pc(' '); - ps(F->strlist[*p++]); + ps(F->strtab[*p++]); break; - case OP_ARRAYPUT: + case OP_NUMBER_X: case OP_CALL: case OP_NEW: case OP_JUMP: @@ -650,9 +647,9 @@ void jsC_dumpfunction(js_State *J, js_Function *F) } for (i = 0; i < F->funlen; i++) { - if (F->funlist[i] != F) { + if (F->funtab[i] != F) { nl(); - jsC_dumpfunction(J, F->funlist[i]); + jsC_dumpfunction(J, F->funtab[i]); } } } diff --git a/jslex.c b/jslex.c index c6d7e11..d7e045b 100644 --- a/jslex.c +++ b/jslex.c @@ -525,7 +525,7 @@ static int lex(js_State *J, const char **sp) case '=': if (ACCEPT('=')) { if (ACCEPT('=')) - return TK_EQ3; + return TK_STRICTEQ; return TK_EQ; } return '='; @@ -533,7 +533,7 @@ static int lex(js_State *J, const char **sp) case '!': if (ACCEPT('=')) { if (ACCEPT('=')) - return TK_NE3; + return TK_STRICTNE; return TK_NE; } return '!'; diff --git a/jslex.h b/jslex.h index add3ea7..a1912e6 100644 --- a/jslex.h +++ b/jslex.h @@ -12,8 +12,8 @@ enum { TK_GE, TK_EQ, TK_NE, - TK_EQ3, - TK_NE3, + TK_STRICTEQ, + TK_STRICTNE, TK_SHL, TK_SHR, TK_USHR, diff --git a/jsobject.c b/jsobject.c index 11d5059..71c999c 100644 --- a/jsobject.c +++ b/jsobject.c @@ -1,5 +1,4 @@ #include "js.h" -#include "jsvalue.h" #include "jsobject.h" /* @@ -21,6 +20,8 @@ static js_Property sentinel = { "", &sentinel, &sentinel, 0 }; +static js_Property undefined = { "", &sentinel, &sentinel, 0, { {0}, JS_TUNDEFINED } }; + static js_Property *newproperty(const char *key) { js_Property *node = malloc(sizeof(js_Property)); @@ -116,11 +117,9 @@ static void js_dumpvalue(js_State *J, js_Value v) case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break; case JS_TNUMBER: printf("%.9g", v.u.number); break; case JS_TSTRING: printf("'%s'", v.u.string); break; - case JS_TREGEXP: printf("/%s/", v.u.regexp.prog); break; case JS_TOBJECT: printf("", v.u.object); break; case JS_TCLOSURE: printf("", v.u.closure); break; case JS_TCFUNCTION: printf("", v.u.cfunction); break; - case JS_TREFERENCE: printf("", v.u.reference); break; } } diff --git a/jsobject.h b/jsobject.h index ee68684..a2da579 100644 --- a/jsobject.h +++ b/jsobject.h @@ -1,6 +1,30 @@ #ifndef js_object_h #define js_object_h +enum js_ValueType { + JS_TUNDEFINED, + JS_TNULL, + JS_TBOOLEAN, + JS_TNUMBER, + JS_TSTRING, + JS_TOBJECT, + JS_TCLOSURE, + JS_TCFUNCTION, +}; + +struct js_Value +{ + union { + int boolean; + double number; + const char *string; + js_Object *object; + js_Closure *closure; + js_CFunction *cfunction; + } u; + js_ValueType type; +}; + struct js_Property { char *key; @@ -13,7 +37,7 @@ struct js_Object { js_Property *properties; js_Object *prototype; - js_Object *parent; + js_Object *outer; }; js_Object *js_newobject(js_State *J); @@ -21,6 +45,7 @@ js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name); js_Property *js_setproperty(js_State *J, js_Object *obj, const char *name); void js_deleteproperty(js_State *J, js_Object *obj, const char *name); +void jsC_dumpvalue(js_State *J, js_Value v); void js_dumpobject(js_State *J, js_Object *obj); #endif diff --git a/jsparse.c b/jsparse.c index 0fd7c3d..747310a 100644 --- a/jsparse.c +++ b/jsparse.c @@ -458,8 +458,8 @@ static js_Ast *equality(js_State *J, int notin) loop: if (accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; } if (accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; } - if (accept(J, TK_EQ3)) { a = EXP2(EQ3, a, relational(J, notin)); goto loop; } - if (accept(J, TK_NE3)) { a = EXP2(NE3, a, relational(J, notin)); goto loop; } + if (accept(J, TK_STRICTEQ)) { a = EXP2(STRICTEQ, a, relational(J, notin)); goto loop; } + if (accept(J, TK_STRICTNE)) { a = EXP2(STRICTNE, a, relational(J, notin)); goto loop; } return a; } diff --git a/jsparse.h b/jsparse.h index 777f896..5cfa59c 100644 --- a/jsparse.h +++ b/jsparse.h @@ -62,8 +62,8 @@ enum EXP_BITAND, EXP_EQ, EXP_NE, - EXP_EQ3, - EXP_NE3, + EXP_STRICTEQ, + EXP_STRICTNE, EXP_LT, EXP_GT, EXP_LE, diff --git a/jsrun.c b/jsrun.c index 2da9550..cfa79c4 100644 --- a/jsrun.c +++ b/jsrun.c @@ -1,5 +1,4 @@ #include "js.h" -#include "jsvalue.h" #include "jsobject.h" #include "jscompile.h" #include "jsrun.h" @@ -8,213 +7,452 @@ static js_Value stack[256]; static int top = 0; -static inline int i32(double d) +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(d) || d == 0) + if (!isfinite(n) || n == 0) return 0; - d = fmod(d, two32); - d = d >= 0 ? floor(d) : ceil(d) + two32; - if (d >= two31) - return d - two32; + n = fmod(n, two32); + n = n >= 0 ? floor(n) : ceil(n) + two32; + if (n >= two31) + return n - two32; else - return d; + return n; } -static inline unsigned int u32(double d) +static inline unsigned int touint32(double n) { - return i32(d); + return toint32(n); } -static inline void push(js_State *J, js_Value v) +static void js_pushvalue(js_State *J, js_Value v) { - stack[top++] = v; + stack[top] = v; + ++top; } -static inline js_Value pop(js_State *J) +void js_pushundefined(js_State *J) { - return stack[--top]; + stack[top].type = JS_TUNDEFINED; + ++top; } -static inline js_Value peek(js_State *J) +void js_pushnull(js_State *J) { - return stack[top-1]; + stack[top].type = JS_TNULL; + ++top; } -static inline void pushnumber(js_State *J, double number) +void js_pushboolean(js_State *J, int v) { - js_Value v; - v.type = JS_TNUMBER; - v.u.number = number; - push(J, v); + stack[top].type = JS_TBOOLEAN; + stack[top].u.boolean = !!v; + ++top; } -static inline double popnumber(js_State *J) +void js_pushnumber(js_State *J, double v) { - js_Value v = pop(J); - if (v.type == JS_TNUMBER) - return v.u.number; - if (v.type == JS_TSTRING) - return strtod(v.u.string, 0); - return 0; + stack[top].type = JS_TNUMBER; + stack[top].u.number = v; + ++top; } -static inline void pushundefined(js_State *J) +void js_pushstring(js_State *J, const char *v) { - js_Value v; - v.type = JS_TUNDEFINED; - push(J, v); + stack[top].type = JS_TSTRING; + stack[top].u.string = v; + ++top; } -static inline void pushnull(js_State *J) +void js_pushobject(js_State *J, js_Object *v) { - js_Value v; - v.type = JS_TNULL; - push(J, v); + stack[top].type = JS_TOBJECT; + stack[top].u.object = v; + ++top; } -static inline void pushboolean(js_State *J, int boolean) +js_Value js_tovalue(js_State *J, int idx) { - js_Value v; - v.type = JS_TBOOLEAN; - v.u.boolean = boolean; - push(J, v); + idx += top; + return stack[idx]; } -static inline int popboolean(js_State *J) +int js_toboolean(js_State *J, int idx) { - js_Value v = pop(J); - if (v.type == JS_TBOOLEAN) - return v.u.boolean; - if (v.type == JS_TNUMBER) - return v.u.number != 0; - return 0; + const char *s; + double n; + idx += top; + 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; + } } -static inline void pushreference(js_State *J, js_Property *reference) +double js_tonumber(js_State *J, int idx) { - js_Value v; - v.type = JS_TREFERENCE; - v.u.reference = reference; - push(J, v); + idx += top; + 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; + } } -static inline js_Property *popreference(js_State *J) +double js_tointeger(js_State *J, int idx) { - js_Value v = pop(J); - if (v.type == JS_TREFERENCE) - return v.u.reference; - return 0; + return toint32(js_tonumber(J, idx)); } -static inline js_Property *peekreference(js_State *J) +int js_toint32(js_State *J, int idx) { - js_Value v = peek(J); - if (v.type == JS_TREFERENCE) - return v.u.reference; - return 0; + return toint32(js_tonumber(J, idx)); } -#define UNARY(X) a = popnumber(J); pushnumber(J, X) -#define BINARY(X) b = popnumber(J); a = popnumber(J); pushnumber(J, X) +unsigned int js_touint32(js_State *J, int idx) +{ + return toint32(js_tonumber(J, idx)); +} + +const char *js_tostring(js_State *J, int idx) +{ + char buf[20]; + idx += top; + 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 += top; + 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_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 */ +} + +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, addr; - js_Property *p; - js_Value v; - double a, b; - const char *s; + 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_NUMBER: - pushnumber(J, F->numlist[*pc++]); - break; + case OP_POP: js_pop(J, 1); break; + case OP_DUP: js_dup(J); break; + case OP_DUP2: js_dup2(J); break; + case OP_DUP1ROT4: js_dup1rot4(J); break; - case OP_LOADVAR: - s = F->strlist[*pc++]; - p = js_getproperty(J, E, s); - if (p) - push(J, p->value); + 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: break; + + case OP_NEWOBJECT: js_pushobject(J, js_newobject(J)); break; + case OP_NEWARRAY: js_pushobject(J, js_newobject(J)); break; + + // FUNDEC + // VARDEC + + case OP_GETVAR: + ref = js_getproperty(J, E, ST[*pc++]); + if (ref) + js_pushvalue(J, ref->value); else - pushundefined(J); + js_pushundefined(J); break; - case OP_AVAR: - s = F->strlist[*pc++]; - p = js_setproperty(J, E, s); - pushreference(J, p); + case OP_SETVAR: + ref = js_setproperty(J, E, ST[*pc++]); + if (ref) + ref->value = js_tovalue(J, -1); break; - case OP_LOAD: - p = peekreference(J); - if (p) - push(J, p->value); + // 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 - pushundefined(J); + js_pushundefined(J); break; - case OP_STORE: - v = pop(J); - p = popreference(J); - if (p) - p->value = v; - push(J, v); + 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; - case OP_UNDEF: pushundefined(J); break; - case OP_NULL: pushnull(J); break; - case OP_TRUE: pushboolean(J, 1); break; - case OP_FALSE: pushboolean(J, 0); break; + // OP_DELPROP - case OP_POS: UNARY(a); break; - case OP_NEG: UNARY(-a); break; - case OP_BITNOT: UNARY(~i32(a)); break; - case OP_LOGNOT: UNARY(!a); break; + /* Unary expressions */ - case OP_ADD: BINARY(a + b); break; - case OP_SUB: BINARY(a - b); break; - case OP_MUL: BINARY(a * b); break; - case OP_DIV: BINARY(a / b); break; - case OP_MOD: BINARY(fmod(a, b)); break; - case OP_SHL: BINARY(i32(a) << (u32(b) & 0x1F)); break; - case OP_SHR: BINARY(i32(a) >> (u32(b) & 0x1F)); break; - case OP_USHR: BINARY(u32(a) >> (u32(b) & 0x1F)); break; - case OP_BITAND: BINARY(i32(a) & i32(b)); break; - case OP_BITXOR: BINARY(i32(a) ^ i32(b)); break; - case OP_BITOR: BINARY(i32(a) | i32(b)); break; - - case OP_LT: BINARY(a < b); break; - case OP_GT: BINARY(a > b); break; - case OP_LE: BINARY(a <= b); break; - case OP_GE: BINARY(a >= b); break; - case OP_EQ: BINARY(a == b); break; - case OP_NE: BINARY(a != b); break; - - case OP_JFALSE: - addr = *pc++; - if (!popboolean(J)) - pc = F->code + addr; + case OP_POS: + x = js_tonumber(J, -1); + js_pop(J, 1); + js_pushnumber(J, x); break; - case OP_JTRUE: - addr = *pc++; - if (popboolean(J)) - pc = F->code + addr; + 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 = F->code + *pc; + pc = pcstart + *pc; break; - case OP_POP: pop(J); break; - case OP_RETURN: return; + 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)); @@ -223,9 +461,22 @@ static void runfun(js_State *J, js_Function *F, js_Object *E) } } +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 *varenv = js_newobject(J); + + if (setjmp(J->jb)) { + js_dumpobject(J, varenv); + return; + } + runfun(J, F, varenv); + js_dumpobject(J, varenv); } diff --git a/jsrun.h b/jsrun.h index 8c31272..3982d36 100644 --- a/jsrun.h +++ b/jsrun.h @@ -1,6 +1,7 @@ #ifndef js_run_h #define js_run_h +void jsR_error(js_State *J, const char *message); void jsR_runfunction(js_State *J, js_Function *F); #endif diff --git a/jsvalue.h b/jsvalue.h deleted file mode 100644 index e5c53fb..0000000 --- a/jsvalue.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef js_value_h -#define js_value_h - -enum js_ValueType { - JS_TUNDEFINED, - JS_TNULL, - JS_TBOOLEAN, - JS_TNUMBER, - JS_TSTRING, - JS_TREGEXP, - JS_TOBJECT, - JS_TCLOSURE, - JS_TCFUNCTION, - JS_TREFERENCE, /* l-value from aval/aindex/amember */ -}; - -struct js_Value -{ - union { - int boolean; - double number; - const char *string; - struct { - const char *prog; - unsigned char flags; - } regexp; - js_Object *object; - js_Closure *closure; - js_CFunction *cfunction; - js_Property *reference; - } u; - js_ValueType type; -}; - -void jsC_dumpvalue(js_State *J, js_Value v); - -#endif