Bug 1181869 - Update Bindings to use normal Rooted primitives; r=shu
authorTerrence Cole <terrence@mozilla.com>
Thu, 09 Jul 2015 13:34:50 -0700
changeset 253311 d588ff0a68d9f982bddcaba5ab304c2153f0962f
parent 253310 d22de68da18093d54975a3d054b2818e52b9f228
child 253312 73d2c854d7703ee94c0d0ddbc71cfb947648cd15
push id29065
push userryanvm@gmail.com
push dateFri, 17 Jul 2015 14:26:32 +0000
treeherderautoland@911935404233 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1181869
milestone42.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 1181869 - Update Bindings to use normal Rooted primitives; r=shu
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/gc/RootMarking.cpp
js/src/jspubtd.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -443,22 +443,23 @@ frontend::CompileScript(ExclusiveContext
      */
     if (!bce.emit1(JSOP_RETRVAL))
         return nullptr;
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).  They may have block-scoped
     // locals, however, which are allocated to the fixed part of the stack
     // frame.
-    InternalHandle<Bindings*> bindings(script, &script->bindings);
-    if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0,
+    Rooted<Bindings> bindings(cx, script->bindings);
+    if (!Bindings::initWithTemporaryStorage(cx, &bindings, 0, 0, 0,
                                             pc->blockScopeDepth, 0, 0, nullptr))
     {
         return nullptr;
     }
+    script->bindings = bindings;
 
     if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
         return nullptr;
 
     // Note that this marking must happen before we tell Debugger
     // about the new script, in case Debugger delazifies the script's
     // inner functions.
     if (options.forEval)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3420,18 +3420,17 @@ BytecodeEmitter::emitFunctionScript(Pars
     else
         fun->setScript(script);
 
     if (funbox->argumentsHasLocalBinding()) {
         MOZ_ASSERT(offset() == 0);  /* See JSScript::argumentsBytecode. */
         switchToPrologue();
         if (!emit1(JSOP_ARGUMENTS))
             return false;
-        InternalBindingsHandle bindings(script, &script->bindings);
-        BindingIter bi = Bindings::argumentsBinding(cx, bindings);
+        BindingIter bi = Bindings::argumentsBinding(cx, script);
         if (script->bindingIsAliased(bi)) {
             ScopeCoordinate sc;
             sc.setHops(0);
             sc.setSlot(0);  // initialize to silence GCC warning
             JS_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().arguments, &sc));
             if (!emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical))
                 return false;
         } else {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -366,17 +366,17 @@ AppendPackedBindings(const ParseContext<
             ++*numUnaliased;
     }
 }
 
 template <typename ParseHandler>
 bool
 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
                                                      LifoAlloc& alloc,
-                                                     InternalHandle<Bindings*> bindings) const
+                                                     MutableHandle<Bindings> bindings) const
 {
     MOZ_ASSERT(sc->isFunctionBox());
     MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
     MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
 
     /*
      * Avoid pathological edge cases by explicitly limiting the total number of
      * bindings to what will fit in a uint32_t.
@@ -842,20 +842,20 @@ Parser<FullParseHandler>::standaloneFunc
         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
             Definition* dn = r.front().value().get<FullParseHandler>();
             MOZ_ASSERT(dn->isPlaceholder());
 
             handler.deoptimizeUsesWithin(dn, fn->pn_pos);
         }
     }
 
-    InternalHandle<Bindings*> funboxBindings =
-        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings))
-        return null();
+    Rooted<Bindings> bindings(context, funbox->bindings);
+    if (!funpc.generateFunctionBindings(context, tokenStream, alloc, &bindings))
+        return null();
+    funbox->bindings = bindings;
 
     return fn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkFunctionArguments()
 {
@@ -1465,19 +1465,22 @@ Parser<FullParseHandler>::leaveFunction(
                 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
             }
 
             /* Mark the outer dn as escaping. */
             outer_dn->pn_dflags |= PND_CLOSED;
         }
     }
 
-    InternalHandle<Bindings*> bindings =
-        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    return pc->generateFunctionBindings(context, tokenStream, alloc, bindings);
+    Rooted<Bindings> bindings(context, funbox->bindings);
+    if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
+        return false;
+    funbox->bindings = bindings;
+
+    return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler>* outerpc,
                                           FunctionSyntaxKind kind)
 {
     outerpc->blockidGen = pc->blockidGen;
@@ -2487,20 +2490,20 @@ Parser<FullParseHandler>::standaloneLazy
     if (fun->isNamedLambda()) {
         if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
             Definition* dn = p.value().get<FullParseHandler>();
             if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                 return nullptr;
         }
     }
 
