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.
This commit is contained in:
Tor Andersson
2018-11-05 14:31:12 +01:00
parent f5de9d4d2e
commit bb65f18fcc
6 changed files with 95 additions and 130 deletions

View File

@@ -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);

View File

@@ -27,13 +27,10 @@ enum js_OpCode
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> */

View File

@@ -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:

View File

@@ -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, ']');
}

73
jsrun.c
View File

@@ -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++];

View File

@@ -17,12 +17,9 @@
"false",
"this",
"current",
"initlocal",
"getlocal",
"setlocal",
"dellocal",
"initvar",
"defvar",
"hasvar",
"getvar",
"setvar",