Bug 1263778 - Rename a bunch of low-level [[Prototype]] access methods to make their interactions with statically-known and dynamically-computed [[Prototype]]s clearer. r=efaust
authorJeff Walden <jwalden@mit.edu>
Fri, 18 Mar 2016 16:45:31 -0700
changeset 296026 6ff565c59df8b540306742bfb40dfcfffb282f8a
parent 296025 242bab2c3b069e0c0b0aa16eea73d11157556b95
child 296027 7c1ce3b16cdc6b23eec48feaa378f6bf9e6f1e61
push id30230
push usercbook@mozilla.com
push dateWed, 04 May 2016 09:55:16 +0000
treeherdermozilla-central@311c7ea8803d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1263778
milestone49.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 1263778 - Rename a bunch of low-level [[Prototype]] access methods to make their interactions with statically-known and dynamically-computed [[Prototype]]s clearer. r=efaust
dom/base/WindowNamedPropertiesHandler.h
dom/base/nsGlobalWindow.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/RegExp.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/jit/BaselineIC.cpp
js/src/jit/CacheIR.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/SharedIC.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscompartment.cpp
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/vm/Interpreter.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.cpp
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/PIC.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
js/src/vm/Shape.cpp
js/src/vm/TaggedProto.cpp
js/src/vm/TaggedProto.h
js/src/vm/TypeInference.cpp
js/src/vm/UnboxedObject.cpp
--- a/dom/base/WindowNamedPropertiesHandler.h
+++ b/dom/base/WindowNamedPropertiesHandler.h
@@ -32,19 +32,18 @@ public:
                  JS::ObjectOpResult &result) const override;
   virtual bool
   ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
                JS::AutoIdVector& aProps) const override;
   virtual bool
   delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
           JS::ObjectOpResult &aResult) const override;
 
-  // No need for getPrototypeIfOrdinary here: this object shouldn't have a
-  // lazy prototype, so this trap would never be called (and the inherited
-  // version, from BaseProxyHandler, just crashes).
+  // No need for getPrototypeIfOrdinary here: window named-properties objects
+  // have static prototypes, so this trap is never called.
 
   virtual bool
   preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                     JS::ObjectOpResult& aResult) const override
   {
     return aResult.failCantPreventExtensions();
   }
   virtual bool
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -920,23 +920,24 @@ nsOuterWindowProxy::getPrototypeIfOrdina
                                            JS::Handle<JSObject*> proxy,
                                            bool* isOrdinary,
                                            JS::MutableHandle<JSObject*> protop) const
 {
   // Window's [[GetPrototypeOf]] trap isn't the ordinary definition:
   //
   //   https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof
   //
-  // We nonetheless can implement it here using a non-"lazy" [[Prototype]],
-  // because wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp)
-  // supply all the non-ordinary behavior.
+  // We nonetheless can implement it with a static [[Prototype]], because
+  // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply
+  // all non-ordinary behavior.
   //
   // But from a spec point of view, it's the exact same object in both cases --
-  // only the observer's changed.  So both cases *must* report non-ordinary,
-  // even if non-"lazy" [[Prototype]] usually means ordinary.
+  // only the observer's changed.  So this getPrototypeIfOrdinary trap on the
+  // non-wrapper object *must* report non-ordinary, even if static [[Prototype]]
+  // usually means ordinary.
   *isOrdinary = false;
   return true;
 }
 
 bool
 nsOuterWindowProxy::preventExtensions(JSContext* cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::ObjectOpResult& result) const
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -227,17 +227,17 @@ MapIteratorObject::next(JSContext* cx, H
 
 /* static */ JSObject*
 MapIteratorObject::createResultPair(JSContext* cx)
 {
     RootedArrayObject resultPairObj(cx, NewDenseFullyAllocatedArray(cx, 2, nullptr, TenuredObject));
     if (!resultPairObj)
         return nullptr;
 
-    Rooted<TaggedProto> proto(cx, resultPairObj->getTaggedProto());
+    Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto());
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto);
     if (!group)
         return nullptr;
     resultPairObj->setGroup(group);
 
     resultPairObj->setDenseInitializedLength(2);
     resultPairObj->initDenseElement(0, NullValue());
     resultPairObj->initDenseElement(1, NullValue());
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -606,17 +606,17 @@ js::ObjectCreateImpl(JSContext* cx, Hand
     }
 
     return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind);
 }
 
 PlainObject*
 js::ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj)
 {
-    RootedObject proto(cx, templateObj->getProto());
+    RootedObject proto(cx, templateObj->staticPrototype());
     RootedObjectGroup group(cx, templateObj->group());
     return ObjectCreateImpl(cx, proto, GenericObject, group);
 }
 
 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
 bool
 js::obj_create(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1604,22 +1604,22 @@ js::RegExpInstanceOptimizableRaw(JSConte
     NativeObject* nobj = static_cast<NativeObject*>(rx);
 
     Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
     if (shape == nobj->lastProperty()) {
         *result = true;
         return true;
     }
 
-    if (rx->hasLazyPrototype()) {
+    if (!rx->hasStaticPrototype()) {
         *result = false;
         return true;
     }
 
-    if (rx->getTaggedProto().toObjectOrNull() != proto) {
+    if (rx->staticPrototype() != proto) {
         *result = false;
         return true;
     }
 
     if (!RegExpObject::isInitialShape(nobj)) {
         *result = false;
         return true;
     }
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1674,17 +1674,17 @@ TypedObject::obj_lookupProperty(JSContex
                                 MutableHandleObject objp, MutableHandleShape propp)
 {
     if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
         MarkNonNativePropertyFound<CanGC>(propp);
         objp.set(obj);
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
         propp.set(nullptr);
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
@@ -1746,17 +1746,17 @@ TypedObject::obj_hasProperty(JSContext* 
       case type::Struct:
         size_t index;
         if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) {
             *foundp = true;
             return true;
         }
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
@@ -1803,17 +1803,17 @@ TypedObject::obj_getProperty(JSContext* 
             break;
 
         size_t offset = descr->fieldOffset(fieldIndex);
         Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
         return Reify(cx, fieldType, typedObj, offset, vp);
       }
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, proto, receiver, id, vp);
 }
 
@@ -1831,17 +1831,17 @@ TypedObject::obj_getElement(JSContext* c
       case type::Simd:
       case type::Struct:
         break;
 
       case type::Array:
         return obj_getArrayElement(cx, typedObj, descr, index, vp);
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return GetElement(cx, proto, receiver, index, vp);
 }
 
@@ -2014,17 +2014,17 @@ IsOwnId(JSContext* cx, HandleObject obj,
 }
 
 bool
 TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto)
         return result.succeed();
 
     return DeleteProperty(cx, proto, id, result);
 }
 
 bool
 TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -525,17 +525,18 @@ class TypedObject : public JSObject
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
     static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                               bool enumerableOnly);
 
   public:
     TypedProto& typedProto() const {
-        return getProto()->as<TypedProto>();
+        // Typed objects' prototypes can't be modified.
+        return staticPrototype()->as<TypedProto>();
     }
 
     TypeDescr& typeDescr() const {
         return group()->typeDescr();
     }
 
     int32_t offset() const;
     int32_t length() const;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -780,22 +780,24 @@ IsCacheableSetPropAddSlot(JSContext* cx,
         obj->getClass()->getAddProperty())
     {
         return false;
     }
 
     size_t chainDepth = 0;
     // Walk up the object prototype chain and ensure that all prototypes are
     // native, and that all prototypes have no setter defined on the property.
-    for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) {
+    for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
         chainDepth++;
         // if prototype is non-native, don't optimize
         if (!proto->isNative())
             return false;
 
+        MOZ_ASSERT(proto->hasStaticPrototype());
+
         // if prototype defines this property in a non-plain way, don't optimize
         Shape* protoShape = proto->as<NativeObject>().lookup(cx, id);
         if (protoShape && !protoShape->hasDefaultSetter())
             return false;
 
         // Otherwise, if there's no such property, watch out for a resolve hook
         // that would need to be invoked and thus prevent inlining of property
         // addition.
@@ -2347,23 +2349,23 @@ static bool
 SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj)
 {
     static const size_t MAX_DEPTH = ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH;
     ICSetElem_DenseOrUnboxedArrayAddImpl<MAX_DEPTH>* nstub = stub->toImplUnchecked<MAX_DEPTH>();
 
     if (obj->maybeShape() != nstub->shape(0))
         return false;
 
-    JSObject* proto = obj->getProto();
+    JSObject* proto = obj->staticPrototype();
     for (size_t i = 0; i < stub->protoChainDepth(); i++) {
         if (!proto->isNative())
             return false;
         if (proto->as<NativeObject>().lastProperty() != nstub->shape(i + 1))
             return false;
-        proto = obj->getProto();
+        proto = obj->staticPrototype();
         if (!proto) {
             if (i != stub->protoChainDepth() - 1)
                 return false;
             break;
         }
     }
 
     return true;
@@ -2474,22 +2476,22 @@ CanOptimizeDenseOrUnboxedArraySetElem(JS
         return false;
 
     // The checks are not complete.  The object may have a setter definition,
     // either directly, or via a prototype, or via the target object for a prototype
     // which is a proxy, that handles a particular integer write.
     // Scan the prototype and shape chain to make sure that this is not the case.
     if (obj->isIndexed())
         return false;
-    JSObject* curObj = obj->getProto();
+    JSObject* curObj = obj->staticPrototype();
     while (curObj) {
         ++*protoDepthOut;
         if (!curObj->isNative() || curObj->isIndexed())
             return false;
-        curObj = curObj->getProto();
+        curObj = curObj->staticPrototype();
     }
 
     if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH)
         return false;
 
     *isAddingCaseOut = true;
     return true;
 }
