Bug 1112778, part 3 - Rename JSObject::preventExtensions -> js::PreventExtensions and a few others, and move them to jsobj.cpp. Uninline several functions that have no business being inlined. r=Waldo.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 18 Dec 2014 05:26:42 -0600
changeset 251147 42e833ab7dea24b06adf2a374b4dadacec777387
parent 251146 ec05328eb3252d9adc6529e47f6f5b428959fa09
child 251148 d172d554c7326d971e275f51e31208cb871d0195
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1112778
milestone38.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 1112778, part 3 - Rename JSObject::preventExtensions -> js::PreventExtensions and a few others, and move them to jsobj.cpp. Uninline several functions that have no business being inlined. r=Waldo.
js/src/builtin/Intl.cpp
js/src/builtin/Object.cpp
js/src/builtin/TestingFunctions.cpp
js/src/jit/VMFunctions.cpp
js/src/jsapi.cpp
js/src/jsarray.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/CrossCompartmentWrapper.cpp
js/src/proxy/DirectProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/ScriptedDirectProxyHandler.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/NativeObject.cpp
js/src/vm/Shape.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -610,17 +610,17 @@ Collator(JSContext *cx, CallArgs args, b
         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
             // 10.1.2.1 step 4
             obj = ToObject(cx, self);
             if (!obj)
                 return false;
 
             // 10.1.2.1 step 5
             bool extensible;
-            if (!JSObject::isExtensible(cx, obj, &extensible))
+            if (!IsExtensible(cx, obj, &extensible))
                 return false;
             if (!extensible)
                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
         } else {
             // 10.1.2.1 step 3.a
             construct = true;
         }
     }
@@ -1096,17 +1096,17 @@ NumberFormat(JSContext *cx, CallArgs arg
         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
             // 11.1.2.1 step 4
             obj = ToObject(cx, self);
             if (!obj)
                 return false;
 
             // 11.1.2.1 step 5
             bool extensible;
-            if (!JSObject::isExtensible(cx, obj, &extensible))
+            if (!IsExtensible(cx, obj, &extensible))
                 return false;
             if (!extensible)
                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
         } else {
             // 11.1.2.1 step 3.a
             construct = true;
         }
     }
@@ -1553,17 +1553,17 @@ DateTimeFormat(JSContext *cx, CallArgs a
         if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
             // 12.1.2.1 step 4
             obj = ToObject(cx, self);
             if (!obj)
                 return false;
 
             // 12.1.2.1 step 5
             bool extensible;
-            if (!JSObject::isExtensible(cx, obj, &extensible))
+            if (!IsExtensible(cx, obj, &extensible))
                 return false;
             if (!extensible)
                 return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
         } else {
             // 12.1.2.1 step 3.a
             construct = true;
         }
     }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -416,17 +416,17 @@ js::obj_getPrototypeOf(JSContext *cx, un
 
     /* Steps 1-2. */
     RootedObject obj(cx, ToObject(cx, args.get(0)));
     if (!obj)
         return false;
 
     /* Step 3. */
     RootedObject proto(cx);
-    if (!JSObject::getProto(cx, obj, &proto))
+    if (!GetPrototype(cx, obj, &proto))
         return false;
     args.rval().setObjectOrNull(proto);
     return true;
 }
 
 static bool
 obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -462,17 +462,17 @@ obj_setPrototypeOf(JSContext *cx, unsign
         return true;
     }
 
     /* Step 5-6. */
     RootedObject obj(cx, &args[0].toObject());
     RootedObject newProto(cx, args[1].toObjectOrNull());
 
     bool success;
-    if (!JSObject::setProto(cx, obj, newProto, &success))
+    if (!SetPrototype(cx, obj, newProto, &success))
         return false;
 
     /* Step 7. */
     if (!success) {
         UniquePtr<char[], JS::FreePolicy> bytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
                                                                         args[0], NullPtr()));
         if (!bytes)
             return false;
@@ -854,17 +854,17 @@ obj_isExtensible(JSContext *cx, unsigned
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     bool extensible = false;
 
     // Step 2.
     if (args.get(0).isObject()) {
         RootedObject obj(cx, &args.get(0).toObject());
-        if (!JSObject::isExtensible(cx, obj, &extensible))
+        if (!IsExtensible(cx, obj, &extensible))
             return false;
     }
     args.rval().setBoolean(extensible);
     return true;
 }
 
 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
 static bool
@@ -876,17 +876,17 @@ obj_preventExtensions(JSContext *cx, uns
     // Step 1.
     if (!args.get(0).isObject())
         return true;
 
     // Steps 2-3.
     RootedObject obj(cx, &args.get(0).toObject());
 
     bool status;
-    if (!JSObject::preventExtensions(cx, obj, &status))
+    if (!PreventExtensions(cx, obj, &status))
         return false;
 
     // Step 4.
     if (!status) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
         return false;
     }
 
@@ -973,17 +973,17 @@ ProtoGetter(JSContext *cx, unsigned argc
         ReportIncompatible(cx, args);
         return false;
     }
     if (thisv.isPrimitive() && !BoxNonStrictThis(cx, args))
         return false;
 
     RootedObject obj(cx, &args.thisv().toObject());
     RootedObject proto(cx);
-    if (!JSObject::getProto(cx, obj, &proto))
+    if (!GetPrototype(cx, obj, &proto))
         return false;
     args.rval().setObjectOrNull(proto);
     return true;
 }
 
 namespace js {
 size_t sSetProtoCalled = 0;
 }
