Bug 1077949 - Fix TDZ checks when closing over non-dominating lexical declarations in switches. (r=Waldo)

This commit is contained in:
Shu-yu Guo 2014-10-15 18:06:50 -07:00
Родитель 0945f9f310
Коммит ee2ad83c86
2 изменённых файлов: 89 добавлений и 3 удалений

Просмотреть файл

@ -1254,6 +1254,17 @@ ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler
return true;
}
static bool
IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler> *pc, HandleAtom name,
Definition *dn)
{
MOZ_ASSERT(dn->isLet());
StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, nullptr);
if (stmt && stmt->type == STMT_SWITCH)
return dn->pn_cookie.slot() < stmt->firstDominatingLexicalInCase;
return false;
}
/*
* Beware: this function is called for functions nested in other functions or
* global scripts but not for functions compiled through the Function
@ -1338,7 +1349,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHan
// In ES6, lexical bindings cannot be accessed until
// initialized. If we are parsing a function with a
// hoisted body-level use, all free variables that get
// linked to an outer 'let' binding need to be marked as
// linked to an outer lexical binding need to be marked as
// needing dead zone checks. e.g.,
//
// function outer() {
@ -1348,7 +1359,15 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHan
// }
//
// The use of 'x' inside 'inner' needs to be marked.
if (bodyLevelHoistedUse && outer_dn->isLet()) {
//
// Similarly, if we are closing over a lexical binding
// from another case in a switch, those uses also need to
// be marked as needing dead zone checks.
RootedAtom name(context, atom);
if (outer_dn->isLet() &&
(bodyLevelHoistedUse ||
IsNonDominatingInScopedSwitch(outerpc, name, outer_dn)))
{
while (true) {
pnu->pn_dflags |= PND_LET;
if (!pnu->pn_link)
@ -1872,6 +1891,13 @@ Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
// Note that body-level function declaration statements are always
// hoisted to the top, so all accesses to free let variables need the
// dead zone check.
//
// Subtlety: we don't need to check for closing over a non-dominating
// lexical binding in a switch, as lexical declarations currently
// disable syntax parsing. So a non-dominating but textually preceding
// lexical declaration would have aborted syntax parsing, and a
// textually following declaration would return true for
// handler.isPlaceholderDefinition(dn) below.
if (handler.isPlaceholderDefinition(dn) || bodyLevelHoistedUse)
freeVariables[i].setIsHoistedUse();
@ -3142,7 +3168,7 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
handler.setFlag(pn, PND_DEOPTIMIZED);
} else if (stmt->type == STMT_SWITCH && stmt->isBlockScope) {
// See comments above StmtInfoPC and switchStatement for how
// firstDominatingLetInCase is computed.
// firstDominatingLexicalInCase is computed.
MOZ_ASSERT(stmt->firstDominatingLexicalInCase <= stmt->staticBlock().numVariables());
handler.markMaybeUninitializedLexicalUseInSwitch(pn, dn,
stmt->firstDominatingLexicalInCase);

Просмотреть файл

@ -0,0 +1,60 @@
function assertThrowsReferenceError(f) {
var err;
try {
f();
} catch (e) {
err = e;
}
assertEq(err instanceof ReferenceError, true);
}
function f() {
switch (0) {
case 1:
let x
case function() {
print(x)
}():
}
}
assertThrowsReferenceError(f);
function g() {
switch (0) {
case 1:
let x;
case 0:
var inner = function () {
print(x);
}
inner();
break;
}
}
assertThrowsReferenceError(g);
function h() {
switch (0) {
case 0:
var inner = function () {
print(x);
}
inner();
case 1:
let x;
}
}
assertThrowsReferenceError(h);
// Tests that a dominating lexical doesn't throw.
function F() {
switch (0) {
case 0:
let x = 42;
var inner = function () {
assertEq(x, 42);
}
inner();
}
}
F();