@@ -3693,17 +3695,17 @@ TryAttachGlobalNameValueStub(JSContext* 
     RootedNativeObject current(cx, globalLexical);
     while (true) {
         shape = current->lookup(cx, id);
         if (shape)
             break;
         if (current == globalLexical) {
             current = &globalLexical->global();
         } else {
-            JSObject* proto = current->getProto();
+            JSObject* proto = current->staticPrototype();
             if (!proto || !proto->is<NativeObject>())
                 return true;
             current = &proto->as<NativeObject>();
         }
     }
 
     // Instantiate this global property, for use during Ion compilation.
     if (IsIonEnabled(cx))
@@ -3768,17 +3770,17 @@ TryAttachGlobalNameAccessorStub(JSContex
 
     // The property must be found, and it must be found as a normal data property.
     RootedShape shape(cx);
     RootedNativeObject current(cx, global);
     while (true) {
         shape = current->lookup(cx, id);
         if (shape)
             break;
-        JSObject* proto = current->getProto();
+        JSObject* proto = current->staticPrototype();
         if (!proto || !proto->is<NativeObject>())
             return true;
         current = &proto->as<NativeObject>();
     }
 
     // Instantiate this global property, for use during Ion compilation.
     if (IsIonEnabled(cx))
         EnsureTrackPropertyTypes(cx, current, id);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -132,36 +132,34 @@ GeneratePrototypeGuards(CacheIRWriter& w
     // prototype chain is directly altered, then TI will toss the jitcode, so we
     // don't have to worry about it, and any other change to the holder, or
     // adding a shadowing property will result in reshaping the holder, and thus
     // the failure of the shape guard.
     MOZ_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // If the shape does not imply the proto, emit an explicit proto guard.
-        writer.guardProto(objId, obj->getProto());
+        writer.guardProto(objId, obj->staticPrototype());
     }
 
-    JSObject* pobj = IsCacheableDOMProxy(obj)
-                     ? obj->getTaggedProto().toObjectOrNull()
-                     : obj->getProto();
+    JSObject* pobj = obj->staticPrototype();
     if (!pobj)
         return;
 
     while (pobj != holder) {
         if (pobj->hasUncacheableProto()) {
             ObjOperandId protoId = writer.loadObject(pobj);
             if (pobj->isSingleton()) {
                 // Singletons can have their group's |proto| mutated directly.
-                writer.guardProto(protoId, pobj->getProto());
+                writer.guardProto(protoId, pobj->staticPrototype());
             } else {
                 writer.guardGroup(protoId, pobj->group());
             }
         }
-        pobj = pobj->getProto();
+        pobj = pobj->staticPrototype();
     }
 }
 
 static void
 TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId)
 {
     if (obj->is<UnboxedPlainObject>()) {
         writer.guardGroup(objId, obj->group());
@@ -194,22 +192,22 @@ EmitReadSlotResult(CacheIRWriter& writer
         if (holder) {
             // Guard on the holder's shape.
             holderId = writer.loadObject(holder);
             writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
         } else {
             // The property does not exist. Guard on everything in the prototype
             // chain. This is guaranteed to see only Native objects because of
             // CanAttachNativeGetProp().
-            JSObject* proto = obj->getTaggedProto().toObjectOrNull();
+            JSObject* proto = obj->taggedProto().toObjectOrNull();
             ObjOperandId lastObjId = objId;
             while (proto) {
                 ObjOperandId protoId = writer.loadProto(lastObjId);
                 writer.guardShape(protoId, proto->as<NativeObject>().lastProperty());
-                proto = proto->getProto();
+                proto = proto->staticPrototype();
                 lastObjId = protoId;
             }
         }
     } else if (obj->is<UnboxedPlainObject>()) {
         holder = obj->as<UnboxedPlainObject>().maybeExpando();
         holderId = writer.loadUnboxedExpando(objId);
     } else {
         holderId = objId;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6266,17 +6266,17 @@ IonBuilder::createThisScriptedSingleton(
     if (!proto)
         return nullptr;
 
     JSObject* templateObject = inspector->getTemplateObject(pc);
     if (!templateObject)
         return nullptr;
     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
         return nullptr;
-    if (templateObject->getProto() != proto)
+    if (templateObject->staticPrototype() != proto)
         return nullptr;
 
     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
         return nullptr;
 
     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
@@ -6313,17 +6313,17 @@ IonBuilder::createThisScriptedBaseline(M
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return nullptr;
 
     Value protov = target->getSlot(shape->slot());
     if (!protov.isObject())
         return nullptr;
 
     JSObject* proto = checkNurseryObject(&protov.toObject());
-    if (proto != templateObject->getProto())
+    if (proto != templateObject->staticPrototype())
         return nullptr;
 
     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
         return nullptr;
 
     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
@@ -8101,17 +8101,17 @@ IonBuilder::testSingletonProperty(JSObje
             if (obj->isSingleton())
                 return property.singleton(constraints());
             return nullptr;
         }
 
         if (ObjectHasExtraOwnProperty(compartment, objKey, id))
             return nullptr;
 
-        obj = checkNurseryObject(obj->getProto());
+        obj = checkNurseryObject(obj->staticPrototype());
     }
 
     return nullptr;
 }
 
 JSObject*
 IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id)
 {
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -417,54 +417,54 @@ GeneratePrototypeGuards(JSContext* cx, I
      */
     MOZ_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // Note: objectReg and scratchReg may be the same register, so we cannot
         // use objectReg in the rest of this function.
         masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg);
         Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures);
+        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), failures);
     }
 
-    JSObject* pobj = IsCacheableDOMProxy(obj)
-                     ? obj->getTaggedProto().toObjectOrNull()
-                     : obj->getProto();
+    JSObject* pobj = obj->staticPrototype();
     if (!pobj)
         return;
     while (pobj != holder) {
         if (pobj->hasUncacheableProto()) {
             masm.movePtr(ImmGCPtr(pobj), scratchReg);
             Address groupAddr(scratchReg, JSObject::offsetOfGroup());
             if (pobj->isSingleton()) {
                 // Singletons can have their group's |proto| mutated directly.
                 masm.loadPtr(groupAddr, scratchReg);
                 Address protoAddr(scratchReg, ObjectGroup::offsetOfProto());
-                masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->getProto()), failures);
+                masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->staticPrototype()),
+                              failures);
             } else {
                 masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures);
             }
         }
-        pobj = pobj->getProto();
+
+        pobj = pobj->staticPrototype();
     }
 }
 
 // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
 // Ion caches can deal with objects on the proto chain that have uncacheable
 // prototypes.
 bool
 jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder)
 {
     while (obj != holder) {
         /*
          * We cannot assume that we find the holder object on the prototype
          * chain and must check for null proto. The prototype chain can be
          * altered during the lookupProperty call.
          */
-        JSObject* proto = obj->getProto();
+        JSObject* proto = obj->staticPrototype();
         if (!proto || !proto->isNative())
             return false;
         obj = proto;
     }
     return true;
 }
 
 bool
@@ -495,17 +495,17 @@ IsCacheableNoProperty(JSObject* obj, JSO
 
     // Don't generate missing property ICs if we skipped a non-native object, as
     // lookups may extend beyond the prototype chain (e.g. for DOMProxy
     // proxies).
     JSObject* obj2 = obj;
     while (obj2) {
         if (!obj2->isNative())
             return false;
-        obj2 = obj2->getProto();
+        obj2 = obj2->staticPrototype();
     }
 
     // The pc is nullptr if the cache is idempotent. We cannot share missing
     // properties between caches because TI can only try to prove that a type is
     // contained, but does not attempts to check if something does not exists.
     // So the infered type of getprop would be missing and would not contain
     // undefined, as expected for missing properties.
     if (!pc)
@@ -814,29 +814,30 @@ GenerateReadSlot(JSContext* cx, IonScrip
             masm.movePtr(ImmGCPtr(holder), holderReg);
             masm.branchPtr(Assembler::NotEqual,
                            Address(holderReg, JSObject::offsetOfShape()),
                            ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                            &prototypeFailures);
         } else {
             // The property does not exist. Guard on everything in the
             // prototype chain.
-            JSObject* proto = obj->getTaggedProto().toObjectOrNull();
+            JSObject* proto = obj->staticPrototype();
             Register lastReg = object;
             MOZ_ASSERT(scratchReg != object);
             while (proto) {
                 masm.loadObjProto(lastReg, scratchReg);
 
                 // Guard the shape of the current prototype.
+                MOZ_ASSERT(proto->hasStaticPrototype());
                 masm.branchPtr(Assembler::NotEqual,
                                Address(scratchReg, JSObject::offsetOfShape()),
                                ImmGCPtr(proto->as<NativeObject>().lastProperty()),
                                &prototypeFailures);
 
-                proto = proto->getProto();
+                proto = proto->staticPrototype();
                 lastReg = scratchReg;
             }
 
             holderReg = InvalidReg;
         }
     } else if (obj->is<UnboxedPlainObject>()) {
         holder = obj->as<UnboxedPlainObject>().maybeExpando();
         holderReg = scratchReg;
@@ -1798,17 +1799,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
                                            void* returnAddr, bool* emitted)
 {
     MOZ_ASSERT(canAttachStub());
     MOZ_ASSERT(!*emitted);
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     MOZ_ASSERT(monitoredResult());
     MOZ_ASSERT(output().hasValue());
 
-    RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
+    RootedObject checkObj(cx, obj->staticPrototype());
     RootedNativeObject holder(cx);
     RootedShape shape(cx);
 
     NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx, *this, checkObj, id, &holder, &shape,
                                /* skipArrayLen = */true);
     MOZ_ASSERT(canCache != CanAttachArrayLength);
 
@@ -2901,17 +2902,17 @@ GenerateCallSetter(JSContext* cx, IonScr
 }
 
 static bool
 IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleId id,
                                         MutableHandleObject holder, MutableHandleShape shape)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
-    RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
+    RootedObject checkObj(cx, obj->staticPrototype());
     if (!checkObj)
         return false;
 
     if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
         return false;
 
     if (!holder)
         return false;
@@ -3024,30 +3025,30 @@ GenerateAddSlot(JSContext* cx, MacroAsse
         newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
 
     // Guard that the incoming value is in the type set for the property
     // if a type barrier is required.
     if (checkTypeset)
         CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
 
     // Guard shapes along prototype chain.
-    JSObject* proto = obj->getProto();
+    JSObject* proto = obj->staticPrototype();
     Register protoReg = tempReg;
     bool first = true;
     while (proto) {
         Shape* protoShape = proto->as<NativeObject>().lastProperty();
 
         // Load next prototype.
         masm.loadObjProto(first ? object : protoReg, protoReg);
         first = false;
 
         // Ensure that its shape matches.
         masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, failures);
 
-        proto = proto->getProto();
+        proto = proto->staticPrototype();
     }
 
     // Call a stub to (re)allocate dynamic slots, if necessary.
     uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>()
                                   ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots()
                                   : obj->as<NativeObject>().numDynamicSlots();
     if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
         AllocatableRegisterSet regs(RegisterSet::Volatile());
