mirror of
https://github.com/ccxvii/mujs.git
synced 2026-02-06 01:41:37 +08:00
Implement lightweight functions.
When a function does not use eval, arguments, with and has no inner functions it does not need a closure. We can call it without creating a variable object if we store local variables on the stack instead.
This commit is contained in:
141
jscompile.c
141
jscompile.c
@@ -102,6 +102,24 @@ static int addstring(JF, const char *value)
|
||||
return F->strlen++;
|
||||
}
|
||||
|
||||
static void addlocal(JF, const char *name)
|
||||
{
|
||||
if (F->varlen >= F->varcap) {
|
||||
F->varcap = F->varcap ? F->varcap * 2 : 16;
|
||||
F->vartab = realloc(F->vartab, F->varcap * sizeof *F->vartab);
|
||||
}
|
||||
F->vartab[F->varlen++] = name;
|
||||
}
|
||||
|
||||
static int findlocal(JF, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < F->varlen; ++i)
|
||||
if (!strcmp(F->vartab[i], name))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void emitfunction(JF, js_Function *fun)
|
||||
{
|
||||
emit(J, F, OP_CLOSURE);
|
||||
@@ -129,6 +147,20 @@ static void emitstring(JF, int opcode, const char *str)
|
||||
emitraw(J, F, addstring(J, F, str));
|
||||
}
|
||||
|
||||
static void emitlocal(JF, int oploc, int opvar, const char *name)
|
||||
{
|
||||
int i;
|
||||
if (F->lightweight) {
|
||||
i = findlocal(J, F, name);
|
||||
if (i >= 0) {
|
||||
emit(J, F, oploc);
|
||||
emitraw(J, F, i + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitstring(J, F, opvar, name);
|
||||
}
|
||||
|
||||
static int here(JF)
|
||||
{
|
||||
return F->codelen;
|
||||
@@ -241,7 +273,7 @@ static void cassign(JF, js_Ast *lhs, js_Ast *rhs)
|
||||
switch (lhs->type) {
|
||||
case EXP_IDENTIFIER:
|
||||
cexp(J, F, rhs);
|
||||
emitstring(J, F, OP_SETVAR, lhs->string);
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
|
||||
break;
|
||||
case EXP_INDEX:
|
||||
cexp(J, F, lhs->a);
|
||||
@@ -267,14 +299,14 @@ static void cassignforin(JF, js_Ast *stm)
|
||||
if (stm->type == STM_FOR_IN_VAR) {
|
||||
if (lhs->b)
|
||||
jsC_error(J, lhs->b, "more than one loop variable in for-in statement");
|
||||
emitstring(J, F, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */
|
||||
emit(J, F, OP_POP);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (lhs->type) {
|
||||
case EXP_IDENTIFIER:
|
||||
emitstring(J, F, OP_SETVAR, lhs->string);
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
|
||||
emit(J, F, OP_POP);
|
||||
break;
|
||||
case EXP_INDEX:
|
||||
@@ -300,7 +332,7 @@ static void cassignop1(JF, js_Ast *lhs, int dup)
|
||||
{
|
||||
switch (lhs->type) {
|
||||
case EXP_IDENTIFIER:
|
||||
emitstring(J, F, OP_GETVAR, lhs->string);
|
||||
emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, lhs->string);
|
||||
if (dup) emit(J, F, OP_DUP);
|
||||
break;
|
||||
case EXP_INDEX:
|
||||
@@ -326,7 +358,7 @@ static void cassignop2(JF, js_Ast *lhs)
|
||||
{
|
||||
switch (lhs->type) {
|
||||
case EXP_IDENTIFIER:
|
||||
emitstring(J, F, OP_SETVAR, lhs->string);
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
|
||||
break;
|
||||
case EXP_INDEX:
|
||||
break;
|
||||
@@ -351,7 +383,7 @@ static void cdelete(JF, js_Ast *exp)
|
||||
{
|
||||
switch (exp->type) {
|
||||
case EXP_IDENTIFIER:
|
||||
emitstring(J, F, OP_DELVAR, exp->string);
|
||||
emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, exp->string);
|
||||
break;
|
||||
case EXP_INDEX:
|
||||
cexp(J, F, exp->a);
|
||||
@@ -430,7 +462,7 @@ static void cexp(JF, js_Ast *exp)
|
||||
break;
|
||||
|
||||
case EXP_IDENTIFIER:
|
||||
emitstring(J, F, OP_GETVAR, exp->string);
|
||||
emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, exp->string);
|
||||
break;
|
||||
|
||||
case EXP_INDEX:
|
||||
@@ -818,7 +850,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_SETVAR, var->a->string);
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, var->a->string);
|
||||
emit(J, F, OP_POP);
|
||||
}
|
||||
list = list->b;
|
||||
@@ -1036,6 +1068,40 @@ static void cstmlist(JF, js_Ast *list)
|
||||
}
|
||||
}
|
||||
|
||||
/* Analyze */
|
||||
|
||||
static void analyze(JF, js_Ast *node)
|
||||
{
|
||||
if (isfun(node->type)) {
|
||||
F->lightweight = 0;
|
||||
return; /* don't scan inner functions */
|
||||
}
|
||||
|
||||
if (node->type == STM_WITH) {
|
||||
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: illegal use of 'eval'", J->filename, node->line);
|
||||
F->lightweight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->type == EXP_VAR)
|
||||
addlocal(J, F, node->a->string);
|
||||
|
||||
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)
|
||||
@@ -1048,62 +1114,71 @@ static int listlength(js_Ast *list)
|
||||
static void cparams(JF, js_Ast *list)
|
||||
{
|
||||
F->numparams = listlength(list);
|
||||
F->params = malloc(F->numparams * sizeof *F->params);
|
||||
int i = 0;
|
||||
while (list) {
|
||||
F->params[i++] = list->a->string;
|
||||
addlocal(J, F, list->a->string);
|
||||
list = list->b;
|
||||
}
|
||||
}
|
||||
|
||||
static void cvardecs(JF)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < F->varlen; ++i) {
|
||||
emit(J, F, OP_UNDEF);
|
||||
emitstring(J, F, OP_INITVAR, F->vartab[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cfundecs(JF, js_Ast *list)
|
||||
{
|
||||
while (list) {
|
||||
js_Ast *stm = list->a;
|
||||
if (stm->type == AST_FUNDEC) {
|
||||
addlocal(J, F, stm->a->string);
|
||||
emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c, 0));
|
||||
emitstring(J, F, OP_FUNDEC, stm->a->string);
|
||||
emitstring(J, F, OP_INITVAR, stm->a->string);
|
||||
}
|
||||
list = list->b;
|
||||
}
|
||||
}
|
||||
|
||||
static void cvardecs(JF, js_Ast *node)
|
||||
{
|
||||
if (node->type == EXP_VAR) {
|
||||
emitstring(J, F, OP_VARDEC, node->a->string);
|
||||
} else if (!isfun(node->type)) {
|
||||
if (node->a) cvardecs(J, F, node->a);
|
||||
if (node->b) cvardecs(J, F, node->b);
|
||||
if (node->c) cvardecs(J, F, node->c);
|
||||
if (node->d) cvardecs(J, F, node->d);
|
||||
}
|
||||
}
|
||||
|
||||
static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
|
||||
{
|
||||
F->lightweight = 1;
|
||||
F->arguments = 0;
|
||||
|
||||
cparams(J, F, params);
|
||||
|
||||
if (name) {
|
||||
F->name = name->string;
|
||||
emitfunction(J, F, F);
|
||||
emitstring(J, F, OP_FUNDEC, name->string);
|
||||
addlocal(J, F, name->string);
|
||||
} else {
|
||||
F->name = "";
|
||||
}
|
||||
|
||||
cparams(J, F, params);
|
||||
if (body)
|
||||
analyze(J, F, body);
|
||||
|
||||
if (F->script)
|
||||
emit(J, F, OP_UNDEF);
|
||||
|
||||
if (body) {
|
||||
if (!F->lightweight) {
|
||||
cvardecs(J, F);
|
||||
cfundecs(J, F, body);
|
||||
cvardecs(J, F, body);
|
||||
cstmlist(J, F, body);
|
||||
}
|
||||
|
||||
if (name) {
|
||||
if (F->lightweight)
|
||||
emit(J, F, OP_CURRENT);
|
||||
else
|
||||
emitfunction(J, F, F);
|
||||
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, name->string);
|
||||
emit(J, F, OP_POP);
|
||||
}
|
||||
|
||||
if (F->script) {
|
||||
emit(J, F, OP_UNDEF);
|
||||
cstmlist(J, F, body);
|
||||
emit(J, F, OP_RETURN);
|
||||
} else {
|
||||
cstmlist(J, F, body);
|
||||
emit(J, F, OP_UNDEF);
|
||||
emit(J, F, OP_RETURN);
|
||||
}
|
||||
|
||||
15
jscompile.h
15
jscompile.h
@@ -34,10 +34,14 @@ enum js_OpCode
|
||||
OP_FALSE,
|
||||
OP_THIS,
|
||||
OP_GLOBAL,
|
||||
OP_CURRENT, /* currently executing function object */
|
||||
|
||||
OP_FUNDEC, /* <closure> -S- */
|
||||
OP_VARDEC, /* -S- */
|
||||
OP_INITLOCAL, /* <value> -N- */
|
||||
OP_GETLOCAL, /* -N- <value> */
|
||||
OP_SETLOCAL, /* <value> -N- <value> */
|
||||
OP_DELLOCAL, /* -N- false */
|
||||
|
||||
OP_INITVAR, /* <value> -S- */
|
||||
OP_GETVAR, /* -S- <value> */
|
||||
OP_SETVAR, /* <value> -S- <value> */
|
||||
OP_DELVAR, /* -S- <success> */
|
||||
@@ -115,9 +119,9 @@ struct js_Function
|
||||
{
|
||||
const char *name;
|
||||
int script;
|
||||
|
||||
int lightweight;
|
||||
int arguments;
|
||||
int numparams;
|
||||
const char **params;
|
||||
|
||||
short *code;
|
||||
int codecap, codelen;
|
||||
@@ -131,6 +135,9 @@ struct js_Function
|
||||
const char **strtab;
|
||||
int strcap, strlen;
|
||||
|
||||
const char **vartab;
|
||||
int varcap, varlen;
|
||||
|
||||
const char *filename;
|
||||
int line;
|
||||
|
||||
|
||||
17
jsdump.c
17
jsdump.c
@@ -716,11 +716,13 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
|
||||
short *end = F->code + F->codelen;
|
||||
int i;
|
||||
|
||||
printf("function %p %s (", F, F->name);
|
||||
for (i = 0; i < F->numparams; ++i)
|
||||
printf("%s%s", i > 0 ? ", " : "", F->params[i]);
|
||||
printf(")\n");
|
||||
printf("function %p %s\n", F, F->name);
|
||||
printf("\tsource %s:%d\n", F->filename, F->line);
|
||||
printf("\tlightweight:%d\n", F->lightweight);
|
||||
printf("\tparameters:%d\n", F->numparams);
|
||||
printf("\targuments:%d\n", F->arguments);
|
||||
for (i = 0; i < F->varlen; ++i)
|
||||
printf("\tlocal %d %s\n", i + 1, F->vartab[i]);
|
||||
for (i = 0; i < F->funlen; ++i)
|
||||
printf("\tfunction %p %s\n", F->funtab[i], F->funtab[i]->name);
|
||||
for (i = 0; i < F->strlen; ++i) {
|
||||
@@ -753,8 +755,7 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
|
||||
p += 2;
|
||||
break;
|
||||
|
||||
case OP_FUNDEC:
|
||||
case OP_VARDEC:
|
||||
case OP_INITVAR:
|
||||
case OP_GETVAR:
|
||||
case OP_SETVAR:
|
||||
case OP_DELVAR:
|
||||
@@ -767,6 +768,10 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
|
||||
ps(F->strtab[*p++]);
|
||||
break;
|
||||
|
||||
case OP_INITLOCAL:
|
||||
case OP_GETLOCAL:
|
||||
case OP_SETLOCAL:
|
||||
case OP_DELLOCAL:
|
||||
case OP_NUMBER_N:
|
||||
case OP_INITPROP_N:
|
||||
case OP_CALL:
|
||||
|
||||
@@ -63,14 +63,14 @@ static int Fp_toString(js_State *J, int argc)
|
||||
n = strlen("function () { ... }");
|
||||
n += strlen(F->name);
|
||||
for (i = 0; i < F->numparams; ++i)
|
||||
n += strlen(F->params[i]) + 1;
|
||||
n += strlen(F->vartab[i]) + 1;
|
||||
s = malloc(n);
|
||||
strcpy(s, "function ");
|
||||
strcat(s, F->name);
|
||||
strcat(s, "(");
|
||||
for (i = 0; i < F->numparams; ++i) {
|
||||
if (i > 0) strcat(s, ",");
|
||||
strcat(s, F->params[i]);
|
||||
strcat(s, F->vartab[i]);
|
||||
}
|
||||
strcat(s, ") { ... }");
|
||||
js_pushstring(J, s);
|
||||
|
||||
2
jsgc.c
2
jsgc.c
@@ -14,10 +14,10 @@ static void jsG_freeenvironment(js_State *J, js_Environment *env)
|
||||
|
||||
static void jsG_freefunction(js_State *J, js_Function *fun)
|
||||
{
|
||||
free(fun->params);
|
||||
free(fun->funtab);
|
||||
free(fun->numtab);
|
||||
free(fun->strtab);
|
||||
free(fun->vartab);
|
||||
free(fun->code);
|
||||
free(fun);
|
||||
}
|
||||
|
||||
61
jsrun.c
61
jsrun.c
@@ -753,6 +753,31 @@ static int js_delvar(js_State *J, const char *name)
|
||||
|
||||
/* Function calls */
|
||||
|
||||
static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
|
||||
{
|
||||
js_Environment *saveE;
|
||||
js_Value v;
|
||||
int i;
|
||||
|
||||
saveE = J->E;
|
||||
|
||||
J->E = scope;
|
||||
|
||||
if (n > F->numparams) {
|
||||
js_pop(J, F->numparams - n);
|
||||
n = F->numparams;
|
||||
}
|
||||
for (i = n; i < F->varlen; ++i)
|
||||
js_pushundefined(J);
|
||||
|
||||
jsR_run(J, F);
|
||||
v = js_tovalue(J, -1);
|
||||
TOP = --BOT; /* clear stack */
|
||||
js_pushvalue(J, v);
|
||||
|
||||
J->E = saveE;
|
||||
}
|
||||
|
||||
static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
|
||||
{
|
||||
js_Environment *saveE;
|
||||
@@ -764,10 +789,10 @@ static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment
|
||||
J->E = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
|
||||
for (i = 0; i < F->numparams; ++i) {
|
||||
if (i < n)
|
||||
js_decvar(J, F->params[i], i + 1);
|
||||
js_decvar(J, F->vartab[i], i + 1);
|
||||
else {
|
||||
js_pushundefined(J);
|
||||
js_decvar(J, F->params[i], -1);
|
||||
js_decvar(J, F->vartab[i], -1);
|
||||
js_pop(J, 1);
|
||||
}
|
||||
}
|
||||
@@ -822,9 +847,12 @@ void js_call(js_State *J, int n)
|
||||
savebot = BOT;
|
||||
BOT = TOP - n - 1;
|
||||
|
||||
if (obj->type == JS_CFUNCTION)
|
||||
jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
|
||||
else if (obj->type == JS_CSCRIPT)
|
||||
if (obj->type == JS_CFUNCTION) {
|
||||
if (obj->u.f.function->lightweight)
|
||||
jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope);
|
||||
else
|
||||
jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
|
||||
} else if (obj->type == JS_CSCRIPT)
|
||||
jsR_callscript(J, n, obj->u.f.function);
|
||||
else if (obj->type == JS_CCFUNCTION)
|
||||
jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function);
|
||||
@@ -992,14 +1020,27 @@ static void jsR_run(js_State *J, js_Function *F)
|
||||
|
||||
case OP_THIS: js_copy(J, 0); break;
|
||||
case OP_GLOBAL: js_pushobject(J, J->G); break;
|
||||
case OP_CURRENT: js_currentfunction(J); break;
|
||||
|
||||
case OP_FUNDEC:
|
||||
js_decvar(J, ST[*pc++], -1);
|
||||
js_pop(J, 1);
|
||||
case OP_INITLOCAL:
|
||||
STACK[BOT + *pc++] = STACK[--TOP];
|
||||
break;
|
||||
|
||||
case OP_VARDEC:
|
||||
js_pushundefined(J);
|
||||
case OP_GETLOCAL:
|
||||
CHECKSTACK(1);
|
||||
STACK[TOP++] = STACK[BOT + *pc++];
|
||||
break;
|
||||
|
||||
case OP_SETLOCAL:
|
||||
STACK[BOT + *pc++] = STACK[TOP-1];
|
||||
break;
|
||||
|
||||
case OP_DELLOCAL:
|
||||
++pc;
|
||||
js_pushboolean(J, 0);
|
||||
break;
|
||||
|
||||
case OP_INITVAR:
|
||||
js_decvar(J, ST[*pc++], -1);
|
||||
js_pop(J, 1);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user