@@ -1020,17 +1020,17 @@ ProtoSetter(JSContext *cx, unsigned argc
     if (args.length() == 0 || !args[0].isObjectOrNull()) {
         args.rval().setUndefined();
         return true;
     }
 
     Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
 
     bool success;
-    if (!JSObject::setProto(cx, obj, newProto, &success))
+    if (!SetPrototype(cx, obj, newProto, &success))
         return false;
 
     if (!success) {
         js_ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, thisv, js::NullPtr());
         return false;
     }
 
     args.rval().setUndefined();
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2206,17 +2206,17 @@ SetImmutablePrototype(JSContext *cx, uns
     if (!args.get(0).isObject()) {
         JS_ReportError(cx, "setImmutablePrototype: object expected");
         return false;
     }
 
     RootedObject obj(cx, &args[0].toObject());
 
     bool succeeded;
-    if (!JSObject::setImmutablePrototype(cx, obj, &succeeded))
+    if (!js::SetImmutablePrototype(cx, obj, &succeeded))
         return false;
 
     args.rval().setBoolean(succeeded);
     return true;
 }
 
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -198,17 +198,17 @@ bool
 MutatePrototype(JSContext *cx, HandlePlainObject obj, HandleValue value)
 {
     if (!value.isObjectOrNull())
         return true;
 
     RootedObject newProto(cx, value.toObjectOrNull());
 
     bool succeeded;
-    if (!JSObject::setProto(cx, obj, newProto, &succeeded))
+    if (!SetPrototype(cx, obj, newProto, &succeeded))
         return false;
     MOZ_ASSERT(succeeded);
     return true;
 }
 
 bool
 InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value)
 {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2013,39 +2013,51 @@ JS_GetInstancePrivate(JSContext *cx, Han
     if (!JS_InstanceOf(cx, obj, clasp, args))
         return nullptr;
     return obj->as<NativeObject>().getPrivate();
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPrototype(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> protop)
 {
-    return JSObject::getProto(cx, obj, protop);
+    return GetPrototype(cx, obj, protop);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetPrototype(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<JSObject*> proto)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
 
     bool succeeded;
-    if (!JSObject::setProto(cx, obj, proto, &succeeded))
+    if (!SetPrototype(cx, obj, proto, &succeeded))
         return false;
 
     if (!succeeded) {
         RootedValue val(cx, ObjectValue(*obj));
         js_ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, val, js::NullPtr());
         return false;
     }
 
     return true;
 }
 
+JS_PUBLIC_API(bool)
+JS_IsExtensible(JSContext *cx, HandleObject obj, bool *extensible)
+{
+    return IsExtensible(cx, obj, extensible);
+}
+
+JS_PUBLIC_API(bool)
+JS_PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded)
+{
+    return PreventExtensions(cx, obj, succeeded);
+}
+
 JS_PUBLIC_API(JSObject *)
 JS_GetParent(JSObject *obj)
 {
     MOZ_ASSERT(!obj->is<ScopeObject>());
     return obj->getParent();
 }
 
 JS_PUBLIC_API(bool)
@@ -2214,22 +2226,16 @@ JS_NewObjectForConstructor(JSContext *cx
 
     Value callee = args.calleev();
     assertSameCompartment(cx, callee);
     RootedObject obj(cx, &callee.toObject());
     return CreateThis(cx, Valueify(clasp), obj);
 }
 
 JS_PUBLIC_API(bool)
-JS_IsExtensible(JSContext *cx, HandleObject obj, bool *extensible)
-{
-    return JSObject::isExtensible(cx, obj, extensible);
-}
-
-JS_PUBLIC_API(bool)
 JS_IsNative(JSObject *obj)
 {
     return obj->isNative();
 }
 
 JS_PUBLIC_API(JSRuntime *)
 JS_GetObjectRuntime(JSObject *obj)
 {
@@ -2249,17 +2255,17 @@ JS_PUBLIC_API(bool)
 JS_DeepFreezeObject(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
     bool extensible;
-    if (!JSObject::isExtensible(cx, obj, &extensible))
+    if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return true;
 
     if (!JSObject::freeze(cx, obj))
         return false;
 
     /* Walk slots in obj and if any value is a non-null object, seal it. */
@@ -6114,22 +6120,16 @@ JS_DecodeInterpretedFunction(JSContext *
 {
     XDRDecoder decoder(cx, data, length);
     RootedFunction funobj(cx);
     if (!decoder.codeFunction(&funobj))
         return nullptr;
     return funobj;
 }
 
-JS_PUBLIC_API(bool)
-JS_PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded)
-{
-    return JSObject::preventExtensions(cx, obj, succeeded);
-}
-
 JS_PUBLIC_API(void)
 JS::SetAsmJSCacheOps(JSRuntime *rt, const JS::AsmJSCacheOps *ops)
 {
     rt->asmJSCacheOps = *ops;
 }
 
 char *
 JSAutoByteString::encodeLatin1(ExclusiveContext *cx, JSString *str)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -448,17 +448,17 @@ static bool
 array_length_getter(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp)
 {
     RootedObject obj(cx, obj_);
     do {
         if (obj->is<ArrayObject>()) {
             vp.setNumber(obj->as<ArrayObject>().length());
             return true;
         }
-        if (!JSObject::getProto(cx, obj, &obj))
+        if (!GetPrototype(cx, obj, &obj))
             return false;
     } while (obj);
     return true;
 }
 
 static bool
 array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
 {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -889,17 +889,17 @@ CreateFunctionPrototype(JSContext *cx, J
     RootedObject tte(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto, self,
                                                  SingletonObject));
     if (!tte)
         return nullptr;
 
     bool succeeded;
     RootedFunction throwTypeError(cx, NewFunction(cx, tte, ThrowTypeError, 0,
                                                   JSFunction::NATIVE_FUN, self, js::NullPtr()));
-    if (!throwTypeError || !JSObject::preventExtensions(cx, throwTypeError, &succeeded))
+    if (!throwTypeError || !PreventExtensions(cx, throwTypeError, &succeeded))
         return nullptr;
     if (!succeeded) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
         return nullptr;
     }
 
     self->setThrowTypeError(throwTypeError);
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -347,17 +347,17 @@ Snapshot(JSContext *cx, HandleObject pob
             }
         } else {
             MOZ_CRASH("non-native objects must have an enumerate op");
         }
 
         if (flags & JSITER_OWNONLY)
             break;
 
-        if (!JSObject::getProto(cx, pobj, &pobj))
+        if (!GetPrototype(cx, pobj, &pobj))
             return false;
 
     } while (pobj != nullptr);
 
 #ifdef JS_MORE_DETERMINISTIC
 
     /*
      * In some cases the enumeration order for an object depends on the
@@ -1158,17 +1158,17 @@ SuppressDeletedPropertyHelper(JSContext 
             HeapPtrFlatString *props_end = ni->end();
             for (HeapPtrFlatString *idp = props_cursor; idp < props_end; ++idp) {
                 if (predicate(*idp)) {
                     /*
                      * Check whether another property along the prototype chain
                      * became visible as a result of this deletion.
                      */
                     RootedObject proto(cx);
