Bug 611388 - |const| should be block scoped and require an initializer. (r=shu)
authorEric Faust <efaustbmo@gmail.com>
Thu, 30 Oct 2014 17:27:03 -0700
changeset 214018 a9a7f16c817bfa79b501a11f446e856beb00b473
parent 214017 db8ff91163763ca965fa776facbc7d559b782145
child 214019 ed6401282c181e48024ffb661c1a9d3a65a85f11
push id27771
push userryanvm@gmail.com
push dateWed, 05 Nov 2014 19:04:24 +0000
treeherdermozilla-central@305b4fecce99 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs611388
milestone36.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
Bug 611388 - |const| should be block scoped and require an initializer. (r=shu)
b2g/chrome/content/identity.js
dom/xul/templates/tests/chrome/templates_shared.js
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/asm.js/testGlobals.js
js/src/jit-test/tests/auto-regress/bug487570.js
js/src/jit-test/tests/auto-regress/bug495843.js
js/src/jit-test/tests/basic/bug639797.js
js/src/jit-test/tests/basic/functionRedeclConst.js
js/src/jit-test/tests/basic/functionRedeclGlobalConst.js
js/src/jit-test/tests/basic/functionRedeclLet.js
js/src/jit-test/tests/basic/letTDZDelete.js
js/src/jit-test/tests/basic/letTDZEffectful.js
js/src/jit-test/tests/basic/syntax-error-illegal-character.js
js/src/jit-test/tests/basic/testDestructuringVarInsideWith.js
js/src/jit-test/tests/jaeger/recompile/bug641269.js
js/src/js.msg
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/tests/ecma_5/Object/freeze-global-eval-const.js
js/src/tests/js1_5/Regress/regress-360969-03.js
js/src/tests/js1_5/Regress/regress-360969-04.js
js/src/tests/js1_5/extensions/regress-452565.js
js/src/tests/js1_6/Regress/regress-372565.js
js/src/tests/js1_7/block/regress-349507.js
js/src/tests/js1_8/genexps/regress-384991.js
js/src/tests/js1_8_1/regress/regress-452498-068.js
js/src/tests/js1_8_1/regress/regress-452498-092.js
js/src/tests/js1_8_1/regress/regress-452498-101.js
js/src/tests/js1_8_1/regress/regress-452498-102.js
js/src/tests/js1_8_1/regress/regress-452498-112.js
js/src/tests/js1_8_1/regress/regress-452498-117.js
js/src/tests/js1_8_1/regress/regress-452498-160.js
js/src/tests/js1_8_1/regress/regress-452498-185.js
js/src/tests/js1_8_1/regress/regress-452498-187.js
js/src/tests/js1_8_1/regress/regress-452498-192.js
js/src/tests/js1_8_1/strict/12.2.1.js
js/src/tests/js1_8_5/extensions/redeclaration-of-catch-warning.js
js/src/tests/js1_8_5/extensions/reflect-parse.js
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Xdr.h
toolkit/components/osfile/modules/osfile_shared_allthreads.jsm
toolkit/components/places/tests/head_common.js
--- 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.
  *