@@ -3243,17 +3244,17 @@ IsPropertySetInlineable(NativeObject* ob
 }
 
 static bool
 PrototypeChainShadowsPropertyAdd(JSContext* cx, JSObject* obj, jsid id)
 {
     // Walk up the object prototype chain and ensure that all prototypes
     // are native, and that all prototypes have no getter or setter
     // defined on the property
-    for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) {
+    for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
         // If prototype is non-native, don't optimize
         if (!proto->isNative())
             return true;
 
         // If prototype defines this property in a non-plain way, don't optimize
         Shape* protoShape = proto->as<NativeObject>().lookupPure(id);
         if (protoShape && !protoShape->hasDefaultSetter())
             return true;
@@ -3844,17 +3845,17 @@ GetPropertyIC::canAttachDenseElementHole
 
     do {
         if (obj->isIndexed())
             return false;
 
         if (ClassCanHaveExtraProperties(obj->getClass()))
             return false;
 
-        JSObject* proto = obj->getProto();
+        JSObject* proto = obj->staticPrototype();
         if (!proto)
             break;
 
         if (!proto->isNative())
             return false;
 
         // Make sure objects on the prototype don't have dense elements.
         if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
@@ -3880,20 +3881,20 @@ GenerateDenseElementHole(JSContext* cx, 
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object, JSObject::offsetOfShape()),
                                    ImmGCPtr(obj->as<NativeObject>().lastProperty()), &failures);
 
 
     if (obj->hasUncacheableProto()) {
         masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg);
         Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), &failures);
+        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), &failures);
     }
 
-    JSObject* pobj = obj->getProto();
+    JSObject* pobj = obj->staticPrototype();
     while (pobj) {
         MOZ_ASSERT(pobj->as<NativeObject>().lastProperty());
 
         masm.movePtr(ImmGCPtr(pobj), scratchReg);
 
         // Non-singletons with uncacheable protos can change their proto
         // without a shape change, so also guard on the group (which determines
         // the proto) in this case.
@@ -3909,17 +3910,17 @@ GenerateDenseElementHole(JSContext* cx, 
         // Load elements vector.
         masm.loadPtr(Address(scratchReg, NativeObject::offsetOfElements()), scratchReg);
 
         // Also make sure there are no dense elements.
         Label hole;
         Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
         masm.branch32(Assembler::NotEqual, initLength, Imm32(0), &failures);
 
-        pobj = pobj->getProto();
+        pobj = pobj->staticPrototype();
     }
 
     // Ensure the index is an int32 value.
     Register indexReg;
     if (index.hasValue()) {
         // Unbox the index.
         ValueOperand val = index.valueReg();
         masm.branchTestInt32(Assembler::NotEqual, val, &failures);
@@ -4313,25 +4314,25 @@ IsDenseElementSetInlineable(JSObject* ob
         return false;
 
     // The object may have a setter definition,
     // either directly, or via a prototype, or via the target object for a prototype
     // which is a proxy, that handles a particular integer write.
     // Scan the prototype and shape chain to make sure that this is not the case.
     JSObject* curObj = obj;
     while (curObj) {
-        // Ensure object is native.
+        // Ensure object is native.  (This guarantees static prototype below.)
         if (!curObj->isNative())
             return false;
 
         // Ensure all indexed properties are stored in dense elements.
         if (curObj->isIndexed())
             return false;
 
-        curObj = curObj->getProto();
+        curObj = curObj->staticPrototype();
     }
 
     *checkTypeset = false;
     if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, JSID_VOID, val, checkTypeset))
         return false;
 
     return true;
 }
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2059,17 +2059,17 @@ IonBuilder::inlineObjectCreate(CallInfo&
     if (!templateObject)
         return InliningStatus_NotInlined;
 
     MOZ_ASSERT(templateObject->is<PlainObject>());
     MOZ_ASSERT(!templateObject->isSingleton());
 
     // Ensure the argument matches the template object's prototype.
     MDefinition* arg = callInfo.getArg(0);
-    if (JSObject* proto = templateObject->getProto()) {
+    if (JSObject* proto = templateObject->staticPrototype()) {
         if (IsInsideNursery(proto))
             return InliningStatus_NotInlined;
 
         TemporaryTypeSet* types = arg->resultTypeSet();
         if (!types || types->maybeSingleton() != proto)
             return InliningStatus_NotInlined;
 
         MOZ_ASSERT(types->getKnownMIRType() == MIRType::Object);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5618,17 +5618,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
 
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
     if (observed->empty() && name) {
         JSObject* obj;
         if (key->isSingleton())
             obj = key->singleton();
         else
-            obj = key->proto().isLazy() ? nullptr : key->proto().toObjectOrNull();
+            obj = key->proto().isDynamic() ? nullptr : key->proto().toObjectOrNull();
 
         while (obj) {
             if (!obj->getClass()->isNative())
                 break;
 
             TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj);
             if (propertycx)
                 key->ensureTrackedProperty(propertycx, NameToId(name));
@@ -5642,17 +5642,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
                     if (types.length() == 1) {
                         // Note: the return value here is ignored.
                         observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
                         break;
                     }
                 }
             }
 
-            obj = obj->getProto();
+            obj = obj->staticPrototype();
         }
     }
 
     // If any objects which could be observed are similar to ones that have
     // already been observed, add them to the observed type set.
     if (!key->unknownProperties()) {
         HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID);
 
@@ -5830,17 +5830,17 @@ PrototypeHasIndexedProperty(IonBuilder* 
         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(builder->checkNurseryObject(obj));
         if (ClassCanHaveExtraProperties(key->clasp()))
             return true;
         if (key->unknownProperties())
             return true;
         HeapTypeSetKey index = key->property(JSID_VOID);
         if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints()))
             return true;
-        obj = obj->getProto();
+        obj = obj->staticPrototype();
     } while (obj);
 
     return false;
 }
 
 // Whether Array.prototype, or an object on its proto chain, has an indexed property.
 bool
 jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -2167,17 +2167,17 @@ StripPreliminaryObjectStubs(JSContext* c
             iter.unlink(cx);
     }
 }
 
 JSObject*
 GetDOMProxyProto(JSObject* obj)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
-    return obj->getTaggedProto().toObjectOrNull();
+    return obj->staticPrototype();
 }
 
 // Look up a property's shape on an object, being careful never to do any effectful
 // operations.  This procedure not yielding a shape should not be taken as a lack of
 // existence of the property on the object.
 bool
 EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id,
                            MutableHandleObject holder, MutableHandleShape shape,
@@ -2238,40 +2238,35 @@ IsCacheableProtoChain(JSObject* obj, JSO
         if (!obj->is<UnboxedPlainObject>() &&
             !obj->is<UnboxedArrayObject>() &&
             !obj->is<TypedObject>())
         {
             return false;
         }
     }
 
-    // Don't handle objects which require a prototype guard. This should
-    // be uncommon so handling it is likely not worth the complexity.
-    if (obj->hasUncacheableProto())
-        return false;
-
     JSObject* cur = obj;
     while (cur != holder) {
         // We cannot assume that we find the holder object on the prototype
         // chain and must check for null proto. The prototype chain can be
         // altered during the lookupProperty call.
-        JSObject* proto;
-        if (isDOMProxy && cur == obj)
-            proto = cur->getTaggedProto().toObjectOrNull();
-        else
-            proto = cur->getProto();
-
+        MOZ_ASSERT(!cur->hasDynamicPrototype());
+
+        // Don't handle objects which require a prototype guard. This should
+        // be uncommon so handling it is likely not worth the complexity.
+        if (cur->hasUncacheableProto())
+            return false;
+
+        JSObject* proto = cur->staticPrototype();
         if (!proto || !proto->isNative())
             return false;
 
-        if (proto->hasUncacheableProto())
-            return false;
-
         cur = proto;
     }
+
     return true;
 }
 
 bool
 IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy)
 {
     if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy))
         return false;
@@ -2625,17 +2620,17 @@ CheckHasNoSuchProperty(JSContext* cx, JS
                 return false;
         } else if (curObj->is<TypedObject>()) {
             if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name)))
                 return false;
         } else {
             return false;
         }
 
-        JSObject* proto = curObj->getTaggedProto().toObjectOrNull();
+        JSObject* proto = curObj->staticPrototype();
         if (!proto)
             break;
 
         curObj = proto;
         depth++;
     }
 
     if (lastProto)