-                    if (!JSObject::getProto(cx, obj, &proto))
+                    if (!GetPrototype(cx, obj, &proto))
                         return false;
                     if (proto) {
                         RootedObject obj2(cx);
                         RootedShape prop(cx);
                         RootedId id(cx);
                         RootedValue idv(cx, StringValue(*idp));
                         if (!ValueToId<CanGC>(cx, idv, &id))
                             return false;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -601,17 +601,17 @@ DefinePropertyOnObject(JSContext *cx, Ha
     if (!NonProxyLookupOwnProperty<CanGC>(cx, nullptr, obj, id, &obj2, &shape))
         return false;
 
     MOZ_ASSERT(!obj->getOps()->defineProperty);
 
     /* 8.12.9 steps 2-4. */
     if (!shape) {
         bool extensible;
-        if (!JSObject::isExtensible(cx, obj, &extensible))
+        if (!IsExtensible(cx, obj, &extensible))
             return false;
         if (!extensible)
             return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
 
         *rval = true;
 
         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
             MOZ_ASSERT(!obj->getOps()->defineProperty);
@@ -1098,17 +1098,17 @@ JSObject::getSealedOrFrozenAttributes(un
 
 /* static */ bool
 JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
 {
     assertSameCompartment(cx, obj);
     MOZ_ASSERT(it == SEAL || it == FREEZE);
 
     bool succeeded;
-    if (!JSObject::preventExtensions(cx, obj, &succeeded))
+    if (!PreventExtensions(cx, obj, &succeeded))
         return false;
     if (!succeeded) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
         return false;
     }
 
     AutoIdVector props(cx);
     if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
@@ -1197,17 +1197,17 @@ JSObject::sealOrFreeze(JSContext *cx, Ha
 
     return true;
 }
 
 /* static */ bool
 JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
 {
     bool extensible;
-    if (!JSObject::isExtensible(cx, obj, &extensible))
+    if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (extensible) {
         *resultp = false;
         return true;
     }
 
     if (IsAnyTypedArray(obj)) {
         if (it == SEAL) {
@@ -2148,17 +2148,17 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
             FixObjectType(cx, &obj->as<PlainObject>());
         }
     }
 
     {
         uint32_t frozen;
         bool extensible;
         if (mode == XDR_ENCODE) {
-            if (!JSObject::isExtensible(cx, obj, &extensible))
+            if (!IsExtensible(cx, obj, &extensible))
                 return false;
             frozen = extensible ? 0 : 1;
         }
         if (!xdr->codeUint32(&frozen))
             return false;
         if (mode == XDR_DECODE && frozen == 1) {
             if (!JSObject::freeze(cx, obj))
                 return false;
@@ -3224,16 +3224,145 @@ bool
 JSObject::reportNotExtensible(JSContext *cx, unsigned report)
 {
     RootedValue val(cx, ObjectValue(*this));
     return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
                                     JSDVG_IGNORE_STACK, val, js::NullPtr(),
                                     nullptr, nullptr);
 }
 
+
+/*** ES6 standard internal methods ***************************************************************/
+
+bool
+js::SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, bool *succeeded)
+{
+    /*
+     * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's
+     * {get,set}PrototypeOf and setImmutablePrototype methods mediate access to
+     * |obj.[[Prototype]]|.  The Proxy subsystem is responsible for responding
+     * to such attempts.
+     */
+    if (obj->hasLazyPrototype()) {
+        MOZ_ASSERT(obj->is<ProxyObject>());
+        return Proxy::setPrototypeOf(cx, obj, proto, succeeded);
+    }
+
+    /* Disallow mutation of immutable [[Prototype]]s. */
+    if (obj->nonLazyPrototypeIsImmutable()) {
+        *succeeded = false;
+        return true;
+    }
+
+    /*
+     * 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>()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
+                             "incompatible ArrayBuffer");
+        return false;
+    }
+
+    /*
+     * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
+     */
+    if (obj->is<TypedObject>()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
+                             "incompatible TypedObject");
+        return false;
+    }
+
+    /*
+     * Explicitly disallow mutating the [[Prototype]] of Location objects
+     * for flash-related security reasons.
+     */
+    if (!strcmp(obj->getClass()->name, "Location")) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
+                             "incompatible Location object");
+        return false;
+    }
+
+    /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
+    bool extensible;
+    if (!IsExtensible(cx, obj, &extensible))
+        return false;
+    if (!extensible) {
+        *succeeded = false;
+        return true;
+    }
+
+    /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
+    RootedObject obj2(cx);
+    for (obj2 = proto; obj2; ) {
+        if (obj2 == obj) {
+            *succeeded = false;
+            return true;
+        }
+
+        if (!GetPrototype(cx, obj2, &obj2))
+            return false;
+    }
+
+    Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
+    *succeeded = SetClassAndProto(cx, obj, obj->getClass(), taggedProto);
+    return *succeeded;
+}
+
+bool
+js::PreventExtensions(JSContext *cx, HandleObject obj, bool *succeeded)
+{
+    if (obj->is<ProxyObject>())
+        return js::Proxy::preventExtensions(cx, obj, succeeded);
+
+    if (!obj->nonProxyIsExtensible()) {
+        *succeeded = true;
+        return true;
+    }
+
+    /*
+     * Force lazy properties to be resolved by iterating over the objects' own
+     * properties.
+     */
+    AutoIdVector props(cx);
+    if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
+        return false;
+
+    /*
+     * Convert all dense elements to sparse properties. This will shrink the
+     * initialized length and capacity of the object to zero and ensure that no
+     * new dense elements can be added without calling growElements(), which
+     * checks isExtensible().
+     */
+    if (obj->isNative() && !NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
+        return false;
+
+    *succeeded = true;
+    return obj->setFlag(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE);
+}
+
+
+/*** SpiderMonkey nonstandard internal methods ***************************************************/
+
+bool
+js::SetImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeeded)
+{
+    if (obj->hasLazyPrototype()) {
+        if (!cx->shouldBeJSContext())
+            return false;
+        return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
+    }
+
+    if (!obj->setFlag(cx, BaseShape::IMMUTABLE_PROTOTYPE))
+        return false;
+    *succeeded = true;
+    return true;
+}
+
 /* static */ bool
 JSObject::defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     JSConvertOp op = obj->getClass()->convert;
     bool ok;
     if (!op)
         ok = JS::OrdinaryToPrimitive(cx, obj, hint, vp);
     else
@@ -3302,16 +3431,19 @@ js::UnwatchGuts(JSContext *cx, JS::Handl
 }
 
 bool
 js::NativeUnwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
 {
     return UnwatchGuts(cx, obj, id);
 }
 
+
+/* * */
+
 bool
 js::HasDataProperty(JSContext *cx, NativeObject *obj, jsid id, Value *vp)
 {
     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
         *vp = obj->getDenseElement(JSID_TO_INT(id));
         return true;
     }
 
@@ -3438,17 +3570,17 @@ js::IsDelegate(JSContext *cx, HandleObje
     return IsDelegateOfObject(cx, obj, &v.toObject(), result);
 }
 
 bool
 js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result)
 {
     RootedObject obj2(cx, obj);
     for (;;) {
-        if (!JSObject::getProto(cx, obj2, &obj2))
+        if (!GetPrototype(cx, obj2, &obj2))
             return false;
         if (!obj2) {
             *result = false;
             return true;
         }
         if (obj2 == protoObj) {
             *result = true;
             return true;
@@ -3622,16 +3754,19 @@ js_ReportGetterOnlyAssignment(JSContext 
     return JS_ReportErrorFlagsAndNumber(cx,
                                         strict
                                         ? JSREPORT_ERROR
                                         : JSREPORT_WARNING | JSREPORT_STRICT,
                                         js_GetErrorMessage, nullptr,
                                         JSMSG_GETTER_ONLY);
 }
 
+
+/*** Debugging routines **************************************************************************/
+
 #ifdef DEBUG
 
 /*
  * Routines to print out values during debugging.  These are FRIEND_API to help
  * the debugger find them and to support temporarily hacking js_Dump* calls
  * into other code.
  */
 
@@ -3946,16 +4081,19 @@ js_DumpBacktrace(JSContext *cx)
     fprintf(stdout, "%s", sprinter.string());
 #ifdef XP_WIN32
     if (IsDebuggerPresent()) {
         OutputDebugStringA(sprinter.string());
     }
 #endif
 }
 
+
+/* * */
+
 void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info)
 {
     if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
         info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
 
     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
         js::ObjectElements *elements = as<NativeObject>().getElementsHeader();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -73,16 +73,20 @@ extern const Class MathClass;
 
 class GlobalObject;
 class MapObject;
 class NewObjectCache;
 class NormalArgumentsObject;
 class SetObject;
 class StrictArgumentsObject;
 
+// Forward declarations, required for later friend declarations.
+bool PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
+bool SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded);
+
 }  /* namespace js */
 
 /*
  * A JavaScript object. The members common to all objects are as follows:
  *
  * - The |shape_| member stores the shape of the object, which includes the
  *   object's class and the layout of all its properties.
  *
@@ -108,16 +112,19 @@ class JSObject : public js::gc::Cell
      */
     js::HeapPtrTypeObject type_;
 
   private:
     friend class js::Shape;
     friend class js::GCMarker;
     friend class js::NewObjectCache;
     friend class js::Nursery;
+    friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
+    friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj,
+                                          bool *succeeded);
 
     /* Make the type object to use for LAZY_TYPE objects. */
     static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj);
 
   public:
     js::Shape * lastProperty() const {
         MOZ_ASSERT(shape_);
         return shape_;
@@ -332,17 +339,17 @@ class JSObject : public js::gc::Cell
     /*
      * 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 TypeObject. We offer three ways of getting the prototype:
      *
      * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy.
      * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to
      *    check if the proto is an object, nullptr, or lazily computed.
-     * 3. JSObject::getProto(cx, obj, &proto) computes the proto of an object.
+     * 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.
      */
 
     js::TaggedProto getTaggedProto() const {
         return type_->proto();
     }
 
@@ -380,29 +387,16 @@ class JSObject : public js::gc::Cell
 
     // True iff this object's [[Prototype]] is immutable.  Must not be called
     // on proxies with lazy [[Prototype]]!
     bool nonLazyPrototypeIsImmutable() const {
         MOZ_ASSERT(!hasLazyPrototype());
         return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE);
     }
 