-    InternalHandle<Bindings*> bindings =
-        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings))
-        return null();
+    Rooted<Bindings> bindings(context, funbox->bindings);
+    if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
+        return null();
+    funbox->bindings = bindings;
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
 template <typename ParseHandler>
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -208,17 +208,17 @@ struct ParseContext : public GenericPars
      *  - Bindings provide the initial js::Shape to use when creating a dynamic
      *    scope object (js::CallObject) for the function. This shape is used
      *    during dynamic name lookup.
      *  - Sometimes a script's bindings are accessed at runtime to retrieve the
      *    contents of the lexical scope (e.g., from the debugger).
      */
     bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
                                   LifoAlloc& alloc,
-                                  InternalHandle<Bindings*> bindings) const;
+                                  MutableHandle<Bindings> bindings) const;
 
   private:
     ParseContext**  parserPC;     /* this points to the Parser's active pc
                                        and holds either |this| or one of
                                        |this|'s descendents */
 
     // Value for parserPC to restore at the end. Use 'parent' instead for
     // information about the parse chain, this may be nullptr if
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -40,22 +40,16 @@ typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 // Note: the following two functions cannot be static as long as we are using
 // GCC 4.4, since it requires template function parameters to have external
 // linkage.
 
 void
-MarkBindingsRoot(JSTracer* trc, Bindings* bindings, const char* name)
-{
-    bindings->trace(trc);
-}
-
-void
 MarkPropertyDescriptorRoot(JSTracer* trc, JSPropertyDescriptor* pd, const char* name)
 {
     pd->trace(trc);
 }
 
 template <class T>
 static inline bool
 IgnoreExactRoot(T* thingp)
@@ -101,17 +95,16 @@ MarkExactStackRootsAcrossTypes(T context
         trc, context, "exact-objectgroup");
     MarkExactStackRootList<JSString*, TraceRoot>(trc, context, "exact-string");
     MarkExactStackRootList<JS::Symbol*, TraceRoot>(trc, context, "exact-symbol");
     MarkExactStackRootList<jit::JitCode*, TraceRoot>(trc, context, "exact-jitcode");
     MarkExactStackRootList<JSScript*, TraceRoot>(trc, context, "exact-script");
     MarkExactStackRootList<LazyScript*, TraceRoot>(trc, context, "exact-lazy-script");
     MarkExactStackRootList<jsid, TraceRoot>(trc, context, "exact-id");
     MarkExactStackRootList<Value, TraceRoot>(trc, context, "exact-value");
-    MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
     MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
         trc, context, "JSPropertyDescriptor");
     MarkExactStackRootList<JS::StaticTraceable,
                            js::DispatchWrapper<JS::StaticTraceable>::TraceWrapped>(
         trc, context, "StaticTraceable");
 }
 
 static void
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -320,17 +320,16 @@ enum ThingRootKind
     THING_ROOT_OBJECT_GROUP,
     THING_ROOT_STRING,
     THING_ROOT_SYMBOL,
     THING_ROOT_JIT_CODE,
     THING_ROOT_SCRIPT,
     THING_ROOT_LAZY_SCRIPT,
     THING_ROOT_ID,
     THING_ROOT_VALUE,
-    THING_ROOT_BINDINGS,
     THING_ROOT_PROPERTY_DESCRIPTOR,
     THING_ROOT_PROP_DESC,
     THING_ROOT_STATIC_TRACEABLE,
     THING_ROOT_LIMIT
 };
 
 template <typename T>
 struct RootKind;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -56,55 +56,56 @@ using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::RotateLeft;
 
 /* static */ BindingIter
-Bindings::argumentsBinding(ExclusiveContext* cx, InternalBindingsHandle bindings)
+Bindings::argumentsBinding(ExclusiveContext* cx, HandleScript script)
 {
     HandlePropertyName arguments = cx->names().arguments;
-    BindingIter bi(bindings);
+    BindingIter bi(script);
     while (bi->name() != arguments)
         bi++;
     return bi;
 }
 
 bool