@@ -3044,23 +3039,25 @@ ICGetPropNativeCompiler::generateStubCod
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
 GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes)
 {
-    JSObject* curProto = obj->getProto();
+    JSObject* curProto = obj->staticPrototype();
     for (size_t i = 0; i < protoChainDepth; i++) {
         if (!shapes.append(curProto->as<NativeObject>().lastProperty()))
             return false;
-        curProto = curProto->getProto();
+        curProto = curProto->staticPrototype();
     }
-    MOZ_ASSERT(!curProto);
+
+    MOZ_ASSERT(!curProto,
+               "longer prototype chain encountered than this stub permits!");
     return true;
 }
 
 bool
 ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1128,17 +1128,17 @@ JS_ResolveStandardClass(JSContext* cx, H
 JS_PUBLIC_API(bool)
 JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj)
 {
     MOZ_ASSERT_IF(maybeObj, maybeObj->is<GlobalObject>());
 
     // The global object's resolve hook is special: JS_ResolveStandardClass
     // initializes the prototype chain lazily. Only attempt to optimize here
     // if we know the prototype chain has been initialized.
-    if (!maybeObj || !maybeObj->getProto())
+    if (!maybeObj || !maybeObj->staticPrototype())
         return true;
 
     if (!JSID_IS_ATOM(id))
         return false;
 
     JSAtom* atom = JSID_TO_ATOM(id);
 
     // This will return true even for deselected constructors.  (To do
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -823,36 +823,43 @@ ObjectMayHaveExtraIndexedOwnProperties(J
 {
     return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
            obj->isIndexed() ||
            obj->is<TypedArrayObject>() ||
            ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
                              obj->getClass(), INT_TO_JSID(0), obj);
 }
 
+/*
+ * Whether obj may have indexed properties anywhere besides its dense
+ * elements. This includes other indexed properties in its shape hierarchy, and
+ * indexed properties or elements along its prototype chain.
+ */
 bool
 js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
 {
-    /*
-     * Whether obj may have indexed properties anywhere besides its dense
-     * elements. This includes other indexed properties in its shape hierarchy,
-     * and indexed properties or elements along its prototype chain.
-     */
+    MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative());
 
     if (ObjectMayHaveExtraIndexedOwnProperties(obj))
         return true;
 
-    while ((obj = obj->getProto()) != nullptr) {
+    do {
+        MOZ_ASSERT(obj->hasStaticPrototype(),
+                   "dynamic-prototype objects must be non-native, ergo must "
+                   "have failed ObjectMayHaveExtraIndexedOwnProperties");
+
+        obj = obj->staticPrototype();
+        if (!obj)
+            return false; // no extra indexed properties found
+
         if (ObjectMayHaveExtraIndexedOwnProperties(obj))
             return true;
         if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
             return true;
-    }
-
-    return false;
+    } while (true);
 }
 
 static bool
 AddLengthProperty(ExclusiveContext* cx, HandleArrayObject obj)
 {
     /*
      * Add the 'length' property for a newly created array,
      * and update the elements to be an empty array owned by the object.
@@ -2691,17 +2698,17 @@ GetIndexedPropertiesInRange(JSContext* c
     *success = false;
 
     // First, look for proxies or class hooks that can introduce extra
     // properties.
     JSObject* pobj = obj;
     do {
         if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty())
             return true;
-    } while ((pobj = pobj->getProto()));
+    } while ((pobj = pobj->staticPrototype()));
 
     // Collect indexed property names.
     pobj = obj;
     do {
         // Append dense elements.
         NativeObject* nativeObj = &pobj->as<NativeObject>();
         uint32_t initLen = nativeObj->getDenseInitializedLength();
         for (uint32_t i = begin; i < initLen && i < end; i++) {
@@ -2736,17 +2743,17 @@ GetIndexedPropertiesInRange(JSContext* c
                 // Watch out for getters, they can add new properties.
                 if (!shape.hasDefaultGetter())
                     return true;
 
                 if (!indexes.append(i))
                     return false;
             }
         }
-    } while ((pobj = pobj->getProto()));
+    } while ((pobj = pobj->staticPrototype()));
 
     // Sort the indexes.
     Vector<uint32_t> tmp(cx);
     size_t n = indexes.length();
     if (!tmp.resize(n))
         return false;
     if (!MergeSort(indexes.begin(), n, tmp.begin(), SortComparatorIndexes()))
         return false;
@@ -3600,17 +3607,17 @@ js::NewPartlyAllocatedArrayTryUseGroup(E
 template <uint32_t maxLength>
 static inline JSObject*
 NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
                       NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
 {
     if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
         return NewArray<maxLength>(cx, length, nullptr, newKind);
 
-    if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
+    if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
         return NewArray<maxLength>(cx, length, nullptr, newKind);
 
     RootedObjectGroup group(cx, obj->getGroup(cx));
     if (!group)
         return nullptr;
 
     return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind, forceAnalyze);
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -451,17 +451,17 @@ JSCompartment::wrap(JSContext* cx, Mutab
         obj.set(&p->value().get().toObject());
         MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
         return true;
     }
 
     RootedObject existing(cx, existingArg);
     if (existing) {
         // Is it possible to reuse |existing|?
-        if (!existing->getTaggedProto().isLazy() ||
+        if (existing->hasStaticPrototype() ||
             // Note: Class asserted above, so all that's left to check is callability
             existing->isCallable() ||
             obj->isCallable())
         {
             existing = nullptr;
         }
     }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -348,18 +348,17 @@ js::GetGlobalForObjectCrossCompartment(J
 {
     return &obj->global();
 }
 
 JS_FRIEND_API(JSObject*)
 js::GetPrototypeNoProxy(JSObject* obj)
 {
     MOZ_ASSERT(!obj->is<js::ProxyObject>());
-    MOZ_ASSERT(!obj->getTaggedProto().isLazy());
-    return obj->getTaggedProto().toObjectOrNull();
+    return obj->staticPrototype();
 }
 
 JS_FRIEND_API(void)
 js::AssertSameCompartment(JSContext* cx, JSObject* obj)
 {
     assertSameCompartment(cx, obj);
 }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2002,17 +2002,17 @@ js::CloneFunctionReuseScript(JSContext* 
     } else {
         clone->initNative(fun->native(), fun->jitInfo());
     }
 
     /*
      * Clone the function, reusing its script. We can use the same group as
      * the original function provided that its prototype is correct.
      */
-    if (fun->getProto() == clone->getProto())
+    if (fun->staticPrototype() == clone->staticPrototype())
         clone->setGroup(fun->group());
     return clone;
 }
 
 JSFunction*
 js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
                            HandleObject newStaticScope,
                            gc::AllocKind allocKind /* = FUNCTION */,
@@ -2155,18 +2155,18 @@ void
 js::ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clasp)
 {
     RootedValue thisv(cx, call.thisv());
 
 #ifdef DEBUG
     if (thisv.isObject()) {
         MOZ_ASSERT(thisv.toObject().getClass() != clasp ||
                    !thisv.toObject().isNative() ||
-                   !thisv.toObject().getProto() ||
-                   thisv.toObject().getProto()->getClass() != clasp);
+                   !thisv.toObject().staticPrototype() ||
+                   thisv.toObject().staticPrototype()->getClass() != clasp);
     } else if (thisv.isString()) {
         MOZ_ASSERT(clasp != &StringObject::class_);
     } else if (thisv.isNumber()) {
         MOZ_ASSERT(clasp != &NumberObject::class_);
     } else if (thisv.isBoolean()) {
         MOZ_ASSERT(clasp != &BooleanObject::class_);
     } else if (thisv.isSymbol()) {
         MOZ_ASSERT(clasp != &SymbolObject::class_);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -106,18 +106,20 @@ Enumerate(JSContext* cx, HandleObject po
         // If we've already seen this, we definitely won't add it.
         IdSet::AddPtr p = ht->lookupForAdd(id);
         if (MOZ_UNLIKELY(!!p))
             return true;
 
         // It's not necessary to add properties to the hash table at the end of
         // the prototype chain, but custom enumeration behaviors might return
         // duplicated properties, so always add in such cases.
-        if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOpsEnumerate()) && !ht->add(p, id))
-            return false;
+        if (pobj->is<ProxyObject>() || pobj->staticPrototype() || pobj->getOpsEnumerate()) {
+            if (!ht->add(p, id))
+                return false;
+        }
     }
 
     // Symbol-keyed properties and nonenumerable properties are skipped unless
     // the caller specifically asks for them. A caller can also filter out
     // non-symbols by asking for JSITER_SYMBOLSONLY.
     if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
         return true;
     if (!enumerable && !(flags & JSITER_HIDDEN))
@@ -696,17 +698,21 @@ VectorToKeyIterator(JSContext* cx, Handl
         return false;
 
     if (numGuards) {
         // Fill in the guard array from scratch.
         JSObject* pobj = obj;
         size_t ind = 0;
         do {
             ni->guard_array[ind++].init(ReceiverGuard(pobj));
-            pobj = pobj->getProto();
+
+            // The one caller of this method that passes |numGuards > 0|, does
+            // so only if the entire chain consists of cacheable objects (that
+            // necessarily have static prototypes).
+            pobj = pobj->staticPrototype();
         } while (pobj);
         MOZ_ASSERT(ind == numGuards);
     }
 
     objp.set(iterobj);
 
     RegisterEnumerator(cx, iterobj, ni);
     return true;
@@ -838,47 +844,46 @@ js::GetIterator(JSContext* cx, HandleObj
         // iterated over.
         PropertyIteratorObject* last = cx->runtime()->nativeIterCache.last;
         if (last) {
             NativeIterator* lastni = last->getNativeIterator();
             if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                 CanCompareIterableObjectToCache(obj) &&
                 ReceiverGuard(obj) == lastni->guard_array[0])
             {
-                JSObject* proto = obj->getProto();
+                JSObject* proto = obj->staticPrototype();
                 if (CanCompareIterableObjectToCache(proto) &&
                     ReceiverGuard(proto) == lastni->guard_array[1] &&
-                    !proto->getProto())
+                    !proto->staticPrototype())
                 {
                     objp.set(last);
                     UpdateNativeIterator(lastni, obj);
                     RegisterEnumerator(cx, last, lastni);
                     return true;
                 }
             }
         }
 
-        /*
-         * The iterator object for JSITER_ENUMERATE never escapes, so we
-         * don't care for the proper parent/proto to be set. This also
-         * allows us to re-use a previous iterator object that is not
-         * currently active.
-         */
+        // The iterator object for JSITER_ENUMERATE never escapes, so we don't
+        // care that the "proper" prototype is set.  This also lets us reuse an
+        // old, inactive iterator object.
         {
             JSObject* pobj = obj;
             do {
                 if (!CanCacheIterableObject(cx, pobj)) {
                     guards.clear();
                     goto miss;
                 }
+
                 ReceiverGuard guard(pobj);
                 key = (key + (key << 16)) ^ guard.hash();
                 if (!guards.append(guard))
                     return false;
-                pobj = pobj->getProto();
+
+                pobj = pobj->staticPrototype();
             } while (pobj);
         }
 
         PropertyIteratorObject* iterobj = cx->runtime()->nativeIterCache.get(key);
         if (iterobj) {
             NativeIterator* ni = iterobj->getNativeIterator();
             if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                 ni->guard_key == key &&
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -496,17 +496,17 @@ js::SetIntegrityLevel(JSContext* cx, Han
         HandleNativeObject nobj = obj.as<NativeObject>();
 
         // Seal/freeze non-dictionary objects by constructing a new shape
         // hierarchy mirroring the original one, which can be shared if many
         // objects with the same structure are sealed/frozen. If we use the
         // generic path below then any non-empty object will be converted to
         // dictionary mode.
         RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(),
-                                                         nobj->getTaggedProto(),
+                                                         nobj->taggedProto(),
                                                          nobj->numFixedSlots(),
                                                          nobj->lastProperty()->getObjectFlags()));
         if (!last)
             return false;
 
         // Get an in-order list of the shapes in this object.
         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
