mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 932517 - Treat let as a contextual keyword in sloppy mode and make it versionless. (r=jorendorff)
This commit is contained in:
parent
3a242f3099
commit
456707fc56
@ -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) {
|
exports['test load'] = function(assert) {
|
||||||
let fixture = sandbox();
|
let fixture = sandbox();
|
||||||
load(fixture, fixturesURI + 'sandbox-normal.js');
|
load(fixture, fixturesURI + 'sandbox-normal.js');
|
||||||
|
@ -116,10 +116,10 @@ function report(testName, success) {
|
|||||||
<script type="text/javascript"><![CDATA[
|
<script type="text/javascript"><![CDATA[
|
||||||
try {
|
try {
|
||||||
eval("let x = 1;");
|
eval("let x = 1;");
|
||||||
var success = false;
|
var success = true;
|
||||||
}
|
}
|
||||||
catch (e) { success = true; }
|
catch (e) { success = false; }
|
||||||
is(success, true, "JS 1.7 should not work in versionless HTML script tags");
|
is(success, true, "let should work in versionless HTML script tags");
|
||||||
]]></script>
|
]]></script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
@ -5195,6 +5195,10 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||||||
*/
|
*/
|
||||||
bool isForDecl = false;
|
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. */
|
/* Non-null when isForDecl is true for a 'for (let ...)' statement. */
|
||||||
RootedStaticBlockObject blockObj(context);
|
RootedStaticBlockObject blockObj(context);
|
||||||
|
|
||||||
@ -5222,19 +5226,38 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||||||
isForDecl = true;
|
isForDecl = true;
|
||||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||||
pn1 = variables(yieldHandling, PNK_VAR, InForInit);
|
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();
|
handler.disableSyntaxParser();
|
||||||
bool constDecl = tt == TOK_CONST;
|
|
||||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
// Check for the backwards-compatibility corner case in sloppy
|
||||||
isForDecl = true;
|
// mode like |for (let in e)| where the 'let' token should be
|
||||||
blockObj = StaticBlockObject::create(context);
|
// parsed as an identifier.
|
||||||
if (!blockObj)
|
bool parseDecl;
|
||||||
return null();
|
if (tt == TOK_NAME) {
|
||||||
// Initialize the enclosing scope manually for the call to
|
if (!peekShouldParseLetDeclaration(&parseDecl, TokenStream::Operand))
|
||||||
// |variables| below.
|
return null();
|
||||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
letIsIdentifier = !parseDecl;
|
||||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
} else {
|
||||||
nullptr, blockObj, DontHoistVars);
|
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 {
|
} else {
|
||||||
// Pass |InProhibited| when parsing an expression so that |in|
|
// Pass |InProhibited| when parsing an expression so that |in|
|
||||||
// isn't parsed in a RelationalExpression as a binary operator.
|
// isn't parsed in a RelationalExpression as a binary operator.
|
||||||
@ -5309,9 +5332,17 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||||||
bool isForIn, isForOf;
|
bool isForIn, isForOf;
|
||||||
if (!matchInOrOf(&isForIn, &isForOf))
|
if (!matchInOrOf(&isForIn, &isForOf))
|
||||||
return null();
|
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)
|
if (isForIn)
|
||||||
headKind = PNK_FORIN;
|
headKind = PNK_FORIN;
|
||||||
else if (isForOf)
|
else if (isForOf && !letIsIdentifier)
|
||||||
headKind = PNK_FOROF;
|
headKind = PNK_FOROF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5567,12 +5598,12 @@ Parser<SyntaxParseHandler>::forStatement(YieldHandling yieldHandling)
|
|||||||
isForDecl = true;
|
isForDecl = true;
|
||||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||||
lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl);
|
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());
|
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||||
return null();
|
return null();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
|
lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||||
}
|
}
|
||||||
if (!lhsNode)
|
if (!lhsNode)
|
||||||
@ -6627,6 +6658,71 @@ Parser<SyntaxParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||||||
return SyntaxParseHandler::NodeFailure;
|
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>
|
template <typename ParseHandler>
|
||||||
typename ParseHandler::Node
|
typename ParseHandler::Node
|
||||||
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
|
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
|
||||||
@ -6696,6 +6792,16 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TOK_NAME: {
|
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;
|
TokenKind next;
|
||||||
if (!tokenStream.peekToken(&next))
|
if (!tokenStream.peekToken(&next))
|
||||||
return null();
|
return null();
|
||||||
|
@ -800,6 +800,15 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||||||
ParseNodeKind headKind);
|
ParseNodeKind headKind);
|
||||||
bool checkForHeadConstInitializers(Node pn1);
|
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:
|
public:
|
||||||
enum FunctionCallBehavior {
|
enum FunctionCallBehavior {
|
||||||
PermitAssignmentToFunctionCalls,
|
PermitAssignmentToFunctionCalls,
|
||||||
|
@ -999,6 +999,11 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||||||
|
|
||||||
if (kw->tokentype != TOK_STRICT_RESERVED) {
|
if (kw->tokentype != TOK_STRICT_RESERVED) {
|
||||||
if (kw->version <= versionNumber()) {
|
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.
|
// Working keyword.
|
||||||
if (ttp) {
|
if (ttp) {
|
||||||
*ttp = kw->tokentype;
|
*ttp = kw->tokentype;
|
||||||
@ -1006,12 +1011,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
|
|||||||
}
|
}
|
||||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
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.
|
// Strict reserved word.
|
||||||
|
@ -365,6 +365,13 @@ class MOZ_STACK_CLASS TokenStream
|
|||||||
return currentToken().name();
|
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 {
|
bool isCurrentTokenAssignment() const {
|
||||||
return TokenKindIsAssignment(currentToken().type);
|
return TokenKindIsAssignment(currentToken().type);
|
||||||
}
|
}
|
||||||
@ -999,12 +1006,12 @@ class MOZ_STACK_CLASS TokenStream
|
|||||||
void updateLineInfoForEOL();
|
void updateLineInfoForEOL();
|
||||||
void updateFlagsForEOL();
|
void updateFlagsForEOL();
|
||||||
|
|
||||||
const Token& nextToken() {
|
const Token& nextToken() const {
|
||||||
MOZ_ASSERT(hasLookahead());
|
MOZ_ASSERT(hasLookahead());
|
||||||
return tokens[(cursor + 1) & ntokensMask];
|
return tokens[(cursor + 1) & ntokensMask];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasLookahead() { return lookahead > 0; }
|
bool hasLookahead() const { return lookahead > 0; }
|
||||||
|
|
||||||
// Options used for parsing/tokenizing.
|
// Options used for parsing/tokenizing.
|
||||||
const ReadOnlyCompileOptions& options_;
|
const ReadOnlyCompileOptions& options_;
|
||||||
|
@ -57,7 +57,7 @@ var strictIdentifiers = [
|
|||||||
'static'
|
'static'
|
||||||
];
|
];
|
||||||
assertThrowsInstanceOf(() => new Function('[...yield] = []'), SyntaxError);
|
assertThrowsInstanceOf(() => new Function('[...yield] = []'), SyntaxError);
|
||||||
assertThrowsInstanceOf(() => new Function('[...let] = []'), SyntaxError);
|
assertThrowsInstanceOf(() => new Function('"use strict"; [...let] = []'), SyntaxError);
|
||||||
|
|
||||||
strictIdentifiers.forEach(ident =>
|
strictIdentifiers.forEach(ident =>
|
||||||
assertThrowsInstanceOf(() =>
|
assertThrowsInstanceOf(() =>
|
||||||
|
29
js/src/jit-test/tests/parser/letContextualKeyword.js
Normal file
29
js/src/jit-test/tests/parser/letContextualKeyword.js
Normal file
@ -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",
|
||||||
"var x,",
|
"var x,",
|
||||||
"var x =",
|
"var x =",
|
||||||
"let",
|
|
||||||
"let x,",
|
"let x,",
|
||||||
"let x =",
|
"let x =",
|
||||||
"const",
|
"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
|
try
|
||||||
{
|
{
|
||||||
expect = "SyntaxError";
|
expect = "No Error";
|
||||||
eval('let = 1;');
|
eval('let = 1;');
|
||||||
actual = 'No Error';
|
actual = 'No Error';
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ function test()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
expect = "SyntaxError";
|
expect = "No Error";
|
||||||
eval('var let = 1;');
|
eval('var let = 1;');
|
||||||
actual = 'No Error';
|
actual = 'No Error';
|
||||||
}
|
}
|
||||||
|
@ -62,10 +62,6 @@
|
|||||||
* when strict. Punt logic to parser. \
|
* when strict. Punt logic to parser. \
|
||||||
*/ \
|
*/ \
|
||||||
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
|
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
|
||||||
/* \
|
macro(let, let, TOK_LET, 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)
|
|
||||||
|
|
||||||
#endif /* vm_Keywords_h */
|
#endif /* vm_Keywords_h */
|
||||||
|
Loading…
Reference in New Issue
Block a user