-    // Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently
-    // trying to change it will not work.  If an internal error occurred,
-    // returns false.  Otherwise, |*succeeded| is set to true iff |obj|'s
-    // [[Prototype]] is now immutable.
-    static bool
-    setImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded);
-
-    static inline bool getProto(JSContext *cx, js::HandleObject obj,
-                                js::MutableHandleObject protop);
-    // Returns false on error, success of operation in outparam.
-    static inline bool setProto(JSContext *cx, JS::HandleObject obj,
-                                JS::HandleObject proto, bool *succeeded);
-
     // uninlinedSetType() is the same as setType(), but not inlined.
     inline void setType(js::types::TypeObject *newType);
     void uninlinedSetType(js::types::TypeObject *newType);
 
 #ifdef DEBUG
     bool hasNewType(const js::Class *clasp, js::types::TypeObject *newType);
 #endif
 
@@ -488,36 +482,27 @@ class JSObject : public js::gc::Cell
     static inline bool clearType(JSContext *cx, js::HandleObject obj);
     static bool clearParent(JSContext *cx, js::HandleObject obj);
 
     /*
      * ES5 meta-object properties and operations.
      */
 
   public:
-    static inline bool
-    isExtensible(js::ExclusiveContext *cx, js::HandleObject obj, bool *extensible);
-
     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
     // This method really shouldn't exist -- but there are a few internal
     // places that want it (JITs and the like), and it'd be a pain to mark them
     // all as friends.
     bool nonProxyIsExtensible() const {
         MOZ_ASSERT(!uninlinedIsProxy());
 
         // [[Extensible]] for ordinary non-proxy objects is an object flag.
         return !lastProperty()->hasObjectFlag(js::BaseShape::NOT_EXTENSIBLE);
     }
 
