shithub: libmujs

Download patch

ref: 8a26e2094385afe25b97b27b5da368e6f4432669
parent: 75cab70afd12dfe404f863448b5726c62c15f3f3
author: Tor Andersson <[email protected]>
date: Mon Apr 17 11:00:52 EDT 2017

Compile functions with per-function strictness.

Recognize 'use strict' pragma.
Move strictness checks into compiler from parser.

--- a/jscompile.c
+++ b/jscompile.c
@@ -1,4 +1,5 @@
 #include "jsi.h"
+#include "jslex.h"
 #include "jsparse.h"
 #include "jscompile.h"
 #include "jsvalue.h" /* for jsV_numbertostring */
@@ -31,8 +32,27 @@
 	js_throw(J);
 }
 
-static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body, int script)
+static const char *futurewords[] = {
+	"class", "const", "enum", "export", "extends", "import", "super",
+};
+
+static const char *strictfuturewords[] = {
+	"implements", "interface", "let", "package", "private", "protected",
+	"public", "static", "yield",
+};
+
+static void checkfutureword(JF, js_Ast *exp)
 {
+	if (jsY_findword(exp->string, futurewords, nelem(futurewords)) >= 0)
+		jsC_error(J, exp, "'%s' is a future reserved word", exp->string);
+	if (F->strict) {
+		if (jsY_findword(exp->string, strictfuturewords, nelem(strictfuturewords)) >= 0)
+			jsC_error(J, exp, "'%s' is a strict mode future reserved word", exp->string);
+	}
+}
+
+static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body, int script, int default_strict)
+{
 	js_Function *F = js_malloc(J, sizeof *F);
 	memset(F, 0, sizeof *F);
 	F->gcmark = 0;
@@ -43,6 +63,7 @@
 	F->filename = js_intern(J, J->filename);
 	F->line = name ? name->line : params ? params->line : body ? body->line : 1;
 	F->script = script;
+	F->strict = default_strict;
 	F->name = name ? name->string : "";
 
 	cfunbody(J, F, name, params, body);
@@ -118,19 +139,19 @@
 static void addlocal(JF, js_Ast *ident, int reuse)
 {
 	const char *name = ident->string;
-	if (J->strict) {
+	if (F->strict) {
 		if (!strcmp(name, "arguments"))
 			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");
 	}
-	if (reuse || J->strict) {
+	if (reuse || F->strict) {
 		int i;
 		for (i = 0; i < F->varlen; ++i) {
 			if (!strcmp(F->vartab[i], name)) {
 				if (reuse)
 					return;
-				if (J->strict)
+				if (F->strict)
 					jsC_error(J, ident, "duplicate formal parameter '%s'", name);
 			}
 		}
@@ -186,7 +207,8 @@
 static void emitlocal(JF, int oploc, int opvar, js_Ast *ident)
 {
 	int i;
-	if (J->strict && oploc == OP_SETLOCAL) {
+	checkfutureword(J, F, ident);
+	if (F->strict && oploc == OP_SETLOCAL) {
 		if (!strcmp(ident->string, "arguments"))
 			jsC_error(J, ident, "'arguments' is read-only in strict mode");
 		if (!strcmp(ident->string, "eval"))
@@ -314,7 +336,7 @@
 		else
 			jsC_error(J, prop, "invalid property name in object initializer");
 
-		if (J->strict)
+		if (F->strict)
 			checkdup(J, F, head, kv);
 
 		switch (kv->type) {
@@ -324,11 +346,11 @@
 			emit(J, F, OP_INITPROP);
 			break;
 		case EXP_PROP_GET:
-			emitfunction(J, F, newfun(J, NULL, kv->b, kv->c, 0));
+			emitfunction(J, F, newfun(J, NULL, NULL, kv->c, 0, F->strict));
 			emit(J, F, OP_INITGETTER);
 			break;
 		case EXP_PROP_SET:
-			emitfunction(J, F, newfun(J, NULL, kv->b, kv->c, 0));
+			emitfunction(J, F, newfun(J, NULL, kv->b, kv->c, 0, F->strict));
 			emit(J, F, OP_INITSETTER);
 			break;
 		}
@@ -464,7 +486,7 @@
 {
 	switch (exp->type) {
 	case EXP_IDENTIFIER:
-		if (J->strict)
+		if (F->strict)
 			jsC_error(J, exp, "delete on an unqualified name is not allowed in strict mode");
 		emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, exp);
 		break;
@@ -556,7 +578,7 @@
 		break;
 
 	case EXP_FUN:
-		emitfunction(J, F, newfun(J, exp->a, exp->b, exp->c, 0));
+		emitfunction(J, F, newfun(J, exp->a, exp->b, exp->c, 0, F->strict));
 		break;
 
 	case EXP_IDENTIFIER:
@@ -866,7 +888,8 @@
 	L1 = emitjump(J, F, OP_TRY);
 	{
 		/* if we get here, we have caught an exception in the try block */
-		if (J->strict) {
+		checkfutureword(J, F, catchvar);
+		if (F->strict) {
 			if (!strcmp(catchvar->string, "arguments"))
 				jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
 			if (!strcmp(catchvar->string, "eval"))
@@ -896,7 +919,8 @@
 			emit(J, F, OP_THROW); /* rethrow exception */
 		}
 		label(J, F, L2);
-		if (J->strict) {
+		if (F->strict) {
+			checkfutureword(J, F, catchvar);
 			if (!strcmp(catchvar->string, "arguments"))
 				jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
 			if (!strcmp(catchvar->string, "eval"))
@@ -1102,6 +1126,7 @@
 
 	case STM_BREAK:
 		if (stm->a) {
+			checkfutureword(J, F, stm->a);
 			target = breaktarget(J, F, stm->parent, stm->a->string);
 			if (!target)
 				jsC_error(J, stm, "break label '%s' not found", stm->a->string);
@@ -1116,6 +1141,7 @@
 
 	case STM_CONTINUE:
 		if (stm->a) {
+			checkfutureword(J, F, stm->a);
 			target = continuetarget(J, F, stm->parent, stm->a->string);
 			if (!target)
 				jsC_error(J, stm, "continue label '%s' not found", stm->a->string);
@@ -1146,6 +1172,8 @@
 		break;
 
 	case STM_WITH:
+		if (F->strict)
+			jsC_error(J, stm->a, "'with' statements are not allowed in strict mode");
 		cexp(J, F, stm->a);
 		emit(J, F, OP_WITH);
 		cstm(J, F, stm->b);
@@ -1235,6 +1263,7 @@
 {
 	F->numparams = listlength(list);
 	while (list) {
+		checkfutureword(J, F, list->a);
 		addlocal(J, F, list->a, 0);
 		list = list->b;
 	}
@@ -1246,6 +1275,7 @@
 		return; /* stop at inner functions */
 
 	if (node->type == EXP_VAR) {
+		checkfutureword(J, F, node->a);
 		if (F->lightweight)
 			addlocal(J, F, node->a, 1);
 		else
@@ -1263,7 +1293,7 @@
 	while (list) {
 		js_Ast *stm = list->a;
 		if (stm->type == AST_FUNDEC) {
-			emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c, 0));
+			emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c, 0, F->strict));
 			emitstring(J, F, OP_INITVAR, stm->a->string);
 		}
 		list = list->b;
@@ -1281,9 +1311,15 @@
 	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"))
+			F->strict = 1;
+
 	cparams(J, F, params);
 
 	if (name) {
+		checkfutureword(J, F, name);
 		emit(J, F, OP_CURRENT);
 		if (F->lightweight) {
 			addlocal(J, F, name, 0);
@@ -1312,10 +1348,10 @@
 
 js_Function *jsC_compilefunction(js_State *J, js_Ast *prog)
 {
-	return newfun(J, prog->a, prog->b, prog->c, 0);
+	return newfun(J, prog->a, prog->b, prog->c, 0, J->default_strict);
 }
 
 js_Function *jsC_compile(js_State *J, js_Ast *prog)
 {
-	return newfun(J, NULL, NULL, prog, 1);
+	return newfun(J, NULL, NULL, prog, 1, J->default_strict);
 }
--- a/jscompile.h
+++ b/jscompile.h
@@ -121,6 +121,7 @@
 	const char *name;
 	int script;
 	int lightweight;
+	int strict;
 	int arguments;
 	int numparams;
 
--- a/jsi.h
+++ b/jsi.h
@@ -150,6 +150,7 @@
 
 	js_StringNode *strings;
 
+	int default_strict;
 	int strict;
 
 	/* parser input source */
--- a/jsparse.c
+++ b/jsparse.c
@@ -149,28 +149,10 @@
 
 /* Literals */
 
-static const char *futurewords[] = {
-	"class", "const", "enum", "export", "extends", "import", "super",
-};
-
-static const char *strictfuturewords[] = {
-	"implements", "interface", "let", "package", "private", "protected",
-	"public", "static", "yield",
-};
-
-static void checkfutureword(js_State *J, const char *s)
-{
-	if (jsY_findword(s, futurewords, nelem(futurewords)) >= 0)
-		jsP_error(J, "'%s' is a future reserved word", s);
-	if (J->strict && jsY_findword(s, strictfuturewords, nelem(strictfuturewords)) >= 0)
-		jsP_error(J, "'%s' is a strict mode future reserved word", s);
-}
-
 static js_Ast *identifier(js_State *J)
 {
 	js_Ast *a;
 	if (J->lookahead == TK_IDENTIFIER) {
-		checkfutureword(J, J->text);
 		a = jsP_newstrnode(J, AST_IDENTIFIER, J->text);
 		jsP_next(J);
 		return a;
@@ -328,7 +310,6 @@
 	js_Ast *a;
 
 	if (J->lookahead == TK_IDENTIFIER) {
-		checkfutureword(J, J->text);
 		a = jsP_newstrnode(J, EXP_IDENTIFIER, J->text);
 		jsP_next(J);
 		return a;
@@ -760,8 +741,6 @@
 	}
 
 	if (jsP_accept(J, TK_WITH)) {
-		if (J->strict)
-			jsP_error(J, "'with' statements are not allowed in strict mode");
 		jsP_expect(J, '(');
 		a = expression(J, 0);
 		jsP_expect(J, ')');
--- a/jsstate.c
+++ b/jsstate.c
@@ -188,7 +188,7 @@
 	J->alloc = alloc;
 
 	if (flags & JS_STRICT)
-		J->strict = 1;
+		J->strict = J->default_strict = 1;
 
 	J->trace[0].name = "-top-";
 	J->trace[0].file = "native";