1315815 - Don't treat async or await as a keyword when they contain escapes.
parent
b90c7abc6a
commit
58fb36de9e
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue