diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 53edb08d7..2a1429d99 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -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: diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 49a670fb0..201c25054 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2346,6 +2346,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: @@ -8346,7 +8358,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; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 1c80e99db..bd2ffd145 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -1789,6 +1789,14 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& 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: diff --git a/js/src/frontend/ParseMaps.h b/js/src/frontend/ParseMaps.h index 899a78e0e..6d10fa631 100644 --- a/js/src/frontend/ParseMaps.h +++ b/js/src/frontend/ParseMaps.h @@ -441,6 +441,18 @@ class AtomDecls return p.value().front(); } + /* 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(); + 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); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 12115d51f..1fe9be615 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -395,6 +395,24 @@ ParseContext::updateDecl(TokenStream& ts, JSAtom* atom, Node pn) Definition* newDecl = &pn->template as(); 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 @@ -2374,9 +2392,31 @@ Parser::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. diff --git a/js/src/js.msg b/js/src/js.msg index 62d263d6f..961eccf7a 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -249,6 +249,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") diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index bcf52ce0b..f4ae35d8f 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -772,6 +772,7 @@ struct JSCompartment DeprecatedFlagsArgument = 7, // JS 1.3 or older // NO LONGER USING 8 // NO LONGER USING 9 + DeprecatedBlockScopeFunRedecl = 10, DeprecatedLanguageExtensionCount }; diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js new file mode 100644 index 000000000..6f023b454 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-deprecated-redecl.js @@ -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); diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 77d7c5362..8a08927c1 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -853,6 +853,20 @@ class StaticBlockObject : public BlockObject 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(v.toPrivate()); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 968a46a0d..f15d5affc 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 331; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 332; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 424, +static_assert(JSErr_Limit == 425, "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 " diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index b57b4d085..27b81b7b4 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -481,14 +481,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",