@@ -684,17 +684,17 @@ NewObject(ExclusiveContext* cx, HandleOb
     return obj;
 }
 
 void
 NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
                           gc::AllocKind kind, NativeObject* obj)
 {
     MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
-    MOZ_ASSERT(obj->getTaggedProto() == proto);
+    MOZ_ASSERT(obj->taggedProto() == proto);
     return fill(entry, clasp, proto.raw(), kind, obj);
 }
 
 bool
 js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
                                        NewObjectKind newKind, const Class* clasp)
 {
     return cxArg->isJSContext() &&
@@ -909,17 +909,17 @@ CreateThisForFunctionWithGroup(JSContext
             RootedPlainObject templateObject(cx, newScript->templateObject());
             MOZ_ASSERT(templateObject->group() == group);
 
             RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
             if (!res)
                 return nullptr;
 
             if (newKind == SingletonObject) {
-                Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->getProto()));
+                Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype()));
                 if (!res->splicePrototype(cx, &PlainObject::class_, proto))
                     return nullptr;
             } else {
                 res->setGroup(group);
             }
             return res;
         }
 
@@ -1336,23 +1336,23 @@ InitializePropertiesFromCompatibleNative
     uint32_t initialized = src->getDenseInitializedLength();
     for (uint32_t i = 0; i < initialized; ++i) {
         dst->setDenseInitializedLength(i + 1);
         dst->initDenseElement(i, src->getDenseElement(i));
     }
 
     MOZ_ASSERT(!src->hasPrivate());
     RootedShape shape(cx);
-    if (src->getProto() == dst->getProto()) {
+    if (src->staticPrototype() == dst->staticPrototype()) {
         shape = src->lastProperty();
     } else {
         // We need to generate a new shape for dst that has dst's proto but all
         // the property information from src.  Note that we asserted above that
         // dst's object flags are 0.
-        shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(),
+        shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(),
                                             dst->numFixedSlots(), 0);
         if (!shape)
             return false;
 
         // Get an in-order list of the shapes in the src object.
         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
         for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) {
             if (!shapes.append(&r.front()))
@@ -1938,17 +1938,17 @@ js::SetClassAndProto(JSContext* cx, Hand
                 return false;
         }
         if (!obj->isDelegate()) {
             // If |obj| is not a proto of another object, we don't need to
             // reshape the whole proto chain.
             MOZ_ASSERT(obj == oldproto);
             break;
         }
-        oldproto = oldproto->getProto();
+        oldproto = oldproto->staticPrototype();
     }
 
     if (proto.isObject() && !proto.toObject()->setDelegate(cx))
         return false;
 
     if (obj->isSingleton()) {
         /*
          * Just splice the prototype, but mark the properties as unknown for
@@ -1988,17 +1988,17 @@ js::SetClassAndProto(JSContext* cx, Hand
 /* static */ bool
 JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
 {
     MOZ_ASSERT(!obj->isSingleton());
 
     MarkObjectGroupUnknownProperties(cx, obj->group());
 
     ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
-                                                         obj->getTaggedProto());
+                                                         obj->taggedProto());
     if (!group)
         return false;
 
     obj->group_ = group;
     return true;
 }
 
 static bool
@@ -2290,17 +2290,17 @@ js::LookupPropertyPure(ExclusiveContext*
             return true;
         }
 
         if (isTypedArrayOutOfRange) {
             *objp = nullptr;
             return true;
         }
 
-        obj = obj->getProto();
+        obj = obj->staticPrototype();
     } while (obj);
 
     *objp = nullptr;
     *propp = nullptr;
     return true;
 }
 
 bool
@@ -2493,23 +2493,23 @@ JSObject::reportNotExtensible(JSContext*
                                  JSDVG_IGNORE_STACK, val, nullptr,
                                  nullptr, nullptr);
 }
 
 bool
 js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
                            MutableHandleObject protop)
 {
-    if (obj->getTaggedProto().isLazy()) {
+    if (obj->hasDynamicPrototype()) {
         MOZ_ASSERT(obj->is<js::ProxyObject>());
         return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop);
     }
 
     *isOrdinary = true;
-    protop.set(obj->getTaggedProto().toObjectOrNull());
+    protop.set(obj->staticPrototype());
     return true;
 }
 
 // Our immutable-prototype behavior is non-standard, and it's unclear whether
 // it's shippable.  (Or at least it's unclear whether it's shippable with any
 // provided-by-default uses exposed to script.)  If this bool is true,
 // immutable-prototype behavior is enforced; if it's false, behavior is not
 // enforced, and immutable-prototype bits stored on objects are completely
@@ -2522,29 +2522,25 @@ JS_ImmutablePrototypesEnabled()
     return ImmutablePrototypesEnabled;
 }
 
 /*** ES6 standard internal methods ***************************************************************/
 
 bool
 js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult& result)
 {
-    /*
-     * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's
-     * {get,set}Prototype and setImmutablePrototype methods mediate access to
-     * |obj.[[Prototype]]|.  The Proxy subsystem is responsible for responding
-     * to such attempts.
-     */
-    if (obj->hasLazyPrototype()) {
+    // The proxy trap subsystem fully handles prototype-setting for proxies
+    // with dynamic [[Prototype]]s.
+    if (obj->hasDynamicPrototype()) {
         MOZ_ASSERT(obj->is<ProxyObject>());
         return Proxy::setPrototype(cx, obj, proto, result);
     }
 
     /* Disallow mutation of immutable [[Prototype]]s. */
-    if (obj->nonLazyPrototypeIsImmutable() && ImmutablePrototypesEnabled)
+    if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled)
         return result.fail(JSMSG_CANT_SET_PROTO);
 
     /*
      * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
      * due to their complicated delegate-object shenanigans can't easily
      * have a mutable [[Prototype]].
      */
     if (obj->is<ArrayBufferObject>()) {
@@ -2571,17 +2567,17 @@ js::SetPrototype(JSContext* cx, HandleOb
                              "incompatible Location object");
         return false;
     }
 
     /*
      * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
      * Since the values in question are objects, we can just compare pointers.
      */
-    if (proto == obj->getProto())
+    if (proto == obj->staticPrototype())
         return result.succeed();
 
     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
     bool extensible;
     if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return result.fail(JSMSG_CANT_SET_PROTO);
@@ -2781,17 +2777,17 @@ js::DefineElement(ExclusiveContext* cx, 
 }
 
 
 /*** SpiderMonkey nonstandard internal methods ***************************************************/
 
 bool
 js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeeded)
 {
-    if (obj->hasLazyPrototype()) {
+    if (obj->hasDynamicPrototype()) {
         if (!cx->shouldBeJSContext())
             return false;
         return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
     }
 
     if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE))
         return false;
     *succeeded = true;
@@ -3466,17 +3462,18 @@ JSObject::dump()
     if (obj->isQualifiedVarObj()) fprintf(stderr, " varobj");
     if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj");
     if (obj->watched()) fprintf(stderr, " watched");
     if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
     if (obj->isNewGroupUnknown()) fprintf(stderr, " new_type_unknown");
     if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
     if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
     if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared");
-    if (!obj->hasLazyPrototype() && obj->nonLazyPrototypeIsImmutable()) fprintf(stderr, " immutable_prototype");
+    if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable())
+        fprintf(stderr, " immutable_prototype");
 
     if (obj->isNative()) {
         NativeObject* nobj = &obj->as<NativeObject>();
         if (nobj->inDictionaryMode())
             fprintf(stderr, " inDictionaryMode");
         if (nobj->hasShapeTable())
             fprintf(stderr, " hasShapeTable");
     }
@@ -3492,19 +3489,19 @@ JSObject::dump()
                 dumpValue(nobj->getDenseElement(i));
                 fprintf(stderr, "\n");
                 fflush(stderr);
             }
         }
     }
 
     fprintf(stderr, "proto ");
-    TaggedProto proto = obj->getTaggedProto();
-    if (proto.isLazy())
-        fprintf(stderr, "<lazy>");
+    TaggedProto proto = obj->taggedProto();
+    if (proto.isDynamic())
+        fprintf(stderr, "<dynamic>");
     else
         dumpValue(ObjectOrNullValue(proto.toObjectOrNull()));
     fputc('\n', stderr);
 
     if (clasp->flags & JSCLASS_HAS_PRIVATE)
         fprintf(stderr, "private %p\n", obj->as<NativeObject>().getPrivate());
 
     if (!obj->isNative())
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -260,16 +260,19 @@ class JSObject : public js::gc::Cell
 
     // Objects with an uncacheable proto can have their prototype mutated
     // without inducing a shape change on the object. JIT inline caches should
     // do an explicit group guard to guard against this. Singletons always
     // generate a new shape when their prototype changes, regardless of this
     // hasUncacheableProto flag.
     inline bool hasUncacheableProto() const;
     bool setUncacheableProto(js::ExclusiveContext* cx) {
+        MOZ_ASSERT(hasStaticPrototype(),
+                   "uncacheability as a concept is only applicable to static "
+                   "(not dynamically-computed) prototypes");
         return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
     }
 
     /*
      * Whether SETLELEM was used to access this object. See also the comment near
      * PropertyTree::MAX_HEIGHT.
      */
     inline bool hadElementsAccess() const;
@@ -350,68 +353,71 @@ class JSObject : public js::gc::Cell
     inline js::ObjectGroup* getGroup(JSContext* cx);
 
     const js::HeapPtrObjectGroup& groupFromGC() const {
         /* Direct field access for use by GC. */
         return group_;
     }
 
     /*
-     * We allow the prototype of an object to be lazily computed if the object
-     * is a proxy. In the lazy case, we store (JSObject*)0x1 in the proto field
-     * of the object's group. We offer three ways of getting the prototype:
+     * We permit proxies to dynamically compute their prototype if desired.
+     * (Not all proxies will so desire: in particular, most DOM proxies can
+     * track their prototype with a single, nullable JSObject*.)  If a proxy
+     * so desires, we store (JSObject*)0x1 in the proto field of the object's
+     * group.
      *
-     * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy
-     *    with a relevant getPrototype() handler.
-     * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to
+     * We offer three ways to get an object's prototype:
+     *
+     * 1. obj->staticPrototype() returns the prototype, but it asserts if obj
+     *    is a proxy, and the proxy has opted to dynamically compute its
+     *    prototype using a getPrototype() handler.
+     * 2. obj->taggedProto() returns a TaggedProto, which can be tested to
      *    check if the proto is an object, nullptr, or lazily computed.
      * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object.
-     *    If obj is a proxy and the proto is lazy, this code may allocate or
-     *    GC in order to compute the proto. Currently, it will not run JS code.
+     *    If obj is a proxy with dynamically-computed prototype, this code may
+     *    perform arbitrary behavior (allocation, GC, run JS) while computing
+     *    the proto.
      */
 