-Bindings::initWithTemporaryStorage(ExclusiveContext* cx, InternalBindingsHandle self,
+Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings> self,
                                    uint32_t numArgs, uint32_t numVars,
                                    uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
                                    uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
-                                   Binding* bindingArray)
+                                   const Binding* bindingArray)
 {
-    MOZ_ASSERT(!self->callObjShape_);
-    MOZ_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
+    MOZ_ASSERT(!self.callObjShape());
+    MOZ_ASSERT(self.bindingArrayUsingTemporaryStorage());
+    MOZ_ASSERT(!self.bindingArray());
     MOZ_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
     MOZ_ASSERT(numArgs <= ARGC_LIMIT);
     MOZ_ASSERT(numVars <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBlockScoped <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBodyLevelLexicals <= LOCALNO_LIMIT);
     mozilla::DebugOnly<uint64_t> totalSlots = uint64_t(numVars) +
                                               uint64_t(numBodyLevelLexicals) +
                                               uint64_t(numBlockScoped);
     MOZ_ASSERT(totalSlots <= LOCALNO_LIMIT);
     MOZ_ASSERT(UINT32_MAX - numArgs >= totalSlots);
 
     MOZ_ASSERT(numUnaliasedVars <= numVars);
     MOZ_ASSERT(numUnaliasedBodyLevelLexicals <= numBodyLevelLexicals);
 
-    self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
-    self->numArgs_ = numArgs;
-    self->numVars_ = numVars;
-    self->numBodyLevelLexicals_ = numBodyLevelLexicals;
-    self->numBlockScoped_ = numBlockScoped;
-    self->numUnaliasedVars_ = numUnaliasedVars;
-    self->numUnaliasedBodyLevelLexicals_ = numUnaliasedBodyLevelLexicals;
+    self.setBindingArray(bindingArray, TEMPORARY_STORAGE_BIT);
+    self.setNumArgs(numArgs);
+    self.setNumVars(numVars);
+    self.setNumBodyLevelLexicals(numBodyLevelLexicals);
+    self.setNumBlockScoped(numBlockScoped);
+    self.setNumUnaliasedVars(numUnaliasedVars);
+    self.setNumUnaliasedBodyLevelLexicals(numUnaliasedBodyLevelLexicals);
 
     // Get the initial shape to use when creating CallObjects for this script.
     // After creation, a CallObject's shape may change completely (via direct eval() or
     // other operations that mutate the lexical scope). However, since the
     // lexical bindings added to the initial shape are permanent and the
     // allocKind/nfixed of a CallObject cannot change, one may assume that the
     // slot location (whether in the fixed or dynamic slots) of a variable is
     // the same as in the initial shape. (This is assumed by the interpreter and
@@ -129,17 +130,17 @@ Bindings::initWithTemporaryStorage(Exclu
                 bi.localIndex() >= numVars)
             {
                 aliasedBodyLevelLexicalBegin = nslots;
             }
 
             nslots++;
         }
     }
-    self->aliasedBodyLevelLexicalBegin_ = aliasedBodyLevelLexicalBegin;
+    self.setAliasedBodyLevelLexicalBegin(aliasedBodyLevelLexicalBegin);
 
     // Put as many of nslots inline into the object header as possible.
     uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots));
 
     // Start with the empty shape and then append one shape per aliased binding.
     RootedShape shape(cx,
         EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
                                     nfixed, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE));
@@ -185,17 +186,17 @@ Bindings::initWithTemporaryStorage(Exclu
             return false;
 
         MOZ_ASSERT(slot < nslots);
         slot++;
     }
     MOZ_ASSERT(slot == nslots);
 
     MOZ_ASSERT(!shape->inDictionary());
-    self->callObjShape_.init(shape);
+    self.setCallObjShape(shape);
     return true;
 }
 
 bool
 Bindings::initTrivial(ExclusiveContext* cx)
 {
     Shape* shape = EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
                                                CallObject::RESERVED_SLOTS,
@@ -213,22 +214,22 @@ Bindings::switchToScriptStorage(Binding*
     MOZ_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT));
 
     if (count() > 0)
         PodCopy(newBindingArray, bindingArray(), count());
     bindingArrayAndFlag_ = uintptr_t(newBindingArray);
     return reinterpret_cast<uint8_t*>(newBindingArray + count());
 }
 
-bool
-Bindings::clone(JSContext* cx, InternalBindingsHandle self,
+/* static */ bool
+Bindings::clone(JSContext* cx, MutableHandle<Bindings> self,
                 uint8_t* dstScriptData, HandleScript srcScript)
 {
     /* The clone has the same bindingArray_ offset as 'src'. */
-    Bindings& src = srcScript->bindings;
+    Handle<Bindings> src = Handle<Bindings>::fromMarkedLocation(&srcScript->bindings);
     ptrdiff_t off = (uint8_t*)src.bindingArray() - srcScript->data;
     MOZ_ASSERT(off >= 0);
     MOZ_ASSERT(size_t(off) <= srcScript->dataSize());
     Binding* dstPackedBindings = (Binding*)(dstScriptData + off);
 
     /*
      * Since atoms are shareable throughout the runtime, we can simply copy
      * the source's bindingArray directly.
@@ -238,26 +239,20 @@ Bindings::clone(JSContext* cx, InternalB
                                   src.numBlockScoped(),
                                   src.numUnaliasedVars(),
                                   src.numUnaliasedBodyLevelLexicals(),
                                   src.bindingArray()))
     {
         return false;
     }
 
-    self->switchToScriptStorage(dstPackedBindings);
+    self.switchToScriptStorage(dstPackedBindings);
     return true;
 }
 
-/* static */ Bindings
-GCMethods<Bindings>::initial()
-{
-    return Bindings();
-}
-
 template<XDRMode mode>
 static bool
 XDRScriptBindings(XDRState<mode>* xdr, LifoAllocScope& las, uint16_t numArgs, uint32_t numVars,
                   uint16_t numBodyLevelLexicals, uint16_t numBlockScoped,
                   uint32_t numUnaliasedVars, uint16_t numUnaliasedBodyLevelLexicals,
                   HandleScript script)
 {
     JSContext* cx = xdr->cx();
@@ -297,24 +292,25 @@ XDRScriptBindings(XDRState<mode>* xdr, L
 
             PropertyName* name = atoms[i].toString()->asAtom().asPropertyName();
             Binding::Kind kind = Binding::Kind(u8 >> 1);
             bool aliased = bool(u8 & 1);
 
             bindingArray[i] = Binding(name, kind, aliased);
         }
 
-        InternalBindingsHandle bindings(script, &script->bindings);
-        if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars,
+        Rooted<Bindings> bindings(cx, script->bindings);
+        if (!Bindings::initWithTemporaryStorage(cx, &bindings, numArgs, numVars,
                                                 numBodyLevelLexicals, numBlockScoped,
                                                 numUnaliasedVars, numUnaliasedBodyLevelLexicals,
                                                 bindingArray))
         {
             return false;
         }
+        script->bindings = bindings;
     }
 
     return true;
 }
 
 bool
 Bindings::bindingIsAliased(uint32_t bindingIndex)
 {
@@ -3120,19 +3116,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     size_t size = src->dataSize();
     uint8_t* data = AllocScriptData(cx->zone(), size);
     if (size && !data)
         return false;
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
-    InternalHandle<Bindings*> bindingsHandle =
-        InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
-    if (!Bindings::clone(cx, bindingsHandle, data, src))
+    if (!Bindings::clone(cx, &bindings, data, src))
         return false;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject* vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
@@ -3707,18 +3701,17 @@ void
 js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
                             HandleScript script, JSObject* argsobj)
 {
     /*
      * Replace any optimized arguments in the frame with an explicit arguments
      * object. Note that 'arguments' may have already been overwritten.
      */
 
-    InternalBindingsHandle bindings(script, &script->bindings);
-    BindingIter bi = Bindings::argumentsBinding(cx, bindings);
+    BindingIter bi = Bindings::argumentsBinding(cx, script);
 
     if (script->bindingIsAliased(bi)) {
         /*
          * Scan the script to find the slot in the call object that 'arguments'
          * is assigned to.
          */
         jsbytecode* pc = script->code();
         while (*pc != JSOP_ARGUMENTS)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -195,29 +195,28 @@ class Binding
 
     bool aliased() const {
         return bool(bits_ & ALIASED_BIT);
     }
 };
 
 JS_STATIC_ASSERT(sizeof(Binding) == sizeof(uintptr_t));
 
-class Bindings;
-typedef InternalHandle<Bindings*> InternalBindingsHandle;
-
 /*
  * Formal parameters and local variables are stored in a shape tree
  * path encapsulated within this class.  This class represents bindings for
  * both function and top-level scripts (the latter is needed to track names in
  * strict mode eval code, to give such code its own lexical environment).
  */
-class Bindings
+class Bindings : public JS::StaticTraceable
 {
     friend class BindingIter;
     friend class AliasedFormalIter;
+    template <typename Outer> friend class BindingsOperations;
+    template <typename Outer> friend class MutableBindingsOperations;
 
     RelocatablePtrShape callObjShape_;
     uintptr_t bindingArrayAndFlag_;
     uint16_t numArgs_;
     uint16_t numBlockScoped_;
     uint16_t numBodyLevelLexicals_;
     uint16_t aliasedBodyLevelLexicalBegin_;
     uint16_t numUnaliasedBodyLevelLexicals_;
@@ -245,30 +244,38 @@ class Bindings
     }
 
   public:
 
     Binding* bindingArray() const {
         return reinterpret_cast<Binding*>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
     }
 
-    inline Bindings();
+    Bindings()
+      : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
+        numArgs_(0), numBlockScoped_(0),
+        numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0),
+        numVars_(0), numUnaliasedVars_(0)
+    {}
 
     /*
      * Initialize a Bindings with a pointer into temporary storage.
      * bindingArray must have length numArgs + numVars +
      * numBodyLevelLexicals. Before the temporary storage is release,
      * switchToScriptStorage must be called, providing a pointer into the
      * Binding array stored in script->data.
      */
-    static bool initWithTemporaryStorage(ExclusiveContext* cx, InternalBindingsHandle self,
-                                         uint32_t numArgs, uint32_t numVars,
-                                         uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
-                                         uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
-                                         Binding* bindingArray);
+    static bool initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings> self,
+                                         uint32_t numArgs,
+                                         uint32_t numVars,
+                                         uint32_t numBodyLevelLexicals,
+                                         uint32_t numBlockScoped,
+                                         uint32_t numUnaliasedVars,
+                                         uint32_t numUnaliasedBodyLevelLexicals,
+                                         const Binding* bindingArray);
 
     // Initialize a trivial Bindings with no slots and an empty callObjShape.
     bool initTrivial(ExclusiveContext* cx);
 
     // CompileScript parses and compiles one statement at a time, but the result
     // is one Script object.  There will be no vars or bindings, because those
     // go on the global, but there may be block-scoped locals, and the number of
     // block-scoped locals may increase as we parse more expressions.  This