-    // Attempt to change the [[Extensible]] bit on |obj| to false.  Indicate
-    // success or failure through the |*succeeded| outparam, or actual error
-    // through the return value.
-    static bool
-    preventExtensions(JSContext *cx, js::HandleObject obj, bool *succeeded);
-
   private:
     enum ImmutabilityType { SEAL, FREEZE };
 
     /*
      * The guts of Object.seal (ES5 15.2.3.8) and Object.freeze (ES5 15.2.3.9): mark the
      * object as non-extensible, and adjust each property's attributes appropriately: each
      * property becomes non-configurable, and if |freeze|, data properties become
      * read-only as well.
@@ -886,18 +871,81 @@ class ValueArray {
     js::Value *array;
     size_t length;
 
     ValueArray(js::Value *v, size_t c) : array(v), length(c) {}
 };
 
 namespace js {
 
+/*** Standard internal methods ********************************************************************
+ *
+ * The functions below are the fundamental operations on objects.
+ *
+ * ES6 specifies 14 internal methods that define how objects behave.  The spec
+ * is actually quite good on this topic, though you may have to read it a few
+ * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3.
+ *
+ * When 'obj' is an ordinary object, these functions have boring standard
+ * behavior as specified by ES6 draft rev 29 section 9.1; see the section about
+ * internal methods in vm/NativeObject.h.
+ *
+ * Proxies override the behavior of internal methods. So when 'obj' is a proxy,
+ * any one of the functions below could do just about anything. See jsproxy.h.
+ */
+
+/*
+ * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
+ *
+ * If obj is definitely not a proxy, the infallible obj->getProto() can be used
+ * instead. See the comment on JSObject::getTaggedProto().
+ */
+inline bool
+GetPrototype(JSContext *cx, HandleObject obj, MutableHandleObject protop);
+
+/*
+ * ES6 [[SetPrototypeOf]]. Change obj's prototype to proto.
+ *
+ * Returns false on error, success of operation in outparam. For example, if
+ * obj is not extensible, its prototype is fixed. js::SetPrototype will return
+ * true, because no exception is thrown for this; but *succeeded will be false.
+ */
+extern bool
+SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, bool *succeeded);
+
+/*
+ * ES6 [[IsExtensible]]. Extensible objects can have new properties defined on
+ * them. Inextensible objects can't, and their [[Prototype]] slot is fixed as
+ * well.
+ */
+inline bool
+IsExtensible(ExclusiveContext *cx, HandleObject obj, bool *extensible);
+
+/*
+ * ES6 [[PreventExtensions]]. Attempt to change the [[Extensible]] bit on |obj|
+ * to false.  Indicate success or failure through the |*succeeded| outparam, or
+ * actual error through the return value.
+ */
+extern bool
+PreventExtensions(JSContext *cx, HandleObject obj, bool *succeeded);
+
+
+/*** SpiderMonkey nonstandard internal methods ***************************************************/
+
+/*
+ * Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently
+ * trying to change it will not work.  If an internal error occurred,
+ * returns false.  Otherwise, |*succeeded| is set to true iff |obj|'s
+ * [[Prototype]] is now immutable.
+ */
+extern bool
+SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded);
+
 /* Set *resultp to tell whether obj has an own property with the given id. */
-bool
+extern bool
 HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp);
 
 template <AllowGC allowGC>
 extern bool
 NonProxyLookupOwnProperty(JSContext *cx, LookupGenericOp lookup,
                           typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                           typename MaybeRooted<jsid, allowGC>::HandleType id,
                           typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -159,111 +159,35 @@ JSObject::clearType(JSContext *cx, js::H
 inline void
 JSObject::setType(js::types::TypeObject *newType)
 {
     MOZ_ASSERT(newType);
     MOZ_ASSERT(!hasSingletonType());
     type_ = newType;
 }
 
-/* static */ inline bool
-JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop)
+inline bool
+js::GetPrototype(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop)
 {
     if (obj->getTaggedProto().isLazy()) {
         MOZ_ASSERT(obj->is<js::ProxyObject>());
         return js::Proxy::getPrototypeOf(cx, obj, protop);
     } else {
         protop.set(obj->getTaggedProto().toObjectOrNull());
         return true;
     }
 }
 
-/* static */ inline bool
-JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded)
+inline bool
+js::IsExtensible(ExclusiveContext *cx, HandleObject obj, bool *extensible)
 {
-    /*
-     * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's
-     * {get,set}PrototypeOf and setImmutablePrototype methods mediate access to
-     * |obj.[[Prototype]]|.  The Proxy subsystem is responsible for responding
-     * to such attempts.
-     */
-    if (obj->hasLazyPrototype()) {
-        MOZ_ASSERT(obj->is<js::ProxyObject>());
-        return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded);
-    }
-
-    /* Disallow mutation of immutable [[Prototype]]s. */
-    if (obj->nonLazyPrototypeIsImmutable()) {
-        *succeeded = false;
-        return true;
-    }
-
-    /*
-     * 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<js::ArrayBufferObject>()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
-                             "incompatible ArrayBuffer");
-        return false;
-    }
-
-    /*
-     * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
-     */
-    if (obj->is<js::TypedObject>()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
-                             "incompatible TypedObject");
-        return false;
-    }
-
-    /*
-     * Explicitly disallow mutating the [[Prototype]] of Location objects
-     * for flash-related security reasons.
-     */
-    if (!strcmp(obj->getClass()->name, "Location")) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
-                             "incompatible Location object");
-        return false;
-    }
-
-    /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
-    bool extensible;
-    if (!JSObject::isExtensible(cx, obj, &extensible))
-        return false;
-    if (!extensible) {
-        *succeeded = false;
-        return true;
-    }
-
-    /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
-    js::RootedObject obj2(cx);
-    for (obj2 = proto; obj2; ) {
-        if (obj2 == obj) {
-            *succeeded = false;
-            return true;
-        }
-
-        if (!JSObject::getProto(cx, obj2, &obj2))
-            return false;
-    }
-
-    JS::Rooted<js::TaggedProto> taggedProto(cx, js::TaggedProto(proto));
-    *succeeded = SetClassAndProto(cx, obj, obj->getClass(), taggedProto);
-    return *succeeded;
-}
-
-/* static */ inline bool
-JSObject::isExtensible(js::ExclusiveContext *cx, js::HandleObject obj, bool *extensible)
-{
-    if (obj->is<js::ProxyObject>()) {
+    if (obj->is<ProxyObject>()) {
         if (!cx->shouldBeJSContext())
             return false;
-        return js::Proxy::isExtensible(cx->asJSContext(), obj, extensible);
+        return Proxy::isExtensible(cx->asJSContext(), obj, extensible);
     }
 
     *extensible = obj->nonProxyIsExtensible();
     return true;
 }
 
 inline bool
 JSObject::isQualifiedVarObj()
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -87,17 +87,17 @@ BaseProxyHandler::set(JSContext *cx, Han
     if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc))
         return false;
 
     // Step 4.
     if (!ownDesc.object()) {
         // The spec calls this variable "parent", but that word has weird
         // connotations in SpiderMonkey, so let's go with "proto".
         RootedObject proto(cx);
-        if (!JSObject::getProto(cx, proxy, &proto))
+        if (!GetPrototype(cx, proxy, &proto))
             return false;
         if (proto)
             return JSObject::setGeneric(cx, proto, receiver, id, vp, strict);
 
         // Change ownDesc to be a complete descriptor for a configurable,
         // writable, enumerable data property. Then fall through to step 5.
         ownDesc.clear();
         ownDesc.setAttributes(JSPROP_ENUMERATE);
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -79,17 +79,17 @@ CrossCompartmentWrapper::delete_(JSConte
 
 bool
 CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper,
                                         MutableHandleObject protop) const
 {
     {
         RootedObject wrapped(cx, wrappedObject(wrapper));
         AutoCompartment call(cx, wrapped);
-        if (!JSObject::getProto(cx, wrapped, protop))
+        if (!GetPrototype(cx, wrapped, protop))
             return false;
         if (protop)
             protop->setDelegate(cx);
     }
 
     return cx->compartment()->wrap(cx, protop);
 }
 
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -112,45 +112,45 @@ DirectProxyHandler::hasInstance(JSContex
     *bp = !!b;
     return true;
 }
 
 bool
 DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::getProto(cx, target, protop);
+    return GetPrototype(cx, target, protop);
 }
 
 bool
 DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::setProto(cx, target, proto, bp);
