ref: 68862386d80d02e24bbae650f11be0ec07e11195
parent: e39e0bdcd1bab3e9f07bab9b106fa7f9b97093e5
author: Tor Andersson <tor@ccxvii.net>
date: Mon Feb 10 13:00:13 EST 2014
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.
--- a/jscompile.c
+++ b/jscompile.c
@@ -102,6 +102,24 @@
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 @@
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 @@
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,7 +299,7 @@
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;
}
@@ -274,7 +306,7 @@
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 @@
{
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
}
}
+/* 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 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);
}
--- a/jscompile.h
+++ b/jscompile.h
@@ -34,10 +34,14 @@
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 @@
{
const char *name;
int script;
-
+ int lightweight;
+ int arguments;
int numparams;
- const char **params;
short *code;
int codecap, codelen;
@@ -130,6 +134,9 @@
const char **strtab;
int strcap, strlen;
+
+ const char **vartab;
+ int varcap, varlen;
const char *filename;
int line;
--- a/jsdump.c
+++ b/jsdump.c
@@ -716,11 +716,13 @@
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 @@
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 @@
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:
--- a/jsfunction.c
+++ b/jsfunction.c
@@ -63,7 +63,7 @@
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);
@@ -70,7 +70,7 @@
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);
--- a/jsgc.c
+++ b/jsgc.c
@@ -14,10 +14,10 @@
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);
}
--- a/jsrun.c
+++ b/jsrun.c
@@ -753,6 +753,31 @@
/* 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 @@
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 @@
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 @@
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;