shithub: libmujs

Download patch

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",