-    js::TaggedProto getTaggedProto() const {
+    js::TaggedProto taggedProto() const {
         return group_->proto();
     }
 
     bool hasTenuredProto() const;
 
     bool uninlinedIsProxy() const;
 
-    JSObject* getProto() const {
-        MOZ_ASSERT(!hasLazyPrototype());
-        return getTaggedProto().toObjectOrNull();
+    JSObject* staticPrototype() const {
+        MOZ_ASSERT(hasStaticPrototype());
+        return taggedProto().toObjectOrNull();
+    }
+
+    // Normal objects and a subset of proxies have an uninteresting, static
+    // (albeit perhaps mutable) [[Prototype]].  For such objects the
+    // [[Prototype]] is just a value returned when needed for accesses, or
+    // modified in response to requests.  These objects store the
+    // [[Prototype]] directly within |obj->group_|.
+    bool hasStaticPrototype() const {
+        return !hasDynamicPrototype();
     }
 
-    // Normal objects and a subset of proxies have uninteresting [[Prototype]].
-    // For such objects the [[Prototype]] is just a value returned when needed
-    // for accesses, or modified in response to requests.  These objects store
-    // the [[Prototype]] directly within |obj->type_|.
-    //
-    // Proxies that don't have such a simple [[Prototype]] instead have a
-    // "lazy" [[Prototype]].  Accessing the [[Prototype]] of such an object
-    // requires going through the proxy handler {get,set}Prototype and
-    // setImmutablePrototype methods.  This is most commonly useful for proxies
-    // that are wrappers around other objects.  If the [[Prototype]] of the
-    // underlying object changes, the [[Prototype]] of the wrapper must also
-    // simultaneously change.  We implement this by having the handler methods
-    // simply delegate to the wrapped object, forwarding its response to the
-    // caller.
-    //
-    // This method returns true if this object has a non-simple [[Prototype]]
-    // as described above, or false otherwise.
-    bool hasLazyPrototype() const {
-        bool lazy = getTaggedProto().isLazy();
-        MOZ_ASSERT_IF(lazy, uninlinedIsProxy());
-        return lazy;
+    // The remaining proxies have a [[Prototype]] requiring dynamic computation
+    // for every access, going through the proxy handler {get,set}Prototype and
+    // setImmutablePrototype methods.  (Wrappers particularly use this to keep
+    // the wrapper/wrappee [[Prototype]]s consistent.)
+    bool hasDynamicPrototype() const {
+        bool dynamic = taggedProto().isDynamic();
+        MOZ_ASSERT_IF(dynamic, uninlinedIsProxy());
+        MOZ_ASSERT_IF(dynamic, !isNative());
+        return dynamic;
     }
 
-    // True iff this object's [[Prototype]] is immutable.  Must not be called
-    // on proxies with lazy [[Prototype]]!
-    inline bool nonLazyPrototypeIsImmutable() const;
+    // True iff this object's [[Prototype]] is immutable.  Must be called only
+    // on objects with a static [[Prototype]]!
+    inline bool staticPrototypeIsImmutable() const;
 
     inline void setGroup(js::ObjectGroup* group);
 
     /*
      * Mark an object that has been iterated over and is a singleton. We need
      * to recover this information in the object's type information after it
      * is purged on GC.
      */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -112,17 +112,17 @@ JSObject::finalize(js::FreeOp* fop)
 }
 
 /* static */ inline bool
 JSObject::setSingleton(js::ExclusiveContext* cx, js::HandleObject obj)
 {
     MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj));
 
     js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
-                                                                 obj->getTaggedProto());
+                                                                 obj->taggedProto());
     if (!group)
         return false;
 
     obj->group_ = group;
     return true;
 }
 
 inline js::ObjectGroup*
@@ -147,23 +147,23 @@ JSObject::setGroup(js::ObjectGroup* grou
 }
 
 
 /*** Standard internal methods *******************************************************************/
 
 inline bool
 js::GetPrototype(JSContext* cx, js::HandleObject obj, js::MutableHandleObject protop)
 {
-    if (obj->getTaggedProto().isLazy()) {
+    if (obj->hasDynamicPrototype()) {
         MOZ_ASSERT(obj->is<js::ProxyObject>());
         return js::Proxy::getPrototype(cx, obj, protop);
-    } else {
-        protop.set(obj->getTaggedProto().toObjectOrNull());
-        return true;
     }
+
+    protop.set(obj->taggedProto().toObjectOrNull());
+    return true;
 }
 
 inline bool
 js::IsExtensible(ExclusiveContext* cx, HandleObject obj, bool* extensible)
 {
     if (obj->is<ProxyObject>()) {
         if (!cx->shouldBeJSContext())
             return false;
@@ -482,19 +482,19 @@ JSObject::hadElementsAccess() const
 
 inline bool
 JSObject::isIndexed() const
 {
     return hasAllFlags(js::BaseShape::INDEXED);
 }
 
 inline bool
-JSObject::nonLazyPrototypeIsImmutable() const
+JSObject::staticPrototypeIsImmutable() const
 {
-    MOZ_ASSERT(!hasLazyPrototype());
+    MOZ_ASSERT(hasStaticPrototype());
     return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE);
 }
 
 inline bool
 JSObject::isIteratedSingleton() const
 {
     return hasAllFlags(js::BaseShape::ITERATED_SINGLETON);
 }
@@ -559,17 +559,17 @@ IsNativeFunction(const js::Value& v, JSN
  */
 static MOZ_ALWAYS_INLINE bool
 ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid methodid, JSNative native)
 {
     MOZ_ASSERT(obj->getClass() == clasp);
 
     Value v;
     if (!HasDataProperty(cx, obj, methodid, &v)) {
-        JSObject* proto = obj->getProto();
+        JSObject* proto = obj->staticPrototype();
         if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as<NativeObject>(), methodid, &v))
             return false;
     }
 
     return IsNativeFunction(v, native);
 }
 
 // Return whether looking up 'valueOf' on 'obj' definitely resolves to the
@@ -580,17 +580,17 @@ HasObjectValueOf(JSObject* obj, JSContex
 {
     if (obj->is<ProxyObject>() || !obj->isNative())
         return false;
 
     jsid valueOf = NameToId(cx->names().valueOf);
 
     Value v;
     while (!HasDataProperty(cx, &obj->as<NativeObject>(), valueOf, &v)) {
-        obj = obj->getProto();
+        obj = obj->staticPrototype();
         if (!obj || obj->is<ProxyObject>() || !obj->isNative())
             return false;
     }
 
     return IsNativeFunction(v, obj_valueOf);
 }
 
 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -391,36 +391,36 @@ JSObject*
 BaseProxyHandler::weakmapKeyDelegate(JSObject* proxy) const
 {
     return nullptr;
 }
 
 bool
 BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const
 {
-    MOZ_CRASH("must override getPrototype with lazy prototype");
+    MOZ_CRASH("must override getPrototype with dynamic prototype");
 }
 
 bool
 BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                                ObjectOpResult& result) const
 {
-    // Disallow sets of protos on proxies with lazy protos, but no hook.
+    // Disallow sets of protos on proxies with dynamic prototypes but no hook.
     // This keeps us away from the footgun of having the first proto set opt
     // you out of having dynamic protos altogether.
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
                          "incompatible Proxy");
     return false;
 }
 
 bool
 BaseProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                                          MutableHandleObject protop) const
 {
-    MOZ_CRASH("must override getPrototypeIfOrdinary with lazy prototype");
+    MOZ_CRASH("must override getPrototypeIfOrdinary with dynamic prototype");
 }
 
 bool
 BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const
 {
     *succeeded = false;
     return true;
 }
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -178,34 +178,34 @@ js::AppendUnique(JSContext* cx, AutoIdVe
             uniqueOthers.append(others[i]);
     }
     return base.appendAll(uniqueOthers);
 }
 
 /* static */ bool
 Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto)
 {
-    MOZ_ASSERT(proxy->hasLazyPrototype());
+    MOZ_ASSERT(proxy->hasDynamicPrototype());
     JS_CHECK_RECURSION(cx, return false);
     return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
 }
 
 /* static */ bool
 Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result)
 {
-    MOZ_ASSERT(proxy->hasLazyPrototype());
+    MOZ_ASSERT(proxy->hasDynamicPrototype());
     JS_CHECK_RECURSION(cx, return false);
     return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result);
 }
 
 /* static */ bool
 Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                               MutableHandleObject proto)
 {
-    MOZ_ASSERT(proxy->hasLazyPrototype());
+    MOZ_ASSERT(proxy->hasDynamicPrototype());
     JS_CHECK_RECURSION(cx, return false);
     return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary,
                                                                       proto);
 }
 
 /* static */ bool
 Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded)
 {
@@ -777,17 +777,17 @@ js::NewProxyObject(JSContext* cx, const 
 }
 
 void
 ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv)
 {
     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
     MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
     MOZ_ASSERT(!IsWindowProxy(this));
-    MOZ_ASSERT(hasLazyPrototype());
+    MOZ_ASSERT(hasDynamicPrototype());
 
     setHandler(handler);
     setCrossCompartmentPrivate(priv);
     setExtra(0, UndefinedValue());
     setExtra(1, UndefinedValue());
 }
 
 JS_FRIEND_API(JSObject*)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3358,30 +3358,32 @@ END_CASE(JSOP_DEFFUN)
 
 CASE(JSOP_LAMBDA)
 {
     /* Load the specified function object literal. */
     ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain());
     if (!obj)
         goto error;
-    MOZ_ASSERT(obj->getProto());
+
+    MOZ_ASSERT(obj->staticPrototype());
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_LAMBDA)
 
 CASE(JSOP_LAMBDA_ARROW)
 {
     /* Load the specified function object literal. */
     ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
     ReservedRooted<Value> newTarget(&rootValue1, REGS.sp[-1]);
     JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), newTarget);
     if (!obj)
         goto error;
