Bug 791465 - Fix processDirectives' ASI emulation. code=André Bargull r=benjamin
authorBenjamin Peterson <benjamin@python.org>
Sat, 13 Oct 2012 09:23:53 -0400
changeset 110275 31b3592edb37a1d234302dc95689101d3c32731e
parent 110274 ab275d939bf2e86e73a76ac30dad5daae85c7e0d
child 110276 97a10c2ac350a9981d88634fb53aab7845f98d36
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbenjamin
bugs791465
milestone19.0a1
Bug 791465 - Fix processDirectives' ASI emulation. code=André Bargull r=benjamin
js/src/frontend/Parser.cpp
js/src/frontend/TokenStream.h
js/src/jit-test/tests/basic/bug791465.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1852,17 +1852,23 @@ Parser::processDirectives(ParseNode *stm
     for (TokenKind tt = tokenStream.getToken(TSF_OPERAND); tt == TOK_STRING; tt = tokenStream.getToken(TSF_OPERAND)) {
         ParseNode *stringNode = atomNode(PNK_STRING, JSOP_STRING);
         if (!stringNode)
             return false;
         const Token directive = tokenStream.currentToken();
         bool isDirective = IsEscapeFreeStringLiteral(directive);
         JSAtom *atom = directive.atom();
         TokenKind next = tokenStream.peekTokenSameLine();
-        if (next != TOK_EOF && next != TOK_EOL && next != TOK_SEMI && next != TOK_RC) {
+
+        // We need to check whether the directive ends explicitly or implicitly
+        // due to ASI. In the latter case, the expression must not continue on
+        // the next line.
+        if (next != TOK_EOF && next != TOK_SEMI && next != TOK_RC &&
+           (next != TOK_EOL || TokenContinuesStringExpression(tokenStream.peekToken())))
+        {
             freeTree(stringNode);
             if (next == TOK_ERROR)
                 return false;
             break;
         }
         tokenStream.matchToken(TOK_SEMI);
         if (isDirective) {
             // It's a directive. Is it one we know?
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -177,16 +177,51 @@ TokenKindIsShift(TokenKind tt)
 
 inline bool
 TokenKindIsAssignment(TokenKind tt)
 {
     return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
 }
 
 inline bool
+TokenContinuesStringExpression(TokenKind tt)
+{
+    switch (tt) {
+      // comma expression
+      case TOK_COMMA:
+      // conditional expression
+      case TOK_HOOK:
+      // binary expression
+      case TOK_OR:
+      case TOK_AND:
+      case TOK_BITOR:
+      case TOK_BITXOR:
+      case TOK_BITAND:
+      case TOK_PLUS:
+      case TOK_MINUS:
+      case TOK_STAR:
+      case TOK_DIV:
+      case TOK_MOD:
+      case TOK_IN:
+      case TOK_INSTANCEOF:
+      // member expression
+      case TOK_DOT:
+      case TOK_LB:
+      case TOK_LP:
+      case TOK_DBLDOT:
+        return true;
+      default:
+        return TokenKindIsEquality(tt) ||
+               TokenKindIsRelational(tt) ||
+               TokenKindIsShift(tt) ||
+               TokenKindIsAssignment(tt);
+    }
+}
+
+inline bool
 TokenKindIsDecl(TokenKind tt)
 {
 #if JS_HAS_BLOCK_SCOPE
     return tt == TOK_VAR || tt == TOK_LET;
 #else
     return tt == TOK_VAR;
 #endif
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug791465.js
@@ -0,0 +1,83 @@
+load(libdir + "asserts.js");
+
+function xmlAllowed() {
+  return (typeof options == "function") &&
+         (options() + "").contains("allow_xml");
+}
+
+
+var valid_strict_funs = [
+  // directive ends on next line
+  function () {
+    "use strict"
+    ;
+  },
+  function () {
+    "use strict"
+  },
+  // directive ends on same line
+  function () { "use strict"; },
+  function () { "use strict" },
+];
+
+for (var f of valid_strict_funs) {
+  assertThrowsInstanceOf(function() { f.caller }, TypeError);
+}
+
+
+var binary_ops = [
+  "||", "&&",
+  "|", "^", "&",
+  "==", "!=", "===", "!==",
+  "<", "<=", ">", ">=", "in", "instanceof",
+  "<<", ">>", ">>>",
+  "+", "-",
+  "*", "/", "%",
+];
+
+var invalid_strict_funs = [
+  function () {
+    "use strict"
+    , "not";
+  },
+  function () {
+    "use strict"
+    ? 1 : 0;
+  },
+  function () {
+    "use strict"
+    .length;
+  },
+  function () {
+    "use strict"
+    [0];
+  },
+  function () {
+    "use strict"
+    ();
+  },
+  ...(xmlAllowed() ? [Function("'use strict'\n..not")] : []),
+  ...[Function("'use strict'\n " + op + " 'not'") for (op of binary_ops)],
+];
+
+for (var f of invalid_strict_funs) {
+  f.caller;
+}
+
+
+var assignment_ops = [
+   "=", "+=", "-=",
+  "|=", "^=", "&=",
+  "<<=", ">>=", ">>>=",
+  "*=", "/=", "%=",
+];
+
+var invalid_strict_funs_referror = [
+  ...[("'use strict'\n " + op + " 'not'") for (op of assignment_ops)],
+];
+
+// assignment with string literal as LHS is an early error, therefore we
+// can only test for ReferenceError
+for (var f of invalid_strict_funs_referror) {
+  assertThrowsInstanceOf(function() { Function(f) }, ReferenceError);
+}