From bb65f18fcc76fda0870e0c45c4c0ade770979182 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 5 Nov 2018 14:31:12 +0100 Subject: [PATCH] Set 'lightweight' and 'arguments' during compile pass. Avoid doing a separate analysis pass by using the same bytecode for both lightweight and non-lightweight functions. --- jscompile.c | 131 +++++++++++++++++++--------------------------------- jscompile.h | 3 -- jsdump.c | 8 ++-- jsrepr.c | 7 +-- jsrun.c | 73 ++++++++++++++++------------- opnames.h | 3 -- 6 files changed, 95 insertions(+), 130 deletions(-) diff --git a/jscompile.c b/jscompile.c index d2c61f9..ea3ecb4 100644 --- a/jscompile.c +++ b/jscompile.c @@ -138,7 +138,7 @@ static int addstring(JF, const char *value) return F->strlen++; } -static void addlocal(JF, js_Ast *ident, int reuse) +static int addlocal(JF, js_Ast *ident, int reuse) { const char *name = ident->string; if (F->strict) { @@ -146,13 +146,16 @@ static void addlocal(JF, js_Ast *ident, int reuse) jsC_error(J, ident, "redefining 'arguments' is not allowed in strict mode"); if (!strcmp(name, "eval")) jsC_error(J, ident, "redefining 'eval' is not allowed in strict mode"); + } else { + if (!strcmp(name, "eval")) + js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line); } if (reuse || F->strict) { int i; for (i = 0; i < F->varlen; ++i) { if (!strcmp(F->vartab[i], name)) { if (reuse) - return; + return i+1; if (F->strict) jsC_error(J, ident, "duplicate formal parameter '%s'", name); } @@ -162,7 +165,8 @@ static void addlocal(JF, js_Ast *ident, int reuse) F->varcap = F->varcap ? F->varcap * 2 : 16; F->vartab = js_realloc(J, F->vartab, F->varcap * sizeof *F->vartab); } - F->vartab[F->varlen++] = name; + F->vartab[F->varlen] = name; + return ++F->varlen; } static int findlocal(JF, const char *name) @@ -176,6 +180,7 @@ static int findlocal(JF, const char *name) static void emitfunction(JF, js_Function *fun) { + F->lightweight = 0; emit(J, F, OP_CLOSURE); emitarg(J, F, addfunction(J, F, fun)); } @@ -208,23 +213,32 @@ static void emitstring(JF, int opcode, const char *str) static void emitlocal(JF, int oploc, int opvar, js_Ast *ident) { + int is_arguments = !strcmp(ident->string, "arguments"); + int is_eval = !strcmp(ident->string, "eval"); int i; + + if (is_arguments) { + F->lightweight = 0; + F->arguments = 1; + } + checkfutureword(J, F, ident); if (F->strict && oploc == OP_SETLOCAL) { - if (!strcmp(ident->string, "arguments")) + if (is_arguments) jsC_error(J, ident, "'arguments' is read-only in strict mode"); - if (!strcmp(ident->string, "eval")) + if (is_eval) jsC_error(J, ident, "'eval' is read-only in strict mode"); } - if (F->lightweight) { - i = findlocal(J, F, ident->string); - if (i >= 0) { - emit(J, F, oploc); - emitarg(J, F, i); - return; - } + if (is_eval) + js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line); + + i = findlocal(J, F, ident->string); + if (i < 0) { + emitstring(J, F, opvar, ident->string); + } else { + emit(J, F, oploc); + emitarg(J, F, i); } - emitstring(J, F, opvar, ident->string); } static int here(JF) @@ -540,6 +554,7 @@ static void cdelete(JF, js_Ast *exp) static void ceval(JF, js_Ast *fun, js_Ast *args) { + F->lightweight = 0; int n = cargs(J, F, args); if (n == 0) emit(J, F, OP_UNDEF); @@ -1270,6 +1285,7 @@ static void cstm(JF, js_Ast *stm) break; case STM_WITH: + F->lightweight = 0; if (F->strict) jsC_error(J, stm->a, "'with' statements are not allowed in strict mode"); cexp(J, F, stm->a); @@ -1283,6 +1299,7 @@ static void cstm(JF, js_Ast *stm) case STM_TRY: emitline(J, F, stm); if (stm->b && stm->c) { + F->lightweight = 0; if (stm->d) ctrycatchfinally(J, F, stm->a, stm->b, stm->c, stm->d); else @@ -1319,49 +1336,6 @@ static void cstmlist(JF, js_Ast *list) } } -/* Analyze */ - -static void analyze(JF, js_Ast *node) -{ - if (node->type == AST_LIST) { - while (node) { - analyze(J, F, node->a); - node = node->b; - } - return; - } - - if (isfun(node->type)) { - F->lightweight = 0; - return; /* don't scan inner functions */ - } - - if (node->type == STM_WITH) { - F->lightweight = 0; - } - - if (node->type == STM_TRY && node->c) { - F->lightweight = 0; - } - - if (node->type == EXP_IDENTIFIER) { - if (!strcmp(node->string, "arguments")) { - F->lightweight = 0; - F->arguments = 1; - } else if (!strcmp(node->string, "eval")) { - /* eval may only be used as a direct function call */ - if (!node->parent || node->parent->type != EXP_CALL || node->parent->a != node) - js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, node->line); - F->lightweight = 0; - } - } - - if (node->a) analyze(J, F, node->a); - if (node->b) analyze(J, F, node->b); - if (node->c) analyze(J, F, node->c); - if (node->d) analyze(J, F, node->d); -} - /* Declarations and programs */ static int listlength(js_Ast *list) @@ -1371,18 +1345,14 @@ static int listlength(js_Ast *list) return n; } -static int cparams(JF, js_Ast *list, js_Ast *fname) +static void cparams(JF, js_Ast *list, js_Ast *fname) { - int shadow = 0; F->numparams = listlength(list); while (list) { checkfutureword(J, F, list->a); addlocal(J, F, list->a, 0); - if (fname && !strcmp(fname->string, list->a->string)) - shadow = 1; list = list->b; } - return shadow; } static void cvardecs(JF, js_Ast *node) @@ -1400,10 +1370,7 @@ static void cvardecs(JF, js_Ast *node) if (node->type == EXP_VAR) { checkfutureword(J, F, node->a); - if (F->lightweight) - addlocal(J, F, node->a, 1); - else - emitstring(J, F, OP_DEFVAR, node->a->string); + addlocal(J, F, node->a, 1); } if (node->a) cvardecs(J, F, node->a); @@ -1420,7 +1387,10 @@ static void cfundecs(JF, js_Ast *list) emitline(J, F, stm); emitfunction(J, F, newfun(J, stm->line, stm->a, stm->b, stm->c, 0, F->strict)); emitline(J, F, stm); - emitstring(J, F, OP_INITVAR, stm->a->string); + emit(J, F, OP_SETLOCAL); + int v = addlocal(J, F, stm->a, 0); + emitarg(J, F, v); + emit(J, F, OP_POP); } list = list->b; } @@ -1428,17 +1398,12 @@ static void cfundecs(JF, js_Ast *list) static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) { - int shadow; - F->lightweight = 1; F->arguments = 0; if (F->script) F->lightweight = 0; - if (body) - analyze(J, F, body); - /* Check if first statement is 'use strict': */ if (body && body->type == AST_LIST && body->a && body->a->type == EXP_STRING) if (!strcmp(body->a->string, "use strict")) @@ -1446,25 +1411,23 @@ static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) F->lastline = F->line; - shadow = cparams(J, F, params, name); - - if (name && !shadow) { - checkfutureword(J, F, name); - emit(J, F, OP_CURRENT); - if (F->lightweight) { - addlocal(J, F, name, 0); - emit(J, F, OP_INITLOCAL); - emitarg(J, F, findlocal(J, F, name->string)); - } else { - emitstring(J, F, OP_INITVAR, name->string); - } - } + cparams(J, F, params, name); if (body) { cvardecs(J, F, body); cfundecs(J, F, body); } + if (name) { + checkfutureword(J, F, name); + if (findlocal(J, F, name->string) < 0) { + emit(J, F, OP_CURRENT); + emit(J, F, OP_SETLOCAL); + emitarg(J, F, addlocal(J, F, name, 0)); + emit(J, F, OP_POP); + } + } + if (F->script) { emit(J, F, OP_UNDEF); cstmlist(J, F, body); diff --git a/jscompile.h b/jscompile.h index 50fffa1..172f8fd 100644 --- a/jscompile.h +++ b/jscompile.h @@ -27,13 +27,10 @@ enum js_OpCode OP_THIS, OP_CURRENT, /* currently executing function object */ - OP_INITLOCAL, /* -K- */ OP_GETLOCAL, /* -K- */ OP_SETLOCAL, /* -K- */ OP_DELLOCAL, /* -K- false */ - OP_INITVAR, /* -S- */ - OP_DEFVAR, /* -S- */ OP_HASVAR, /* -S- ( | undefined ) */ OP_GETVAR, /* -S- */ OP_SETVAR, /* -S- */ diff --git a/jsdump.c b/jsdump.c index a49b8cf..560216d 100644 --- a/jsdump.c +++ b/jsdump.c @@ -814,8 +814,6 @@ void jsC_dumpfunction(js_State *J, js_Function *F) p += 2; break; - case OP_INITVAR: - case OP_DEFVAR: case OP_GETVAR: case OP_HASVAR: case OP_SETVAR: @@ -828,11 +826,13 @@ void jsC_dumpfunction(js_State *J, js_Function *F) ps(F->strtab[*p++]); break; - case OP_CLOSURE: - case OP_INITLOCAL: case OP_GETLOCAL: case OP_SETLOCAL: case OP_DELLOCAL: + printf(" %s", F->vartab[*p++ - 1]); + break; + + case OP_CLOSURE: case OP_CALL: case OP_NEW: case OP_JUMP: diff --git a/jsrepr.c b/jsrepr.c index 92f2e36..9844967 100644 --- a/jsrepr.c +++ b/jsrepr.c @@ -116,9 +116,10 @@ static void reprarray(js_State *J, js_Buffer **sb) for (i = 0; i < n; ++i) { if (i > 0) js_puts(J, sb, ", "); - js_getindex(J, -1, i); - reprvalue(J, sb); - js_pop(J, 1); + if (js_hasindex(J, -1, i)) { + reprvalue(J, sb); + js_pop(J, 1); + } } js_putc(J, sb, ']'); } diff --git a/jsrun.c b/jsrun.c index c4537e0..81dbb64 100644 --- a/jsrun.c +++ b/jsrun.c @@ -857,11 +857,6 @@ static void js_initvar(js_State *J, const char *name, int idx) jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL); } -static void js_defvar(js_State *J, const char *name) -{ - jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, NULL, NULL, NULL); -} - static int js_hasvar(js_State *J, const char *name) { js_Environment *E = J->E; @@ -954,6 +949,7 @@ static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environmen js_pop(J, n - F->numparams); n = F->numparams; } + for (i = n; i < F->varlen; ++i) js_pushundefined(J); @@ -990,17 +986,16 @@ static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment 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); - } - } + for (i = 0; i < n && i < F->numparams; ++i) + js_initvar(J, F->vartab[i], i + 1); js_pop(J, n); + for (; i < F->varlen; ++i) { + js_pushundefined(J); + js_initvar(J, F->vartab[i], -1); + js_pop(J, 1); + } + jsR_run(J, F); v = *stackidx(J, -1); TOP = --BOT; /* clear stack */ @@ -1012,11 +1007,20 @@ static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment static void jsR_callscript(js_State *J, int n, js_Function *F, js_Environment *scope) { js_Value v; + int i; if (scope) jsR_savescope(J, scope); + /* scripts take no arguments */ js_pop(J, n); + + for (i = 0; i < F->varlen; ++i) { + js_pushundefined(J); + js_initvar(J, F->vartab[i], -1); + js_pop(J, 1); + } + jsR_run(J, F); v = *stackidx(J, -1); TOP = --BOT; /* clear stack */ @@ -1286,6 +1290,8 @@ static void jsR_run(js_State *J, js_Function *F) js_Function **FT = F->funtab; double *NT = F->numtab; const char **ST = F->strtab; + const char **VT = F->vartab-1; + int lightweight = F->lightweight; js_Instruction *pcstart = F->code; js_Instruction *pc = F->code; enum js_OpCode opcode; @@ -1347,32 +1353,33 @@ static void jsR_run(js_State *J, js_Function *F) js_currentfunction(J); break; - case OP_INITLOCAL: - STACK[BOT + *pc++] = STACK[--TOP]; - break; - case OP_GETLOCAL: - CHECKSTACK(1); - STACK[TOP++] = STACK[BOT + *pc++]; + if (lightweight) { + CHECKSTACK(1); + STACK[TOP++] = STACK[BOT + *pc++]; + } else { + str = VT[*pc++]; + if (!js_hasvar(J, str)) + js_referenceerror(J, "'%s' is not defined", str); + } break; case OP_SETLOCAL: - STACK[BOT + *pc++] = STACK[TOP-1]; + if (lightweight) { + STACK[BOT + *pc++] = STACK[TOP-1]; + } else { + js_setvar(J, VT[*pc++]); + } 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; + if (lightweight) { + ++pc; + js_pushboolean(J, 0); + } else { + b = js_delvar(J, VT[*pc++]); + js_pushboolean(J, b); + } case OP_GETVAR: str = ST[*pc++]; diff --git a/opnames.h b/opnames.h index 68a44b0..56e270d 100644 --- a/opnames.h +++ b/opnames.h @@ -17,12 +17,9 @@ "false", "this", "current", -"initlocal", "getlocal", "setlocal", "dellocal", -"initvar", -"defvar", "hasvar", "getvar", "setvar",