From 5f99a47be94ccfe922d750777f0b3981afd20fa1 Mon Sep 17 00:00:00 2001 From: Eric Faust Date: Thu, 30 Oct 2014 17:27:03 -0700 Subject: [PATCH] Bug 611388 - |const| should be block scoped and require an initializer. (r=shu) --- .../tests/chrome/templates_shared.js | 23 +- js/src/frontend/BytecodeEmitter.cpp | 20 +- js/src/frontend/FullParseHandler.h | 4 +- js/src/frontend/ParseNode.cpp | 4 +- js/src/frontend/ParseNode.h | 16 +- js/src/frontend/Parser.cpp | 219 ++++++++++++------ js/src/frontend/Parser.h | 13 +- js/src/jit-test/tests/asm.js/testGlobals.js | 1 - .../jit-test/tests/auto-regress/bug487570.js | 2 +- .../jit-test/tests/auto-regress/bug495843.js | 2 +- js/src/jit-test/tests/basic/bug639797.js | 2 +- .../tests/basic/functionRedeclConst.js | 5 + .../tests/basic/functionRedeclGlobalConst.js | 3 + .../jit-test/tests/basic/functionRedeclLet.js | 5 + js/src/jit-test/tests/basic/letTDZDelete.js | 13 +- .../jit-test/tests/basic/letTDZEffectful.js | 13 +- .../basic/syntax-error-illegal-character.js | 8 +- .../basic/testDestructuringVarInsideWith.js | 2 +- .../tests/jaeger/recompile/bug641269.js | 2 + js/src/js.msg | 7 +- js/src/jsreflect.cpp | 26 ++- js/src/jsscript.cpp | 4 +- js/src/jsscript.h | 5 + .../ecma_5/Object/freeze-global-eval-const.js | 2 +- .../tests/js1_5/Regress/regress-360969-03.js | 2 +- .../tests/js1_5/Regress/regress-360969-04.js | 2 +- .../tests/js1_5/extensions/regress-452565.js | 2 +- js/src/tests/js1_6/Regress/regress-372565.js | 4 +- js/src/tests/js1_7/block/regress-349507.js | 3 +- js/src/tests/js1_8/genexps/regress-384991.js | 2 +- .../js1_8_1/regress/regress-452498-068.js | 2 +- .../js1_8_1/regress/regress-452498-092.js | 2 +- .../js1_8_1/regress/regress-452498-101.js | 2 +- .../js1_8_1/regress/regress-452498-102.js | 4 +- .../js1_8_1/regress/regress-452498-112.js | 4 +- .../js1_8_1/regress/regress-452498-117.js | 6 +- .../js1_8_1/regress/regress-452498-160.js | 2 +- .../js1_8_1/regress/regress-452498-185.js | 2 +- .../js1_8_1/regress/regress-452498-187.js | 2 +- .../js1_8_1/regress/regress-452498-192.js | 3 +- js/src/tests/js1_8_1/strict/12.2.1.js | 8 +- .../redeclaration-of-catch-warning.js | 2 +- .../tests/js1_8_5/extensions/reflect-parse.js | 12 +- js/src/vm/ScopeObject.cpp | 30 ++- js/src/vm/ScopeObject.h | 2 +- js/src/vm/Xdr.h | 2 +- .../modules/osfile_shared_allthreads.jsm | 10 +- .../components/places/tests/head_common.js | 5 +- 48 files changed, 312 insertions(+), 204 deletions(-) create mode 100644 js/src/jit-test/tests/basic/functionRedeclConst.js create mode 100644 js/src/jit-test/tests/basic/functionRedeclGlobalConst.js create mode 100644 js/src/jit-test/tests/basic/functionRedeclLet.js diff --git a/dom/xul/templates/tests/chrome/templates_shared.js b/dom/xul/templates/tests/chrome/templates_shared.js index 93b13d316af1..a73e602424d0 100644 --- a/dom/xul/templates/tests/chrome/templates_shared.js +++ b/dom/xul/templates/tests/chrome/templates_shared.js @@ -54,12 +54,23 @@ const debug = false; var expectedConsoleMessages = []; var expectLoggedMessages = null; -try { - const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]. - getService(Components.interfaces.nsIRDFService); - const ContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"]. - getService(Components.interfaces.nsIRDFContainerUtils); -} catch(ex) { } +function get_RDF() { + try { + return Components.classes["@mozilla.org/rdf/rdf-service;1"]. + getService(Components.interfaces.nsIRDFService); + } catch (ex) { } +} + +function get_ContainerUtils() +{ + try { + return Components.classes["@mozilla.org/rdf/container-utils;1"]. + getService(Components.interfaces.nsIRDFContainerUtils); + } catch(ex) { } +} + +const RDF = get_RDF(); +const ContainerUtils = get_ContainerUtils(); var xmlDoc; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 039b51938c64..5f6c04c06f2f 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1240,7 +1240,7 @@ LookupAliasedName(BytecodeEmitter *bce, HandleScript script, PropertyName *name, if (freeVariables[i].isHoistedUse() && bindingIndex >= lexicalBegin) { MOZ_ASSERT(pn); MOZ_ASSERT(pn->isUsed()); - pn->pn_dflags |= PND_LET; + pn->pn_dflags |= PND_LEXICAL; } break; @@ -1289,7 +1289,7 @@ AssignHops(BytecodeEmitter *bce, ParseNode *pn, unsigned src, ScopeCoordinate *d static inline MaybeCheckLexical NodeNeedsCheckLexical(ParseNode *pn) { - return pn->isHoistedLetUse() ? CheckLexical : DontCheckLexical; + return pn->isHoistedLexicalUse() ? CheckLexical : DontCheckLexical; } static bool @@ -1461,6 +1461,7 @@ BytecodeEmitter::isAliasedName(ParseNode *pn) switch (dn->kind()) { case Definition::LET: + case Definition::CONST: /* * There are two ways to alias a let variable: nested functions and * dynamic scope operations. (This is overly conservative since the @@ -1485,7 +1486,7 @@ BytecodeEmitter::isAliasedName(ParseNode *pn) */ return script->formalIsAliased(pn->pn_cookie.slot()); case Definition::VAR: - case Definition::CONST: + case Definition::GLOBALCONST: MOZ_ASSERT_IF(sc->allLocalsAliased(), script->varIsAliased(pn->pn_cookie.slot())); return script->varIsAliased(pn->pn_cookie.slot()); case Definition::PLACEHOLDER: @@ -1777,6 +1778,7 @@ BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case Definition::VAR: + case Definition::GLOBALCONST: case Definition::CONST: case Definition::LET: switch (op) { @@ -2077,7 +2079,7 @@ CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool } } - if (pn->isHoistedLetUse()) { + if (pn->isHoistedLexicalUse()) { // Hoisted uses of lexical bindings throw on access. *answer = true; } @@ -4656,7 +4658,7 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet) ParseNode *varList = pnLet->pn_left; MOZ_ASSERT(varList->isArity(PN_LIST)); ParseNode *letBody = pnLet->pn_right; - MOZ_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE)); + MOZ_ASSERT(letBody->isLexical() && letBody->isKind(PNK_LEXICALSCOPE)); int letHeadDepth = bce->stackDepth; @@ -4746,7 +4748,7 @@ static bool EmitForInOrOfVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *letDecl) { *letDecl = pn->isKind(PNK_LEXICALSCOPE); - MOZ_ASSERT_IF(*letDecl, pn->isLet()); + MOZ_ASSERT_IF(*letDecl, pn->isLexical()); // If the left part is 'var x', emit code to define x if necessary using a // prolog opcode, but do not emit a pop. If it is 'let x', EnterBlockScope @@ -6699,7 +6701,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) // Assign the destructuring arguments before defining any functions, // see bug 419662. MOZ_ASSERT(pnchild->isKind(PNK_SEMI)); - MOZ_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_CONST)); + MOZ_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_GLOBALCONST)); if (!EmitTree(cx, bce, pnchild)) return false; pnchild = pnchild->pn_next; @@ -6832,7 +6834,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_VAR: - case PNK_CONST: + case PNK_GLOBALCONST: if (!EmitVariables(cx, bce, pn, InitializeVars)) return false; break; @@ -6995,6 +6997,8 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_LET: + case PNK_CONST: + MOZ_ASSERT_IF(pn->isKind(PNK_CONST), !pn->isArity(PN_BINARY)); ok = pn->isArity(PN_BINARY) ? EmitLet(cx, bce, pn) : EmitVariables(cx, bce, pn, InitializeVars); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index a86dcff80a98..9ed975ddcef9 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -667,8 +667,8 @@ class FullParseHandler uint16_t firstDominatingLexicalSlot) { MOZ_ASSERT(pn->isUsed()); - if (dn->isLet() && dn->pn_cookie.slot() < firstDominatingLexicalSlot) - pn->pn_dflags |= PND_LET; + if (dn->isLexical() && dn->pn_cookie.slot() < firstDominatingLexicalSlot) + pn->pn_dflags |= PND_LEXICAL; } static uintptr_t definitionToBits(Definition *dn) { diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index a51bc7b7af4e..8327cc2f03fe 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -308,7 +308,7 @@ const char * Definition::kindString(Kind kind) { static const char * const table[] = { - "", js_var_str, js_const_str, js_let_str, js_function_str, "argument", "unknown" + "", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown" }; MOZ_ASSERT(unsigned(kind) <= unsigned(ARG)); @@ -505,7 +505,7 @@ Parser::cloneLeftHandSide(ParseNode *opn) if (opn->isDefn()) { /* We copied some definition-specific state into pn. Clear it out. */ pn->pn_cookie.makeFree(); - pn->pn_dflags &= ~(PND_LET | PND_BOUND); + pn->pn_dflags &= ~(PND_LEXICAL | PND_BOUND); pn->setDefn(false); handler.linkUseToDef(pn, (Definition *) opn); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 2a4d73922290..4ef509ec9bf6 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -114,6 +114,7 @@ class UpvarCookie F(CONTINUE) \ F(VAR) \ F(CONST) \ + F(GLOBALCONST) \ F(WITH) \ F(RETURN) \ F(NEW) \ @@ -682,7 +683,8 @@ class ParseNode Definition *resolve(); /* PN_CODE and PN_NAME pn_dflags bits. */ -#define PND_LET 0x01 /* let (block-scoped) binding or use of a hoisted let */ +#define PND_LEXICAL 0x01 /* lexical (block-scoped) binding or use of a hoisted + let or const */ #define PND_CONST 0x02 /* const binding (orthogonal to let) */ #define PND_ASSIGNED 0x04 /* set if ever LHS of assignment */ #define PND_PLACEHOLDER 0x08 /* placeholder definition for lexdep */ @@ -764,7 +766,7 @@ class ParseNode inline bool test(unsigned flag) const; - bool isLet() const { return test(PND_LET) && !isUsed(); } + bool isLexical() const { return test(PND_LEXICAL) && !isUsed(); } bool isConst() const { return test(PND_CONST); } bool isPlaceholder() const { return test(PND_PLACEHOLDER); } bool isDeoptimized() const { return test(PND_DEOPTIMIZED); } @@ -772,7 +774,7 @@ class ParseNode bool isClosed() const { return test(PND_CLOSED); } bool isBound() const { return test(PND_BOUND); } bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); } - bool isHoistedLetUse() const { return test(PND_LET) && isUsed(); } + bool isHoistedLexicalUse() const { return test(PND_LEXICAL) && isUsed(); } /* True if pn is a parsenode representing a literal constant. */ bool isLiteral() const { @@ -1414,7 +1416,7 @@ struct Definition : public ParseNode return pn_cookie.isFree(); } - enum Kind { MISSING = 0, VAR, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER }; + enum Kind { MISSING = 0, VAR, GLOBALCONST, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER }; bool canHaveInitializer() { return int(kind()) <= int(ARG); } @@ -1433,10 +1435,10 @@ struct Definition : public ParseNode return PLACEHOLDER; if (isOp(JSOP_GETARG)) return ARG; + if (isLexical()) + return isConst() ? CONST : LET; if (isConst()) - return CONST; - if (isLet()) - return LET; + return GLOBALCONST; return VAR; } }; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index f884e872013e..762e11916314 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -122,7 +122,7 @@ MarkUsesAsHoistedLexical(ParseNode *pn) // Distinguish hoisted uses as a different JSOp for easier compilation. while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->isUsed()); - pnu->pn_dflags |= PND_LET; + pnu->pn_dflags |= PND_LEXICAL; pnup = &pnu->pn_link; } } @@ -137,7 +137,7 @@ ParseContext::define(TokenStream &ts, MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); Definition *prevDef = nullptr; - if (kind == Definition::LET) + if (kind == Definition::LET || kind == Definition::CONST) prevDef = decls_.lookupFirst(name); else MOZ_ASSERT(!decls_.lookupFirst(name)); @@ -148,7 +148,8 @@ ParseContext::define(TokenStream &ts, if (prevDef) { ParseNode **pnup = &prevDef->dn_uses; ParseNode *pnu; - unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid; + unsigned start = (kind == Definition::LET || kind == Definition::CONST) ? pn->pn_blockid + : bodyid; while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->pn_blockid >= bodyid); @@ -170,7 +171,7 @@ ParseContext::define(TokenStream &ts, pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED; } - MOZ_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name)); + MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONST, !lexdeps->lookup(name)); pn->setDefn(true); pn->pn_dflags &= ~PND_PLACEHOLDER; if (kind == Definition::CONST) @@ -197,7 +198,7 @@ ParseContext::define(TokenStream &ts, return false; break; - case Definition::CONST: + case Definition::GLOBALCONST: case Definition::VAR: if (sc->isFunctionBox()) { dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETLOCAL : JSOP_GETLOCAL); @@ -215,8 +216,9 @@ ParseContext::define(TokenStream &ts, break; case Definition::LET: + case Definition::CONST: dn->setOp(JSOP_INITLEXICAL); - dn->pn_dflags |= (PND_LET | PND_BOUND); + dn->pn_dflags |= (PND_LEXICAL | PND_BOUND); MOZ_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */ if (atBodyLevel()) { if (!bodyLevelLexicals_.append(dn)) @@ -314,7 +316,8 @@ template void ParseContext::popLetDecl(JSAtom *atom) { - MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET); + MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET || + ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::CONST); decls_.remove(atom); } @@ -338,6 +341,7 @@ AppendPackedBindings(const ParseContext *pc, const DeclVector &vec kind = Binding::VARIABLE; break; case Definition::CONST: + case Definition::GLOBALCONST: kind = Binding::CONSTANT; break; case Definition::ARG: @@ -1175,6 +1179,7 @@ struct BindData JSOp op; /* prolog bytecode or nop */ Binder binder; /* binder, discriminates u */ + bool isConst; /* const binding? */ struct LetData { explicit LetData(ExclusiveContext *cx) : blockObj(cx) {} @@ -1183,18 +1188,21 @@ struct BindData unsigned overflow; } let; - void initLet(VarContext varContext, StaticBlockObject *blockObj, unsigned overflow) { + void initLexical(VarContext varContext, StaticBlockObject *blockObj, unsigned overflow, + bool isConst = false) { this->pn = ParseHandler::null(); this->op = JSOP_INITLEXICAL; - this->binder = Parser::bindLet; + this->isConst = isConst; + this->binder = Parser::bindLexical; this->let.varContext = varContext; this->let.blockObj = blockObj; this->let.overflow = overflow; } - void initVarOrConst(JSOp op) { + void initVarOrGlobalConst(JSOp op) { this->op = op; - this->binder = Parser::bindVarOrConst; + this->isConst = op == JSOP_DEFCONST; + this->binder = Parser::bindVarOrGlobalConst; } }; @@ -1295,7 +1303,7 @@ static bool IsNonDominatingInScopedSwitch(ParseContext *pc, HandleAtom name, Definition *dn) { - MOZ_ASSERT(dn->isLet()); + MOZ_ASSERT(dn->isLexical()); StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); if (stmt && stmt->type == STMT_SWITCH) return dn->pn_cookie.slot() < stmt->firstDominatingLexicalInCase; @@ -1304,9 +1312,9 @@ IsNonDominatingInScopedSwitch(ParseContext *pc, HandleAtom nam static void AssociateUsesWithOuterDefinition(ParseNode *pnu, Definition *dn, Definition *outer_dn, - bool markUsesAsLet) + bool markUsesAsLexical) { - uint32_t dflags = markUsesAsLet ? PND_LET : 0; + uint32_t dflags = markUsesAsLexical ? PND_LEXICAL : 0; while (true) { pnu->pn_lexdef = outer_dn; pnu->pn_dflags |= dflags; @@ -1418,10 +1426,10 @@ Parser::leaveFunction(ParseNode *fn, ParseContextisLet() && - (bodyLevelHoistedUse || - IsNonDominatingInScopedSwitch(outerpc, name, outer_dn)); - AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLet); + bool markUsesAsLexical = outer_dn->isLexical() && + (bodyLevelHoistedUse || + IsNonDominatingInScopedSwitch(outerpc, name, outer_dn)); + AssociateUsesWithOuterDefinition(pnu, dn, outer_dn, markUsesAsLexical); } outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER; @@ -1770,7 +1778,8 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, MOZ_ASSERT(!dn->isUsed()); MOZ_ASSERT(dn->isDefn()); - bool throwRedeclarationError = dn->kind() == Definition::CONST || + bool throwRedeclarationError = dn->kind() == Definition::GLOBALCONST || + dn->kind() == Definition::CONST || dn->kind() == Definition::LET; if (options().extraWarningsOption || throwRedeclarationError) { JSAutoByteString name; @@ -1999,7 +2008,10 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). */ if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { - if (dn == Definition::CONST) { + if (dn == Definition::GLOBALCONST || + dn == Definition::CONST || + dn == Definition::LET) + { JSAutoByteString name; if (!AtomToPrintableString(context, funName, &name) || !report(ParseError, false, null(), JSMSG_REDECLARED_VAR, @@ -2853,7 +2865,7 @@ Parser::matchLabel(MutableHandle label) template bool -Parser::reportRedeclaration(Node pn, bool isConst, HandlePropertyName name) +Parser::reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name) { JSAutoByteString printable; if (!AtomToPrintableString(context, name, &printable)) @@ -2863,14 +2875,18 @@ Parser::reportRedeclaration(Node pn, bool isConst, HandlePropertyN if (stmt && stmt->type == STMT_CATCH) { report(ParseError, false, pn, JSMSG_REDECLARED_CATCH_IDENTIFIER, printable.ptr()); } else { - report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", - printable.ptr()); + if (redeclKind == Definition::ARG) { + report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, printable.ptr()); + } else { + report(ParseError, false, pn, JSMSG_REDECLARED_VAR, Definition::kindString(redeclKind), + printable.ptr()); + } } return false; } /* - * Define a let-variable in a block, let-expression, or comprehension scope. pc + * Define a lexical binding in a block, let-expression, or comprehension scope. pc * must already be in such a scope. * * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a @@ -2880,8 +2896,8 @@ Parser::reportRedeclaration(Node pn, bool isConst, HandlePropertyN */ template <> /* static */ bool -Parser::bindLet(BindData *data, - HandlePropertyName name, Parser *parser) +Parser::bindLexical(BindData *data, + HandlePropertyName name, Parser *parser) { ParseContext *pc = parser->pc; ParseNode *pn = data->pn; @@ -2918,25 +2934,34 @@ Parser::bindLet(BindData *data, if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index)) return false; + Definition *dn = pc->decls().lookupFirst(name); + Definition::Kind bindingKind = data->isConst ? Definition::CONST : Definition::LET; + /* * For bindings that are hoisted to the beginning of the block/function, * define() right now. Otherwise, delay define until PushLetScope. */ if (data->let.varContext == HoistVars) { - Definition *dn = pc->decls().lookupFirst(name); if (dn && dn->pn_blockid == pc->blockid()) - return parser->reportRedeclaration(pn, dn->isConst(), name); - if (!pc->define(parser->tokenStream, name, pn, Definition::LET)) + return parser->reportRedeclaration(pn, dn->kind(), name); + if (!pc->define(parser->tokenStream, name, pn, bindingKind)) return false; } if (blockObj) { bool redeclared; RootedId id(cx, NameToId(name)); - RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared)); + RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, + data->isConst, index, &redeclared)); if (!shape) { - if (redeclared) - parser->reportRedeclaration(pn, false, name); + if (redeclared) { + // The only way to be redeclared without a previous definition is if we're in a + // comma separated list in a DontHoistVars block, so a let block of for header. In + // that case, we must be redeclaring the same type of definition as we're trying to + // make. + Definition::Kind dnKind = dn ? dn->kind() : bindingKind; + parser->reportRedeclaration(pn, dnKind, name); + } return false; } @@ -2954,8 +2979,8 @@ Parser::bindLet(BindData *data, template <> /* static */ bool -Parser::bindLet(BindData *data, - HandlePropertyName name, Parser *parser) +Parser::bindLexical(BindData *data, + HandlePropertyName name, Parser *parser) { if (!parser->checkStrictBinding(name, data->pn)) return false; @@ -3097,8 +3122,8 @@ OuterLet(ParseContext *pc, StmtInfoPC *stmt, HandleAtom atom) template /* static */ bool -Parser::bindVarOrConst(BindData *data, - HandlePropertyName name, Parser *parser) +Parser::bindVarOrGlobalConst(BindData *data, + HandlePropertyName name, Parser *parser) { ExclusiveContext *cx = parser->context; ParseContext *pc = parser->pc; @@ -3138,7 +3163,7 @@ Parser::bindVarOrConst(BindData *data, if (defs.empty()) { return pc->define(parser->tokenStream, name, pn, - isConstDecl ? Definition::CONST : Definition::VAR); + isConstDecl ? Definition::GLOBALCONST : Definition::VAR); } /* @@ -3165,6 +3190,7 @@ Parser::bindVarOrConst(BindData *data, bool inCatchBody = (stmt && stmt->type == STMT_CATCH); bool error = (isConstDecl || dn_kind == Definition::CONST || + dn_kind == Definition::GLOBALCONST || (dn_kind == Definition::LET && (!inCatchBody || OuterLet(pc, stmt, name)))); @@ -3511,7 +3537,7 @@ Parser::pushLetScope(HandleStaticBlockObject blockObj, StmtInf if (!pn) return null(); - pn->pn_dflags |= PND_LET; + pn->pn_dflags |= PND_LEXICAL; /* Populate the new scope with decls found in the head with updated blockid. */ if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid))) @@ -3698,10 +3724,10 @@ Parser::variables(ParseNodeKind kind, bool *psimple, * The four options here are: * - PNK_VAR: We're parsing var declarations. * - PNK_CONST: We're parsing const declarations. + * - PNK_GLOBALCONST: We're parsing const declarations at toplevel (see bug 589119). * - PNK_LET: We are parsing a let declaration. - * - PNK_CALL: We are parsing the head of a let block. */ - MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL); + MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_GLOBALCONST); /* * The simple flag is set if the declaration has the form 'var x', with @@ -3709,7 +3735,11 @@ Parser::variables(ParseNodeKind kind, bool *psimple, */ MOZ_ASSERT_IF(psimple, *psimple); - JSOp op = kind == PNK_LET ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST; + JSOp op = JSOP_NOP; + if (kind == PNK_VAR) + op = JSOP_DEFVAR; + else if (kind == PNK_GLOBALCONST) + op = JSOP_DEFCONST; Node pn = handler.newList(kind, null(), op); if (!pn) @@ -3721,10 +3751,12 @@ Parser::variables(ParseNodeKind kind, bool *psimple, * this code will change soon. */ BindData data(context); - if (kind == PNK_LET) - data.initLet(varContext, blockObj, JSMSG_TOO_MANY_LOCALS); - else - data.initVarOrConst(op); + if (kind == PNK_VAR || kind == PNK_GLOBALCONST) { + data.initVarOrGlobalConst(op); + } else { + data.initLexical(varContext, blockObj, JSMSG_TOO_MANY_LOCALS, + /* isConst = */ kind == PNK_CONST); + } bool first = true; Node pn2; @@ -3757,7 +3789,8 @@ Parser::variables(ParseNodeKind kind, bool *psimple, // See comment below for bindBeforeInitializer in the code that // handles the non-destructuring case. - bool bindBeforeInitializer = kind != PNK_LET || parsingForInOrOfInit; + bool bindBeforeInitializer = (kind != PNK_LET && kind != PNK_CONST) || + parsingForInOrOfInit; if (bindBeforeInitializer && !checkDestructuring(&data, pn2)) return null(); @@ -3794,10 +3827,10 @@ Parser::variables(ParseNodeKind kind, bool *psimple, } RootedPropertyName name(context, tokenStream.currentName()); - pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext); + pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_GLOBALCONST, varContext); if (!pn2) return null(); - if (data.op == JSOP_DEFCONST) + if (data.isConst) handler.setFlag(pn2, PND_CONST); data.pn = pn2; @@ -3818,7 +3851,7 @@ Parser::variables(ParseNodeKind kind, bool *psimple, // If we are not parsing a let declaration, bind the name // now. Otherwise we must wait until after parsing the initializing // assignment. - bool bindBeforeInitializer = kind != PNK_LET; + bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST; if (bindBeforeInitializer && !data.binder(&data, name, this)) return null(); @@ -3832,6 +3865,11 @@ Parser::variables(ParseNodeKind kind, bool *psimple, if (!handler.finishInitializerAssignment(pn2, init, data.op)) return null(); } else { + if (data.isConst && !pc->parsingForInit) { + report(ParseError, false, null(), JSMSG_BAD_CONST_DECL); + return null(); + } + if (!data.binder(&data, name, this)) return null(); } @@ -3849,7 +3887,7 @@ Parser::variables(ParseNodeKind kind, bool *psimple, template <> ParseNode * -Parser::letDeclaration() +Parser::lexicalDeclaration(bool isConst) { handler.disableSyntaxParser(); @@ -3869,7 +3907,8 @@ Parser::letDeclaration() */ StmtInfoPC *stmt = pc->topStmt; if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { - report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK); + report(ParseError, false, null(), JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + isConst ? "const" : "let"); return null(); } @@ -3888,9 +3927,10 @@ Parser::letDeclaration() * conflicting slots. Forbid top-level let declarations to * prevent such conflicts from ever occurring. */ - bool globalLet = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt; - if (options().selfHostingMode && globalLet) { - report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LET); + bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt; + if (options().selfHostingMode && isGlobal) { + report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, + isConst ? "'const'" : "'let'"); return null(); } @@ -3908,7 +3948,12 @@ Parser::letDeclaration() * FIXME global-level lets are still considered vars until * other bugs are fixed. */ - pn = variables(globalLet ? PNK_VAR : PNK_LET); + ParseNodeKind kind = PNK_LET; + if (isGlobal) + kind = isConst ? PNK_GLOBALCONST : PNK_VAR; + else if (isConst) + kind = PNK_CONST; + pn = variables(kind); if (!pn) return null(); pn->pn_xflags |= PNX_POPVAR; @@ -3968,7 +4013,8 @@ Parser::letDeclaration() pc->blockNode = pn1; } - pn = variables(PNK_LET, nullptr, &pc->staticScope->as(), HoistVars); + pn = variables(isConst ? PNK_CONST : PNK_LET, nullptr, + &pc->staticScope->as(), HoistVars); if (!pn) return null(); pn->pn_xflags = PNX_POPVAR; @@ -3979,7 +4025,7 @@ Parser::letDeclaration() template <> SyntaxParseHandler::Node -Parser::letDeclaration() +Parser::lexicalDeclaration(bool) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; @@ -4000,7 +4046,7 @@ Parser::letStatement() pn = letBlock(LetStatement); MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); } else { - pn = letDeclaration(); + pn = lexicalDeclaration(/* isConst = */ false); } return pn; } @@ -4257,8 +4303,7 @@ Parser::exportDeclaration() break; case TOK_VAR: - case TOK_CONST: - kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); + kid = variables(PNK_VAR); if (!kid) return null(); kid->pn_xflags = PNX_POPVAR; @@ -4274,7 +4319,8 @@ Parser::exportDeclaration() // and fall through. tokenStream.ungetToken(); case TOK_LET: - kid = letDeclaration(); + case TOK_CONST: + kid = lexicalDeclaration(tt == TOK_CONST); if (!kid) return null(); break; @@ -4418,7 +4464,7 @@ Parser::isValidForStatementLHS(ParseNode *pn1, JSVersion versi if (isForDecl) { if (pn1->pn_count > 1) return false; - if (pn1->isOp(JSOP_DEFCONST)) + if (pn1->isKind(PNK_CONST)) return false; // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning. @@ -4456,6 +4502,22 @@ Parser::isValidForStatementLHS(ParseNode *pn1, JSVersion versi } } +template <> +bool +Parser::checkForHeadConstInitializers(ParseNode *pn1) +{ + if (!pn1->isKind(PNK_CONST)) + return true; + + for (ParseNode *assign = pn1->pn_head; assign; assign = assign->pn_next) { + MOZ_ASSERT(assign->isKind(PNK_ASSIGN) || assign->isKind(PNK_NAME)); + if (assign->isKind(PNK_NAME) && !assign->isAssigned()) + return false; + // PNK_ASSIGN nodes (destructuring assignment) are always assignments. + } + return true; +} + template <> ParseNode * Parser::forStatement() @@ -4515,11 +4577,11 @@ Parser::forStatement() * clause of an ordinary for loop. */ pc->parsingForInit = true; - if (tt == TOK_VAR || tt == TOK_CONST) { + if (tt == TOK_VAR) { isForDecl = true; tokenStream.consumeKnownToken(tt); - pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); - } else if (tt == TOK_LET) { + pn1 = variables(PNK_VAR); + } else if (tt == TOK_LET || tt == TOK_CONST) { handler.disableSyntaxParser(); tokenStream.consumeKnownToken(tt); if (!tokenStream.peekToken(&tt)) @@ -4531,7 +4593,8 @@ Parser::forStatement() blockObj = StaticBlockObject::create(context); if (!blockObj) return null(); - pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars); + pn1 = variables(tt == TOK_CONST ? PNK_CONST: PNK_LET, nullptr, blockObj, + DontHoistVars); } } else { pn1 = expr(); @@ -4723,8 +4786,14 @@ Parser::forStatement() if (blockObj) { /* * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }' - * to induce the correct scoping for A. + * to induce the correct scoping for A. Ensure here that the previously + * unchecked assignment mandate for const declarations holds. */ + if (!checkForHeadConstInitializers(pn1)) { + report(ParseError, false, nullptr, JSMSG_BAD_CONST_DECL); + return null(); + } + forLetImpliedBlock = pushLetScope(blockObj, &letStmt); if (!forLetImpliedBlock) return null(); @@ -4849,7 +4918,7 @@ Parser::forStatement() if (tt == TOK_VAR) { isForDecl = true; tokenStream.consumeKnownToken(tt); - lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl); + lhsNode = variables(PNK_VAR, &simpleForDecl); } else if (tt == TOK_CONST || tt == TOK_LET) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); @@ -5518,8 +5587,8 @@ Parser::tryStatement() * scoped, not a property of a new Object instance. This is * an intentional change that anticipates ECMA Ed. 4. */ - data.initLet(HoistVars, &pc->staticScope->template as(), - JSMSG_TOO_MANY_CATCH_VARS); + data.initLexical(HoistVars, &pc->staticScope->template as(), + JSMSG_TOO_MANY_CATCH_VARS); MOZ_ASSERT(data.let.blockObj); if (!tokenStream.getToken(&tt)) @@ -5646,9 +5715,10 @@ Parser::statement(bool canHaveDirectives) case TOK_CONST: if (!abortIfSyntaxParser()) return null(); - // FALL THROUGH + return lexicalDeclaration(/* isConst = */ true); + case TOK_VAR: { - Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR); + Node pn = variables(PNK_VAR); if (!pn) return null(); @@ -6625,7 +6695,8 @@ Parser::legacyComprehensionTail(ParseNode *bodyExpr, unsigned return null(); MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object); - data.initLet(HoistVars, &pc->staticScope->as(), JSMSG_ARRAY_INIT_TOO_BIG); + data.initLexical(HoistVars, &pc->staticScope->as(), + JSMSG_ARRAY_INIT_TOO_BIG); while (true) { /* @@ -7068,7 +7139,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); if (!blockObj) return null(); - data.initLet(DontHoistVars, blockObj, JSMSG_TOO_MANY_LOCALS); + data.initLexical(DontHoistVars, blockObj, JSMSG_TOO_MANY_LOCALS); Node lhs = newName(name); if (!lhs) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 06293e08f8d9..c28f0f036076 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -542,7 +542,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node tryStatement(); Node debuggerStatement(); - Node letDeclaration(); + Node lexicalDeclaration(bool isConst); Node letStatement(); Node importDeclaration(); Node exportDeclaration(); @@ -634,6 +634,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach, ParseNodeKind headKind); + bool checkForHeadConstInitializers(Node pn1); bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder); bool checkStrictAssignment(Node lhs); bool checkStrictBinding(PropertyName *name, Node pn); @@ -667,16 +668,16 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter HandlePropertyName name, Parser *parser); static bool - bindLet(BindData *data, - HandlePropertyName name, Parser *parser); + bindLexical(BindData *data, + HandlePropertyName name, Parser *parser); static bool - bindVarOrConst(BindData *data, - HandlePropertyName name, Parser *parser); + bindVarOrGlobalConst(BindData *data, + HandlePropertyName name, Parser *parser); static Node null() { return ParseHandler::null(); } - bool reportRedeclaration(Node pn, bool isConst, HandlePropertyName name); + bool reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name); bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum); DefinitionNode getOrCreateLexicalDependency(ParseContext *pc, JSAtom *atom); diff --git a/js/src/jit-test/tests/asm.js/testGlobals.js b/js/src/jit-test/tests/asm.js/testGlobals.js index a41c6fe95ab2..3ceea9612058 100644 --- a/js/src/jit-test/tests/asm.js/testGlobals.js +++ b/js/src/jit-test/tests/asm.js/testGlobals.js @@ -2,7 +2,6 @@ load(libdir + "asm.js"); load(libdir + "asserts.js"); assertAsmTypeFail(USE_ASM + "var i; function f(){} return f"); -assertAsmTypeFail(USE_ASM + "const i; function f(){} return f"); assertEq(asmLink(asmCompile(USE_ASM + "var i=0; function f(){} return f"))(), undefined); assertEq(asmLink(asmCompile(USE_ASM + "const i=0; function f(){} return f"))(), undefined); assertEq(asmLink(asmCompile(USE_ASM + "var i=42; function f(){ return i|0 } return f"))(), 42); diff --git a/js/src/jit-test/tests/auto-regress/bug487570.js b/js/src/jit-test/tests/auto-regress/bug487570.js index 9e4a7b349616..d8f0b09d1769 100644 --- a/js/src/jit-test/tests/auto-regress/bug487570.js +++ b/js/src/jit-test/tests/auto-regress/bug487570.js @@ -9,5 +9,5 @@ for each (y in []) ) ) -{const functional} +{const functional=undefined} })() diff --git a/js/src/jit-test/tests/auto-regress/bug495843.js b/js/src/jit-test/tests/auto-regress/bug495843.js index 137e4bb26311..673e8294eca6 100644 --- a/js/src/jit-test/tests/auto-regress/bug495843.js +++ b/js/src/jit-test/tests/auto-regress/bug495843.js @@ -1,5 +1,5 @@ // Binary: cache/js-dbg-64-fe91973cc783-linux // Flags: // -const x;[x]=''; +const [x]=''; for(;[] && false;){} diff --git a/js/src/jit-test/tests/basic/bug639797.js b/js/src/jit-test/tests/basic/bug639797.js index c1511ba1790a..f061fac46d18 100644 --- a/js/src/jit-test/tests/basic/bug639797.js +++ b/js/src/jit-test/tests/basic/bug639797.js @@ -1 +1 @@ -Function("with([])const x=0")() +Function("with([]){const x=0}")() diff --git a/js/src/jit-test/tests/basic/functionRedeclConst.js b/js/src/jit-test/tests/basic/functionRedeclConst.js new file mode 100644 index 000000000000..8b0af6546507 --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclConst.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +{ + const x = 0; + function x() { } +} diff --git a/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js b/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js new file mode 100644 index 000000000000..90784cc18028 --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js @@ -0,0 +1,3 @@ +// |jit-test| error: TypeError +const x = 0; +function x() { } diff --git a/js/src/jit-test/tests/basic/functionRedeclLet.js b/js/src/jit-test/tests/basic/functionRedeclLet.js new file mode 100644 index 000000000000..6ec6dfd499ab --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclLet.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +{ + let x; + function x() { } +} diff --git a/js/src/jit-test/tests/basic/letTDZDelete.js b/js/src/jit-test/tests/basic/letTDZDelete.js index 6fb1048f0cbe..966ee0ef1f64 100644 --- a/js/src/jit-test/tests/basic/letTDZDelete.js +++ b/js/src/jit-test/tests/basic/letTDZDelete.js @@ -9,15 +9,4 @@ function assertThrowsReferenceError(f) { } assertThrowsReferenceError(function () { delete x; let x; }); - -// FIXME do this unconditionally once bug 611388 lands. -function constIsLexical() { - try { - (function () { z++; const z; })(); - return false; - } catch (e) { - return true; - } -} -if (constIsLexical()) - assertThrowsReferenceError(function () { delete x; const x; }); +assertThrowsReferenceError(function () { delete x; const x = undefined; }); diff --git a/js/src/jit-test/tests/basic/letTDZEffectful.js b/js/src/jit-test/tests/basic/letTDZEffectful.js index e2f859bf1e43..62245d8c16be 100644 --- a/js/src/jit-test/tests/basic/letTDZEffectful.js +++ b/js/src/jit-test/tests/basic/letTDZEffectful.js @@ -10,15 +10,4 @@ function assertThrowsReferenceError(f) { // TDZ is effectful, don't optimize out x. assertThrowsReferenceError(function () { x; let x; }); - -// FIXME do this unconditionally once bug 611388 lands. -function constIsLexical() { - try { - (function () { z++; const z; })(); - return false; - } catch (e) { - return true; - } -} -if (constIsLexical()) - assertThrowsReferenceError(function () { x; const x; }); +assertThrowsReferenceError(function () { x; const x = undefined; }); diff --git a/js/src/jit-test/tests/basic/syntax-error-illegal-character.js b/js/src/jit-test/tests/basic/syntax-error-illegal-character.js index 3544308c912d..1f186667f6d3 100644 --- a/js/src/jit-test/tests/basic/syntax-error-illegal-character.js +++ b/js/src/jit-test/tests/basic/syntax-error-illegal-character.js @@ -235,10 +235,10 @@ test("const x = 1 @"); test("const x = 1 + @"); test("const x = 1 + 2 @"); test("const x = 1 + 2, @"); -test("const x = 1 + 2, y @"); -test("const x = 1 + 2, y, @"); -test("const x = 1 + 2, y, z @"); -test("const x = 1 + 2, y, z; @"); +test("const x = 1 + 2, y = 0@"); +test("const x = 1 + 2, y = 0, @"); +test("const x = 1 + 2, y = 0, z = 0 @"); +test("const x = 1 + 2, y = 0, z = 0; @"); test("const [ @"); test("const [ x @"); diff --git a/js/src/jit-test/tests/basic/testDestructuringVarInsideWith.js b/js/src/jit-test/tests/basic/testDestructuringVarInsideWith.js index 54cae4a2112b..b233c9ceb2f3 100644 --- a/js/src/jit-test/tests/basic/testDestructuringVarInsideWith.js +++ b/js/src/jit-test/tests/basic/testDestructuringVarInsideWith.js @@ -1,4 +1,4 @@ with ({b:1}) { const [ b ] = []; + assertEq(b, undefined); } -assertEq(b, undefined); diff --git a/js/src/jit-test/tests/jaeger/recompile/bug641269.js b/js/src/jit-test/tests/jaeger/recompile/bug641269.js index 64a2429efdd9..7eca2e2d8c17 100644 --- a/js/src/jit-test/tests/jaeger/recompile/bug641269.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug641269.js @@ -1,3 +1,5 @@ +// |jit-test| error: ReferenceError + var g = newGlobal(); var dbg = new g.Debugger(this); diff --git a/js/src/js.msg b/js/src/js.msg index 7e9845c9ac85..abb167d27405 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -110,7 +110,7 @@ MSG_DEF(JSMSG_BAD_PROTOTYPE, 1, JSEXN_TYPEERR, "'prototype' property o MSG_DEF(JSMSG_IN_NOT_OBJECT, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0, JSEXN_RANGEERR, "too many constructor arguments") MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments") -MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access let declaration `{0}' before initialization") +MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access lexical declaration `{0}' before initialization") // Date MSG_DEF(JSMSG_INVALID_DATE, 0, JSEXN_RANGEERR, "invalid date") @@ -181,6 +181,7 @@ MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated") +MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration") MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop") MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator") MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET, 0, JSEXN_SYNTAXERR, "invalid destructuring target") @@ -244,7 +245,7 @@ MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 0, JSEXN_SYNTAXERR, "for-of loop variable MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") -MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK, 0, JSEXN_SYNTAXERR, "let declaration not directly within block") +MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block") MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression") MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence") MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'") @@ -285,7 +286,7 @@ MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_TYPEERR, "redeclaration of i MSG_DEF(JSMSG_REDECLARED_PARAM, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") MSG_DEF(JSMSG_RESERVED_ID, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") MSG_DEF(JSMSG_REST_WITH_DEFAULT, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default") -MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LET,0, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level 'let' declarations") +MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations") MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups") MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition") MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer") diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 1208bc976f8e..0ad286e2290b 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -1732,7 +1732,7 @@ class ASTSerializer bool sourceElement(ParseNode *pn, MutableHandleValue dst); bool declaration(ParseNode *pn, MutableHandleValue dst); - bool variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst); + bool variableDeclaration(ParseNode *pn, bool lexical, MutableHandleValue dst); bool variableDeclarator(ParseNode *pn, MutableHandleValue dst); bool let(ParseNode *pn, bool expr, MutableHandleValue dst); bool importDeclaration(ParseNode *pn, MutableHandleValue dst); @@ -2001,6 +2001,7 @@ ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst) { MOZ_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_VAR) || + pn->isKind(PNK_GLOBALCONST) || pn->isKind(PNK_LET) || pn->isKind(PNK_CONST)); @@ -2009,21 +2010,28 @@ ASTSerializer::declaration(ParseNode *pn, MutableHandleValue dst) return function(pn, AST_FUNC_DECL, dst); case PNK_VAR: - case PNK_CONST: + case PNK_GLOBALCONST: return variableDeclaration(pn, false, dst); default: - MOZ_ASSERT(pn->isKind(PNK_LET)); + MOZ_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_CONST)); return variableDeclaration(pn, true, dst); } } bool -ASTSerializer::variableDeclaration(ParseNode *pn, bool let, MutableHandleValue dst) +ASTSerializer::variableDeclaration(ParseNode *pn, bool lexical, MutableHandleValue dst) { - MOZ_ASSERT(let ? pn->isKind(PNK_LET) : (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))); + MOZ_ASSERT_IF(lexical, pn->isKind(PNK_LET) || pn->isKind(PNK_CONST)); + MOZ_ASSERT_IF(!lexical, pn->isKind(PNK_VAR) || pn->isKind(PNK_GLOBALCONST)); - VarDeclKind kind = let ? VARDECL_LET : pn->isKind(PNK_VAR) ? VARDECL_VAR : VARDECL_CONST; + VarDeclKind kind = VARDECL_ERR; + // Treat both the toplevel const binding (secretly var-like) and the lexical const + // the same way + if (lexical) + kind = pn->isKind(PNK_LET) ? VARDECL_LET : VARDECL_CONST; + else + kind = pn->isKind(PNK_VAR) ? VARDECL_VAR : VARDECL_CONST; NodeVector dtors(cx); if (!dtors.reserve(pn->pn_count)) @@ -2166,6 +2174,7 @@ ASTSerializer::exportDeclaration(ParseNode *pn, MutableHandleValue dst) case PNK_VAR: case PNK_CONST: + case PNK_GLOBALCONST: case PNK_LET: if (!variableDeclaration(kid, kind == PNK_LET, &decl)) return false; @@ -2308,7 +2317,7 @@ ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst) return true; } - return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) + return (pn->isKind(PNK_VAR) || pn->isKind(PNK_GLOBALCONST)) ? variableDeclaration(pn, false, dst) : expression(pn, dst); } @@ -2341,10 +2350,11 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst) switch (pn->getKind()) { case PNK_FUNCTION: case PNK_VAR: - case PNK_CONST: + case PNK_GLOBALCONST: return declaration(pn, dst); case PNK_LET: + case PNK_CONST: return pn->isArity(PN_BINARY) ? let(pn, false, dst) : declaration(pn, dst); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9c64f062e1d6..de5615dd5ae8 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -129,11 +129,11 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle if (bi->aliased()) { // Per ES6, lexical bindings cannot be accessed until // initialized. Remember the first aliased slot that is a - // body-level let, so that they may be initialized to sentinel + // body-level lexical, so that they may be initialized to sentinel // magic values. if (numBodyLevelLexicals > 0 && nslots < aliasedBodyLevelLexicalBegin && - bi->kind() == Binding::VARIABLE && + bi.isBodyLevelLexical() && bi.localIndex() >= numVars) { aliasedBodyLevelLexicalBegin = nslots; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index e1aa95fb76a2..1cba777dc8a7 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1740,6 +1740,11 @@ class BindingIter MOZ_ASSERT(i_ >= bindings_->numArgs()); return i_ - bindings_->numArgs(); } + bool isBodyLevelLexical() const { + MOZ_ASSERT(!done()); + const Binding &binding = **this; + return binding.kind() != Binding::ARGUMENT; + } const Binding &operator*() const { MOZ_ASSERT(!done()); return bindings_->bindingArray()[i_]; } const Binding *operator->() const { MOZ_ASSERT(!done()); return &bindings_->bindingArray()[i_]; } diff --git a/js/src/tests/ecma_5/Object/freeze-global-eval-const.js b/js/src/tests/ecma_5/Object/freeze-global-eval-const.js index 0fb507c2d768..a43f5c51c9b6 100644 --- a/js/src/tests/ecma_5/Object/freeze-global-eval-const.js +++ b/js/src/tests/ecma_5/Object/freeze-global-eval-const.js @@ -5,7 +5,7 @@ */ try { - evalcx("Object.freeze(this); eval('const q;')"); + evalcx("Object.freeze(this); eval('const q = undefined;')"); } catch (e) { assertEq(e.message, "({lazy:false}) is not extensible"); } diff --git a/js/src/tests/js1_5/Regress/regress-360969-03.js b/js/src/tests/js1_5/Regress/regress-360969-03.js index 8f5c0ece416b..19f29362260d 100644 --- a/js/src/tests/js1_5/Regress/regress-360969-03.js +++ b/js/src/tests/js1_5/Regress/regress-360969-03.js @@ -28,7 +28,7 @@ function test() for (var i = 0; i < limit; i++) { - eval('const pv' + i + ';'); + eval('const pv' + i + ' = undefined;'); } reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/Regress/regress-360969-04.js b/js/src/tests/js1_5/Regress/regress-360969-04.js index 110fea217e2e..ea53e0ebaf80 100644 --- a/js/src/tests/js1_5/Regress/regress-360969-04.js +++ b/js/src/tests/js1_5/Regress/regress-360969-04.js @@ -21,7 +21,7 @@ var limit = 2 << 16; for (var i = 0; i < limit; i++) { - eval('const pv' + i + ';'); + eval('const pv' + i + ' = undefined;'); } reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-452565.js b/js/src/tests/js1_5/extensions/regress-452565.js index a51f6016e998..9a68f2ae417b 100644 --- a/js/src/tests/js1_5/extensions/regress-452565.js +++ b/js/src/tests/js1_5/extensions/regress-452565.js @@ -14,7 +14,7 @@ printStatus (summary); jit(true); -const c; (function() { for (var j=0;j<5;++j) { c = 1; } })(); +const c = undefined; (function() { for (var j=0;j<5;++j) { c = 1; } })(); jit(false); diff --git a/js/src/tests/js1_6/Regress/regress-372565.js b/js/src/tests/js1_6/Regress/regress-372565.js index 1e1056201321..e5c0e1b07bfb 100644 --- a/js/src/tests/js1_6/Regress/regress-372565.js +++ b/js/src/tests/js1_6/Regress/regress-372565.js @@ -19,8 +19,8 @@ function test() enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - - (function() { for each(x in y) { } const x; }); + + (function() { for each(x in y) { } const x = undefined; }); reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_7/block/regress-349507.js b/js/src/tests/js1_7/block/regress-349507.js index dba51204a6b0..e0c4dc4daf95 100644 --- a/js/src/tests/js1_7/block/regress-349507.js +++ b/js/src/tests/js1_7/block/regress-349507.js @@ -20,7 +20,6 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = /TypeError: redeclaration of const b/; try { eval('(function() { let(x = 1) { const b = 2 }; let b = 3; })'); @@ -30,7 +29,7 @@ function test() actual = ex + ''; } - reportMatch(expect, actual, summary); + reportCompare(expect, actual, summary); exitFunc ('test'); } diff --git a/js/src/tests/js1_8/genexps/regress-384991.js b/js/src/tests/js1_8/genexps/regress-384991.js index 7dd1cef7be39..a34adfb3fe6d 100644 --- a/js/src/tests/js1_8/genexps/regress-384991.js +++ b/js/src/tests/js1_8/genexps/regress-384991.js @@ -47,7 +47,7 @@ function test() try { actual = 'No Error'; - (function () { f(x = yield); const x; }); + (function () { f(x = yield); const x = undefined; }); } catch(ex) { diff --git a/js/src/tests/js1_8_1/regress/regress-452498-068.js b/js/src/tests/js1_8_1/regress/regress-452498-068.js index 5143fd389482..352ada492c67 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-068.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-068.js @@ -46,7 +46,7 @@ function test() // ===== for( - const NaN; + const NaN = undefined; this.__defineSetter__("x4", function(){}); (eval("", (p={})))) let ({} = (((x ))(function ([]) {})), x1) y; diff --git a/js/src/tests/js1_8_1/regress/regress-452498-092.js b/js/src/tests/js1_8_1/regress/regress-452498-092.js index 383c83609299..40347f29e59a 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-092.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-092.js @@ -25,7 +25,7 @@ function test() expect = 'TypeError: redeclaration of formal parameter e'; try { - eval('(function (e) { var e; const e; });'); + eval('(function (e) { var e; const e = undefined; });'); } catch(ex) { diff --git a/js/src/tests/js1_8_1/regress/regress-452498-101.js b/js/src/tests/js1_8_1/regress/regress-452498-101.js index c8cea7feb3f6..5bd15b2b74a6 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-101.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-101.js @@ -22,7 +22,7 @@ function test() // ------- Comment #101 From Gary Kwong [:nth10sd] - uneval(function(){with({functional: []}){x5, y = this;const y }}); + uneval(function(){with({functional: []}){x5, y = this;const y = undefined }}); // Assertion failure: strcmp(rval, with_cookie) == 0, at ../jsopcode.cpp:2567 reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-102.js b/js/src/tests/js1_8_1/regress/regress-452498-102.js index 2e5212d2eda1..81e6ae5dbfc5 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-102.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-102.js @@ -32,9 +32,9 @@ function test() function f() { "" + (function(){ for( ; [function(){}] ; x = 0) - with({x: ""}) + with({x: ""}) { const x = [] - }); + }}); } f(); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-112.js b/js/src/tests/js1_8_1/regress/regress-452498-112.js index 0d274ab7eff6..19aa63b10ec3 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-112.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-112.js @@ -22,11 +22,11 @@ function test() // ------- Comment #112 From Jesse Ruderman - expect = 'TypeError: q is not a function'; + expect = 'ReferenceError: can\'t access lexical declaration `q\' before initialization'; try { - q = new Function("(function() { q(3); })(); const q;"); q(); + q = new Function("(function() { q(3); })(); const q = undefined;"); q(); } catch(ex) { diff --git a/js/src/tests/js1_8_1/regress/regress-452498-117.js b/js/src/tests/js1_8_1/regress/regress-452498-117.js index 6ede09562d80..b52a5f55465f 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-117.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-117.js @@ -28,7 +28,7 @@ function test() try { - eval('x; function x(){}; const x;'); + eval('x; function x(){}; const x = undefined;'); } catch(ex) { @@ -46,7 +46,7 @@ function test() // ===== try { - (function(){(yield []) (function(){with({}){x} }); const x;})(); + (function(){(yield []) (function(){with({}){x} }); const x = undefined;})(); } catch(ex) { @@ -81,7 +81,7 @@ function test() // ===== try { - eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x;} }})();'); + eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x = undefined;} }})();'); } catch(ex) { diff --git a/js/src/tests/js1_8_1/regress/regress-452498-160.js b/js/src/tests/js1_8_1/regress/regress-452498-160.js index a42f49264506..6498a0b5aaac 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-160.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js @@ -22,7 +22,7 @@ function test() printStatus (summary); // crash [@ js_Interpret] - (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y });"))(); + (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))(); x = NaN; reportCompare(expect, actual, summary + ': 2'); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-185.js b/js/src/tests/js1_8_1/regress/regress-452498-185.js index 72c0844aa5fc..f8ca37f6f9be 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-185.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-185.js @@ -21,7 +21,7 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'TypeError: redeclaration of variable e'; + expect = 'TypeError: redeclaration of var e'; try { eval('{ var e = 3; let e = ""; } print(typeof e);'); diff --git a/js/src/tests/js1_8_1/regress/regress-452498-187.js b/js/src/tests/js1_8_1/regress/regress-452498-187.js index 0f9a011d4d0b..d1d0ffdf2762 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-187.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-187.js @@ -24,7 +24,7 @@ function test() expect = 'SyntaxError: invalid for/in left-hand side'; try { - eval('const x; for (x in []);'); + eval('const x = undefined; for (x in []);'); } catch(ex) { diff --git a/js/src/tests/js1_8_1/regress/regress-452498-192.js b/js/src/tests/js1_8_1/regress/regress-452498-192.js index 32835b1122ef..6f176788b1f1 100644 --- a/js/src/tests/js1_8_1/regress/regress-452498-192.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-192.js @@ -21,7 +21,8 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); - with({x: (x -= 0)}){([]); const x } + let x; + with({x: (x -= 0)}){([]); const x = undefined; } reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_8_1/strict/12.2.1.js b/js/src/tests/js1_8_1/strict/12.2.1.js index f57d7f4a50a4..674c7a7c6052 100644 --- a/js/src/tests/js1_8_1/strict/12.2.1.js +++ b/js/src/tests/js1_8_1/strict/12.2.1.js @@ -26,19 +26,19 @@ assertEq(testLenientAndStrict('let x,arguments;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('const eval;', +assertEq(testLenientAndStrict('const eval = undefined;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('const x,eval;', +assertEq(testLenientAndStrict('const x = undefined,eval = undefined;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('const arguments;', +assertEq(testLenientAndStrict('const arguments = undefined;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('const x,arguments;', +assertEq(testLenientAndStrict('const x = undefined,arguments = undefined;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); diff --git a/js/src/tests/js1_8_5/extensions/redeclaration-of-catch-warning.js b/js/src/tests/js1_8_5/extensions/redeclaration-of-catch-warning.js index b9951693a1c7..30d1a201e02d 100644 --- a/js/src/tests/js1_8_5/extensions/redeclaration-of-catch-warning.js +++ b/js/src/tests/js1_8_5/extensions/redeclaration-of-catch-warning.js @@ -30,7 +30,7 @@ function assertRedeclarationErrorThrown(expression) } } -assertRedeclarationErrorThrown("try {} catch(e) { const e; }"); +assertRedeclarationErrorThrown("try {} catch(e) { const e = undefined; }"); assertRedeclarationErrorThrown("try {} catch(e) { let e; }"); if (typeof reportCompare === "function") diff --git a/js/src/tests/js1_8_5/extensions/reflect-parse.js b/js/src/tests/js1_8_5/extensions/reflect-parse.js index a380e1aa5bde..7e7d73c22594 100644 --- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -686,6 +686,10 @@ testParamPatternCombinations(function(n) ("[a" + n + ", ..." + "b" + n + "]"), function testVarPatternCombinations(makePattSrc, makePattPatt) { var pattSrcs = makePatternCombinations(function(n) ("x" + n), makePattSrc); var pattPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: null }), makePattPatt); + // It's illegal to have uninitialized const declarations, so we need a + // separate set of patterns and sources. + var constSrcs = makePatternCombinations(function(n) ("x" + n + " = undefined"), makePattSrc); + var constPatts = makePatternCombinations(function(n) ({ id: ident("x" + n), init: ident("undefined") }), makePattPatt); for (var i = 0; i < pattSrcs.length; i++) { // variable declarations in blocks @@ -695,15 +699,15 @@ function testVarPatternCombinations(makePattSrc, makePattPatt) { assertLocalDecl("let " + pattSrcs[i].join(",") + ";", letDecl(pattPatts[i])); assertBlockDecl("let " + pattSrcs[i].join(",") + ";", letDecl(pattPatts[i])); - assertDecl("const " + pattSrcs[i].join(",") + ";", constDecl(pattPatts[i])); + assertDecl("const " + constSrcs[i].join(",") + ";", constDecl(constPatts[i])); // variable declarations in for-loop heads assertStmt("for (var " + pattSrcs[i].join(",") + "; foo; bar);", forStmt(varDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); assertStmt("for (let " + pattSrcs[i].join(",") + "; foo; bar);", letStmt(pattPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt))); - assertStmt("for (const " + pattSrcs[i].join(",") + "; foo; bar);", - forStmt(constDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); + assertStmt("for (const " + constSrcs[i].join(",") + "; foo; bar);", + letStmt(constPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt))); } } @@ -1075,7 +1079,7 @@ assertGlobalStmt("for (;;) continue", forStmt(null, null, null, 15), { continueS assertBlockDecl("var x", "var", { variableDeclaration: function(kind) kind }); assertBlockDecl("let x", "let", { variableDeclaration: function(kind) kind }); -assertBlockDecl("const x", "const", { variableDeclaration: function(kind) kind }); +assertBlockDecl("const x = undefined", "const", { variableDeclaration: function(kind) kind }); assertBlockDecl("function f() { }", "function", { functionDeclaration: function() "function" }); assertGlobalExpr("(x,y,z)", 1, { sequenceExpression: function() 1 }); diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index 7eb41d4beacb..5ff420e002d2 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -694,7 +694,7 @@ StaticBlockObject::create(ExclusiveContext *cx) /* static */ Shape * StaticBlockObject::addVar(ExclusiveContext *cx, Handle block, HandleId id, - unsigned index, bool *redeclared) + bool constant, unsigned index, bool *redeclared) { MOZ_ASSERT(JSID_IS_ATOM(id)); MOZ_ASSERT(index < LOCAL_INDEX_LIMIT); @@ -713,11 +713,13 @@ StaticBlockObject::addVar(ExclusiveContext *cx, Handle block * block's shape later. */ uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index; + uint32_t readonly = constant ? JSPROP_READONLY : 0; + uint32_t propFlags = readonly | JSPROP_ENUMERATE | JSPROP_PERMANENT; return NativeObject::addPropertyInternal(cx, block, id, /* getter = */ nullptr, /* setter = */ nullptr, slot, - JSPROP_ENUMERATE | JSPROP_PERMANENT, + propFlags, /* attrs = */ 0, spp, /* allowDictionary = */ false); @@ -785,18 +787,20 @@ js::XDRStaticBlockObject(XDRState *xdr, HandleObject enclosingScope, ? AtomToId(atom) : INT_TO_JSID(i)); + uint32_t propFlags; + if (!xdr->codeUint32(&propFlags)) + return false; + + bool readonly = !!(propFlags & 1); + bool redeclared; - if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) { + if (!StaticBlockObject::addVar(cx, obj, id, readonly, i, &redeclared)) { MOZ_ASSERT(!redeclared); return false; } - uint32_t aliased; - if (!xdr->codeUint32(&aliased)) - return false; - - MOZ_ASSERT(aliased == 0 || aliased == 1); - obj->setAliased(i, !!aliased); + bool aliased = !!(propFlags >> 1); + obj->setAliased(i, aliased); } } else { AutoShapeVector shapes(cx); @@ -823,8 +827,10 @@ js::XDRStaticBlockObject(XDRState *xdr, HandleObject enclosingScope, if (!XDRAtom(xdr, &atom)) return false; - uint32_t aliased = obj->isAliased(i); - if (!xdr->codeUint32(&aliased)) + bool aliased = obj->isAliased(i); + bool readonly = !shape->writable(); + uint32_t propFlags = (aliased << 1) | readonly; + if (!xdr->codeUint32(&propFlags)) return false; } } @@ -862,7 +868,7 @@ CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, HandleshapeToIndex(**p); bool redeclared; - if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) { + if (!StaticBlockObject::addVar(cx, clone, id, !(*p)->writable(), i, &redeclared)) { MOZ_ASSERT(!redeclared); return nullptr; } diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 10c2d64c76c8..cf36e41caea8 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -569,7 +569,7 @@ class StaticBlockObject : public BlockObject static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16); static Shape *addVar(ExclusiveContext *cx, Handle block, HandleId id, - unsigned index, bool *redeclared); + bool constant, unsigned index, bool *redeclared); }; class ClonedBlockObject : public BlockObject diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 2de2bb995923..09b04f996118 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -34,7 +34,7 @@ namespace js { * Nightly) and without (all others). FIXME: Bug 1066322 - Enable ES6 symbols * in all builds. */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 190; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 192; static_assert(XDR_BYTECODE_VERSION_SUBTRAHEND % 2 == 0, "see the comment above"); static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - (XDR_BYTECODE_VERSION_SUBTRAHEND diff --git a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm index 1faa07b9f562..a6110bb5fd66 100644 --- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm @@ -19,16 +19,18 @@ // Boilerplate used to be able to import this module both from the main // thread and from worker threads. + +// Since const is lexically scoped, hoist the +// conditionally-useful definition ourselves. +const Cu = typeof Components != "undefined" ? Components.utils : undefined; +const Ci = typeof Components != "undefined" ? Components.interfaces : undefined; +const Cc = typeof Components != "undefined" ? Components.classes : undefined; if (typeof Components != "undefined") { // Global definition of |exports|, to keep everybody happy. // In non-main thread, |exports| is provided by the module // loader. this.exports = {}; - const Cu = Components.utils; - const Ci = Components.interfaces; - const Cc = Components.classes; - Cu.import("resource://gre/modules/Services.jsm", this); } diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index a9da6abbf14a..421c1508307e 100644 --- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -410,9 +410,8 @@ function shutdownPlaces(aKeepAliveConnection) } const FILENAME_BOOKMARKS_HTML = "bookmarks.html"; -let (backup_date = new Date().toLocaleFormat("%Y-%m-%d")) { - const FILENAME_BOOKMARKS_JSON = "bookmarks-" + backup_date + ".json"; -} +const FILENAME_BOOKMARKS_JSON = "bookmarks-" + + (new Date().toLocaleFormat("%Y-%m-%d")) + ".json"; /** * Creates a bookmarks.html file in the profile folder from a given source file.