+    return SetPrototype(cx, target, proto, bp);
 }
 
 bool
 DirectProxyHandler::setImmutablePrototype(JSContext *cx, HandleObject proxy, bool *succeeded) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::setImmutablePrototype(cx, target, succeeded);
+    return SetImmutablePrototype(cx, target, succeeded);
 }
 
 bool
 DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::preventExtensions(cx, target, succeeded);
+    return PreventExtensions(cx, target, succeeded);
 }
 
 bool
 DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::isExtensible(cx, target, extensible);
+    return IsExtensible(cx, target, extensible);
 }
 
 bool
 DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
                                   JSContext *cx) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return ObjectClassIs(target, classValue, cx);
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -82,17 +82,17 @@ js::assertEnteredPolicy(JSContext *cx, J
     MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId->get() == id);
     MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredAction & act);
 }
 #endif
 
 #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall)                   \
     JS_BEGIN_MACRO                                                           \
         RootedObject proto(cx);                                              \
-        if (!JSObject::getProto(cx, proxy, &proto))                          \
+        if (!GetPrototype(cx, proxy, &proto))                                \
             return false;                                                    \
         if (!proto)                                                          \
             return true;                                                     \
         assertSameCompartment(cx, proxy, proto);                             \
         return protoCall;                                                    \
     JS_END_MACRO                                                             \
 
 bool
@@ -365,17 +365,17 @@ Proxy::enumerate(JSContext *cx, HandleOb
         return handler->enumerate(cx, proxy, objp);
     }
 
     AutoIdVector props(cx);
     if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
         return false;
 
     RootedObject proto(cx);
-    if (!JSObject::getProto(cx, proxy, &proto))
+    if (!GetPrototype(cx, proxy, &proto))
         return false;
     if (!proto)
         return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp);
     assertSameCompartment(cx, proxy, proto);
 
     AutoIdVector protoProps(cx);
     return GetPropertyKeys(cx, proto, 0, &protoProps) &&
            AppendUnique(cx, props, protoProps) &&
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -199,17 +199,17 @@ ArrayToIdVector(JSContext *cx, HandleObj
 
         // step iv
         bool isFixed;
         if (!HasOwnProperty(cx, target, id, &isFixed))
             return false;
 
         // step v
         bool extensible;
-        if (!JSObject::isExtensible(cx, target, &extensible))
+        if (!IsExtensible(cx, target, &extensible))
             return false;
         if (!extensible && !isFixed) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
             return false;
         }
 
         // step vi
         if (!props.append(id))
@@ -246,17 +246,17 @@ ArrayToIdVector(JSContext *cx, HandleObj
 
         // step ii
         bool isFixed;
         if (!HasOwnProperty(cx, target, id, &isFixed))
             return false;
 
         // step iii
         bool extensible;
-        if (!JSObject::isExtensible(cx, target, &extensible))
+        if (!IsExtensible(cx, target, &extensible))
             return false;
         if (!extensible && isFixed) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
             return false;
         }
     }
 
     // step n
@@ -340,17 +340,17 @@ ScriptedDirectProxyHandler::preventExten
         return false;
 
     // Step 9.
     bool booleanTrapResult = ToBoolean(trapResult);
 
     // Step 11.
     if (booleanTrapResult) {
         bool extensible;
-        if (!JSObject::isExtensible(cx, target, &extensible))
+        if (!IsExtensible(cx, target, &extensible))
             return false;
         if (extensible) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
             return false;
         }
     }
 
     // Step 12.
@@ -391,17 +391,17 @@ ScriptedDirectProxyHandler::isExtensible
     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
         return false;
 
     // step 8
     bool booleanTrapResult = ToBoolean(trapResult);
 
     // step 10-11
     bool targetResult;
-    if (!JSObject::isExtensible(cx, target, &targetResult))
+    if (!IsExtensible(cx, target, &targetResult))
         return false;
 
     // step 12
     if (targetResult != booleanTrapResult) {
        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
        return false;
     }
 
