mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1235590 - Allow redeclaring block-scoped functions and warn about deprecation for now. (r=jorendorff)
This commit is contained in:
parent
49a9478019
commit
8f3e583a52
@ -2415,6 +2415,14 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
||||
return declaration(pn, dst);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
// XXXshu NOP check used only for phasing in block-scope function
|
||||
// XXXshu early errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
if (pn->pn_left->isKind(PNK_NOP))
|
||||
return builder.emptyStatement(&pn->pn_pos, dst);
|
||||
return declaration(pn->pn_left, dst);
|
||||
|
||||
case PNK_LETBLOCK:
|
||||
|
@ -2344,6 +2344,18 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
||||
// XXXshu NOP check used only for phasing in block-scope function
|
||||
// XXXshu early errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
if (pn->pn_left->isKind(PNK_NOP)) {
|
||||
*answer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return checkSideEffects(pn->pn_left, answer);
|
||||
|
||||
case PNK_ARGSBODY:
|
||||
@ -8543,7 +8555,19 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
||||
|
||||
switch (pn->getKind()) {
|
||||
case PNK_FUNCTION:
|
||||
if (!emitFunction(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
// XXXshu NOP check used only for phasing in block-scope function
|
||||
// XXXshu early errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
if (pn->pn_left->isKind(PNK_NOP))
|
||||
break;
|
||||
if (!emitFunction(pn))
|
||||
return false;
|
||||
break;
|
||||
|
@ -1789,6 +1789,14 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
return FoldFunction(cx, pn, parser, inGenexpLambda);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
// XXXshu NOP check used only for phasing in block-scope function
|
||||
// XXXshu early errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
if (pn->pn_left->isKind(PNK_NOP))
|
||||
return true;
|
||||
return FoldFunction(cx, pn->pn_left, parser, inGenexpLambda);
|
||||
|
||||
case PNK_MODULE:
|
||||
|
@ -441,6 +441,18 @@ class AtomDecls
|
||||
return p.value().front<ParseHandler>();
|
||||
}
|
||||
|
||||
/* Return the definition at the tail of the chain for |atom|. */
|
||||
DefinitionNode lookupLast(JSAtom* atom) const {
|
||||
MOZ_ASSERT(map);
|
||||
DefinitionList::Range range = lookupMulti(atom);
|
||||
DefinitionNode dn = ParseHandler::nullDefinition();
|
||||
while (!range.empty()) {
|
||||
dn = range.front<ParseHandler>();
|
||||
range.popFront();
|
||||
}
|
||||
return dn;
|
||||
}
|
||||
|
||||
/* Perform a lookup that can iterate over the definitions associated with |atom|. */
|
||||
DefinitionList::Range lookupMulti(JSAtom* atom) const {
|
||||
MOZ_ASSERT(map);
|
||||
|
@ -392,6 +392,24 @@ ParseContext<ParseHandler>::updateDecl(TokenStream& ts, JSAtom* atom, Node pn)
|
||||
Definition* newDecl = &pn->template as<Definition>();
|
||||
decls_.updateFirst(atom, newDecl);
|
||||
|
||||
if (oldDecl->isOp(JSOP_INITLEXICAL)) {
|
||||
// XXXshu Special case used only for phasing in block-scope function
|
||||
// XXXshu early errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
MOZ_ASSERT(oldDecl->getKind() == PNK_FUNCTION);
|
||||
MOZ_ASSERT(newDecl->getKind() == PNK_FUNCTION);
|
||||
MOZ_ASSERT(!sc->strict());
|
||||
MOZ_ASSERT(oldDecl->isBound());
|
||||
MOZ_ASSERT(!oldDecl->pn_scopecoord.isFree());
|
||||
newDecl->pn_scopecoord = oldDecl->pn_scopecoord;
|
||||
newDecl->pn_dflags |= PND_BOUND;
|
||||
newDecl->setOp(JSOP_INITLEXICAL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sc->isGlobalContext() || oldDecl->isDeoptimized()) {
|
||||
MOZ_ASSERT(newDecl->isFreeVar());
|
||||
// Global 'var' bindings have no slots, but are still tracked for
|
||||
@ -2409,9 +2427,31 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
if (annexDef->kind() == Definition::CONSTANT ||
|
||||
annexDef->kind() == Definition::LET)
|
||||
{
|
||||
// Do not emit Annex B assignment if we would've
|
||||
// thrown a redeclaration error.
|
||||
annexDef = nullptr;
|
||||
if (annexDef->isKind(PNK_FUNCTION)) {
|
||||
// XXXshu Code used only for phasing in block-scope
|
||||
// XXXshu function early errors. Ignore redeclarations
|
||||
// XXXshu here and generate Annex B assignments for
|
||||
// XXXshu block-scoped functions that redeclare other
|
||||
// XXXshu block-scoped functions.
|
||||
// XXXshu
|
||||
// XXXshu Get the possibly-synthesized var that was
|
||||
// XXXshu already made for the first of the block-scoped
|
||||
// XXXshu functions.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
annexDef = pc->decls().lookupLast(funName);
|
||||
if (annexDef->kind() == Definition::CONSTANT ||
|
||||
annexDef->kind() == Definition::LET)
|
||||
{
|
||||
annexDef = nullptr;
|
||||
}
|
||||
} else {
|
||||
// Do not emit Annex B assignment if we would've
|
||||
// thrown a redeclaration error.
|
||||
annexDef = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Synthesize a new 'var' binding if one does not exist.
|
||||
@ -3653,8 +3693,41 @@ Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler>* data,
|
||||
// The reason we compare using >= instead of == on the block id is to
|
||||
// detect redeclarations where a 'var' binding first appeared in a
|
||||
// nested block: |{ var x; } let x;|
|
||||
if (dn && dn->pn_blockid >= pc->blockid())
|
||||
if (dn && dn->pn_blockid >= pc->blockid()) {
|
||||
// XXXshu Used only for phasing in block-scope function early
|
||||
// XXXshu errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
if (pn->isKind(PNK_FUNCTION) && dn->isKind(PNK_FUNCTION) && !pc->sc->strict()) {
|
||||
if (!parser->makeDefIntoUse(dn, pn, name))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(blockScope);
|
||||
Shape* shape = blockScope->lastProperty()->search(cx, NameToId(name));
|
||||
MOZ_ASSERT(shape);
|
||||
uint32_t oldDefIndex = blockScope->shapeToIndex(*shape);
|
||||
blockScope->updateDefinitionParseNode(oldDefIndex, dn,
|
||||
reinterpret_cast<Definition*>(pn));
|
||||
|
||||
parser->addTelemetry(JSCompartment::DeprecatedBlockScopeFunRedecl);
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(cx, name, &bytes))
|
||||
return false;
|
||||
if (!parser->report(ParseWarning, false, null(),
|
||||
JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL,
|
||||
bytes.ptr()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return parser->reportRedeclaration(pn, dn->kind(), name);
|
||||
}
|
||||
|
||||
if (!pc->define(parser->tokenStream, name, pn, bindingKind))
|
||||
return false;
|
||||
}
|
||||
|
@ -252,6 +252,7 @@ MSG_DEF(JSMSG_DEPRECATED_FLAGS_ARG, 0, JSEXN_NONE, "flags argument of String.
|
||||
MSG_DEF(JSMSG_DEPRECATED_FOR_EACH, 0, JSEXN_NONE, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead")
|
||||
MSG_DEF(JSMSG_DEPRECATED_OCTAL, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated")
|
||||
MSG_DEF(JSMSG_DEPRECATED_PRAGMA, 1, JSEXN_NONE, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
|
||||
MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, 1, JSEXN_NONE, "redeclaration of block-scoped function `{0}' is deprecated")
|
||||
MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME, 1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
|
||||
MSG_DEF(JSMSG_DUPLICATE_FORMAL, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
|
||||
MSG_DEF(JSMSG_DUPLICATE_LABEL, 0, JSEXN_SYNTAXERR, "duplicate label")
|
||||
|
@ -753,6 +753,7 @@ struct JSCompartment
|
||||
DeprecatedFlagsArgument = 7, // JS 1.3 or older
|
||||
// NO LONGER USING 8
|
||||
// NO LONGER USING 9
|
||||
DeprecatedBlockScopeFunRedecl = 10,
|
||||
DeprecatedLanguageExtensionCount
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,66 @@
|
||||
{
|
||||
assertEq(f(), 4);
|
||||
function f() { return 3; }
|
||||
assertEq(f(), 4);
|
||||
function f() { return 4; }
|
||||
assertEq(f(), 4);
|
||||
}
|
||||
|
||||
// Annex B still works.
|
||||
assertEq(f(), 4);
|
||||
|
||||
function test() {
|
||||
{
|
||||
assertEq(f(), 2);
|
||||
function f() { return 1; }
|
||||
assertEq(f(), 2);
|
||||
function f() { return 2; }
|
||||
assertEq(f(), 2);
|
||||
}
|
||||
|
||||
// Annex B still works.
|
||||
assertEq(f(), 2);
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
var log = '';
|
||||
|
||||
try {
|
||||
// Strict mode still cannot redeclare.
|
||||
eval(`"use strict";
|
||||
{
|
||||
function f() { }
|
||||
function f() { }
|
||||
}`);
|
||||
} catch (e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
log += 'e';
|
||||
}
|
||||
|
||||
try {
|
||||
// Redeclaring an explicitly 'let'-declared binding doesn't work.
|
||||
eval(`{
|
||||
let x = 42;
|
||||
function x() {}
|
||||
}`);
|
||||
} catch (e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
log += 'e';
|
||||
}
|
||||
|
||||
try {
|
||||
// Redeclaring an explicitly 'const'-declared binding doesn't work.
|
||||
eval(`{
|
||||
const x = 42;
|
||||
function x() {}
|
||||
}`);
|
||||
} catch (e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
log += 'e';
|
||||
}
|
||||
|
||||
assertEq(log, 'eee');
|
||||
|
||||
if ('reportCompare' in this)
|
||||
reportCompare(true, true);
|
@ -265,6 +265,20 @@ class StaticBlockScope : public NestedStaticScope
|
||||
setSlotValue(i, PrivateValue(def));
|
||||
}
|
||||
|
||||
// XXXshu Used only for phasing in block-scope function early
|
||||
// XXXshu errors.
|
||||
// XXXshu
|
||||
// XXXshu Back out when major version >= 50. See [1].
|
||||
// XXXshu
|
||||
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
|
||||
void updateDefinitionParseNode(unsigned i,
|
||||
frontend::Definition* oldDef,
|
||||
frontend::Definition* newDef)
|
||||
{
|
||||
MOZ_ASSERT(definitionParseNode(i) == oldDef);
|
||||
setSlotValue(i, PrivateValue(newDef));
|
||||
}
|
||||
|
||||
frontend::Definition* definitionParseNode(unsigned i) {
|
||||
Value v = slotValue(i);
|
||||
return reinterpret_cast<frontend::Definition*>(v.toPrivate());
|
||||
|
@ -29,11 +29,11 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 341;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 342;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 437,
|
||||
static_assert(JSErr_Limit == 438,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
@ -507,14 +507,14 @@
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete)"
|
||||
"description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10"
|
||||
},
|
||||
"JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": {
|
||||
"alert_emails": ["jdemooij@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete)"
|
||||
"description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7, RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10"
|
||||
},
|
||||
"XUL_CACHE_DISABLED": {
|
||||
"expires_in_version": "default",
|
||||
|
Loading…
Reference in New Issue
Block a user