зеркало из https://github.com/mozilla/gecko-dev.git
Bug 932517 - Treat let as a contextual keyword in sloppy mode and make it versionless. (r=jorendorff)
This commit is contained in:
Родитель
554ca9db03
Коммит
d68a20c4a7
|
@ -84,13 +84,6 @@ exports['test exceptions'] = function(assert) {
|
|||
}
|
||||
};
|
||||
|
||||
exports['test opt version'] = function(assert) {
|
||||
let fixture = sandbox();
|
||||
assert.throws(function() {
|
||||
evaluate(fixture, 'let a = 2;', 'test.js', 1, '1.5');
|
||||
}, 'No let in js 1.5');
|
||||
};
|
||||
|
||||
exports['test load'] = function(assert) {
|
||||
let fixture = sandbox();
|
||||
load(fixture, fixturesURI + 'sandbox-normal.js');
|
||||
|
|
|
@ -116,10 +116,10 @@ function report(testName, success) {
|
|||
<script type="text/javascript"><![CDATA[
|
||||
try {
|
||||
eval("let x = 1;");
|
||||
var success = false;
|
||||
var success = true;
|
||||
}
|
||||
catch (e) { success = true; }
|
||||
is(success, true, "JS 1.7 should not work in versionless HTML script tags");
|
||||
catch (e) { success = false; }
|
||||
is(success, true, "let should work in versionless HTML script tags");
|
||||
]]></script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -5195,6 +5195,10 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
*/
|
||||
bool isForDecl = false;
|
||||
|
||||
// True if a 'let' token at the head is parsed as an identifier instead of
|
||||
// as starting a declaration.
|
||||
bool letIsIdentifier = false;
|
||||
|
||||
/* Non-null when isForDecl is true for a 'for (let ...)' statement. */
|
||||
RootedStaticBlockObject blockObj(context);
|
||||
|
||||
|
@ -5222,19 +5226,38 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
isForDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
pn1 = variables(yieldHandling, PNK_VAR, InForInit);
|
||||
} else if (tt == TOK_LET || tt == TOK_CONST) {
|
||||
} else if (tt == TOK_LET || tt == TOK_CONST ||
|
||||
(tt == TOK_NAME && tokenStream.nextName() == context->names().let)) {
|
||||
handler.disableSyntaxParser();
|
||||
bool constDecl = tt == TOK_CONST;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
isForDecl = true;
|
||||
blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
// Initialize the enclosing scope manually for the call to
|
||||
// |variables| below.
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
||||
nullptr, blockObj, DontHoistVars);
|
||||
|
||||
// Check for the backwards-compatibility corner case in sloppy
|
||||
// mode like |for (let in e)| where the 'let' token should be
|
||||
// parsed as an identifier.
|
||||
bool parseDecl;
|
||||
if (tt == TOK_NAME) {
|
||||
if (!peekShouldParseLetDeclaration(&parseDecl, TokenStream::Operand))
|
||||
return null();
|
||||
letIsIdentifier = !parseDecl;
|
||||
} else {
|
||||
parseDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
}
|
||||
|
||||
if (parseDecl) {
|
||||
bool constDecl = tt == TOK_CONST;
|
||||
isForDecl = true;
|
||||
blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
|
||||
// Initialize the enclosing scope manually for the call to
|
||||
// |variables| below.
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
||||
nullptr, blockObj, DontHoistVars);
|
||||
} else {
|
||||
pn1 = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||
}
|
||||
} else {
|
||||
// Pass |InProhibited| when parsing an expression so that |in|
|
||||
// isn't parsed in a RelationalExpression as a binary operator.
|
||||
|
@ -5309,9 +5332,17 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
bool isForIn, isForOf;
|
||||
if (!matchInOrOf(&isForIn, &isForOf))
|
||||
return null();
|
||||
|
||||
// In for-in loops, a 'let' token may be used as an identifier for
|
||||
// backwards-compatibility reasons, e.g., |for (let in e)|. In for-of
|
||||
// loops, a 'let' token is never parsed as an identifier. Forbid
|
||||
// trying to parse a for-of loop if we have parsed a 'let' token as an
|
||||
// identifier above.
|
||||
//
|
||||
// See ES6 13.7.5.1.
|
||||
if (isForIn)
|
||||
headKind = PNK_FORIN;
|
||||
else if (isForOf)
|
||||
else if (isForOf && !letIsIdentifier)
|
||||
headKind = PNK_FOROF;
|
||||
}
|
||||
|
||||
|
@ -5567,12 +5598,12 @@ Parser<SyntaxParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||
isForDecl = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl);
|
||||
}
|
||||
else if (tt == TOK_CONST || tt == TOK_LET) {
|
||||
} else if (tt == TOK_CONST || tt == TOK_LET ||
|
||||
(tt == TOK_NAME && tokenStream.nextName() == context->names().let))
|
||||
{
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||
}
|
||||
if (!lhsNode)
|
||||
|
@ -6627,6 +6658,71 @@ Parser<SyntaxParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
|
||||
{
|
||||
// 'let' is a reserved keyword in strict mode and we shouldn't get here.
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
|
||||
TokenKind tt;
|
||||
*parseDeclOut = false;
|
||||
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return false;
|
||||
|
||||
switch (tt) {
|
||||
case TOK_NAME:
|
||||
// |let let| is disallowed per ES6 13.3.1.1.
|
||||
*parseDeclOut = tokenStream.nextName() != context->names().let;
|
||||
break;
|
||||
|
||||
case TOK_LC:
|
||||
case TOK_LB:
|
||||
// A following name is always a declaration.
|
||||
//
|
||||
// |let {| and |let [| are destructuring declarations.
|
||||
*parseDeclOut = true;
|
||||
break;
|
||||
|
||||
case TOK_LP:
|
||||
// Only parse let blocks for 1.7 and 1.8. Do not expose deprecated let
|
||||
// blocks to content.
|
||||
*parseDeclOut = versionNumber() == JSVERSION_1_7 || versionNumber() == JSVERSION_1_8;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
|
||||
TokenStream::Modifier modifier)
|
||||
{
|
||||
*parseDeclOut = false;
|
||||
|
||||
#ifdef DEBUG
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, modifier))
|
||||
return false;
|
||||
MOZ_ASSERT(tt == TOK_NAME && tokenStream.nextName() == context->names().let);
|
||||
#endif
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_NAME, modifier);
|
||||
if (!shouldParseLetDeclaration(parseDeclOut))
|
||||
return false;
|
||||
|
||||
// Unget the TOK_NAME of 'let' if not parsing a declaration.
|
||||
if (!*parseDeclOut)
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
|
||||
|
@ -6696,6 +6792,16 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||
}
|
||||
|
||||
case TOK_NAME: {
|
||||
// 'let' is a contextual keyword in sloppy node. In strict mode, it is
|
||||
// always lexed as TOK_LET.
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
bool parseDecl;
|
||||
if (!shouldParseLetDeclaration(&parseDecl))
|
||||
return null();
|
||||
if (parseDecl)
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
}
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
|
|
@ -800,6 +800,15 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
ParseNodeKind headKind);
|
||||
bool checkForHeadConstInitializers(Node pn1);
|
||||
|
||||
// Use when the current token is TOK_NAME and is known to be 'let'.
|
||||
bool shouldParseLetDeclaration(bool* parseDeclOut);
|
||||
|
||||
// Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
|
||||
// let declaration should be parsed, the TOK_NAME token of 'let' is
|
||||
// consumed. Otherwise, the current token remains the TOK_NAME token of
|
||||
// 'let'.
|
||||
bool peekShouldParseLetDeclaration(bool* parseDeclOut, TokenStream::Modifier modifier);
|
||||
|
||||
public:
|
||||
enum FunctionCallBehavior {
|
||||
PermitAssignmentToFunctionCalls,
|
||||
|
|
|
@ -999,6 +999,11 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||
|
||||
if (kw->tokentype != TOK_STRICT_RESERVED) {
|
||||
if (kw->version <= versionNumber()) {
|
||||
// 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;
|
||||
|
@ -1006,12 +1011,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||
}
|
||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
// The keyword is not in this version. Treat it as an identifier, unless
|
||||
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
|
||||
// the code below (ES5 forbids it in strict mode).
|
||||
if (kw->tokentype != TOK_LET)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strict reserved word.
|
||||
|
|
|
@ -365,6 +365,13 @@ class MOZ_STACK_CLASS TokenStream
|
|||
return currentToken().name();
|
||||
}
|
||||
|
||||
PropertyName* nextName() const {
|
||||
if (nextToken().type == TOK_YIELD)
|
||||
return cx->names().yield;
|
||||
MOZ_ASSERT(nextToken().type == TOK_NAME);
|
||||
return nextToken().name();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
@ -999,12 +1006,12 @@ class MOZ_STACK_CLASS TokenStream
|
|||
void updateLineInfoForEOL();
|
||||
void updateFlagsForEOL();
|
||||
|
||||
const Token& nextToken() {
|
||||
const Token& nextToken() const {
|
||||
MOZ_ASSERT(hasLookahead());
|
||||
return tokens[(cursor + 1) & ntokensMask];
|
||||
}
|
||||
|
||||
bool hasLookahead() { return lookahead > 0; }
|
||||
bool hasLookahead() const { return lookahead > 0; }
|
||||
|
||||
// Options used for parsing/tokenizing.
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
|
|
|
@ -57,7 +57,7 @@ var strictIdentifiers = [
|
|||
'static'
|
||||
];
|
||||
assertThrowsInstanceOf(() => new Function('[...yield] = []'), SyntaxError);
|
||||
assertThrowsInstanceOf(() => new Function('[...let] = []'), SyntaxError);
|
||||
assertThrowsInstanceOf(() => new Function('"use strict"; [...let] = []'), SyntaxError);
|
||||
|
||||
strictIdentifiers.forEach(ident =>
|
||||
assertThrowsInstanceOf(() =>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
function expectError(str) {
|
||||
var log = "";
|
||||
try {
|
||||
eval(str);
|
||||
} catch (e) {
|
||||
log += "e";
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
}
|
||||
assertEq(log, "e");
|
||||
}
|
||||
|
||||
eval(`let x = 42; assertEq(x, 42);`);
|
||||
eval(`var let = 42; assertEq(let, 42);`);
|
||||
eval(`let;`);
|
||||
eval(`[...let] = [];`);
|
||||
eval(`function let() { return 42; } assertEq(let(), 42);`)
|
||||
eval(`let {x:x} = {x:42}; assertEq(x, 42);`);
|
||||
eval(`let [x] = [42]; assertEq(x, 42);`);
|
||||
eval(`for (let x in [1]) { assertEq(x, "0"); }`);
|
||||
eval(`for (let x of [1]) { assertEq(x, 1); }`);
|
||||
eval(`for (let i = 0; i < 1; i++) { assertEq(i, 0); }`);
|
||||
eval(`for (let in [1]) { assertEq(let, "0"); }`);
|
||||
eval(`for (let of of [1]) { assertEq(of, 1); }`);
|
||||
eval(`for (let/1;;) { break; }`);
|
||||
expectError(`for (let of [1]) { }`);
|
||||
expectError(`let let = 42;`);
|
||||
expectError(`"use strict"; var let = 42;`);
|
||||
expectError(`"use strict"; function let() {}`);
|
||||
expectError(`"use strict"; for (let of [1]) {}`);
|
|
@ -6,7 +6,6 @@ var cases = [
|
|||
"var",
|
||||
"var x,",
|
||||
"var x =",
|
||||
"let",
|
||||
"let x,",
|
||||
"let x =",
|
||||
"const",
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 352392;
|
||||
var summary = 'Do not hang/crash |for each| over object with getter set to map';
|
||||
var actual = 'No Crash';
|
||||
var expect = 'No Crash';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
expect = 'SyntaxError: invalid for each loop';
|
||||
try
|
||||
{
|
||||
var obj = { };
|
||||
Object.defineProperty(obj, "y", { get: Array.prototype.map, enumerable: true, configurable: true });
|
||||
eval('(function() { for each(let z in obj) { } })()');
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
actual = ex + '';
|
||||
}
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
|
@ -40,7 +40,7 @@ reportCompare(expect, actual, summary + ': local: yield = 1');
|
|||
|
||||
try
|
||||
{
|
||||
expect = "SyntaxError";
|
||||
expect = "No Error";
|
||||
eval('let = 1;');
|
||||
actual = 'No Error';
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ function test()
|
|||
|
||||
try
|
||||
{
|
||||
expect = "SyntaxError";
|
||||
expect = "No Error";
|
||||
eval('var let = 1;');
|
||||
actual = 'No Error';
|
||||
}
|
||||
|
|
|
@ -62,10 +62,6 @@
|
|||
* when strict. Punt logic to parser. \
|
||||
*/ \
|
||||
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
|
||||
/* \
|
||||
* Let is a future reserved keyword in strict mode, and a keyword in \
|
||||
* JS1.7. \
|
||||
*/ \
|
||||
macro(let, let, TOK_LET, JSVERSION_1_7)
|
||||
macro(let, let, TOK_LET, JSVERSION_DEFAULT)
|
||||
|
||||
#endif /* vm_Keywords_h */
|
||||
|
|
Загрузка…
Ссылка в новой задаче