-    MOZ_ASSERT(obj->getProto());
+
+    MOZ_ASSERT(obj->staticPrototype());
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_LAMBDA_ARROW)
 
 CASE(JSOP_CALLEE)
     MOZ_ASSERT(REGS.fp()->isFunctionFrame());
     PUSH_COPY(REGS.fp()->calleev());
 END_CASE(JSOP_CALLEE)
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -561,17 +561,17 @@ LookupPropertyInline(ExclusiveContext* c
         if (done) {
             if (propp)
                 objp.set(current);
             else
                 objp.set(nullptr);
             return true;
         }
 
-        typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
+        typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->staticPrototype());
 
         if (!proto)
             break;
         if (!proto->isNative()) {
             if (!cx->shouldBeJSContext() || !allowGC)
                 return false;
             return LookupProperty(cx->asJSContext(),
                                   MaybeRooted<JSObject*, allowGC>::toHandle(proto),
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1081,17 +1081,17 @@ PurgeProtoChain(ExclusiveContext* cx, JS
         /* Lookups will not be cached through non-native protos. */
         if (!obj->isNative())
             break;
 
         shape = obj->as<NativeObject>().lookup(cx, id);
         if (shape)
             return obj->as<NativeObject>().shadowingShapeChange(cx, *shape);
 
-        obj = obj->getProto();
+        obj = obj->staticPrototype();
     }
 
     return true;
 }
 
 static bool
 PurgeScopeChainHelper(ExclusiveContext* cx, HandleObject objArg, HandleId id)
 {
@@ -1100,17 +1100,17 @@ PurgeScopeChainHelper(ExclusiveContext* 
 
     MOZ_ASSERT(obj->isNative());
     MOZ_ASSERT(obj->isDelegate());
 
     /* Lookups on integer ids cannot be cached through prototypes. */
     if (JSID_IS_INT(id))
         return true;
 
-    if (!PurgeProtoChain(cx, obj->getProto(), id))
+    if (!PurgeProtoChain(cx, obj->staticPrototype(), id))
         return false;
 
     /*
      * We must purge the scope chain only for Call objects as they are the only
      * kind of cacheable non-global object that can gain properties after outer
      * properties with the same names have been cached or traced. Call objects
      * may gain such properties via eval introducing new vars; see bug 490364.
      */
@@ -1641,17 +1641,17 @@ js::NativeHasProperty(JSContext* cx, Han
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
         //   being resolved.
         // What they all have in common is we do not want to keep walking
         // the prototype chain, and always claim that the property
         // doesn't exist.
-        RootedObject proto(cx, done ? nullptr : pobj->getProto());
+        RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
 
         // Step 8.
         if (!proto) {
             *foundp = false;
             return true;
         }
 
         // Step 7.a. If the prototype is also native, this step is a
@@ -2013,17 +2013,17 @@ NativeGetPropertyInline(JSContext* cx,
         // Steps 4.a-b. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
         //   being resolved.
         // What they all have in common is we do not want to keep walking
         // the prototype chain.
-        RootedObject proto(cx, done ? nullptr : pobj->getProto());
+        RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
 
         // Step 4.c. The spec algorithm simply returns undefined if proto is
         // null, but see the comment on GetNonexistentProperty.
         if (!proto)
             return GetNonexistentProperty(cx, obj, id, receiver, nameLookup, vp);
 
         // Step 4.d. If the prototype is also native, this step is a
         // recursive tail call, and we don't need to go through all the
@@ -2223,19 +2223,20 @@ js::SetPropertyByDefining(JSContext* cx,
 // When setting |id| for |receiver| and |obj| has no property for id, continue
 // the search up the prototype chain.
 bool
 js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                        HandleValue receiver, ObjectOpResult& result)
 {
     MOZ_ASSERT(!obj->is<ProxyObject>());
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (proto)
         return SetProperty(cx, proto, id, v, receiver, result);
+
     return SetPropertyByDefining(cx, id, v, receiver, result);
 }
 
 /*
  * Implement "the rest of" assignment to a property when no property receiver[id]
  * was found anywhere on the prototype chain.
  *
  * FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9,
@@ -2399,17 +2400,17 @@ js::NativeSetProperty(JSContext* cx, Han
         // Steps 4.a-b. The check for 'done' on this next line is tricky.
         // done can be true in exactly these unlikely-sounding cases:
         // - We're looking up an element, and pobj is a TypedArray that
         //   doesn't have that many elements.
         // - We're being called from a resolve hook to assign to the property
         //   being resolved.
         // What they all have in common is we do not want to keep walking
         // the prototype chain.
-        RootedObject proto(cx, done ? nullptr : pobj->getProto());
+        RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
         if (!proto) {
             // Step 4.d.i (and step 5).
             return SetNonexistentProperty(cx, id, v, receiver, qualified, result);
         }
 
         // Step 4.c.i. If the prototype is also native, this step is a
         // recursive tail call, and we don't need to go through all the
         // plumbing of SetProperty; the top of the loop is where we're going to
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -255,17 +255,17 @@ bool
 JSObject::shouldSplicePrototype(JSContext* cx)
 {
     /*
      * During bootstrapping, if inference is enabled we need to make sure not
      * to splice a new prototype in for Function.prototype or the global
      * object if their __proto__ had previously been set to null, as this
      * will change the prototype for all other objects with the same type.
      */
-    if (getProto() != nullptr)
+    if (staticPrototype() != nullptr)
         return false;
     return isSingleton();
 }
 
 bool
 JSObject::splicePrototype(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto)
 {
     MOZ_ASSERT(cx->compartment() == compartment());
@@ -322,17 +322,17 @@ JSObject::makeLazyGroup(JSContext* cx, H
         initialFlags |= OBJECT_FLAG_ITERATED;
 
     if (obj->isIndexed())
         initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
 
     if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
         initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
 
-    Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
+    Rooted<TaggedProto> proto(cx, obj->taggedProto());
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto,
                                                            initialFlags);
     if (!group)
         return nullptr;
 
     AutoEnterAnalysis enter(cx);
 
     /* Fill in the type according to the state of this object. */
@@ -496,17 +496,17 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
         MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
         MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
                               group->clasp() == &UnboxedPlainObject::class_);
         MOZ_ASSERT(group->proto() == proto);
         return group;
     }
 
     ObjectGroupFlags initialFlags = 0;
-    if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
+    if (proto.isDynamic() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
         initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
 
     Rooted<TaggedProto> protoRoot(cx, proto);
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
                                                            protoRoot, initialFlags);
     if (!group)
         return nullptr;
 
@@ -1215,17 +1215,17 @@ ObjectGroup::newPlainObject(ExclusiveCon
             return nullptr;
 
         // Don't make entries with duplicate property names, which will show up
         // here as objects with fewer properties than we thought we were
         // adding to the object. In this case, reset the object's group to the
         // default (which will have unknown properties) so that the group we
         // just created will be collected by the GC.
         if (obj->slotSpan() != nproperties) {
-            ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->getTaggedProto());
+            ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->taggedProto());
             if (!group)
                 return nullptr;
             obj->setGroup(group);
             return obj;
         }
 
         // Keep track of the initial objects we create with this type.
         // If the initial ones have a consistent shape and property types, we
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -94,16 +94,20 @@ class ObjectGroup : public gc::TenuredCe
     const Class* clasp() const {
         return clasp_;
     }
 
     void setClasp(const Class* clasp) {
         clasp_ = clasp;
     }
 
+    bool hasDynamicPrototype() const {
+        return proto_.isDynamic();
+    }
+
     const HeapPtr<TaggedProto>& proto() const {
         return proto_;
     }
 
     HeapPtr<TaggedProto>& proto() {
         return proto_;
     }
 
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -176,24 +176,17 @@ js::ForOfPIC::Chain::getMatchingStub(JSO
 
     return nullptr;
 }
 
 bool
 js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj)
 {
     MOZ_ASSERT(obj->is<ArrayObject>());
-
-    // Ensure object's prototype is the actual Array.prototype
-    if (!obj->getTaggedProto().isObject())
-        return false;
-    if (obj->getTaggedProto().toObject() != arrayProto_)
-        return false;
-
-    return true;
+    return obj->staticPrototype() == arrayProto_;
 }
 
 bool
 js::ForOfPIC::Chain::isArrayStateStillSane()
 {
     // Ensure that canonical Array.prototype has matching shape.
     if (arrayProto_->lastProperty() != arrayProtoShape_)
         return false;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -844,17 +844,17 @@ RegExpCompartment::createMatchResultTemp
 
     /* Create template array object */
     RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
                                      nullptr, TenuredObject));
     if (!templateObject)
         return matchResultTemplateObject_; // = nullptr
 
     // Create a new group for the template.
-    Rooted<TaggedProto> proto(cx, templateObject->getTaggedProto());
+    Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
     if (!group)
         return matchResultTemplateObject_; // = nullptr
     templateObject->setGroup(group);
 
     /* Set dummy index property */
     RootedValue index(cx, Int32Value(0));
     if (!NativeDefineProperty(cx, templateObject, cx->names().index, index, nullptr, nullptr,
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -892,17 +892,17 @@ class NonSyntacticVariablesObject : publ
                                                Handle<ClonedBlockObject*> globalLexical);
 };
 
 class NestedScopeObject : public ScopeObject
 {
   public:
     // Return the static scope corresponding to this scope chain object.
     inline NestedStaticScope* staticScope() {
-        return &getProto()->as<NestedStaticScope>();
+        return &staticPrototype()->as<NestedStaticScope>();
     }
 
     void initEnclosingScope(JSObject* obj) {
         MOZ_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
         setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
     }
 };
 
@@ -922,17 +922,17 @@ class DynamicWithObject : public NestedS
         NonSyntacticWith
     };
 
     static DynamicWithObject*
     create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith,
            WithKind kind = SyntacticWith);
 
     StaticWithScope& staticWith() const {
-        return getProto()->as<StaticWithScope>();
+        return staticPrototype()->as<StaticWithScope>();
     }
 
     /* Return the 'o' in 'with (o)'. */
     JSObject& object() const {
         return getReservedSlot(OBJECT_SLOT).toObject();
     }
 
     /* Return object for GetThisValue. */
