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