author | Eric Faust <efaustbmo@gmail.com> |
Thu, 30 Oct 2014 17:27:03 -0700 | |
changeset 214018 | a9a7f16c817bfa79b501a11f446e856beb00b473 |
parent 214017 | db8ff91163763ca965fa776facbc7d559b782145 |
child 214019 | ed6401282c181e48024ffb661c1a9d3a65a85f11 |
push id | 27771 |
push user | ryanvm@gmail.com |
push date | Wed, 05 Nov 2014 19:04:24 +0000 |
treeherder | mozilla-central@305b4fecce99 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | shu |
bugs | 611388 |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/b2g/chrome/content/identity.js +++ b/b2g/chrome/content/identity.js @@ -26,25 +26,25 @@ XPCOMUtils.defineLazyModuleGetter(this, function log(...aMessageArgs) { Logger.log.apply(Logger, ["injected identity.js"].concat(aMessageArgs)); } log("\n\n======================= identity.js =======================\n\n"); // This script may be injected more than once into an iframe. -// Ensure we don't redefine contstants +// It's hard to do this with |const| like we should, so use var instead. if (typeof kIdentityJSLoaded === 'undefined') { - const kIdentityDelegateWatch = "identity-delegate-watch"; - const kIdentityDelegateRequest = "identity-delegate-request"; - const kIdentityDelegateLogout = "identity-delegate-logout"; - const kIdentityDelegateReady = "identity-delegate-ready"; - const kIdentityDelegateFinished = "identity-delegate-finished"; - const kIdentityControllerDoMethod = "identity-controller-doMethod"; - const kIdentktyJSLoaded = true; + var kIdentityDelegateWatch = "identity-delegate-watch"; + var kIdentityDelegateRequest = "identity-delegate-request"; + var kIdentityDelegateLogout = "identity-delegate-logout"; + var kIdentityDelegateReady = "identity-delegate-ready"; + var kIdentityDelegateFinished = "identity-delegate-finished"; + var kIdentityControllerDoMethod = "identity-controller-doMethod"; + var kIdentktyJSLoaded = true; } var showUI = false; var options = {}; var isLoaded = false; var func = null; /*
--- a/dom/xul/templates/tests/chrome/templates_shared.js +++ b/dom/xul/templates/tests/chrome/templates_shared.js @@ -49,22 +49,33 @@ const ZOO_NS = "http://www.some-fictitious-zoo.com/"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 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; function test_template() { var root = document.getElementById("root"); var ds;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1235,17 +1235,17 @@ LookupAliasedName(BytecodeEmitter *bce, // a possible hoisted use and is a lexical binding. If so, // mark it as such so we emit a dead zone check. if (freeVariables) { for (uint32_t i = 0; i < numFreeVariables; i++) { if (freeVariables[i].atom() == 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; } } } *pslot = slot; @@ -1284,17 +1284,17 @@ AssignHops(BytecodeEmitter *bce, ParseNo dst->setHops(src); return true; } static inline MaybeCheckLexical NodeNeedsCheckLexical(ParseNode *pn) { - return pn->isHoistedLetUse() ? CheckLexical : DontCheckLexical; + return pn->isHoistedLexicalUse() ? CheckLexical : DontCheckLexical; } static bool EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce) { /* * While pn->pn_cookie tells us how many function scopes are between the use and the def this * is not the same as how many hops up the dynamic scope chain are needed. In particular: @@ -1456,16 +1456,17 @@ BytecodeEmitter::isAliasedName(ParseNode MOZ_ASSERT(dn->isBound()); /* If dn is in an enclosing function, it is definitely aliased. */ if (dn->pn_cookie.level() != script->staticLevel()) return true; 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 * bindingsAccessedDynamically flag, checked by allLocalsAliased, is * function-wide.) * * In addition all locals in generators are marked as aliased, to ensure * that they are allocated on scope chains instead of on the stack. See @@ -1480,17 +1481,17 @@ BytecodeEmitter::isAliasedName(ParseNode * a given name is aliased. This is necessary to avoid generating a * shape for the call object with with more than one name for a given * slot (which violates internal engine invariants). All this means that * the '|| sc->allLocalsAliased()' disjunct is incorrect since it will * mark both parameters in function(x,x) as aliased. */ 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: case Definition::NAMED_LAMBDA: case Definition::MISSING: MOZ_CRASH("unexpected dn->kind"); } return false; @@ -1772,16 +1773,17 @@ BindNameToSlotHelper(ExclusiveContext *c case JSOP_NAME: op = JSOP_GETARG; break; case JSOP_SETNAME: op = JSOP_SETARG; break; default: MOZ_CRASH("arg"); } MOZ_ASSERT(!pn->isConst()); break; case Definition::VAR: + case Definition::GLOBALCONST: case Definition::CONST: case Definition::LET: switch (op) { case JSOP_NAME: op = JSOP_GETLOCAL; break; case JSOP_SETNAME: op = JSOP_SETLOCAL; break; case JSOP_SETCONST: op = JSOP_SETLOCAL; break; default: MOZ_CRASH("local"); } @@ -2072,17 +2074,17 @@ CheckSideEffects(ExclusiveContext *cx, B * Not a use of an unshadowed named function expression's given * name, so this expression could invoke a getter that has side * effects. */ *answer = true; } } - if (pn->isHoistedLetUse()) { + if (pn->isHoistedLexicalUse()) { // Hoisted uses of lexical bindings throw on access. *answer = true; } if (pn->isKind(PNK_DOT)) { /* Dotted property references in general can call getters. */ *answer = true; } @@ -4658,17 +4660,17 @@ EmitIf(ExclusiveContext *cx, BytecodeEmi */ MOZ_NEVER_INLINE static bool EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet) { MOZ_ASSERT(pnLet->isArity(PN_BINARY)); 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; if (!EmitVariables(cx, bce, varList, PushInitialValues, true)) return false; /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */ uint32_t valuesPushed = bce->stackDepth - letHeadDepth; @@ -4748,17 +4750,17 @@ EmitIterator(ExclusiveContext *cx, Bytec CheckTypeSet(cx, bce, JSOP_CALL); return true; } 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 // will initialize let bindings in EmitForOf and EmitForIn with // undefineds. // // Due to the horror of legacy comprehensions, there is a third case where // we have PNK_LET without a lexical scope, because those expressions are @@ -6701,17 +6703,17 @@ frontend::EmitTree(ExclusiveContext *cx, // 1. Destructuring // 2. Defaults // 3. Functions ParseNode *pnchild = pnlast->pn_head; if (pnlast->pn_xflags & PNX_DESTRUCT) { // 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; } bool hasDefaults = bce->sc->asFunctionBox()->hasDefaults(); if (hasDefaults) { ParseNode *rest = nullptr; bool restIsDefn = false; @@ -6834,17 +6836,17 @@ frontend::EmitTree(ExclusiveContext *cx, break; case PNK_CATCH: if (!EmitCatch(cx, bce, pn)) return false; break; case PNK_VAR: - case PNK_CONST: + case PNK_GLOBALCONST: if (!EmitVariables(cx, bce, pn, InitializeVars)) return false; break; case PNK_RETURN: ok = EmitReturn(cx, bce, pn); break; @@ -6997,16 +6999,18 @@ frontend::EmitTree(ExclusiveContext *cx, ok = EmitCallOrNew(cx, bce, pn); break; case PNK_LEXICALSCOPE: ok = EmitLexicalScope(cx, bce, 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); break; case PNK_IMPORT: case PNK_EXPORT: // TODO: Implement emitter support for modules
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -662,18 +662,18 @@ class FullParseHandler } bool dependencyCovered(ParseNode *pn, unsigned blockid, bool functionScope) { return pn->pn_blockid >= blockid; } void markMaybeUninitializedLexicalUseInSwitch(ParseNode *pn, Definition *dn, 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) { return uintptr_t(dn); } static Definition *definitionFromBits(uintptr_t bits) { return (Definition *) bits; }
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -303,17 +303,17 @@ ParseNode::newBinaryOrAppend(ParseNodeKi return handler->new_<BinaryNode>(kind, op, left, right); } 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)); return table[kind]; } namespace js { namespace frontend { @@ -500,17 +500,17 @@ Parser<FullParseHandler>::cloneLeftHandS pn->pn_link = dn->dn_uses; dn->dn_uses = pn; } else { pn->pn_expr = nullptr; 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); } } return pn; }
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -109,16 +109,17 @@ class UpvarCookie F(DEFAULT) \ F(WHILE) \ F(DOWHILE) \ F(FOR) \ F(BREAK) \ F(CONTINUE) \ F(VAR) \ F(CONST) \ + F(GLOBALCONST) \ F(WITH) \ F(RETURN) \ F(NEW) \ F(DELETE) \ F(TRY) \ F(CATCH) \ F(CATCHLIST) \ F(FINALLY) \ @@ -677,17 +678,18 @@ class ParseNode } ParseNode *maybeExpr() { return pn_used ? nullptr : expr(); } Definition *maybeLexDef() { return pn_used ? lexdef() : nullptr; } 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 */ #define PND_BOUND 0x10 /* bound to a stack or global slot */ #define PND_DEOPTIMIZED 0x20 /* former pn_used name node, pn_lexdef still valid, but this use no longer optimizable via an upvar opcode */ #define PND_CLOSED 0x40 /* variable is closed over */ @@ -759,25 +761,25 @@ class ParseNode if (kid && kid->getKind() == PNK_STRING && !kid->pn_parens) return kid->pn_atom; } return nullptr; } 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); } bool isAssigned() const { return test(PND_ASSIGNED); } 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 { return isKind(PNK_NUMBER) || isKind(PNK_STRING) || isKind(PNK_TRUE) || isKind(PNK_FALSE) || isKind(PNK_NULL); @@ -1409,17 +1411,17 @@ void DumpParseTree(ParseNode *pn, int in */ struct Definition : public ParseNode { bool isFreeVar() const { MOZ_ASSERT(isDefn()); 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); } static const char *kindString(Kind kind); Kind kind() { if (getKind() == PNK_FUNCTION) { if (isOp(JSOP_GETARG)) @@ -1428,20 +1430,20 @@ struct Definition : public ParseNode } MOZ_ASSERT(getKind() == PNK_NAME); if (isOp(JSOP_CALLEE)) return NAMED_LAMBDA; if (isPlaceholder()) 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; } }; class ParseNodeAllocator { public: explicit ParseNodeAllocator(ExclusiveContext *cx, LifoAlloc &alloc)
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -117,43 +117,44 @@ MarkUsesAsHoistedLexical(ParseNode *pn) ParseNode **pnup = &dn->dn_uses; ParseNode *pnu; unsigned start = pn->pn_blockid; // In ES6, lexical bindings cannot be accessed until initialized. // 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; } } // See comment on member function declaration. template <> bool ParseContext<FullParseHandler>::define(TokenStream &ts, HandlePropertyName name, ParseNode *pn, Definition::Kind kind) { MOZ_ASSERT(!pn->isUsed()); 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)); if (!prevDef) prevDef = lexdeps.lookupDefn<FullParseHandler>(name); 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); MOZ_ASSERT(pnu->isUsed()); pnu->pn_lexdef = (Definition *) pn; pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; pnup = &pnu->pn_link; } @@ -165,17 +166,17 @@ ParseContext<FullParseHandler>::define(T if (!pnu && prevDef->isPlaceholder()) lexdeps->remove(name); } 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) pn->pn_dflags |= PND_CONST; Definition *dn = (Definition *)pn; switch (kind) { case Definition::ARG: @@ -192,17 +193,17 @@ ParseContext<FullParseHandler>::define(T return false; } if (name == ts.names().empty) break; if (!decls_.addUnique(name, dn)) 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); dn->pn_blockid = bodyid; dn->pn_dflags |= PND_BOUND; if (!dn->pn_cookie.set(ts, staticLevel, vars_.length())) return false; if (!vars_.append(dn)) @@ -210,18 +211,19 @@ ParseContext<FullParseHandler>::define(T if (!checkLocalsOverflow(ts)) return false; } if (!decls_.addUnique(name, dn)) return false; 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)) return false; if (!checkLocalsOverflow(ts)) return false; } @@ -309,17 +311,18 @@ ParseContext<ParseHandler>::updateDecl(J vars_[oldDecl->pn_cookie.slot()] = newDecl; } } template <typename ParseHandler> void ParseContext<ParseHandler>::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); } template <typename ParseHandler> static void AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst, uint32_t *numUnaliased = nullptr) { @@ -333,16 +336,17 @@ AppendPackedBindings(const ParseContext< // Treat body-level let declarations as var bindings by falling // through. The fact that the binding is in fact a let declaration // is reflected in the slot. All body-level lets go after the // vars. case Definition::VAR: kind = Binding::VARIABLE; break; case Definition::CONST: + case Definition::GLOBALCONST: kind = Binding::CONSTANT; break; case Definition::ARG: kind = Binding::ARGUMENT; break; default: MOZ_CRASH("unexpected dn->kind"); } @@ -1164,36 +1168,40 @@ struct BindData typedef bool (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser); /* name node for definition processing and error source coordinates */ typename ParseHandler::Node pn; JSOp op; /* prolog bytecode or nop */ Binder binder; /* binder, discriminates u */ + bool isConst; /* const binding? */ struct LetData { explicit LetData(ExclusiveContext *cx) : blockObj(cx) {} VarContext varContext; RootedStaticBlockObject blockObj; 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<ParseHandler>::bindLet; + this->isConst = isConst; + this->binder = Parser<ParseHandler>::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<ParseHandler>::bindVarOrConst; + this->isConst = op == JSOP_DEFCONST; + this->binder = Parser<ParseHandler>::bindVarOrGlobalConst; } }; template <typename ParseHandler> JSFunction * Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto) { @@ -1284,28 +1292,28 @@ ConvertDefinitionToNamedLambdaUse(TokenS funbox->setNeedsDeclEnvObject(); return true; } static bool IsNonDominatingInScopedSwitch(ParseContext<FullParseHandler> *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; return false; } static void AssociateUsesWithOuterDefinition(ParseNode *pnu, Definition *dn, Definition *outer_dn, - bool markUsesAsLet) -{ - uint32_t dflags = markUsesAsLet ? PND_LET : 0; + bool markUsesAsLexical) +{ + uint32_t dflags = markUsesAsLexical ? PND_LEXICAL : 0; while (true) { pnu->pn_lexdef = outer_dn; pnu->pn_dflags |= dflags; if (!pnu->pn_link) break; pnu = pnu->pn_link; } pnu->pn_link = outer_dn->dn_uses; @@ -1407,20 +1415,20 @@ Parser<FullParseHandler>::leaveFunction( // } // // The use of 'x' inside 'inner' needs to be marked. // // 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); - bool markUsesAsLet = outer_dn->isLet() && - (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; } /* Mark the outer dn as escaping. */ outer_dn->pn_dflags |= PND_CLOSED; } @@ -1759,17 +1767,18 @@ Parser<FullParseHandler>::checkFunctionD /* * Handle redeclaration and optimize cases where we can statically bind the * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). */ if (Definition *dn = pc->decls().lookupFirst(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; ParseReportKind reporter = throwRedeclarationError ? ParseError : ParseExtraWarning; if (!AtomToPrintableString(context, funName, &name) || !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, @@ -1988,17 +1997,20 @@ Parser<SyntaxParseHandler>::checkFunctio bool bodyLevel = pc->atBodyLevel(); if (kind == Statement) { /* * Handle redeclaration and optimize cases where we can statically bind the * 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, Definition::kindString(dn), name.ptr())) { return false; } } @@ -2842,45 +2854,49 @@ Parser<ParseHandler>::matchLabel(Mutable } else { label.set(nullptr); } return true; } template <typename ParseHandler> bool -Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, HandlePropertyName name) +Parser<ParseHandler>::reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name) { JSAutoByteString printable; if (!AtomToPrintableString(context, name, &printable)) return false; StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); 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 * property for the new variable on the block object, pc->staticScope; * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to * data->pn in a slot of the block object. */ template <> /* static */ bool -Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data, - HandlePropertyName name, Parser<FullParseHandler> *parser) +Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler> *data, + HandlePropertyName name, Parser<FullParseHandler> *parser) { ParseContext<FullParseHandler> *pc = parser->pc; ParseNode *pn = data->pn; if (!parser->checkStrictBinding(name, pn)) return false; ExclusiveContext *cx = parser->context; Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj); @@ -2907,35 +2923,44 @@ Parser<FullParseHandler>::bindLet(BindDa // script->nfixed and body-level lets. // // For body-level lets, the index is bogus at this point and is adjusted // when creating Bindings. See ParseContext::generateFunctionBindings and // AppendPackedBindings. 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; } /* Store pn in the static block object. */ blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition *>(pn)); } else { // Body-level lets are hoisted and need to have been defined via // pc->define above. @@ -2943,18 +2968,18 @@ Parser<FullParseHandler>::bindLet(BindDa MOZ_ASSERT(pc->decls().lookupFirst(name)); } return true; } template <> /* static */ bool -Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data, - HandlePropertyName name, Parser<SyntaxParseHandler> *parser) +Parser<SyntaxParseHandler>::bindLexical(BindData<SyntaxParseHandler> *data, + HandlePropertyName name, Parser<SyntaxParseHandler> *parser) { if (!parser->checkStrictBinding(name, data->pn)) return false; return true; } template <typename ParseHandler, class Op> @@ -3086,18 +3111,18 @@ OuterLet(ParseContext<ParseHandler> *pc, if (stmt->type == STMT_BLOCK) return true; } return false; } template <typename ParseHandler> /* static */ bool -Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data, - HandlePropertyName name, Parser<ParseHandler> *parser) +Parser<ParseHandler>::bindVarOrGlobalConst(BindData<ParseHandler> *data, + HandlePropertyName name, Parser<ParseHandler> *parser) { ExclusiveContext *cx = parser->context; ParseContext<ParseHandler> *pc = parser->pc; Node pn = data->pn; bool isConstDecl = data->op == JSOP_DEFCONST; /* Default best op for pn is JSOP_NAME; we'll try to improve below. */ parser->handler.setOp(pn, JSOP_NAME); @@ -3127,17 +3152,17 @@ Parser<ParseHandler>::bindVarOrConst(Bin return true; } DefinitionList::Range defs = pc->decls().lookupMulti(name); MOZ_ASSERT_IF(stmt, !defs.empty()); if (defs.empty()) { return pc->define(parser->tokenStream, name, pn, - isConstDecl ? Definition::CONST : Definition::VAR); + isConstDecl ? Definition::GLOBALCONST : Definition::VAR); } /* * There was a previous declaration with the same name. The standard * disallows several forms of redeclaration. Critically, * let (x) { var x; } // error * is not allowed which allows us to turn any non-error redeclaration * into a use of the initial declaration. @@ -3154,16 +3179,17 @@ Parser<ParseHandler>::bindVarOrConst(Bin return false; } if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr())) return false; } else { 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)))); if (parser->options().extraWarningsOption ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR : error) { JSAutoByteString bytes; @@ -3500,17 +3526,17 @@ template <> ParseNode * Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) { MOZ_ASSERT(blockObj); ParseNode *pn = pushLexicalScope(blockObj, stmt); 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))) return null(); return pn; } @@ -3687,43 +3713,49 @@ template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple, StaticBlockObject *blockObj, VarContext varContext) { /* * 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 * only one variable declared and no initializer expression. */ 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) return null(); /* * SpiderMonkey const is really "write once per initialization evaluation" * var, whereas let is block scoped. ES-Harmony wants block-scoped const so * this code will change soon. */ BindData<ParseHandler> 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; while (true) { do { if (psimple && !first) *psimple = false; first = false; @@ -3746,17 +3778,18 @@ Parser<ParseHandler>::variables(ParseNod bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) return null(); parsingForInOrOfInit = isForIn || isForOf; } // 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(); if (parsingForInOrOfInit) { tokenStream.ungetToken(); handler.addList(pn, pn2); break; } @@ -3783,20 +3816,20 @@ Parser<ParseHandler>::variables(ParseNod return null(); } else { report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); return null(); } } 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; handler.addList(pn, pn2); bool matched; if (!tokenStream.matchToken(&matched, TOK_ASSIGN)) return null(); @@ -3807,30 +3840,35 @@ Parser<ParseHandler>::variables(ParseNod // In ES6, lexical bindings may not be accessed until // initialized. So a declaration of the form |let x = x| results // in a ReferenceError, as the 'x' on the RHS is accessing the let // binding before it is initialized. // // 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(); Node init = assignExpr(); if (!init) return null(); if (!bindBeforeInitializer && !data.binder(&data, name, this)) return null(); 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(); } } while (false); bool matched; if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); @@ -3838,17 +3876,17 @@ Parser<ParseHandler>::variables(ParseNod break; } return pn; } template <> ParseNode * -Parser<FullParseHandler>::letDeclaration() +Parser<FullParseHandler>::lexicalDeclaration(bool isConst) { handler.disableSyntaxParser(); ParseNode *pn; do { /* * This is a let declaration. We must be directly under a block per the @@ -3858,17 +3896,18 @@ Parser<FullParseHandler>::letDeclaration * find this scope statement and use the same block object. * * If we are the first let declaration in this block (i.e., when the * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. */ 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(); } if (stmt && stmt->isBlockScope) { MOZ_ASSERT(pc->staticScope == stmt->staticScope); } else { if (pc->atBodyLevel()) { /* @@ -3877,19 +3916,20 @@ Parser<FullParseHandler>::letDeclaration * before the global object in the overall chain. This extra * object is present in the scope chain for all code in that * global, including self-hosted code. But self-hosted code * must be usable against *any* global object, including ones * with other let variables -- variables possibly placed in * 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(); } /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment * is the VariableEnvironment, i.e., body-level lets are in * the same environment record as vars. @@ -3897,17 +3937,22 @@ Parser<FullParseHandler>::letDeclaration * However, they cannot be parsed exactly as vars, as ES6 * requires that uninitialized lets throw ReferenceError on use. * * See 8.1.1.1.6 and the note in 13.2.1. * * 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; break; } /* * Some obvious assertions here, but they may help clarify the @@ -3957,28 +4002,29 @@ Parser<FullParseHandler>::letDeclaration pn1->pn_pos = pc->blockNode->pn_pos; pn1->pn_objbox = blockbox; pn1->pn_expr = pc->blockNode; pn1->pn_blockid = pc->blockNode->pn_blockid; pc->blockNode = pn1; } - pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars); + pn = variables(isConst ? PNK_CONST : PNK_LET, nullptr, + &pc->staticScope->as<StaticBlockObject>(), HoistVars); if (!pn) return null(); pn->pn_xflags = PNX_POPVAR; } while (0); return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr; } template <> SyntaxParseHandler::Node -Parser<SyntaxParseHandler>::letDeclaration() +Parser<SyntaxParseHandler>::lexicalDeclaration(bool) { JS_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } template <> ParseNode * Parser<FullParseHandler>::letStatement() @@ -3989,17 +4035,17 @@ Parser<FullParseHandler>::letStatement() ParseNode *pn; TokenKind tt; if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_LP) { pn = letBlock(LetStatement); MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); } else { - pn = letDeclaration(); + pn = lexicalDeclaration(/* isConst = */ false); } return pn; } template <> SyntaxParseHandler::Node Parser<SyntaxParseHandler>::letStatement() { @@ -4246,34 +4292,34 @@ Parser<ParseHandler>::exportDeclaration( case TOK_FUNCTION: kid = functionStmt(); if (!kid) return null(); 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; kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; if (!kid) return null(); break; case TOK_NAME: // Handle the form |export a} in the same way as |export let a|, by // acting as if we've just seen the let keyword. Simply unget the token // and fall through. tokenStream.ungetToken(); case TOK_LET: - kid = letDeclaration(); + case TOK_CONST: + kid = lexicalDeclaration(tt == TOK_CONST); if (!kid) return null(); break; default: report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT); return null(); } @@ -4407,17 +4453,17 @@ template <> bool Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version, bool isForDecl, bool isForEach, ParseNodeKind headKind) { 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. // Hence all other destructuring decls are banned there. if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) { ParseNode *lhs = pn1->pn_head; if (lhs->isKind(PNK_ASSIGN)) lhs = lhs->pn_left; @@ -4446,16 +4492,32 @@ Parser<FullParseHandler>::isValidForStat return true; default: return false; } } template <> +bool +Parser<FullParseHandler>::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<FullParseHandler>::forStatement() { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); uint32_t begin = pos().begin; StmtInfoPC forStmt(context); PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); @@ -4504,33 +4566,34 @@ Parser<FullParseHandler>::forStatement() * recognized as an operator, leaving it available to be parsed as * part of a for/in loop. * * A side effect of this restriction is that (unparenthesized) * expressions involving an 'in' operator are illegal in the init * 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)) return null(); if (tt == TOK_LP) { pn1 = letBlock(LetExpresion); } else { isForDecl = true; 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(); } pc->parsingForInit = false; if (!pn1) return null(); } @@ -4712,18 +4775,24 @@ Parser<FullParseHandler>::forStatement() return null(); } headKind = PNK_FORHEAD; 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(); letStmt.isForLetBlock = true; forLetDecl = pn1; pn1 = nullptr; } @@ -4838,17 +4907,17 @@ Parser<SyntaxParseHandler>::forStatement if (tt == TOK_SEMI) { lhsNode = null(); } else { /* Set lhsNode to a var list or an initializing expression. */ pc->parsingForInit = true; 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()); return null(); } else { lhsNode = expr(); } @@ -5507,18 +5576,18 @@ Parser<ParseHandler>::tryStatement() */ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); /* * Contrary to ECMA Ed. 3, the catch variable is lexically * 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<StaticBlockObject>(), - JSMSG_TOO_MANY_CATCH_VARS); + data.initLexical(HoistVars, &pc->staticScope->template as<StaticBlockObject>(), + JSMSG_TOO_MANY_CATCH_VARS); MOZ_ASSERT(data.let.blockObj); if (!tokenStream.getToken(&tt)) return null(); Node catchName; switch (tt) { case TOK_LB: case TOK_LC: @@ -5635,19 +5704,20 @@ Parser<ParseHandler>::statement(bool can return null(); switch (tt) { case TOK_LC: return blockStatement(); 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(); // Tell js_EmitTree to generate a final POP. handler.setListFlag(pn, PNX_POPVAR); if (!MatchOrInsertSemicolon(tokenStream)) return null(); @@ -6614,17 +6684,18 @@ Parser<FullParseHandler>::legacyComprehe LegacyCompExprTransplanter transplanter(bodyExpr, this, outerpc, comprehensionKind, adjust); if (!transplanter.init()) return null(); if (!transplanter.transplant(bodyExpr)) return null(); MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object); - data.initLet(HoistVars, &pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG); + data.initLexical(HoistVars, &pc->staticScope->as<StaticBlockObject>(), + JSMSG_ARRAY_INIT_TOO_BIG); while (true) { /* * FOR node is binary, left is loop control and right is body. Use * index to count each block-local let-variable on the left-hand side * of the in/of. */ pn2 = BinaryNode::create(PNK_FOR, &handler); @@ -7057,17 +7128,17 @@ Parser<ParseHandler>::comprehensionFor(G TokenPos headPos(begin, pos().end); StmtInfoPC stmtInfo(context); BindData<ParseHandler> data(context); 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(); Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP); if (!decls) return null(); data.pn = lhs; if (!data.binder(&data, name, this))
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -537,17 +537,17 @@ class Parser : private JS::AutoGCRooter, Node breakStatement(); Node returnStatement(); Node withStatement(); Node labeledStatement(); Node throwStatement(); Node tryStatement(); Node debuggerStatement(); - Node letDeclaration(); + Node lexicalDeclaration(bool isConst); Node letStatement(); Node importDeclaration(); Node exportDeclaration(); Node expressionStatement(); Node variables(ParseNodeKind kind, bool *psimple = nullptr, StaticBlockObject *blockObj = nullptr, VarContext varContext = HoistVars); Node expr(); @@ -629,16 +629,17 @@ class Parser : private JS::AutoGCRooter, bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind, bool *pbodyProcessed, bool *pbodyLevelHoistedUse); bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body); bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc, bool bodyLevelHoistedUse); 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); bool defineArg(Node funcpn, HandlePropertyName name, bool disallowDuplicateArgs = false, Node *duplicatedArg = nullptr); Node pushLexicalScope(StmtInfoPC *stmt); Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt); Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt); @@ -662,26 +663,26 @@ class Parser : private JS::AutoGCRooter, return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos); } static bool bindDestructuringArg(BindData<ParseHandler> *data, HandlePropertyName name, Parser<ParseHandler> *parser); static bool - bindLet(BindData<ParseHandler> *data, - HandlePropertyName name, Parser<ParseHandler> *parser); + bindLexical(BindData<ParseHandler> *data, + HandlePropertyName name, Parser<ParseHandler> *parser); static bool - bindVarOrConst(BindData<ParseHandler> *data, - HandlePropertyName name, Parser<ParseHandler> *parser); + bindVarOrGlobalConst(BindData<ParseHandler> *data, + HandlePropertyName name, Parser<ParseHandler> *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<ParseHandler> *pc, JSAtom *atom); bool leaveFunction(Node fn, ParseContext<ParseHandler> *outerpc, bool bodyLevelHoistedUse, FunctionSyntaxKind kind = Expression); TokenPos pos() const { return tokenStream.currentToken().pos; }
--- a/js/src/jit-test/tests/asm.js/testGlobals.js +++ b/js/src/jit-test/tests/asm.js/testGlobals.js @@ -1,13 +1,12 @@ 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); assertEq(asmLink(asmCompile(USE_ASM + "const i=42; function f(){ return i|0 } return f"))(), 42); assertEq(asmLink(asmCompile(USE_ASM + "var i=4.2; function f(){ return +i } return f"))(), 4.2); assertEq(asmLink(asmCompile(USE_ASM + "const i=4.2; function f(){ return +i } return f"))(), 4.2); assertAsmTypeFail(USE_ASM + "var i=42; function f(){ return +(i+.1) } return f"); assertAsmTypeFail(USE_ASM + "const i=42; function f(){ return +(i+.1) } return f");
--- a/js/src/jit-test/tests/auto-regress/bug487570.js +++ b/js/src/jit-test/tests/auto-regress/bug487570.js @@ -4,10 +4,10 @@ // Flags: // (function(){ for each (var x in new ( (function (){x})() for each (y in []) ) ) -{const functional} +{const functional=undefined} })()
--- 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;){}
--- a/js/src/jit-test/tests/basic/bug639797.js +++ b/js/src/jit-test/tests/basic/bug639797.js @@ -1,1 +1,1 @@ -Function("with([])const x=0")() +Function("with([]){const x=0}")()
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclConst.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +{ + const x = 0; + function x() { } +}
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclGlobalConst.js @@ -0,0 +1,3 @@ +// |jit-test| error: TypeError +const x = 0; +function x() { }
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/functionRedeclLet.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +{ + let x; + function x() { } +}
--- a/js/src/jit-test/tests/basic/letTDZDelete.js +++ b/js/src/jit-test/tests/basic/letTDZDelete.js @@ -4,20 +4,9 @@ function assertThrowsReferenceError(f) { f(); } catch (ex) { e = ex; } assertEq(e instanceof ReferenceError, true); } 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; });
--- a/js/src/jit-test/tests/basic/letTDZEffectful.js +++ b/js/src/jit-test/tests/basic/letTDZEffectful.js @@ -5,20 +5,9 @@ function assertThrowsReferenceError(f) { } catch (ex) { e = ex; } assertEq(e instanceof ReferenceError, true); } // 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; });
--- a/js/src/jit-test/tests/basic/syntax-error-illegal-character.js +++ b/js/src/jit-test/tests/basic/syntax-error-illegal-character.js @@ -230,20 +230,20 @@ test("let { x: y } = @"); test("const @"); test("const x @"); test("const x = @"); 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 @"); test("const [ x, @"); test("const [ x, ... @"); test("const { @"); test("const { x @"); test("const { x: @");
--- 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);
--- a/js/src/jit-test/tests/jaeger/recompile/bug641269.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug641269.js @@ -1,6 +1,8 @@ +// |jit-test| error: ReferenceError + var g = newGlobal(); var dbg = new g.Debugger(this); (function() { const x = [][x] })()
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -105,17 +105,17 @@ MSG_DEF(JSMSG_JSON_CYCLIC_VALUE, 1 // Runtime errors MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 0, JSEXN_REFERENCEERR, "invalid assignment left-hand side") MSG_DEF(JSMSG_BAD_PROTOTYPE, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object") 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") MSG_DEF(JSMSG_BAD_TOISOSTRING_PROP, 0, JSEXN_TYPEERR, "toISOString property is not callable") // String MSG_DEF(JSMSG_BAD_URI, 0, JSEXN_URIERR, "malformed URI sequence") MSG_DEF(JSMSG_INVALID_NORMALIZE_FORM, 0, JSEXN_RANGEERR, "form must be one of 'NFC', 'NFD', 'NFKC', or 'NFKD'") @@ -176,16 +176,17 @@ MSG_DEF(JSMSG_UNKNOWN_FORMAT, 1 MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must have {1} argument{2}") MSG_DEF(JSMSG_ARGUMENTS_AND_REST, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter") MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initialiser too large") MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") 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") MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") MSG_DEF(JSMSG_BAD_DUP_ARGS, 0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context") MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 0, JSEXN_SYNTAXERR, "invalid for each loop") MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0} returns a value") @@ -239,17 +240,17 @@ MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level") MSG_DEF(JSMSG_INVALID_FOR_IN_INIT, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer") MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 0, JSEXN_SYNTAXERR, "for-of loop variable declaration may not have an initializer") 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'") MSG_DEF(JSMSG_MISSING_EXPONENT, 0, JSEXN_SYNTAXERR, "missing exponent") MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression") MSG_DEF(JSMSG_MISSING_FORMAL, 0, JSEXN_SYNTAXERR, "missing formal parameter") MSG_DEF(JSMSG_MISSING_HEXDIGITS, 0, JSEXN_SYNTAXERR, "missing hexadecimal digits after '0x'") MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits after '0o'") @@ -280,17 +281,17 @@ MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 0 MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object") MSG_DEF(JSMSG_PAREN_IN_PAREN, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list") MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list") MSG_DEF(JSMSG_REDECLARED_CATCH_IDENTIFIER, 1, JSEXN_TYPEERR, "redeclaration of identifier '{0}' in catch") 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") MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 0, JSEXN_SYNTAXERR, "missing ; before statement") MSG_DEF(JSMSG_SOURCE_TOO_LONG, 0, JSEXN_RANGEERR, "source is too long") MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT, 0, JSEXN_ERR, "strict mode code may not contain unparenthesized let expression statements") MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements") MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
--- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -1727,17 +1727,17 @@ class ASTSerializer bool expressions(ParseNode *pn, NodeVector &elts); bool leftAssociate(ParseNode *pn, MutableHandleValue dst); bool functionArgs(ParseNode *pn, ParseNode *pnargs, ParseNode *pndestruct, ParseNode *pnbody, NodeVector &args, NodeVector &defaults, MutableHandleValue rest); 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); bool importSpecifier(ParseNode *pn, MutableHandleValue dst); bool exportDeclaration(ParseNode *pn, MutableHandleValue dst); bool exportSpecifier(ParseNode *pn, MutableHandleValue dst); bool optStatement(ParseNode *pn, MutableHandleValue dst) { @@ -1996,39 +1996,47 @@ ASTSerializer::sourceElement(ParseNode * return statement(pn, dst); } bool 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)); switch (pn->getKind()) { case PNK_FUNCTION: 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))); - - VarDeclKind kind = let ? VARDECL_LET : pn->isKind(PNK_VAR) ? VARDECL_VAR : VARDECL_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 = 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)) return false; for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { RootedValue child(cx); if (!variableDeclarator(next, &child)) return false; @@ -2161,16 +2169,17 @@ ASTSerializer::exportDeclaration(ParseNo case PNK_FUNCTION: if (!function(kid, AST_FUNC_DECL, &decl)) return false; break; case PNK_VAR: case PNK_CONST: + case PNK_GLOBALCONST: case PNK_LET: if (!variableDeclaration(kid, kind == PNK_LET, &decl)) return false; break; default: LOCAL_NOT_REACHED("unexpected statement type"); } @@ -2303,17 +2312,17 @@ ASTSerializer::tryStatement(ParseNode *p bool ASTSerializer::forInit(ParseNode *pn, MutableHandleValue dst) { if (!pn) { dst.setMagic(JS_SERIALIZE_NO_NODE); 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); } bool ASTSerializer::forOf(ParseNode *loop, ParseNode *head, HandleValue var, HandleValue stmt, MutableHandleValue dst) { @@ -2336,20 +2345,21 @@ ASTSerializer::forIn(ParseNode *loop, Pa bool ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst) { JS_CHECK_RECURSION(cx, return false); 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); case PNK_IMPORT: return importDeclaration(pn, dst); case PNK_EXPORT:
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -124,21 +124,21 @@ Bindings::initWithTemporaryStorage(Exclu // any time, such accesses are mediated by DebugScopeProxy (see // DebugScopeProxy::handleUnaliasedAccess). uint32_t nslots = CallObject::RESERVED_SLOTS; uint32_t aliasedBodyLevelLexicalBegin = UINT16_MAX; for (BindingIter bi(self); bi; bi++) { 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; } nslots++; } }
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1739,16 +1739,21 @@ class BindingIter MOZ_ASSERT(!done()); return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs(); } uint32_t localIndex() const { MOZ_ASSERT(!done()); 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_]; } }; /* * This helper function fills the given BindingVector with the sequential * values of BindingIter.
--- a/js/src/tests/ecma_5/Object/freeze-global-eval-const.js +++ b/js/src/tests/ecma_5/Object/freeze-global-eval-const.js @@ -1,13 +1,13 @@ // |reftest| skip-if(!xulRuntime.shell) -- uses evalcx /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ 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"); } reportCompare(0, 0, "don't crash");
--- a/js/src/tests/js1_5/Regress/regress-360969-03.js +++ b/js/src/tests/js1_5/Regress/regress-360969-03.js @@ -23,17 +23,17 @@ function test() var start = new Date(); var p; var i; var limit = 2 << 16; for (var i = 0; i < limit; i++) { - eval('const pv' + i + ';'); + eval('const pv' + i + ' = undefined;'); } reportCompare(expect, actual, summary); var stop = new Date(); print('Elapsed time: ' + Math.floor((stop - start)/1000) + ' seconds');
--- a/js/src/tests/js1_5/Regress/regress-360969-04.js +++ b/js/src/tests/js1_5/Regress/regress-360969-04.js @@ -16,16 +16,16 @@ printStatus (summary); var start = new Date(); var p; var i; var limit = 2 << 16; for (var i = 0; i < limit; i++) { - eval('const pv' + i + ';'); + eval('const pv' + i + ' = undefined;'); } reportCompare(expect, actual, summary); var stop = new Date(); print('Elapsed time: ' + Math.floor((stop - start)/1000) + ' seconds');
--- a/js/src/tests/js1_5/extensions/regress-452565.js +++ b/js/src/tests/js1_5/extensions/regress-452565.js @@ -9,13 +9,13 @@ var summary = 'Do not assert with JIT: ! var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); 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); reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_6/Regress/regress-372565.js +++ b/js/src/tests/js1_6/Regress/regress-372565.js @@ -14,15 +14,15 @@ var expect = ''; test(); //----------------------------------------------------------------------------- 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); exitFunc ('test'); }
--- a/js/src/tests/js1_7/block/regress-349507.js +++ b/js/src/tests/js1_7/block/regress-349507.js @@ -15,22 +15,21 @@ test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - expect = /TypeError: redeclaration of const b/; try { eval('(function() { let(x = 1) { const b = 2 }; let b = 3; })'); } catch(ex) { actual = ex + ''; } - reportMatch(expect, actual, summary); + reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8/genexps/regress-384991.js +++ b/js/src/tests/js1_8/genexps/regress-384991.js @@ -42,17 +42,17 @@ function test() { actual = ex + ''; } reportCompare(expect, actual, summary + ': 2'); try { actual = 'No Error'; - (function () { f(x = yield); const x; }); + (function () { f(x = yield); const x = undefined; }); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': 3'); exitFunc ('test');
--- a/js/src/tests/js1_8_1/regress/regress-452498-068.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-068.js @@ -41,17 +41,17 @@ function test() // ===== (function(q){return q;} for each (\u3056 in [])) // ===== for( - const NaN; + const NaN = undefined; this.__defineSetter__("x4", function(){}); (eval("", (p={})))) let ({} = (((x ))(function ([]) {})), x1) y; // ===== function f(){ var c; eval("{var c = NaN, c;}"); } f();
--- a/js/src/tests/js1_8_1/regress/regress-452498-092.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-092.js @@ -20,17 +20,17 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #92 From Jesse Ruderman 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) { actual = ex + ''; } // Without patch: "TypeError: redeclaration of var e" // expected new behavior // With patch: "TypeError: redeclaration of formal parameter e:"
--- a/js/src/tests/js1_8_1/regress/regress-452498-101.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-101.js @@ -17,15 +17,15 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- 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); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/regress/regress-452498-102.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-102.js @@ -27,19 +27,19 @@ function test() (function(){function x(){} function x()y})(); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== function f() { "" + (function(){ for( ; [function(){}] ; x = 0) - with({x: ""}) + with({x: ""}) { const x = [] - }); + }}); } f(); // Assertion failure: ss->top - saveTop <= 1U, at ../jsopcode.cpp:2156 // ===== try
--- a/js/src/tests/js1_8_1/regress/regress-452498-112.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-112.js @@ -17,21 +17,21 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- 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) { actual = ex + ''; } reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_8_1/regress/regress-452498-117.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-117.js @@ -23,17 +23,17 @@ function test() // ------- Comment #117 From Gary Kwong [:nth10sd] // The following all do not require -j. // ===== try { - eval('x; function x(){}; const x;'); + eval('x; function x(){}; const x = undefined;'); } catch(ex) { } // Assertion failure: !pn->isPlaceholder(), at ../jsparse.cpp:4876 // ===== (function(){ var x; eval("var x; x = null"); })(); @@ -41,17 +41,17 @@ function test() // Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 // ===== (function(){const x = 0, y = delete x;})() // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== try { - (function(){(yield []) (function(){with({}){x} }); const x;})(); + (function(){(yield []) (function(){with({}){x} }); const x = undefined;})(); } catch(ex) { } // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2022 // ===== try @@ -76,17 +76,17 @@ function test() // Assertion failure: pnu->pn_lexdef == dn, at ../jsemit.cpp:1817 // ===== uneval(function(){for(var [arguments] = ({ get y(){} }) in y ) (x);}); // Assertion failure: n != 0, at ../jsfun.cpp:2689 // ===== 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) { } // Assertion failure: op == JSOP_GETLOCAL, at ../jsemit.cpp:4557 // ===== try
--- a/js/src/tests/js1_8_1/regress/regress-452498-160.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js @@ -17,17 +17,17 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); 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'); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 ({ set z(v){}, set y(v)--x, set w(v)--w }); reportCompare(expect, actual, summary + ': 3'); exitFunc ('test');
--- a/js/src/tests/js1_8_1/regress/regress-452498-185.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-185.js @@ -16,17 +16,17 @@ test(); //----------------------------------------------------------------------------- function test() { enterFunc ('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);'); } catch(ex) { actual = ex + ''; }
--- a/js/src/tests/js1_8_1/regress/regress-452498-187.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-187.js @@ -19,17 +19,17 @@ function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for/in left-hand side'; try { - eval('const x; for (x in []);'); + eval('const x = undefined; for (x in []);'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_8_1/regress/regress-452498-192.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-192.js @@ -16,14 +16,15 @@ test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - with({x: (x -= 0)}){([]); const x } + let x; + with({x: (x -= 0)}){([]); const x = undefined; } reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/strict/12.2.1.js +++ b/js/src/tests/js1_8_1/strict/12.2.1.js @@ -21,29 +21,29 @@ assertEq(testLenientAndStrict('let x,eva assertEq(testLenientAndStrict('let arguments;', parsesSuccessfully, parseRaisesException(SyntaxError)), true); 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); /* * In strict mode code, 'let' declarations appearing in 'for' * or 'for in' statements may not bind 'eval' or 'arguments'. */
--- 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 @@ -25,13 +25,13 @@ function assertRedeclarationErrorThrown( } catch(e) { assertEq(e.message.indexOf("catch") > 0, true, "wrong error, got " + e.message); } } -assertRedeclarationErrorThrown("try {} catch(e) { const e; }"); +assertRedeclarationErrorThrown("try {} catch(e) { const e = undefined; }"); assertRedeclarationErrorThrown("try {} catch(e) { let e; }"); if (typeof reportCompare === "function") reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -681,34 +681,38 @@ testParamPatternCombinations(function(n) function(n) (arrPatt([ident("a" + n), spread(ident("b" + n))]))); // destructuring variable declarations 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 assertDecl("var " + pattSrcs[i].join(",") + ";", varDecl(pattPatts[i])); assertGlobalDecl("let " + pattSrcs[i].join(",") + ";", varDecl(pattPatts[i])); 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))); } } testVarPatternCombinations(function (n) ("{a" + n + ":x" + n + "," + "b" + n + ":y" + n + "," + "c" + n + ":z" + n + "} = 0"), function (n) ({ id: objPatt([{ key: ident("a" + n), value: ident("x" + n) }, { key: ident("b" + n), value: ident("y" + n) }, { key: ident("c" + n), value: ident("z" + n) }]), init: lit(0) })); @@ -1070,17 +1074,17 @@ assertGlobalStmt("try { } catch(e) { }", assertGlobalStmt(";", 11, { emptyStatement: function() 11 }); assertGlobalStmt("debugger;", 12, { debuggerStatement: function() 12 }); assertGlobalStmt("42;", 13, { expressionStatement: function() 13 }); assertGlobalStmt("for (;;) break", forStmt(null, null, null, 14), { breakStatement: function() 14 }); assertGlobalStmt("for (;;) continue", forStmt(null, null, null, 15), { continueStatement: function() 15 }); 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 }); assertGlobalExpr("(x ? y : z)", 2, { conditionalExpression: function() 2 }); assertGlobalExpr("x + y", 3, { binaryExpression: function() 3 }); assertGlobalExpr("delete x", 4, { unaryExpression: function() 4 }); assertGlobalExpr("x = y", 5, { assignmentExpression: function() 5 }); assertGlobalExpr("x || y", 6, { logicalExpression: function() 6 });
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -677,17 +677,17 @@ StaticBlockObject::create(ExclusiveConte if (!obj) return nullptr; return &obj->as<StaticBlockObject>(); } /* static */ Shape * StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> 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); *redeclared = false; /* Inline JSObject::addProperty in order to trap the redefinition case. */ Shape **spp; @@ -696,21 +696,23 @@ StaticBlockObject::addVar(ExclusiveConte return nullptr; } /* * Don't convert this object to dictionary mode so that we can clone the * 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<SequentialExecution>(cx, block, id, /* getter = */ nullptr, /* setter = */ nullptr, slot, - JSPROP_ENUMERATE | JSPROP_PERMANENT, + propFlags, /* attrs = */ 0, spp, /* allowDictionary = */ false); } const Class BlockObject::class_ = { "Block", JSCLASS_IMPLEMENTS_BARRIERS | @@ -768,28 +770,30 @@ js::XDRStaticBlockObject(XDRState<mode> RootedAtom atom(cx); if (!XDRAtom(xdr, &atom)) return false; RootedId id(cx, atom != cx->runtime()->emptyString ? 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); if (!shapes.growBy(count)) return false; for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) shapes[obj->shapeToIndex(r.front())].set(&r.front()); @@ -806,18 +810,20 @@ js::XDRStaticBlockObject(XDRState<mode> MOZ_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid)); atom = JSID_IS_ATOM(propid) ? JSID_TO_ATOM(propid) : cx->runtime()->emptyString; 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; } } return true; } template bool js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, MutableHandle<StaticBlockObject*>); @@ -845,17 +851,17 @@ CloneStaticBlockObject(JSContext *cx, Ha for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront()) shapes[srcBlock->shapeToIndex(r.front())].set(&r.front()); for (Shape **p = shapes.begin(); p != shapes.end(); ++p) { RootedId id(cx, (*p)->propid()); unsigned i = srcBlock->shapeToIndex(**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; } clone->setAliased(i, srcBlock->isAliased(i)); } return clone;
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -565,17 +565,17 @@ class StaticBlockObject : public BlockOb * While ScopeCoordinate can generally reference up to 2^24 slots, block objects have an * additional limitation that all slot indices must be storable as uint16_t short-ids in the * associated Shape. If we could remove the block dependencies on shape->shortid, we could * remove INDEX_LIMIT. */ static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16); static Shape *addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id, - unsigned index, bool *redeclared); + bool constant, unsigned index, bool *redeclared); }; class ClonedBlockObject : public BlockObject { public: static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame);
--- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,17 +29,17 @@ namespace js { * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode * * === GREETINGS, FELLOW SUBTRAHEND INCREMENTER! === * For the time being, please increment the subtrahend by 2 each time it * changes, because we have two flavors of bytecode: with JSOP_SYMBOL (in * 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 #ifdef JS_HAS_SYMBOLS + 1 #endif ));
--- a/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm +++ b/toolkit/components/osfile/modules/osfile_shared_allthreads.jsm @@ -14,26 +14,28 @@ * - primitives for importing C functions; * - primitives for dealing with integers, pointers, typed arrays; * - the base class OSError; * - a few additional utilities. */ // 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); } let EXPORTED_SYMBOLS = [ "LOG", "clone", "Config", "Constants",
--- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -405,19 +405,18 @@ function promiseClearHistory() { function shutdownPlaces(aKeepAliveConnection) { let hs = PlacesUtils.history.QueryInterface(Ci.nsIObserver); hs.observe(null, "profile-change-teardown", null); hs.observe(null, "profile-before-change", null); } 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. * * @param aFilename * Name of the file to copy to the profile folder. This file must * exist in the directory that contains the test files. *