summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-06-08 13:02:16 -0400
committerGaming4JC <g4jc@hyperbola.info>2019-07-18 22:38:09 -0400
commit5e76f72c539cd0e2fc0d38e4475f494868b5e859 (patch)
tree78046947a2841bd399c60ab283e1e80003ab76b0 /js
parentbf2610c0cfc96327178a5d3d7121181c2f375f48 (diff)
downloadUXP-5e76f72c539cd0e2fc0d38e4475f494868b5e859.tar
UXP-5e76f72c539cd0e2fc0d38e4475f494868b5e859.tar.gz
UXP-5e76f72c539cd0e2fc0d38e4475f494868b5e859.tar.lz
UXP-5e76f72c539cd0e2fc0d38e4475f494868b5e859.tar.xz
UXP-5e76f72c539cd0e2fc0d38e4475f494868b5e859.zip
1315815 - Don't treat async or await as a keyword when they contain escapes.
Diffstat (limited to 'js')
-rw-r--r--js/src/frontend/FullParseHandler.h20
-rw-r--r--js/src/frontend/Parser.cpp95
-rw-r--r--js/src/frontend/SyntaxParseHandler.h29
-rw-r--r--js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js54
4 files changed, 134 insertions, 64 deletions
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index b619cf24c..3938d9743 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -10,6 +10,8 @@
#include "mozilla/Attributes.h"
#include "mozilla/PodOperations.h"
+#include <string.h>
+
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
@@ -870,29 +872,25 @@ class FullParseHandler
return node->isKind(PNK_NAME);
}
- bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
-
- return node->pn_atom == cx->names().eval;
+ bool isEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
+ return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval;
}
const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) {
MOZ_ASSERT(isNameAnyParentheses(node),
"must only call this function on known names");
- if (nameIsEvalAnyParentheses(node, cx))
+ if (isEvalAnyParentheses(node, cx))
return js_eval_str;
if (node->pn_atom == cx->names().arguments)
return js_arguments_str;
return nullptr;
}
- bool nameIsUnparenthesizedAsync(ParseNode* node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
-
- return node->pn_atom == cx->names().async;
+ bool isAsyncKeyword(ParseNode* node, ExclusiveContext* cx) {
+ return node->isKind(PNK_NAME) &&
+ node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
+ node->pn_atom == cx->names().async;
}
bool isCall(ParseNode* pn) {
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 3b7a0e612..7d923420a 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5147,7 +5147,10 @@ Parser<FullParseHandler>::exportDeclaration()
return null();
break;
default: {
- if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ if (tt == TOK_NAME &&
+ tokenStream.currentName() == context->names().async &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
@@ -7045,21 +7048,23 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
if (!tokenStream.peekToken(&next))
return null();
- if (!tokenStream.currentToken().nameContainsEscape() &&
- tokenStream.currentName() == context->names().let &&
- nextTokenContinuesLetDeclaration(next, yieldHandling))
- {
- return lexicalDeclaration(yieldHandling, /* isConst = */ false);
- }
+ if (!tokenStream.currentToken().nameContainsEscape()) {
+ if (tokenStream.currentName() == context->names().let &&
+ nextTokenContinuesLetDeclaration(next, yieldHandling))
+ {
+ return lexicalDeclaration(yieldHandling, /* isConst = */ false);
+ }
- if (tokenStream.currentName() == context->names().async) {
- TokenKind nextSameLine = TOK_EOF;
- if (!tokenStream.peekTokenSameLine(&nextSameLine))
- return null();
- if (nextSameLine == TOK_FUNCTION) {
- uint32_t preludeStart = pos().begin;
- tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction);
+ if (tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine)) {
+ return null();
+ }
+ if (nextSameLine == TOK_FUNCTION) {
+ uint32_t preludeStart = pos().begin;
+ tokenStream.consumeKnownToken(TOK_FUNCTION);
+ return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction);
+ }
}
}
@@ -7516,7 +7521,10 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
return yieldExpression(inHandling);
bool maybeAsyncArrow = false;
- if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ if (tt == TOK_NAME &&
+ tokenStream.currentName() == context->names().async &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
@@ -7537,6 +7545,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (maybeAsyncArrow) {
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
MOZ_ASSERT(tokenStream.currentName() == context->names().async);
+ MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
TokenKind tt;
if (!tokenStream.getToken(&tt))
@@ -7613,7 +7622,9 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (next == TOK_NAME) {
tokenStream.consumeKnownToken(next, TokenStream::Operand);
- if (tokenStream.currentName() == context->names().async) {
+ if (tokenStream.currentName() == context->names().async &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
@@ -8448,8 +8459,27 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
JSOp op = JSOP_CALL;
bool maybeAsyncArrow = false;
- if (tt == TOK_LP && handler.isNameAnyParentheses(lhs)) {
- if (handler.nameIsEvalAnyParentheses(lhs, context)) {
+ if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
+ // Use the JSOP_FUN{APPLY,CALL} optimizations given the
+ // right syntax.
+ if (prop == context->names().apply) {
+ op = JSOP_FUNAPPLY;
+ if (pc->isFunctionBox()) {
+ pc->functionBox()->usesApply = true;
+ }
+ } else if (prop == context->names().call) {
+ op = JSOP_FUNCALL;
+ }
+ } else if (tt == TOK_LP) {
+ if (handler.isAsyncKeyword(lhs, context)) {
+ // |async (| can be the start of an async arrow
+ // function, so we need to defer reporting possible
+ // errors from destructuring syntax. To give better
+ // error messages, we only allow the AsyncArrowHead
+ // part of the CoverCallExpressionAndAsyncArrowHead
+ // syntax when the initial name is "async".
+ maybeAsyncArrow = true;
+ } else if (handler.isEvalAnyParentheses(lhs, context)) {
// Select the right EVAL op and flag pc as having a
// direct eval.
op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
@@ -8466,24 +8496,6 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
// it. (If we're not in a method, that's fine, so
// ignore the return value.)
checkAndMarkSuperScope();
- } else if (handler.nameIsUnparenthesizedAsync(lhs, context)) {
- // |async (| can be the start of an async arrow
- // function, so we need to defer reporting possible
- // errors from destructuring syntax. To give better
- // error messages, we only allow the AsyncArrowHead
- // part of the CoverCallExpressionAndAsyncArrowHead
- // syntax when the initial name is "async".
- maybeAsyncArrow = true;
- }
- } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
- // Use the JSOP_FUN{APPLY,CALL} optimizations given the
- // right syntax.
- if (prop == context->names().apply) {
- op = JSOP_FUNAPPLY;
- if (pc->isFunctionBox())
- pc->functionBox()->usesApply = true;
- } else if (prop == context->names().call) {
- op = JSOP_FUNCALL;
}
}
@@ -8816,7 +8828,10 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return null();
}
- if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ if (ltok == TOK_NAME &&
+ tokenStream.currentName() == context->names().async &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
// AsyncMethod[Yield, Await]:
// async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
//
@@ -9404,7 +9419,9 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
case TOK_YIELD:
case TOK_NAME: {
- if (tokenStream.currentName() == context->names().async) {
+ if (tokenStream.currentName() == context->names().async &&
+ !tokenStream.currentToken().nameContainsEscape())
+ {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index 00ea9d35d..9dc3c1072 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -9,6 +9,8 @@
#include "mozilla/Attributes.h"
+#include <string.h>
+
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
@@ -94,10 +96,13 @@ class SyntaxParseHandler
// Nodes representing unparenthesized names.
NodeUnparenthesizedArgumentsName,
- NodeUnparenthesizedAsyncName,
NodeUnparenthesizedEvalName,
NodeUnparenthesizedName,
+ // Node representing the "async" name, which may actually be a
+ // contextual keyword.
+ NodePotentialAsyncKeyword,
+
// Valuable for recognizing potential destructuring patterns.
NodeUnparenthesizedArray,
NodeUnparenthesizedObject,
@@ -183,8 +188,8 @@ class SyntaxParseHandler
lastAtom = name;
if (name == cx->names().arguments)
return NodeUnparenthesizedArgumentsName;
- if (name == cx->names().async)
- return NodeUnparenthesizedAsyncName;
+ if (pos.begin + strlen("async") == pos.end && name == cx->names().async)
+ return NodePotentialAsyncKeyword;
if (name == cx->names().eval)
return NodeUnparenthesizedEvalName;
return NodeUnparenthesizedName;
@@ -497,7 +502,7 @@ class SyntaxParseHandler
return NodeParenthesizedArgumentsName;
if (node == NodeUnparenthesizedEvalName)
return NodeParenthesizedEvalName;
- if (node == NodeUnparenthesizedName || node == NodeUnparenthesizedAsyncName)
+ if (node == NodeUnparenthesizedName || node == NodePotentialAsyncKeyword)
return NodeParenthesizedName;
if (node == NodeUnparenthesizedArray)
@@ -528,9 +533,9 @@ class SyntaxParseHandler
bool isUnparenthesizedName(Node node) {
return node == NodeUnparenthesizedArgumentsName ||
- node == NodeUnparenthesizedAsyncName ||
node == NodeUnparenthesizedEvalName ||
- node == NodeUnparenthesizedName;
+ node == NodeUnparenthesizedName ||
+ node == NodePotentialAsyncKeyword;
}
bool isNameAnyParentheses(Node node) {
@@ -541,9 +546,7 @@ class SyntaxParseHandler
node == NodeParenthesizedName;
}
- bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
+ bool isEvalAnyParentheses(Node node, ExclusiveContext* cx) {
return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName;
}
@@ -551,17 +554,15 @@ class SyntaxParseHandler
MOZ_ASSERT(isNameAnyParentheses(node),
"must only call this method on known names");
- if (nameIsEvalAnyParentheses(node, cx))
+ if (isEvalAnyParentheses(node, cx))
return js_eval_str;
if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName)
return js_arguments_str;
return nullptr;
}
- bool nameIsUnparenthesizedAsync(Node node, ExclusiveContext* cx) {
- MOZ_ASSERT(isNameAnyParentheses(node),
- "must only call this function on known names");
- return node == NodeUnparenthesizedAsyncName;
+ bool isAsyncKeyword(Node node, ExclusiveContext* cx) {
+ return node == NodePotentialAsyncKeyword;
}
PropertyName* maybeDottedProperty(Node node) {
diff --git a/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js
new file mode 100644
index 000000000..d53dff696
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/async-contains-unicode-escape.js
@@ -0,0 +1,54 @@
+var BUGNUMBER = 1315815;
+var summary = "async/await containing escapes";
+
+print(BUGNUMBER + ": " + summary);
+
+// Using "eval" as the argument name is fugly, but it means evals below are
+// *direct* evals, and so their effects in the unescaped case won't extend
+// past each separate |test| call (as would happen if we used a different name
+// that made each eval into an indirect eval, affecting code in the global
+// scope).
+function test(code, eval)
+{
+ var unescaped = code.replace("###", "async");
+ var escaped = code.replace("###", "\\u0061");
+
+ assertThrowsInstanceOf(() => eval(escaped), SyntaxError);
+ eval(unescaped);
+}
+
+test("### function f() {}", eval);
+test("var x = ### function f() {}", eval);
+test("### x => {};", eval);
+test("var x = ### x => {}", eval);
+test("### () => {};", eval);
+test("var x = ### () => {}", eval);
+test("### (y) => {};", eval);
+test("var x = ### (y) => {}", eval);
+test("({ ### x() {} })", eval);
+test("var x = ### function f() {}", eval);
+
+if (typeof parseModule === "function")
+ test("export default ### function f() {}", parseModule);
+
+assertThrowsInstanceOf(() => eval("async await => 1;"),
+ SyntaxError);
+assertThrowsInstanceOf(() => eval("async aw\\u0061it => 1;"),
+ SyntaxError);
+
+var async = 0;
+assertEq(\u0061sync, 0);
+
+var obj = { \u0061sync() { return 1; } };
+assertEq(obj.async(), 1);
+
+async = function() { return 42; };
+
+var z = async(obj);
+assertEq(z, 42);
+
+var w = async(obj)=>{};
+assertEq(typeof w, "function");
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);