@@ -418,17 +418,17 @@ ScriptedDirectProxyHandler::getPropertyD
 {
     JS_CHECK_RECURSION(cx, return false);
 
     if (!GetOwnPropertyDescriptor(cx, proxy, id, desc))
         return false;
     if (desc.object())
         return true;
     RootedObject proto(cx);
-    if (!JSObject::getProto(cx, proxy, &proto))
+    if (!GetPrototype(cx, proxy, &proto))
         return false;
     if (!proto) {
         MOZ_ASSERT(!desc.object());
         return true;
     }
     return JS_GetPropertyDescriptorById(cx, proto, id, desc);
 }
 
@@ -493,31 +493,31 @@ ScriptedDirectProxyHandler::getOwnProper
         // substep b
         if (targetDesc.isPermanent()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
             return false;
         }
 
         // substep c-e
         bool extensibleTarget;
-        if (!JSObject::isExtensible(cx, target, &extensibleTarget))
+        if (!IsExtensible(cx, target, &extensibleTarget))
             return false;
         if (!extensibleTarget) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
             return false;
         }
 
         // substep f
         desc.object().set(nullptr);
         return true;
     }
 
     // step 14-15
     bool extensibleTarget;
-    if (!JSObject::isExtensible(cx, target, &extensibleTarget))
+    if (!IsExtensible(cx, target, &extensibleTarget))
         return false;
 
     // step 16-17
     Rooted<PropDesc> resultDesc(cx);
     if (!resultDesc.initialize(cx, trapResult))
         return false;
 
     // step 18
@@ -601,17 +601,17 @@ ScriptedDirectProxyHandler::defineProper
     if (ToBoolean(trapResult)) {
         // step 14-15
         Rooted<PropertyDescriptor> targetDesc(cx);
         if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc))
             return false;
 
         // step 16-17
         bool extensibleTarget;
-        if (!JSObject::isExtensible(cx, target, &extensibleTarget))
+        if (!IsExtensible(cx, target, &extensibleTarget))
             return false;
 
         // step 18-19
         bool settingConfigFalse = desc.isPermanent();
         if (!targetDesc.object()) {
             // step 20a
             if (!extensibleTarget) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
@@ -848,17 +848,17 @@ ScriptedDirectProxyHandler::has(JSContex
 
         if (desc.object()) {
             if (desc.isPermanent()) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
                 return false;
             }
 
             bool extensible;
-            if (!JSObject::isExtensible(cx, target, &extensible))
+            if (!IsExtensible(cx, target, &extensible))
                 return false;
             if (!extensible) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
                 return false;
             }
         }
     }
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6229,17 +6229,17 @@ DebuggerObject_construct(JSContext *cx, 
 
 static bool
 DebuggerObject_getProto(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get proto", args, dbg, refobj);
     RootedObject proto(cx);
     {
         AutoCompartment ac(cx, refobj);
-        if (!JSObject::getProto(cx, refobj, &proto))
+        if (!GetPrototype(cx, refobj, &proto))
             return false;
     }
     RootedValue protov(cx, ObjectOrNullValue(proto));
     if (!dbg->wrapDebuggeeValue(cx, &protov))
         return false;
     args.rval().set(protov);
     return true;
 }
@@ -6703,106 +6703,106 @@ DebuggerObject_deleteProperty(JSContext 
 
     bool succeeded;
     if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
         return false;
     args.rval().setBoolean(succeeded);
     return true;
 }
 
-enum SealHelperOp { Seal, Freeze, PreventExtensions };
+enum SealHelperOp { OpSeal, OpFreeze, OpPreventExtensions };
 
 static bool
 DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op, const char *name)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, name, args, obj);
 
     Maybe<AutoCompartment> ac;
     ac.emplace(cx, obj);
     ErrorCopier ec(ac);
     bool ok;
-    if (op == Seal) {
+    if (op == OpSeal) {
         ok = JSObject::seal(cx, obj);
-    } else if (op == Freeze) {
+    } else if (op == OpFreeze) {
         ok = JSObject::freeze(cx, obj);
     } else {
-        MOZ_ASSERT(op == PreventExtensions);
+        MOZ_ASSERT(op == OpPreventExtensions);
         bool succeeded;
-        ok = JSObject::preventExtensions(cx, obj, &succeeded);
+        ok = PreventExtensions(cx, obj, &succeeded);
         if (!ok)
             return false;
         if (!succeeded) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
             return false;
         }
     }
     if (!ok)
         return false;
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DebuggerObject_seal(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_sealHelper(cx, argc, vp, Seal, "seal");
+    return DebuggerObject_sealHelper(cx, argc, vp, OpSeal, "seal");
 }
 
 static bool
 DebuggerObject_freeze(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_sealHelper(cx, argc, vp, Freeze, "freeze");
+    return DebuggerObject_sealHelper(cx, argc, vp, OpFreeze, "freeze");
 }
 
 static bool
 DebuggerObject_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_sealHelper(cx, argc, vp, PreventExtensions, "preventExtensions");
+    return DebuggerObject_sealHelper(cx, argc, vp, OpPreventExtensions, "preventExtensions");
 }
 
 static bool
 DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op,
                               const char *name)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, name, args, obj);
 
     Maybe<AutoCompartment> ac;
     ac.emplace(cx, obj);
     ErrorCopier ec(ac);
     bool r;