@@ -1002,17 +1002,17 @@ class ClonedBlockObject : public NestedS
 
     void setSlotValue(unsigned i, const Value& v) {
         setSlot(RESERVED_SLOTS + i, v);
     }
 
   public:
     /* The static block from which this block was cloned. */
     StaticBlockScope& staticBlock() const {
-        return getProto()->as<StaticBlockScope>();
+        return staticPrototype()->as<StaticBlockScope>();
     }
 
     /* Assuming 'put' has been called, return the value of the ith let var. */
     const Value& var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
         MOZ_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
         return slotValue(i);
     }
 
@@ -1382,17 +1382,17 @@ class DebugScopes
 };
 
 }  /* namespace js */
 
 template<>
 inline bool
 JSObject::is<js::StaticBlockScope>() const
 {
-    return hasClass(&js::ClonedBlockObject::class_) && !getProto();
+    return hasClass(&js::ClonedBlockObject::class_) && !staticPrototype();
 }
 
 template<>
 inline bool
 JSObject::is<js::NestedStaticScope>() const
 {
     return is<js::StaticBlockScope>() ||
            is<js::StaticWithScope>();
@@ -1406,17 +1406,17 @@ JSObject::is<js::StaticScope>() const
            is<js::StaticEvalScope>() ||
            is<js::StaticNonSyntacticScope>();
 }
 
 template<>
 inline bool
 JSObject::is<js::ClonedBlockObject>() const
 {
-    return hasClass(&js::ClonedBlockObject::class_) && !!getProto();
+    return hasClass(&js::ClonedBlockObject::class_) && staticPrototype();
 }
 
 template<>
 inline bool
 JSObject::is<js::NestedScopeObject>() const
 {
     return is<js::ClonedBlockObject>() ||
            is<js::DynamicWithObject>();
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -474,17 +474,17 @@ intrinsic_FinishBoundFunctionInit(JSCont
     if (targetObj->isConstructor())
         bound->setIsConstructor();
 
     // 9.4.1.3 BoundFunctionCreate, Steps 2-3., 8.
     RootedObject proto(cx);
     if (!GetPrototype(cx, targetObj, &proto))
         return false;
 
-    if (bound->getProto() != proto) {
+    if (bound->staticPrototype() != proto) {
         if (!SetPrototype(cx, bound, proto))
             return false;
     }
 
     bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]);
     MOZ_ASSERT(!bound->hasGuessedAtom());
 
     // 9.2.11 SetFunctionName, Step 6.
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -129,17 +129,17 @@ EmptyShape::ensureInitialCustomShape(Exc
     // |CreateBlankProto| marked it as a delegate.  These are the only objects
     // of this class that won't use the standard prototype, and there's no
     // reason to pollute the initial shape cache with entries for them.
     if (obj->isDelegate())
         return true;
 
     // Cache the initial shape for non-prototype objects, however, so that
     // future instances will begin life with that shape.
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     EmptyShape::insertInitialShape(cx, shape, proto);
     return true;
 }
 
 inline
 AutoRooterGetterSetter::Inner::Inner(ExclusiveContext* cx, uint8_t attrs,
                                      GetterOp* pgetter_, SetterOp* psetter_)
   : CustomAutoRooter(cx), attrs(attrs),
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1189,17 +1189,17 @@ JSObject::setFlags(ExclusiveContext* cx,
         self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
     Shape* existingShape = self->ensureShape(cx);
     if (!existingShape)
         return false;
 
-    Shape* newShape = Shape::setObjectFlags(cx, flags, self->getTaggedProto(), existingShape);
+    Shape* newShape = Shape::setObjectFlags(cx, flags, self->taggedProto(), existingShape);
     if (!newShape)
         return false;
 
     self->setShapeMaybeNonNative(newShape);
     return true;
 }
 
 bool
--- a/js/src/vm/TaggedProto.cpp
+++ b/js/src/vm/TaggedProto.cpp
@@ -44,15 +44,15 @@ js::HashNumber
 js::TaggedProto::hashCode() const
 {
     return Zone::UniqueIdToHash(uniqueId());
 }
 
 uint64_t
 js::TaggedProto::uniqueId() const
 {
-    if (isLazy())
+    if (isDynamic())
         return uint64_t(1);
     JSObject* obj = toObjectOrNull();
     if (!obj)
         return uint64_t(0);
     return obj->zone()->getUniqueIdInfallible(obj);
 }
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -19,17 +19,17 @@ class TaggedProto
   public:
     static JSObject * const LazyProto;
 
     TaggedProto() : proto(nullptr) {}
     explicit TaggedProto(JSObject* proto) : proto(proto) {}
 
     uintptr_t toWord() const { return uintptr_t(proto); }
 
-    bool isLazy() const {
+    bool isDynamic() const {
         return proto == LazyProto;
     }
     bool isObject() const {
         /* Skip nullptr and LazyProto. */
         return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto);
     }
     JSObject* toObject() const {
         MOZ_ASSERT(isObject());
@@ -78,17 +78,17 @@ template<class Outer>
 class TaggedProtoOperations
 {
     const TaggedProto& value() const {
         return static_cast<const Outer*>(this)->get();
     }
 
   public:
     uintptr_t toWord() const { return value().toWord(); }
-    inline bool isLazy() const { return value().isLazy(); }
+    inline bool isDynamic() const { return value().isDynamic(); }
     inline bool isObject() const { return value().isObject(); }
     inline JSObject* toObject() const { return value().toObject(); }
     inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
     JSObject* raw() const { return value().raw(); }
     HashNumber hashCode() const { return value().hashCode(); }
     uint64_t uniqueId() const { return value().uniqueId(); }
 };
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1210,17 +1210,17 @@ const Class*
 TypeSet::ObjectKey::clasp()
 {
     return isGroup() ? group()->clasp() : singleton()->getClass();
 }
 
 TaggedProto
 TypeSet::ObjectKey::proto()
 {
-    return isGroup() ? group()->proto() : singleton()->getTaggedProto();
+    return isGroup() ? group()->proto() : singleton()->taggedProto();
 }
 
 TypeNewScript*
 TypeSet::ObjectKey::newScript()
 {
     if (isGroup() && group()->newScript())
         return group()->newScript();
     return nullptr;
@@ -2415,17 +2415,17 @@ TemporaryTypeSet::getCommonPrototype(Com
         if (!key)
             continue;
 
         if (key->unknownProperties())
             return false;
 
         TaggedProto nproto = key->proto();
         if (isFirst) {
-            if (nproto.isLazy())
+            if (nproto.isDynamic())
                 return false;
             *proto = nproto.toObjectOrNull();
             isFirst = false;
         } else {
             if (nproto != TaggedProto(*proto))
                 return false;
         }
     }
@@ -3002,18 +3002,21 @@ ObjectGroup::clearNewScript(ExclusiveCon
 }
 
 void
 ObjectGroup::print()
 {
     TaggedProto tagged(proto());
     fprintf(stderr, "%s : %s",
             TypeSet::ObjectGroupString(this),
-            tagged.isObject() ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject()))
-                              : (tagged.isLazy() ? "(lazy)" : "(null)"));
+            tagged.isObject()
+            ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject()))
+            : tagged.isDynamic()
+            ? "(dynamic)"
+            : "(null)");
 
     if (unknownProperties()) {
         fprintf(stderr, " unknown");
     } else {
         if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES))
             fprintf(stderr, " dense");
         if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED))
             fprintf(stderr, " packed");
@@ -3114,17 +3117,17 @@ js::AddClearDefiniteGetterSetterForProto
         }
         if (protoGroup->unknownProperties())
             return false;
         HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
         if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
             return false;
         if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group)))
             return false;
-        proto = proto->getProto();
+        proto = proto->staticPrototype();
     }
     return true;
 }
 
 /*
  * Constraint which clears definite properties on a group should a type set
  * contain any types other than a single object.
  */
@@ -3314,17 +3317,17 @@ JSScript::makeTypes(JSContext* cx)
 /* static */ bool
 JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun,
                                        bool singleton /* = false */)
 {
     if (singleton) {
         if (!setSingleton(cx, fun))
             return false;
     } else {
-        RootedObject funProto(cx, fun->getProto());
+        RootedObject funProto(cx, fun->staticPrototype());
         Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto));
         ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_,
                                                                taggedProto);
         if (!group)
             return false;
 
         fun->setGroup(group);
         group->setInterpretedFunction(fun);
@@ -3607,19 +3610,17 @@ TypeNewScript::registerNewObject(PlainOb
     preliminaryObjects->registerNewObject(res);
 }
 
 static bool
 ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocKind)
 {
     MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty()));
 
-    Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(),
-                                          obj->getTaggedProto(),
-                                          allocKind);
+    Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), obj->taggedProto(), allocKind);
     if (!newShape)
         return false;
 
     obj->setLastPropertyShrinkFixedSlots(newShape);
     return true;
 }
 
 namespace {
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -722,17 +722,17 @@ UnboxedPlainObject::obj_lookupProperty(J
                                        MutableHandleShape propp)
 {
     if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
         MarkNonNativePropertyFound<CanGC>(propp);
         objp.set(obj);
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
         propp.set(nullptr);
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
@@ -773,17 +773,17 @@ UnboxedPlainObject::obj_defineProperty(J
 /* static */ bool
 UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
         *foundp = true;
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
@@ -800,17 +800,17 @@ UnboxedPlainObject::obj_getProperty(JSCo
 
     if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
         if (expando->containsShapeOrElement(cx, id)) {
             RootedObject nexpando(cx, expando);
             return GetProperty(cx, nexpando, receiver, id, vp);
         }
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, proto, receiver, id, vp);
 }
 
@@ -1423,17 +1423,17 @@ UnboxedArrayObject::obj_lookupProperty(J
                                        MutableHandleShape propp)
 {
     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
         MarkNonNativePropertyFound<CanGC>(propp);
         objp.set(obj);
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         objp.set(nullptr);
         propp.set(nullptr);
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
@@ -1474,17 +1474,17 @@ UnboxedArrayObject::obj_defineProperty(J
 /* static */ bool
 UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
         *foundp = true;
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
@@ -1495,17 +1495,17 @@ UnboxedArrayObject::obj_getProperty(JSCo
     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
         if (JSID_IS_INT(id))
             vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
         else
             vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
         return true;
     }
 
-    RootedObject proto(cx, obj->getProto());
+    RootedObject proto(cx, obj->staticPrototype());
     if (!proto) {
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, proto, receiver, id, vp);
 }