@@ -287,17 +294,17 @@ class Bindings
     }
 
     uint8_t* switchToScriptStorage(Binding* newStorage);
 
     /*
      * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData
      * is the pointer to what will eventually be dstScript->data.
      */
-    static bool clone(JSContext* cx, InternalBindingsHandle self, uint8_t* dstScriptData,
+    static bool clone(JSContext* cx, MutableHandle<Bindings> self, uint8_t* dstScriptData,
                       HandleScript srcScript);
 
     uint32_t numArgs() const { return numArgs_; }
     uint32_t numVars() const { return numVars_; }
     uint32_t numBodyLevelLexicals() const { return numBodyLevelLexicals_; }
     uint32_t numBlockScoped() const { return numBlockScoped_; }
     uint32_t numBodyLevelLocals() const { return numVars_ + numBodyLevelLexicals_; }
     uint32_t numUnaliasedBodyLevelLocals() const { return numUnaliasedVars_ + numUnaliasedBodyLevelLexicals_; }
@@ -312,39 +319,148 @@ class Bindings
 
     // Return the size of the bindingArray.
     uint32_t count() const { return numArgs() + numVars() + numBodyLevelLexicals(); }
 
     /* Return the initial shape of call objects created for this scope. */
     Shape* callObjShape() const { return callObjShape_; }
 
     /* Convenience method to get the var index of 'arguments'. */
-    static BindingIter argumentsBinding(ExclusiveContext* cx, InternalBindingsHandle);
+    static BindingIter argumentsBinding(ExclusiveContext* cx, HandleScript script);
 
     /* Return whether the binding at bindingIndex is aliased. */
     bool bindingIsAliased(uint32_t bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
     bool hasAnyAliasedBindings() const {
         if (!callObjShape_)
             return false;
 
         return !callObjShape_->isEmptyShape();
     }
 
     Binding* begin() const { return bindingArray(); }
     Binding* end() const { return bindingArray() + count(); }
 
-    static js::ThingRootKind rootKind() { return js::THING_ROOT_BINDINGS; }
+    static void trace(Bindings* self, JSTracer* trc) { self->trace(trc); }
     void trace(JSTracer* trc);
 };
 
+template <class Outer>
+class BindingsOperations
+{
+    const Bindings& bindings() const { return static_cast<const Outer*>(this)->extract(); }
+
+  public:
+    // Direct data access to the underlying bindings.
+    const RelocatablePtrShape& callObjShape() const {
+        return bindings().callObjShape_;
+    }
+    uint16_t numArgs() const {
+        return bindings().numArgs_;
+    }
+    uint16_t numBlockScoped() const {
+        return bindings().numBlockScoped_;
+    }
+    uint16_t numBodyLevelLexicals() const {
+        return bindings().numBodyLevelLexicals_;
+    }
+    uint16_t aliasedBodyLevelLexicalBegin() const {
+        return bindings().aliasedBodyLevelLexicalBegin_;
+    }
+    uint16_t numUnaliasedBodyLevelLexicals() const {
+        return bindings().numUnaliasedBodyLevelLexicals_;
+    }
+    uint32_t numVars() const {
+        return bindings().numVars_;
+    }
+    uint32_t numUnaliasedVars() const {
+        return bindings().numUnaliasedVars_;
+    }
+
+    // Binding array access.
+    bool bindingArrayUsingTemporaryStorage() const {
+        return bindings().bindingArrayUsingTemporaryStorage();
+    }
+    const Binding* bindingArray() const {
+        return bindings().bindingArray();
+    }
+    uint32_t count() const {
+        return bindings().count();
+    }
+
+    // Helpers.
+    uint32_t numBodyLevelLocals() const {
+        return numVars() + numBodyLevelLexicals();
+    }
+    uint32_t numUnaliasedBodyLevelLocals() const {
+        return numUnaliasedVars() + numUnaliasedBodyLevelLexicals();
+    }
+    uint32_t numAliasedBodyLevelLocals() const {
+        return numBodyLevelLocals() - numUnaliasedBodyLevelLocals();
+    }
+    uint32_t numLocals() const {
+        return numVars() + numBodyLevelLexicals() + numBlockScoped();
+    }
+    uint32_t numFixedLocals() const {
+        return numUnaliasedVars() + numUnaliasedBodyLevelLexicals() + numBlockScoped();
+    }
+    uint32_t lexicalBegin() const {
+        return numArgs() + numVars();
+    }
+};
+
+template <class Outer>
+class MutableBindingsOperations : public BindingsOperations<Outer>
+{
+    Bindings& bindings() { return static_cast<Outer*>(this)->extractMutable(); }
+
+  public:
+    void setCallObjShape(HandleShape shape) { bindings().callObjShape_ = shape; }
+    void setBindingArray(const Binding* bindingArray, uintptr_t temporaryBit) {
+        bindings().bindingArrayAndFlag_ = uintptr_t(bindingArray) | temporaryBit;
+    }
+    void setNumArgs(uint16_t num) { bindings().numArgs_ = num; }
+    void setNumVars(uint32_t num) { bindings().numVars_ = num; }
+    void setNumBodyLevelLexicals(uint16_t num) { bindings().numBodyLevelLexicals_ = num; }
+    void setNumBlockScoped(uint16_t num) { bindings().numBlockScoped_ = num; }
+    void setNumUnaliasedVars(uint32_t num) { bindings().numUnaliasedVars_ = num; }
+    void setNumUnaliasedBodyLevelLexicals(uint16_t num) {
+        bindings().numUnaliasedBodyLevelLexicals_ = num;
+    }
+    void setAliasedBodyLevelLexicalBegin(uint16_t offset) {
+        bindings().aliasedBodyLevelLexicalBegin_ = offset;
+    }
+    uint8_t* switchToScriptStorage(Binding* permanentStorage) {
+        return bindings().switchToScriptStorage(permanentStorage);
+    }
+};
+
 template <>
-struct GCMethods<Bindings> {
-    static Bindings initial();
+class HandleBase<Bindings> : public BindingsOperations<JS::Handle<Bindings>>
+{
+    friend class BindingsOperations<JS::Handle<Bindings>>;
+    const Bindings& extract() const {
+        return static_cast<const JS::Handle<Bindings>*>(this)->get();
+    }
+};
+
+template <>
+class MutableHandleBase<Bindings>
+  : public MutableBindingsOperations<JS::MutableHandle<Bindings>>
+{
+    friend class BindingsOperations<JS::MutableHandle<Bindings>>;
+    const Bindings& extract() const {
+        return static_cast<const JS::MutableHandle<Bindings>*>(this)->get();
+    }
+
+    friend class MutableBindingsOperations<JS::MutableHandle<Bindings>>;
+    Bindings& extractMutable() {
+        return static_cast<JS::MutableHandle<Bindings>*>(this)->get();
+    }
 };
 
 class ScriptCounts
 {
     friend class ::JSScript;
     friend struct ScriptAndCounts;
 
     /*
@@ -1777,30 +1893,34 @@ namespace js {
 /*
  * Iterator over a script's bindings (formals and variables).
  * The order of iteration is:
  *  - first, formal arguments, from index 0 to numArgs
  *  - next, variables, from index 0 to numLocals
  */
 class BindingIter
 {
-    const InternalBindingsHandle bindings_;
+    Handle<Bindings> bindings_;
     uint32_t i_;
     uint32_t unaliasedLocal_;
 
     friend class ::JSScript;
     friend class Bindings;
 
   public:
-    explicit BindingIter(const InternalBindingsHandle& bindings)
-      : bindings_(bindings), i_(0), unaliasedLocal_(0) {}
+    explicit BindingIter(Handle<Bindings> bindings)
+      : bindings_(bindings), i_(0), unaliasedLocal_(0)
+    {}
+
     explicit BindingIter(const HandleScript& script)
-      : bindings_(script, &script->bindings), i_(0), unaliasedLocal_(0) {}
-
-    bool done() const { return i_ == bindings_->count(); }
+      : bindings_(Handle<Bindings>::fromMarkedLocation(&script->bindings)),
+        i_(0), unaliasedLocal_(0)
+    {}
+
+    bool done() const { return i_ == bindings_.count(); }
     explicit operator bool() const { return !done(); }
     BindingIter& operator++() { (*this)++; return *this; }
 
     void operator++(int) {
         MOZ_ASSERT(!done());
         const Binding& binding = **this;
         if (binding.kind() != Binding::ARGUMENT && !binding.aliased())
             unaliasedLocal_++;
@@ -1808,47 +1928,47 @@ class BindingIter
     }
 
     // Stack slots are assigned to arguments (aliased and unaliased) and
     // unaliased locals. frameIndex() returns the slot index. It's invalid to
     // call this method when the iterator is stopped on an aliased local, as it
     // has no stack slot.
     uint32_t frameIndex() const {
         MOZ_ASSERT(!done());
-        if (i_ < bindings_->numArgs())
+        if (i_ < bindings_.numArgs())
             return i_;
         MOZ_ASSERT(!(*this)->aliased());
         return unaliasedLocal_;
     }
 
     // If the current binding is an argument, argIndex() returns its index.
     // It returns the same value as frameIndex(), as slots are allocated for
     // both unaliased and aliased arguments.
     uint32_t argIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ < bindings_->numArgs());
+        MOZ_ASSERT(i_ < bindings_.numArgs());
         return i_;
     }
     uint32_t argOrLocalIndex() const {
         MOZ_ASSERT(!done());
-        return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs();
+        return i_ < bindings_.numArgs() ? i_ : i_ - bindings_.numArgs();
     }
     uint32_t localIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ >= bindings_->numArgs());
-        return i_ - bindings_->numArgs();
+        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_]; }
+    const Binding& operator*() const { MOZ_ASSERT(!done()); return bindings_.bindingArray()[i_]; }
+    const Binding* operator->() const { MOZ_ASSERT(!done()); return &bindings_.bindingArray()[i_]; }
 };
 
 /*
  * Iterator over the aliased formal bindings in ascending index order. This can
  * be veiwed as a filtering of BindingIter with predicate
  *   bi->aliased() && bi->kind() == Binding::ARGUMENT
  */
 class AliasedFormalIter
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -16,24 +16,16 @@
 
 #include "jscompartmentinlines.h"
 
 #include "vm/Shape-inl.h"
 
 namespace js {
 
 inline
-Bindings::Bindings()
-    : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
-      numArgs_(0), numBlockScoped_(0),
-      numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0),
-      numVars_(0), numUnaliasedVars_(0)
-{}
-
-inline
 AliasedFormalIter::AliasedFormalIter(JSScript* script)
   : begin_(script->bindingArray()),
     p_(begin_),
     end_(begin_ + (script->funHasAnyAliasedFormal() ? script->numArgs() : 0)),
     slot_(CallObject::RESERVED_SLOTS)
 {
     settle();
 }