mirror of
https://github.com/ccxvii/mujs.git
synced 2026-02-05 17:29:43 +08:00
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:
131
jscompile.c
131
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);
|
||||
|
||||
@@ -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> */
|
||||
|
||||
8
jsdump.c
8
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:
|
||||
|
||||
7
jsrepr.c
7
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, ']');
|
||||
}
|
||||
|
||||
73
jsrun.c
73
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++];
|
||||
|
||||
Reference in New Issue
Block a user