-    if (op == Seal) {
+    if (op == OpSeal) {
         if (!JSObject::isSealed(cx, obj, &r))
             return false;
-    } else if (op == Freeze) {
+    } else if (op == OpFreeze) {
         if (!JSObject::isFrozen(cx, obj, &r))
             return false;
     } else {
-        if (!JSObject::isExtensible(cx, obj, &r))
+        if (!IsExtensible(cx, obj, &r))
             return false;
     }
     args.rval().setBoolean(r);
     return true;
 }
 
 static bool
 DebuggerObject_isSealed(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_isSealedHelper(cx, argc, vp, Seal, "isSealed");
+    return DebuggerObject_isSealedHelper(cx, argc, vp, OpSeal, "isSealed");
 }
 
 static bool
 DebuggerObject_isFrozen(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_isSealedHelper(cx, argc, vp, Freeze, "isFrozen");
+    return DebuggerObject_isSealedHelper(cx, argc, vp, OpFreeze, "isFrozen");
 }
 
 static bool
 DebuggerObject_isExtensible(JSContext *cx, unsigned argc, Value *vp)
 {
-    return DebuggerObject_isSealedHelper(cx, argc, vp, PreventExtensions, "isExtensible");
+    return DebuggerObject_isSealedHelper(cx, argc, vp, OpPreventExtensions, "isExtensible");
 }
 
 enum ApplyOrCallMode { ApplyMode, CallMode };
 
 static bool
 ApplyOrCall(JSContext *cx, unsigned argc, Value *vp, ApplyOrCallMode mode)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "apply", args, dbg, obj);
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -656,17 +656,17 @@ InitArrayElemOperation(JSContext *cx, js
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
 ProcessCallSiteObjOperation(JSContext *cx, RootedObject &cso, RootedObject &raw,
                             RootedValue &rawValue)
 {
     bool extensible;
-    if (!JSObject::isExtensible(cx, cso, &extensible))
+    if (!IsExtensible(cx, cso, &extensible))
         return false;
     if (extensible) {
         JSAtom *name = cx->names().raw;
         if (!JSObject::defineProperty(cx, cso, name->asPropertyName(), rawValue,
                                       nullptr, nullptr, 0))
         {
             return false;
         }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3152,17 +3152,17 @@ CASE(JSOP_MUTATEPROTO)
         RootedObject &newProto = rootObject1;
         rootObject1 = REGS.sp[-1].toObjectOrNull();
 
         RootedObject &obj = rootObject0;
         obj = &REGS.sp[-2].toObject();
         MOZ_ASSERT(obj->is<PlainObject>());
 
         bool succeeded;
-        if (!JSObject::setProto(cx, obj, newProto, &succeeded))
+        if (!SetPrototype(cx, obj, newProto, &succeeded))
             goto error;
         MOZ_ASSERT(succeeded);
     }
 
     REGS.sp--;
 }
 END_CASE(JSOP_MUTATEPROTO)
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -131,31 +131,16 @@ ObjectElements::MakeElementsCopyOnWrite(
     // elements might be copy on write. Handling this is left to the caller.
     MOZ_ASSERT(!header->isCopyOnWrite());
     header->flags |= COPY_ON_WRITE;
 
     header->ownerObject().init(obj);
     return true;
 }
 
-/* static */ bool
-JSObject::setImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeeded)
-{
-    if (obj->hasLazyPrototype()) {
-        if (!cx->shouldBeJSContext())
-            return false;
-        return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
-    }
-
-    if (!obj->setFlag(cx, BaseShape::IMMUTABLE_PROTOTYPE))
-        return false;
-    *succeeded = true;
-    return true;
-}
-
 #ifdef DEBUG
 void
 js::NativeObject::checkShapeConsistency()
 {
     static int throttle = -1;
     if (throttle < 0) {
         if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
             throttle = atoi(var);
@@ -1847,17 +1832,17 @@ SetPropertyByDefining(JSContext *cx, Han
             return false;
     }
 
     // If the property doesn't already exist, check for an inextensible
     // receiver. (According to the specification, this is supposed to be
     // enforced by [[DefineOwnProperty]], but we haven't implemented that yet.)
     if (!existing) {
         bool extensible;
-        if (!JSObject::isExtensible(cx, receiver, &extensible))
+        if (!IsExtensible(cx, receiver, &extensible))
             return false;
         if (!extensible) {
             // Error in strict mode code, warn with extra warnings option,
             // otherwise do nothing.
             if (strict)
                 return receiver->reportNotExtensible(cx);
             if (cx->compartment()->options().extraWarnings(cx))
                 return receiver->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -491,17 +491,17 @@ NativeObject::addProperty(ExclusiveConte
                           uint32_t slot, unsigned attrs,
                           unsigned flags, bool allowDictionary)
 {
     MOZ_ASSERT(!JSID_IS_VOID(id));
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
 
     bool extensible;
-    if (!JSObject::isExtensible(cx, obj, &extensible))
+    if (!IsExtensible(cx, obj, &extensible))
         return nullptr;
     if (!extensible) {
         if (cx->isJSContext())
             obj->reportNotExtensible(cx->asJSContext());
         return nullptr;
     }
 
     ShapeTable::Entry *entry = nullptr;
@@ -730,17 +730,17 @@ NativeObject::putProperty(ExclusiveConte
     RootedShape shape(cx, Shape::search(cx, obj->lastProperty(), id, &entry, true));
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
         bool extensible;
 
-        if (!JSObject::isExtensible(cx, obj, &extensible))
+        if (!IsExtensible(cx, obj, &extensible))
             return nullptr;
 
         if (!extensible) {
             if (cx->isJSContext())
                 obj->reportNotExtensible(cx->asJSContext());
             return nullptr;
         }
 
@@ -1225,48 +1225,16 @@ Shape::setObjectMetadata(JSContext *cx, 
 
     StackBaseShape base(last);
     base.metadata = metadata;
 
     RootedShape lastRoot(cx, last);
     return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
-/* static */ bool
-JSObject::preventExtensions(JSContext *cx, HandleObject obj, bool *succeeded)
-{
-    if (obj->is<ProxyObject>())
-        return js::Proxy::preventExtensions(cx, obj, succeeded);
-
-    if (!obj->nonProxyIsExtensible()) {
-        *succeeded = true;
-        return true;
-    }
-
-    /*
-     * Force lazy properties to be resolved by iterating over the objects' own
-     * properties.
-     */
-    AutoIdVector props(cx);
-    if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
-        return false;
-
-    /*
-     * Convert all dense elements to sparse properties. This will shrink the
-     * initialized length and capacity of the object to zero and ensure that no
-     * new dense elements can be added without calling growElements(), which
-     * checks isExtensible().
-     */
-    if (obj->isNative() && !NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
-        return false;
-
-    *succeeded = true;
-    return obj->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
-}
-
 bool
 JSObject::setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag_,
                   GenerateShape generateShape)
 {
     BaseShape::Flag flag = (BaseShape::Flag) flag_;
 
     if (lastProperty()->getObjectFlags() & flag)
         return true;