mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1288460 - Allow escape sequences in the keyword-like but non-reserved 'let' Identifier (in non-strict code). r=arai
--HG-- extra : rebase_source : 953fc9fa2747a031360db5405d83e551ef2e1a96
This commit is contained in:
parent
f222967fd3
commit
327cce50c3
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1287426 - Clobber required because of Linux Chromium sandbox file moves.
|
||||
Bug 1288460 requires a clobber due to bug 1298779.
|
||||
|
@ -845,7 +845,11 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
|
||||
if (!pc->sc()->needStrictChecks())
|
||||
return true;
|
||||
|
||||
if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
|
||||
if (name == context->names().eval ||
|
||||
name == context->names().arguments ||
|
||||
name == context->names().let ||
|
||||
IsKeyword(name))
|
||||
{
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(context, name, &bytes))
|
||||
return false;
|
||||
@ -4859,15 +4863,28 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
return node;
|
||||
}
|
||||
|
||||
case TOK_LET:
|
||||
case TOK_CONST:
|
||||
kid = lexicalDeclaration(YieldIsName, tt == TOK_CONST);
|
||||
kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
|
||||
if (!kid)
|
||||
return null();
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
|
||||
case TOK_NAME:
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
if (!checkUnescapedName())
|
||||
return null();
|
||||
|
||||
kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
|
||||
if (!kid)
|
||||
return null();
|
||||
if (!checkExportedNamesForDeclaration(kid))
|
||||
return null();
|
||||
break;
|
||||
}
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
|
||||
return null();
|
||||
@ -5113,16 +5130,15 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
|
||||
// For-in loop backwards compatibility requires that |let| starting a
|
||||
// for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
|
||||
// parse as an identifier. (|let| in for-of is always a declaration.)
|
||||
// Thus we must can't just sniff out TOK_CONST/TOK_LET here. :-(
|
||||
bool parsingLexicalDeclaration = false;
|
||||
bool letIsIdentifier = false;
|
||||
if (tt == TOK_LET || tt == TOK_CONST) {
|
||||
if (tt == TOK_CONST) {
|
||||
parsingLexicalDeclaration = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
} else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"should parse |let| as TOK_LET in strict mode code");
|
||||
|
||||
} else if (tt == TOK_NAME &&
|
||||
tokenStream.nextName() == context->names().let &&
|
||||
!tokenStream.nextNameContainsEscape())
|
||||
{
|
||||
// We could have a {For,Lexical}Declaration, or we could have a
|
||||
// LeftHandSideExpression with lookahead restrictions so it's not
|
||||
// ambiguous with the former. Check for a continuation of the former
|
||||
@ -6420,10 +6436,9 @@ template <class ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
|
||||
"TOK_LET should have been summarily considered a "
|
||||
"LexicalDeclaration");
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
|
||||
MOZ_ASSERT(tokenStream.currentName() == context->names().let);
|
||||
MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
|
||||
|
||||
#ifdef DEBUG
|
||||
TokenKind verify;
|
||||
@ -6531,25 +6546,21 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"observing |let| as TOK_NAME and not TOK_LET implies "
|
||||
"non-strict code (and the edge case of 'use strict' "
|
||||
"immediately followed by |let| on a new line only "
|
||||
"applies to StatementListItems, not to Statements)");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Statement context forbids LexicalDeclaration.
|
||||
if ((next == TOK_LB || next == TOK_LC || next == TOK_NAME) &&
|
||||
// |let| here can only be an Identifier, not a declaration. Give nicer
|
||||
// errors for declaration-looking typos.
|
||||
if (!tokenStream.currentToken().nameContainsEscape() &&
|
||||
tokenStream.currentName() == context->names().let)
|
||||
{
|
||||
bool forbiddenLetDeclaration = false;
|
||||
if (next == TOK_LB) {
|
||||
// ExpressionStatement has a 'let [' lookahead restriction.
|
||||
|
||||
if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
|
||||
// |let| can't be an Identifier in strict mode code. Ditto for
|
||||
// non-standard JavaScript 1.7+.
|
||||
forbiddenLetDeclaration = true;
|
||||
} else {
|
||||
} else if (next == TOK_LB) {
|
||||
// Enforce ExpressionStatement's 'let [' lookahead restriction.
|
||||
forbiddenLetDeclaration = true;
|
||||
} else if (next == TOK_LC || next == TOK_NAME) {
|
||||
// 'let {' and 'let foo' aren't completely forbidden, if ASI
|
||||
// causes 'let' to be the entire Statement. But if they're
|
||||
// same-line, we can aggressively give a better error message.
|
||||
@ -6677,14 +6688,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
|
||||
return null();
|
||||
|
||||
// TOK_LET implies we're in strict mode code where static semantics
|
||||
// forbid IdentifierName to be "let": a stronger restriction than
|
||||
// Statement's lookahead restriction on |let [|. Provide a better error
|
||||
// message here than the default case would.
|
||||
case TOK_LET:
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "let declarations");
|
||||
return null();
|
||||
|
||||
// NOTE: default case handled in the ExpressionStatement section.
|
||||
}
|
||||
}
|
||||
@ -6752,23 +6755,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
if (nextTokenContinuesLetDeclaration(next, yieldHandling))
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
|
||||
// IdentifierName can't be "let" in strict mode code. |let| in
|
||||
// strict mode code is usually TOK_LET, but in this one weird case
|
||||
// in global code it's TOK_NAME:
|
||||
//
|
||||
// "use strict" // ExpressionStatement ended by ASI
|
||||
// let <...whatever else...> // a fresh StatementListItem
|
||||
//
|
||||
// Carefully reject strict mode |let| non-declarations.
|
||||
if (pc->sc()->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"declaration pattern", TokenKindToDesc(next));
|
||||
return null();
|
||||
}
|
||||
if (!tokenStream.currentToken().nameContainsEscape() &&
|
||||
tokenStream.currentName() == context->names().let &&
|
||||
nextTokenContinuesLetDeclaration(next, yieldHandling))
|
||||
{
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
}
|
||||
|
||||
if (next == TOK_COLON)
|
||||
@ -6854,11 +6845,10 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
|
||||
// LexicalDeclaration[In, ?Yield]
|
||||
// LetOrConst BindingList[?In, ?Yield]
|
||||
case TOK_LET:
|
||||
case TOK_CONST:
|
||||
// [In] is the default behavior, because for-loops specially parse
|
||||
// their heads to handle |in| in this situation.
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ tt == TOK_CONST);
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ true);
|
||||
|
||||
// ImportDeclaration (only inside modules)
|
||||
case TOK_IMPORT:
|
||||
@ -8254,6 +8244,16 @@ Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
|
||||
ident = tok.name();
|
||||
MOZ_ASSERT(ident != context->names().yield,
|
||||
"tokenizer should have treated 'yield' as TOK_YIELD");
|
||||
|
||||
if (pc->sc()->strict()) {
|
||||
const char* badName = ident == context->names().let
|
||||
? "let"
|
||||
: nullptr;
|
||||
if (badName) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(tok.type == TOK_YIELD);
|
||||
|
||||
@ -8293,6 +8293,14 @@ Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
|
||||
report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
badName = ident == context->names().let
|
||||
? "let"
|
||||
: nullptr;
|
||||
if (badName) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(tok.type == TOK_YIELD);
|
||||
|
@ -107,7 +107,6 @@
|
||||
macro(THROW, "keyword 'throw'") \
|
||||
macro(DEBUGGER, "keyword 'debugger'") \
|
||||
macro(YIELD, "keyword 'yield'") \
|
||||
macro(LET, "keyword 'let'") \
|
||||
macro(EXPORT, "keyword 'export'") \
|
||||
macro(IMPORT, "keyword 'import'") \
|
||||
macro(CLASS, "keyword 'class'") \
|
||||
@ -239,12 +238,6 @@ TokenKindIsAssignment(TokenKind tt)
|
||||
return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
|
||||
}
|
||||
|
||||
inline bool
|
||||
TokenKindIsDecl(TokenKind tt)
|
||||
{
|
||||
return tt == TOK_VAR || tt == TOK_LET;
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
@ -981,11 +981,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
||||
if (kw->tokentype == TOK_STRICT_RESERVED)
|
||||
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
|
||||
|
||||
// Treat 'let' as an identifier and contextually a keyword in sloppy mode.
|
||||
// It is always a keyword in strict mode.
|
||||
if (kw->tokentype == TOK_LET && !strictMode())
|
||||
return true;
|
||||
|
||||
// Working keyword.
|
||||
if (ttp) {
|
||||
*ttp = kw->tokentype;
|
||||
|
@ -351,6 +351,13 @@ class MOZ_STACK_CLASS TokenStream
|
||||
return nextToken().name();
|
||||
}
|
||||
|
||||
bool nextNameContainsEscape() const {
|
||||
if (nextToken().type == TOK_YIELD)
|
||||
return false;
|
||||
MOZ_ASSERT(nextToken().type == TOK_NAME);
|
||||
return nextToken().nameContainsEscape();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
52
js/src/tests/ecma_6/Syntax/escaped-let-static-identifier.js
Normal file
52
js/src/tests/ecma_6/Syntax/escaped-let-static-identifier.js
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 1288460;
|
||||
var summary =
|
||||
"|let| and |static| are forbidden as Identifier only in strict mode code, " +
|
||||
"and it's permissible to use them as Identifier (with or without " +
|
||||
"containing escapes) in non-strict code";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
function t(code)
|
||||
{
|
||||
var strictSemi = " 'use strict'; " + code;
|
||||
var strictASI = " 'use strict' \n " + code;
|
||||
|
||||
var creationFunctions = [Function];
|
||||
if (typeof evaluate === "function")
|
||||
creationFunctions.push(evaluate);
|
||||
if (typeof parseModule === "function")
|
||||
creationFunctions.push(parseModule);
|
||||
|
||||
for (var func of creationFunctions)
|
||||
{
|
||||
if (typeof parseModule === "function" && func === parseModule)
|
||||
assertThrowsInstanceOf(() => func(code), SyntaxError);
|
||||
else
|
||||
func(code);
|
||||
|
||||
assertThrowsInstanceOf(() => func(strictSemi), SyntaxError);
|
||||
assertThrowsInstanceOf(() => func(strictASI), SyntaxError);
|
||||
}
|
||||
}
|
||||
|
||||
t("l\\u0065t: 42;");
|
||||
t("if (1) l\\u0065t: 42;");
|
||||
t("l\\u0065t = 42;");
|
||||
t("if (1) l\\u0065t = 42;");
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
@ -13,13 +13,19 @@ print(BUGNUMBER + ": " + summary);
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
Function("let: 42")
|
||||
Function("let: 42");
|
||||
Function("l\\u0065t: 42");
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict'; let: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict' \n let: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict'; l\\u0065t: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict' \n l\\u0065t: 42"), SyntaxError);
|
||||
|
||||
eval("let: 42")
|
||||
eval("let: 42");
|
||||
eval("l\\u0065t: 42");
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict'; let: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict' \n let: 42;"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict'; l\\u0065t: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict' \n l\\u0065t: 42;"), SyntaxError);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -61,7 +61,6 @@
|
||||
* future reserved keyword in strict mode, but a keyword in JS1.7 even \
|
||||
* when strict. Punt logic to parser. \
|
||||
*/ \
|
||||
macro(yield, yield, TOK_YIELD) \
|
||||
macro(let, let, TOK_LET)
|
||||
macro(yield, yield, TOK_YIELD)
|
||||
|
||||
#endif /* vm_Keywords_h */
|
||||
|
Loading…
Reference in New Issue
Block a user