ref: bb65f18fcc76fda0870e0c45c4c0ade770979182
parent: f5de9d4d2e3f76cc6b92f71c6f468a2a95d74bfb
author: Tor Andersson <tor.andersson@artifex.com>
date: Mon Nov 5 09:31:12 EST 2018
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.
--- a/jscompile.c
+++ b/jscompile.c
@@ -138,7 +138,7 @@
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,6 +146,9 @@
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;
@@ -152,7 +155,7 @@
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 @@
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 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 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 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 @@
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 @@
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 @@
}
}
-/* 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 @@
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 @@
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 @@
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,8 +1398,6 @@
static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
{
- int shadow;
-
F->lightweight = 1;
F->arguments = 0;
@@ -1436,9 +1404,6 @@
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,23 +1411,21 @@
F->lastline = F->line;
- shadow = cparams(J, F, params, name);
+ 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);
- }
- }
-
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) {
--- a/jscompile.h
+++ b/jscompile.h
@@ -27,13 +27,10 @@
OP_THIS,
OP_CURRENT, /* currently executing function object */
- OP_INITLOCAL, /* <value> -K- */
OP_GETLOCAL, /* -K- <value> */
OP_SETLOCAL, /* <value> -K- <value> */
OP_DELLOCAL, /* -K- false */
- OP_INITVAR, /* <value> -S- */
- OP_DEFVAR, /* -S- */
OP_HASVAR, /* -S- ( <value> | undefined ) */
OP_GETVAR, /* -S- <value> */
OP_SETVAR, /* <value> -S- <value> */
--- a/jsdump.c
+++ b/jsdump.c
@@ -814,8 +814,6 @@
p += 2;
break;
- case OP_INITVAR:
- case OP_DEFVAR:
case OP_GETVAR:
case OP_HASVAR:
case OP_SETVAR:
@@ -828,11 +826,13 @@
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:
--- a/jsrepr.c
+++ b/jsrepr.c
@@ -116,9 +116,10 @@
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, ']');
}
--- a/jsrun.c
+++ b/jsrun.c
@@ -857,11 +857,6 @@
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 @@
js_pop(J, n - F->numparams);
n = F->numparams;
}
+
for (i = n; i < F->varlen; ++i)
js_pushundefined(J);
@@ -990,17 +986,16 @@
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_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 @@
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 @@
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++];
--- a/opnames.h
+++ b/opnames.h
@@ -17,12 +17,9 @@
"false",
"this",
"current",
-"initlocal",
"getlocal",
"setlocal",
"dellocal",
-"initvar",
-"defvar",
"hasvar",
"getvar",
"setvar",