Bug 1046964 - Warn when encountering a string expression statement, that would be a directive but isn't in the right position, when respecting the non-directive would change semantics. r=njn
authorJeff Walden <jwalden@mit.edu>
Wed, 06 Aug 2014 16:37:48 -0700
changeset 198712 cb3cfc3ef3f3e0eb1478f4f85fba4af0bc38e54d
parent 198711 4efe4f88a92f8878ffecf8ee2eca5a6cbc299dfe
child 198713 364dae502747d4527abafba77808faf9d54d7ce7
push id27284
push userryanvm@gmail.com
push dateSat, 09 Aug 2014 15:25:31 +0000
treeherdermozilla-central@ad8cb646fad6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1046964
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1046964 - Warn when encountering a string expression statement, that would be a directive but isn't in the right position, when respecting the non-directive would change semantics. r=njn
js/src/frontend/BytecodeEmitter.cpp
js/src/js.msg
js/src/tests/ecma_5/extensions/misplaced-inconsistent-directive.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5544,22 +5544,48 @@ EmitStatement(ExclusiveContext *cx, Byte
             return false;
         }
         if (op != JSOP_NOP) {
             if (!EmitTree(cx, bce, pn2))
                 return false;
             if (Emit1(cx, bce, op) < 0)
                 return false;
         }
-    } else if (!pn->isDirectivePrologueMember()) {
-        /* Don't complain about directive prologue members; just don't emit their code. */
-        bce->current->currentLine = bce->parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
-        bce->current->lastColumn = 0;
-        if (!bce->reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
-            return false;
+    } else if (pn->isDirectivePrologueMember()) {
+        // Don't complain about directive prologue members; just don't emit
+        // their code.
+    } else {
+        if (JSAtom *atom = pn->isStringExprStatement()) {
+            // Warn if encountering a non-directive prologue member string
+            // expression statement, that is inconsistent with the current
+            // directive prologue.  That is, a script *not* starting with
+            // "use strict" should warn for any "use strict" statements seen
+            // later in the script, because such statements are misleading.
+            const char *directive = nullptr;
+            if (atom == cx->names().useStrict) {
+                if (!bce->sc->strict)
+                    directive = js_useStrict_str;
+            } else if (atom == cx->names().useAsm) {
+                if (bce->sc->isFunctionBox()) {
+                    JSFunction *fun = bce->sc->asFunctionBox()->function();
+                    if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+                        directive = js_useAsm_str;
+                }
+            }
+
+            if (directive) {
+                if (!bce->reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
+                    return false;
+            }
+        } else {
+            bce->current->currentLine = bce->parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
+            bce->current->lastColumn = 0;
+            if (!bce->reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
+                return false;
+        }
     }
 
     return true;
 }
 
 static bool
 EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -269,17 +269,17 @@ MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,   21
 MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE,    216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
 MSG_DEF(JSMSG_LET_COMP_BINDING,       217, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,     218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
 MSG_DEF(JSMSG_BAD_SYMBOL,             219, 1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
 MSG_DEF(JSMSG_BAD_DELETE_OPERAND,     220, 0, JSEXN_REFERENCEERR, "invalid delete operand")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,      221, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,        222, 2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK,  223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
-MSG_DEF(JSMSG_UNUSED224,              224, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE,  224, 1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position")
 MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS,   225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
 MSG_DEF(JSMSG_EVAL_ARITY,             226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,        227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
 MSG_DEF(JSMSG_JSON_BAD_PARSE,         228, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
 MSG_DEF(JSMSG_JSON_BAD_STRINGIFY,     229, 0, JSEXN_ERR, "JSON.stringify")
 MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 230, 0, JSEXN_TYPEERR, "value is not a function or undefined")
 MSG_DEF(JSMSG_NOT_NONNULL_OBJECT,     231, 0, JSEXN_TYPEERR, "value is not a non-null object")
 MSG_DEF(JSMSG_DEPRECATED_OCTAL,       232, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated")
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/misplaced-inconsistent-directive.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs evaluate()
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1046964;
+var summary =
+  "Misplaced directives (e.g. 'use strict') should trigger warnings if they " +
+  "contradict the actually-used semantics";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+options("strict");
+options("werror");
+
+function evaluateNoRval(code)
+{
+  evaluate(code, { compileAndGo: true, noScriptRval: true });
+}
+
+function expectSyntaxError(code)
+{
+  try
+  {
+    evaluateNoRval(code);
+    throw new Error("didn't throw");
+  }
+  catch (e)
+  {
+    assertEq(e instanceof SyntaxError, true,
+             "should have thrown a SyntaxError, instead got:\n" +
+             "    " + e + "\n" +
+             "when evaluating:\n" +
+             "    " + code);
+  }
+}
+
+expectSyntaxError("function f1() {} 'use strict'; function f2() {}");
+expectSyntaxError("function f3() { var x; 'use strict'; }");
+
+expectSyntaxError("function f4() {} 'use asm'; function f5() {}");
+expectSyntaxError("function f6() { var x; 'use strict'; }");
+expectSyntaxError("'use asm'; function f7() {}");
+
+// No errors expected -- useless non-directives, but not contrary to used
+// semantics.
+evaluateNoRval("'use strict'; function f8() {} 'use strict'; function f9() {}");
+evaluateNoRval("'use strict'; function f10() { var z; 'use strict' }");
+
+evaluateNoRval("function f11() { 'use asm'; return {}; }");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");