Bug 1112778, part 10 - Rename the remaining nonstandard internal methods, such as JSObject::getGenericAttributes -> js::GetPropertyAttributes. r=Waldo.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 18 Dec 2014 13:28:06 -0600
changeset 251154 f1aa2f649c70258d34164242781b6be95c3aa0c8
parent 251153 8330481355e1cb6908038732eae0f5a326950bf0
child 251155 07fd53dba10c0ef681611c89dbcbce4c33158c58
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 10 - Rename the remaining nonstandard internal methods, such as JSObject::getGenericAttributes -> js::GetPropertyAttributes. r=Waldo.
js/public/Class.h
js/src/builtin/Eval.cpp
js/src/builtin/Object.cpp
js/src/builtin/TypedObject.cpp
js/src/jsapi.cpp
js/src/jsfriendapi.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/proxy/DirectProxyHandler.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/NativeObject.h
js/src/vm/ScopeObject.cpp
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -488,17 +488,23 @@ namespace js {
 
 struct Class
 {
     JS_CLASS_MEMBERS(FinalizeOp);
     ClassSpec          spec;
     ClassExtension      ext;
     ObjectOps           ops;
 
-    /* Class is not native and its map is not a scope. */
+    /*
+     * Objects of this class aren't native objects. They don't have Shapes that
+     * describe their properties and layout. Classes using this flag must
+     * provide their own property behavior, either by being proxy classes (do
+     * this) or by overriding all the ObjectOps except getElements, watch,
+     * unwatch, and thisObject (don't do this).
+     */
     static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
 
     bool isNative() const {
         return !(flags & NON_NATIVE);
     }
 
     bool hasPrivate() const {
         return !!(flags & JSCLASS_HAS_PRIVATE);
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -266,17 +266,17 @@ EvalKernel(JSContext *cx, const CallArgs
         if (!ComputeThis(cx, caller))
             return false;
         thisv = caller.thisValue();
     } else {
         MOZ_ASSERT(args.callee().global() == *scopeobj);
         staticLevel = 0;
 
         // Use the global as 'this', modulo outerization.
-        JSObject *thisobj = JSObject::thisObject(cx, scopeobj);
+        JSObject *thisobj = GetThisObject(cx, scopeobj);
         if (!thisobj)
             return false;
         thisv = ObjectValue(*thisobj);
     }
 
     Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
     if (!flatStr)
         return false;
@@ -504,17 +504,17 @@ js::ExecuteInGlobalAndReturnScope(JSCont
         return false;
 
     if (!scope->setQualifiedVarObj(cx))
         return false;
 
     if (!scope->setUnqualifiedVarObj(cx))
         return false;
 
-    JSObject *thisobj = JSObject::thisObject(cx, global);
+    JSObject *thisobj = GetThisObject(cx, global);
     if (!thisobj)
         return false;
 
     RootedValue thisv(cx, ObjectValue(*thisobj));
     RootedValue rval(cx);
     if (!ExecuteKernel(cx, script, *scope, thisv, EXECUTE_GLOBAL,
                        NullFramePtr() /* evalInFrame */, rval.address()))
     {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -329,17 +329,17 @@ JS_BasicObjectToString(JSContext *cx, Ha
         return cx->names().objectString;
     if (obj->is<ArrayObject>())
         return cx->names().objectArray;
     if (obj->is<JSFunction>())
         return cx->names().objectFunction;
     if (obj->is<NumberObject>())
         return cx->names().objectNumber;
 
-    const char *className = JSObject::className(cx, obj);
+    const char *className = GetObjectClassName(cx, obj);
 
     if (strcmp(className, "Window") == 0)
         return cx->names().objectWindow;
 
     StringBuffer sb(cx);
     if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
         !sb.append("]"))
     {
@@ -530,17 +530,17 @@ obj_watch(JSContext *cx, unsigned argc, 
     RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
     if (!callable)
         return false;
 
     RootedId propid(cx);
     if (!ValueToId<CanGC>(cx, args[0], &propid))
         return false;
 
-    if (!JSObject::watch(cx, obj, propid, callable))
+    if (!WatchProperty(cx, obj, propid, callable))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
@@ -557,17 +557,17 @@ obj_unwatch(JSContext *cx, unsigned argc
     RootedId id(cx);
     if (args.length() != 0) {
         if (!ValueToId<CanGC>(cx, args[0], &id))
             return false;
     } else {
         id = JSID_VOID;
     }
 
-    if (!JSObject::unwatch(cx, obj, id))
+    if (!UnwatchProperty(cx, obj, id))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2025,17 +2025,17 @@ TypedObject::obj_getGenericAttributes(JS
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *attrsp = 0;
         return true;
     }
 
-    return JSObject::getGenericAttributes(cx, proto, id, attrsp);
+    return GetPropertyAttributes(cx, proto, id, attrsp);
 }
 
 static bool
 IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
 {
     uint32_t index;
     Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
     switch (typedObj->typeDescr().kind()) {
@@ -2064,17 +2064,17 @@ TypedObject::obj_setGenericAttributes(JS
         return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *attrsp = 0;
         return true;
     }
 
-    return JSObject::setGenericAttributes(cx, proto, id, attrsp);
+    return SetPropertyAttributes(cx, proto, id, attrsp);
 }
 
 bool
 TypedObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1911,17 +1911,17 @@ JS_IdToValue(JSContext *cx, jsid id, Mut
 
 JS_PUBLIC_API(bool)
 JS_DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     MOZ_ASSERT(obj != nullptr);
     MOZ_ASSERT(hint == JSTYPE_VOID || hint == JSTYPE_STRING || hint == JSTYPE_NUMBER);
-    return JSObject::defaultValue(cx, obj, hint, vp);
+    return ToPrimitive(cx, obj, hint, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_PropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     return true;
 }
 
@@ -2961,17 +2961,17 @@ GetPropertyDescriptorById(JSContext *cx,
             desc.setSetter(shape->setter());
             MOZ_ASSERT(desc.value().isUndefined());
             if (shape->hasSlot())
                 desc.value().set(obj2->as<NativeObject>().getSlot(shape->slot()));
         }
     } else {
         if (obj2->is<ProxyObject>())
             return Proxy::getPropertyDescriptor(cx, obj2, id, desc);
-        if (!JSObject::getGenericAttributes(cx, obj2, id, &desc.attributesRef()))
+        if (!GetPropertyAttributes(cx, obj2, id, &desc.attributesRef()))
             return false;
         MOZ_ASSERT(desc.getter() == nullptr);
         MOZ_ASSERT(desc.setter() == nullptr);
         MOZ_ASSERT(desc.value().isUndefined());
     }
     return true;
 }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -263,17 +263,17 @@ JS_FRIEND_API(bool)
 js_ObjectClassIs(JSContext *cx, HandleObject obj, ESClassValue classValue)
 {
     return ObjectClassIs(obj, classValue, cx);
 }
 
 JS_FRIEND_API(const char *)
 js_ObjectClassName(JSContext *cx, HandleObject obj)
 {
-    return JSObject::className(cx, obj);
+    return GetObjectClassName(cx, obj);
 }
 
 JS_FRIEND_API(JS::Zone *)
 js::GetCompartmentZone(JSCompartment *comp)
 {
     return comp->zone();
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1173,17 +1173,17 @@ SuppressDeletedPropertyHelper(JSContext 
                         if (!ValueToId<CanGC>(cx, idv, &id))
                             return false;
                         if (!LookupProperty(cx, proto, id, &obj2, &prop))
                             return false;
                         if (prop) {
                             unsigned attrs;
                             if (obj2->isNative())
                                 attrs = GetShapeAttributes(obj2, prop);
-                            else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
+                            else if (!GetPropertyAttributes(cx, obj2, id, &attrs))
                                 return false;
 
                             if (attrs & JSPROP_ENUMERATE)
                                 continue;
                         }
                     }
 
                     /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -984,22 +984,16 @@ js::DefineProperties(JSContext *cx, Hand
     for (size_t i = 0, len = ids.length(); i < len; i++) {
         if (!DefinePropertyOnObject(cx, obj.as<NativeObject>(), ids[i], descs[i], true, &dummy))
             return false;
     }
 
     return true;
 }
 
-extern bool
-js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
-{
-    return DefineProperties(cx, newborn, props);
-}
-
 js::types::TypeObject*
 JSObject::uninlinedGetType(JSContext *cx)
 {
     return getType(cx);
 }
 
 void
 JSObject::uninlinedSetType(js::types::TypeObject *newType)
@@ -1085,27 +1079,27 @@ js::SetIntegrityLevel(JSContext *cx, Han
         MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
         JS_ALWAYS_TRUE(NativeObject::setLastProperty(cx, nobj, last));
     } else {
         RootedId id(cx);
         for (size_t i = 0; i < keys.length(); i++) {
             id = keys[i];
 
             unsigned attrs;
-            if (!JSObject::getGenericAttributes(cx, obj, id, &attrs))
+            if (!GetPropertyAttributes(cx, obj, id, &attrs))
                 return false;
 
             unsigned new_attrs = GetSealedOrFrozenAttributes(attrs, level);
 
             // If we already have the attributes we need, skip the setAttributes call.
             if ((attrs | new_attrs) == attrs)
                 continue;
 
             attrs |= new_attrs;
-            if (!JSObject::setGenericAttributes(cx, obj, id, &attrs))
+            if (!SetPropertyAttributes(cx, obj, id, &attrs))
                 return false;
         }
     }
 
     // Ordinarily ArraySetLength handles this, but we're going behind its back
     // right now, so we must do this manually.  Neither the custom property
     // tree mutations nor the setGenericAttributes call in the above code will
     // do this for us.
@@ -1155,17 +1149,17 @@ js::TestIntegrityLevel(JSContext *cx, Ha
 
     // Steps 9-11. The spec does not permit stopping as soon as we find out the
     // answer is false, so we are cheating a little here (bug 1120512).
     RootedId id(cx);
     for (size_t i = 0, len = props.length(); i < len; i++) {
         id = props[i];
 
         unsigned attrs;
-        if (!JSObject::getGenericAttributes(cx, obj, id, &attrs))
+        if (!GetPropertyAttributes(cx, obj, id, &attrs))
             return false;
 
         // If the property is configurable, this object is neither sealed nor
         // frozen. If the property is a writable data property, this object is
         // not frozen.
         if (!(attrs & JSPROP_PERMANENT) ||
             (level == IntegrityLevel::Frozen &&
              !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
@@ -1176,27 +1170,17 @@ js::TestIntegrityLevel(JSContext *cx, Ha
     }
 
     // All properties checked out. This object is sealed/frozen.
     *result = true;
     return true;
 }
 
 
-/* static */
-const char *
-JSObject::className(JSContext *cx, HandleObject obj)
-{
-    assertSameCompartment(cx, obj);
-
-    if (obj->is<ProxyObject>())
-        return Proxy::className(cx, obj);
-
-    return obj->getClass()->name;
-}
+/* * */
 
 /*
  * Get the GC kind to use for scripted 'new' on the given class.
  * FIXME bug 547327: estimate the size from the allocation site.
  */
 static inline gc::AllocKind
 NewObjectGCKind(const js::Class *clasp)
 {
@@ -3220,19 +3204,16 @@ js::PreventExtensions(JSContext *cx, Han
      */
     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::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
     if (obj->is<ProxyObject>())
         return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc);
 
     RootedObject pobj(cx);
@@ -3258,17 +3239,17 @@ js::GetOwnPropertyDescriptor(JSContext *
         } else {
             // This is either a straight-up data property or (rarely) a
             // property with a JSPropertyOp getter/setter. The latter must be
             // reported to the caller as a plain data property, so don't
             // populate desc.getter/setter, and mask away the SHARED bit.
             desc.attributesRef() &= ~JSPROP_SHARED;
         }
     } else {
-        if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef()))
+        if (!GetPropertyAttributes(cx, pobj, id, &desc.attributesRef()))
             return false;
     }
 
     RootedValue value(cx);
     if (doGet && !GetProperty(cx, obj, obj, id, &value))
         return false;
 
     desc.value().set(value);
@@ -3313,55 +3294,47 @@ js::DefineElement(ExclusiveContext *cx, 
     if (op) {
         if (!cx->shouldBeJSContext())
             return false;
         return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
     }
     return NativeDefineElement(cx, obj.as<NativeObject>(), index, value, getter, setter, attrs);
 }
 
+
+/*** 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)
+bool
+js::ToPrimitive(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
 {
-    JSConvertOp op = obj->getClass()->convert;
     bool ok;
-    if (!op)
+    if (JSConvertOp op = obj->getClass()->convert)
+        ok = op(cx, obj, hint, vp);
+    else
         ok = JS::OrdinaryToPrimitive(cx, obj, hint, vp);
-    else
-        ok = op(cx, obj, hint, vp);
     MOZ_ASSERT_IF(ok, vp.isPrimitive());
     return ok;
 }
 
 bool
-JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
-{
-    RootedValue fval(cx);
-    RootedObject obj(cx, this);
-    if (!GetProperty(cx, obj, obj, id, &fval))
-        return false;
-    return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
-}
-
-bool
 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
 {
     RootedObject obj(cx, GetInnerObject(origObj));
     if (obj->isNative()) {
         // Use sparse indexes for watched objects, as dense elements can be
         // written to without checking the watchpoint map.
         if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
             return false;
@@ -3378,44 +3351,71 @@ js::WatchGuts(JSContext *cx, JS::HandleO
         }
         cx->compartment()->watchpointMap = wpmap;
     }
 
     return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
 }
 
 bool
-js::NativeWatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
-{
-    if (!obj->isNative() || IsAnyTypedArray(obj)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
-                             obj->getClass()->name);
-        return false;
-    }
-
-    return WatchGuts(cx, obj, id, callable);
-}
-
-bool
 js::UnwatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id)
 {
     // Looking in the map for an unsupported object will never hit, so we don't
     // need to check for nativeness or watchable-ness here.
     RootedObject obj(cx, GetInnerObject(origObj));
     if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
         wpmap->unwatch(obj, id, nullptr, nullptr);
     return true;
 }
 
 bool
-js::NativeUnwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
+js::WatchProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
+{
+    if (WatchOp op = obj->getOps()->watch)
+        return op(cx, obj, id, callable);
+
+    if (!obj->isNative() || IsAnyTypedArray(obj)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
+                             obj->getClass()->name);
+        return false;
+    }
+
+    return WatchGuts(cx, obj, id, callable);
+}
+
+bool
+js::UnwatchProperty(JSContext *cx, HandleObject obj, HandleId id)
 {
+    if (UnwatchOp op = obj->getOps()->unwatch)
+        return op(cx, obj, id);
+
     return UnwatchGuts(cx, obj, id);
 }
 
+const char *
+js::GetObjectClassName(JSContext *cx, HandleObject obj)
+{
+    assertSameCompartment(cx, obj);
+
+    if (obj->is<ProxyObject>())
+        return Proxy::className(cx, obj);
+
+    return obj->getClass()->name;
+}
+
+bool
+JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
+{
+    RootedValue fval(cx);
+    RootedObject obj(cx, this);
+    if (!GetProperty(cx, obj, obj, id, &fval))
+        return false;
+    return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
+}
+
 
 /* * */
 
 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));
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -493,19 +493,16 @@ class JSObject : public js::gc::Cell
     // 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);
     }
 
-    /* toString support. */
-    static const char *className(JSContext *cx, js::HandleObject obj);
-
   public:
     /*
      * Iterator-specific getters and setters.
      */
 
     static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
 
     /*
@@ -534,38 +531,16 @@ class JSObject : public js::gc::Cell
 
     static bool nonNativeSetProperty(JSContext *cx, js::HandleObject obj,
                                      js::HandleObject receiver, js::HandleId id,
                                      js::MutableHandleValue vp, bool strict);
     static bool nonNativeSetElement(JSContext *cx, js::HandleObject obj,
                                     js::HandleObject receiver, uint32_t index,
                                     js::MutableHandleValue vp, bool strict);
 
-    static inline bool getGenericAttributes(JSContext *cx, js::HandleObject obj,
-                                            js::HandleId id, unsigned *attrsp);
-
-    static inline bool setGenericAttributes(JSContext *cx, js::HandleObject obj,
-                                            js::HandleId id, unsigned *attrsp);
-
-    static inline bool watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                             JS::HandleObject callable);
-    static inline bool unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
-
-    static bool defaultValue(JSContext *cx, js::HandleObject obj, JSType hint,
-                             js::MutableHandleValue vp);
-
-    static JSObject *thisObject(JSContext *cx, js::HandleObject obj)
-    {
-        if (js::ObjectOp op = obj->getOps()->thisObject)
-            return op(cx, obj);
-        return obj;
-    }
-
-    static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp);
-
     static bool swap(JSContext *cx, JS::HandleObject a, JS::HandleObject b);
 
   private:
     void fixDictionaryShapeAfterSwap();
 
   public:
     inline void initArrayClass();
 
@@ -725,34 +700,16 @@ IsCallable(const Value &v)
 
 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
 inline bool
 IsConstructor(const Value &v)
 {
     return v.isObject() && v.toObject().isConstructor();
 }
 
-inline JSObject *
-GetInnerObject(JSObject *obj)
-{
-    if (js::InnerObjectOp op = obj->getClass()->ext.innerObject) {
-        JS::AutoSuppressGCAnalysis nogc;
-        return op(obj);
-    }
-    return obj;
-}
-
-inline JSObject *
-GetOuterObject(JSContext *cx, js::HandleObject obj)
-{
-    if (js::ObjectOp op = obj->getClass()->ext.outerObject)
-        return op(cx, obj);
-    return obj;
-}
-
 } /* namespace js */
 
 class JSValueArray {
   public:
     const jsval *array;
     size_t length;
 
     JSValueArray(const jsval *v, size_t c) : array(v), length(c) {}
@@ -990,16 +947,138 @@ HasOwnProperty(JSContext *cx, HandleObje
 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,
                           typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp);
 
+/*
+ * Deprecated. An easier-to-use version of LookupProperty that returns only the
+ * property attributes.
+ */
+inline bool
+GetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp);
+
+/*
+ * Deprecated. Search the prototype chain for `obj[id]` and redefine it to have
+ * the given property attributes.
+ */
+inline bool
+SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp);
+
+/*
+ * Set a watchpoint: a synchronous callback when the given property of the
+ * given object is set.
+ *
+ * Watchpoints are nonstandard and do not fit in well with the way ES6
+ * specifies [[Set]]. They are also insufficient for implementing
+ * Object.observe.
+ */
+extern bool
+WatchProperty(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable);
+
+/* Clear a watchpoint. */
+extern bool
+UnwatchProperty(JSContext *cx, HandleObject obj, HandleId id);
+
+/*
+ * ToPrimitive support, currently implemented like an internal method (JSClass::convert).
+ * In ES6 this is just a method, @@toPrimitive. See bug 1054756.
+ */
+extern bool
+ToPrimitive(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
+
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext *cx, MutableHandleValue vp);
+
+MOZ_ALWAYS_INLINE bool
+ToPrimitive(JSContext *cx, JSType preferredType, MutableHandleValue vp);
+
+/*
+ * toString support. (This isn't called GetClassName because there's a macro in
+ * <windows.h> with that name.)
+ */
+extern const char *
+GetObjectClassName(JSContext *cx, HandleObject obj);
+
+/*
+ * Inner and outer objects
+ *
+ * GetInnerObject and GetOuterObject (and also GetThisObject, somewhat) have to
+ * do with Windows and WindowProxies. There's a screwy invariant that actual
+ * Window objects (the global objects of web pages) are never directly exposed
+ * to script. Instead we often substitute a WindowProxy.
+ *
+ * As a result, we have calls to these three "substitute-this-object-for-that-
+ * object" functions sprinkled at apparently arbitrary (but actually *very*
+ * carefully and nervously selected) places throughout the engine and indeed
+ * the universe.
+ */
+
+/*
+ * If obj a WindowProxy, return its current inner Window. Otherwise return obj.
+ *
+ * GetInnerObject is called when we need a scope chain; you never want a
+ * WindowProxy on a scope chain.
+ *
+ * It's also called in a few places where an object comes in from script, and
+ * the user probably intends to operate on the Window, not the
+ * WindowProxy. Object.prototype.watch and various Debugger features do
+ * this. (Users can't simply pass the Window, because the Window isn't exposed
+ * to scripts.)
+ */
+inline JSObject *
+GetInnerObject(JSObject *obj)
+{
+    if (InnerObjectOp op = obj->getClass()->ext.innerObject) {
+        JS::AutoSuppressGCAnalysis nogc;
+        return op(obj);
+    }
+    return obj;
+}
+
+/*
+ * If obj is a Window object, return the WindowProxy. Otherwise return obj.
+ *
+ * This must be called before passing an object to script, if the object might
+ * be a Window. (But usually those cases involve scope objects, and for those,
+ * it is better to call GetThisObject instead.)
+ */
+inline JSObject *
+GetOuterObject(JSContext *cx, HandleObject obj)
+{
+    if (ObjectOp op = obj->getClass()->ext.outerObject)
+        return op(cx, obj);
+    return obj;
+}
+
+/*
+ * Return an object that may be used as `this` in place of obj. For most
+ * objects this just returns obj.
+ *
+ * Some JSObjects shouldn't be exposed directly to script. This includes (at
+ * least) DynamicWithObjects and Window objects. However, since both of those
+ * can be on scope chains, we sometimes would expose those as `this` if we
+ * were not so vigilant about calling GetThisObject where appropriate.
+ *
+ * See comments at ComputeImplicitThis.
+ */
+inline JSObject *
+GetThisObject(JSContext *cx, HandleObject obj)
+{
+    if (ObjectOp op = obj->getOps()->thisObject)
+        return op(cx, obj);
+    return obj;
+}
+
+
+/* * */
+
 typedef JSObject *(*ClassInitializerOp)(JSContext *cx, JS::HandleObject obj);
 
 /* Fast access to builtin constructors and prototypes. */
 bool
 GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp);
 
 bool
 GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp);
@@ -1036,20 +1115,16 @@ extern const char js_propertyIsEnumerabl
 
 #ifdef JS_OLD_GETTER_SETTER_METHODS
 extern const char js_defineGetter_str[];
 extern const char js_defineSetter_str[];
 extern const char js_lookupGetter_str[];
 extern const char js_lookupSetter_str[];
 #endif
 
-extern bool
-js_PopulateObject(JSContext *cx, js::HandleObject newborn, js::HandleObject props);
-
-
 namespace js {
 
 /*
  * The NewObjectKind allows an allocation site to specify the type properties
  * and lifetime requirements that must be fixed at allocation time.
  */
 enum NewObjectKind {
     /* This is the default. Most objects are generic. */
@@ -1227,22 +1302,16 @@ GetFirstArgumentAsObject(JSContext *cx, 
 
 /* Helpers for throwing. These always return false. */
 extern bool
 Throw(JSContext *cx, jsid id, unsigned errorNumber);
 
 extern bool
 Throw(JSContext *cx, JSObject *obj, unsigned errorNumber);
 
-extern bool
-NativeWatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
-
-extern bool
-NativeUnwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
-
 enum class IntegrityLevel {
     Sealed,
     Frozen
 };
 
 /*
  * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
  * of obj's own properties' attributes appropriately: each property becomes
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -19,42 +19,16 @@
 #include "vm/StringObject.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 
-/* static */ inline bool
-JSObject::setGenericAttributes(JSContext *cx, js::HandleObject obj,
-                               js::HandleId id, unsigned *attrsp)
-{
-    js::types::MarkTypePropertyNonData(cx, obj, id);
-    js::GenericAttributesOp op = obj->getOps()->setGenericAttributes;
-    if (op)
-        return op(cx, obj, id, attrsp);
-    return js::NativeSetPropertyAttributes(cx, obj.as<js::NativeObject>(), id, attrsp);
-}
-
-/* static */ inline bool
-JSObject::watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
-                JS::HandleObject callable)
-{
-    js::WatchOp op = obj->getOps()->watch;
-    return (op ? op : js::NativeWatch)(cx, obj, id, callable);
-}
-
-/* static */ inline bool
-JSObject::unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
-{
-    js::UnwatchOp op = obj->getOps()->unwatch;
-    return (op ? op : js::NativeUnwatch)(cx, obj, id);
-}
-
 inline void
 JSObject::finalize(js::FreeOp *fop)
 {
     js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
@@ -233,16 +207,26 @@ js::DeleteElement(JSContext *cx, HandleO
         return false;
     return DeleteProperty(cx, obj, id, succeeded);
 }
 
 
 /* * */
 
 inline bool
+js::SetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
+{
+    types::MarkTypePropertyNonData(cx, obj, id);
+    GenericAttributesOp op = obj->getOps()->setGenericAttributes;
+    if (op)
+        return op(cx, obj, id, attrsp);
+    return NativeSetPropertyAttributes(cx, obj.as<NativeObject>(), id, attrsp);
+}
+
+inline bool
 JSObject::isQualifiedVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isQualifiedVarObj();
     return lastProperty()->hasObjectFlag(js::BaseShape::QUALIFIED_VAROBJ);
 }
 
 inline bool
@@ -448,17 +432,17 @@ HasObjectValueOf(JSObject *obj, JSContex
         if (!obj || obj->is<ProxyObject>() || !obj->isNative())
             return false;
     }
 
     return IsNativeFunction(v, obj_valueOf);
 }
 
 /* ES5 9.1 ToPrimitive(input). */
-static MOZ_ALWAYS_INLINE bool
+MOZ_ALWAYS_INLINE bool
 ToPrimitive(JSContext *cx, MutableHandleValue vp)
 {
     if (vp.isPrimitive())
         return true;
 
     JSObject *obj = &vp.toObject();
 
     /* Optimize new String(...).valueOf(). */
@@ -477,28 +461,28 @@ ToPrimitive(JSContext *cx, MutableHandle
         NumberObject *nobj = &obj->as<NumberObject>();
         if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, js_num_valueOf)) {
             vp.setNumber(nobj->unbox());
             return true;
         }
     }
 
     RootedObject objRoot(cx, obj);
-    return JSObject::defaultValue(cx, objRoot, JSTYPE_VOID, vp);
+    return ToPrimitive(cx, objRoot, JSTYPE_VOID, vp);
 }
 
 /* ES5 9.1 ToPrimitive(input, PreferredType). */
-static MOZ_ALWAYS_INLINE bool
+MOZ_ALWAYS_INLINE bool
 ToPrimitive(JSContext *cx, JSType preferredType, MutableHandleValue vp)
 {
     MOZ_ASSERT(preferredType != JSTYPE_VOID); /* Use the other ToPrimitive! */
     if (vp.isPrimitive())
         return true;
     RootedObject obj(cx, &vp.toObject());
-    return JSObject::defaultValue(cx, obj, preferredType, vp);
+    return ToPrimitive(cx, obj, preferredType, vp);
 }
 
 /*
  * Return true if this is a compiler-created internal function accessed by
  * its own object. Such a function object must not be accessible to script
  * or embedding code.
  */
 inline bool
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -156,17 +156,17 @@ DirectProxyHandler::objectClassIs(Handle
     return ObjectClassIs(target, classValue, cx);
 }
 
 const char *
 DirectProxyHandler::className(JSContext *cx, HandleObject proxy) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JSObject::className(cx, target);
+    return GetObjectClassName(cx, target);
 }
 
 JSString *
 DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy,
                                  unsigned indent) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6043,17 +6043,17 @@ DebuggerGenericEval(JSContext *cx, const
         if (!env)
             return false;
     } else {
         /*
          * Use the global as 'this'. If the global is an inner object, it
          * should have a thisObject hook that returns the appropriate outer
          * object.
          */
-        JSObject *thisObj = JSObject::thisObject(cx, scope);
+        JSObject *thisObj = GetThisObject(cx, scope);
         if (!thisObj)
             return false;
         thisv = ObjectValue(*thisObj);
         env = scope;
     }
 
     /* If evalWithBindings, create the inner environment. */
     if (evalWithBindings) {
@@ -6246,17 +6246,17 @@ DebuggerObject_getProto(JSContext *cx, u
 
 static bool
 DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
     const char *className;
     {
         AutoCompartment ac(cx, refobj);
-        className = JSObject::className(cx, refobj);
+        className = GetObjectClassName(cx, refobj);
     }
     JSAtom *str = Atomize(cx, className, strlen(className));
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -346,17 +346,17 @@ DefVarOrConstOperation(JSContext *cx, Ha
             return false;
     } else if (attrs & JSPROP_READONLY) {
         /*
          * Extension: ordinarily we'd be done here -- but for |const|.  If we
          * see a redeclaration that's |const|, we consider it a conflict.
          */
         unsigned oldAttrs;
         RootedId id(cx, NameToId(dn));
-        if (!JSObject::getGenericAttributes(cx, varobj, id, &oldAttrs))
+        if (!GetPropertyAttributes(cx, varobj, id, &oldAttrs))
             return false;
 
         JSAutoByteString bytes;
         if (AtomToPrintableString(cx, dn, &bytes)) {
             JS_ALWAYS_FALSE(JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
                                                          js_GetErrorMessage,
                                                          nullptr, JSMSG_REDECLARED_VAR,
                                                          (oldAttrs & JSPROP_READONLY)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -83,17 +83,17 @@ js::BoxNonStrictThis(JSContext *cx, Hand
     /*
      * Check for SynthesizeFrame poisoning and fast constructors which
      * didn't check their callee properly.
      */
     MOZ_ASSERT(!thisv.isMagic());
 
     if (thisv.isNullOrUndefined()) {
         Rooted<GlobalObject*> global(cx, cx->global());
-        return JSObject::thisObject(cx, global);
+        return GetThisObject(cx, global);
     }
 
     if (thisv.isObject())
         return &thisv.toObject();
 
     return PrimitiveToObject(cx, thisv);
 }
 
@@ -539,17 +539,17 @@ js::Invoke(JSContext *cx, const Value &t
          * |this| already.  But don't do that if fval is a DOM function.
          */
         if (!fval.isObject() || !fval.toObject().is<JSFunction>() ||
             !fval.toObject().as<JSFunction>().isNative() ||
             !fval.toObject().as<JSFunction>().jitInfo() ||
             fval.toObject().as<JSFunction>().jitInfo()->needsOuterizedThisObject())
         {
             RootedObject thisObj(cx, &args.thisv().toObject());
-            JSObject *thisp = JSObject::thisObject(cx, thisObj);
+            JSObject *thisp = GetThisObject(cx, thisObj);
             if (!thisp)
                 return false;
             args.setThis(ObjectValue(*thisp));
         }
     }
 
     if (!Invoke(cx, args))
         return false;
@@ -680,17 +680,17 @@ js::Execute(JSContext *cx, HandleScript 
 
     /* The VAROBJFIX option makes varObj == globalObj in global code. */
     if (!cx->runtime()->options().varObjFix()) {
         if (!scopeChain->setQualifiedVarObj(cx))
             return false;
     }
 
     /* Use the scope chain as 'this', modulo outerization. */
-    JSObject *thisObj = JSObject::thisObject(cx, scopeChain);
+    JSObject *thisObj = GetThisObject(cx, scopeChain);
     if (!thisObj)
         return false;
     Value thisv = ObjectValue(*thisObj);
 
     return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
                          NullFramePtr() /* evalInFrame */, rval);
 }
 
@@ -1175,26 +1175,27 @@ JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSO
 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
 
 /*
  * Compute the implicit |this| parameter for a call expression where the callee
  * funval was resolved from an unqualified name reference to a property on obj
  * (an object on the scope chain).
  *
  * We can avoid computing |this| eagerly and push the implicit callee-coerced
- * |this| value, undefined, if any of these conditions hold:
+ * |this| value, undefined, if either of these conditions hold:
  *
  * 1. The nominal |this|, obj, is a global object.
  *
  * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
  *    is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
  *    censored with undefined.
  *
- * Otherwise, we bind |this| to obj->thisObject(). Only names inside |with|
- * statements and embedding-specific scope objects fall into this category.
+ * Otherwise, we bind |this| to GetThisObject(cx, obj). Only names inside
+ * |with| statements and embedding-specific scope objects fall into this
+ * category.
  *
  * If the callee is a strict mode function, then code implementing JSOP_THIS
  * in the interpreter and JITs will leave undefined as |this|. If funval is a
  * function not in strict mode, JSOP_THIS code replaces undefined with funval's
  * global.
  *
  * We set *vp to undefined early to reduce code size and bias this code for the
  * common and future-friendly cases.
@@ -1205,17 +1206,17 @@ ComputeImplicitThis(JSContext *cx, Handl
     vp.setUndefined();
 
     if (obj->is<GlobalObject>())
         return true;
 
     if (IsCacheableNonGlobalScope(obj))
         return true;
 
-    JSObject *nobj = JSObject::thisObject(cx, obj);
+    JSObject *nobj = GetThisObject(cx, obj);
     if (!nobj)
         return false;
 
     vp.setObject(*nobj);
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2138,17 +2138,17 @@ js::NativeGetPropertyAttributes(JSContex
     RootedShape shape(cx);
     if (!NativeLookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
         return false;
     if (!shape) {
         *attrsp = 0;
         return true;
     }
     if (!nobj->isNative())
-        return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
+        return GetPropertyAttributes(cx, nobj, id, attrsp);
 
     *attrsp = GetShapeAttributes(nobj, shape);
     return true;
 }
 
 bool
 js::NativeSetPropertyAttributes(JSContext *cx, HandleNativeObject obj, HandleId id,
                                 unsigned *attrsp)
@@ -2172,17 +2172,17 @@ js::NativeSetPropertyAttributes(JSContex
     }
     if (nobj->isNative()) {
         if (!NativeObject::changePropertyAttributes(cx, nobj.as<NativeObject>(), shape, *attrsp))
             return false;
         if (*attrsp & JSPROP_READONLY)
             types::MarkTypePropertyNonWritable(cx, nobj, id);
         return true;
     } else {
-        return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
+        return SetPropertyAttributes(cx, nobj, id, attrsp);
     }
 }
 
 bool
 js::NativeDeleteProperty(JSContext *cx, HandleNativeObject obj, HandleId id, bool *succeeded)
 {
     RootedObject proto(cx);
     RootedShape shape(cx);
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1392,19 +1392,17 @@ inline bool
 js::SetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
                MutableHandleValue vp, bool strict)
 {
     if (obj->getOps()->setElement)
         return JSObject::nonNativeSetElement(cx, obj, receiver, index, vp, strict);
     return NativeSetElement(cx, obj.as<NativeObject>(), receiver, index, vp, strict);
 }
 
-/* static */ inline bool
-JSObject::getGenericAttributes(JSContext *cx, js::HandleObject obj,
-                               js::HandleId id, unsigned *attrsp)
+inline bool
+js::GetPropertyAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
-    js::GenericAttributesOp op = obj->getOps()->getGenericAttributes;
-    if (op)
+    if (GenericAttributesOp op = obj->getOps()->getGenericAttributes)
         return op(cx, obj, id, attrsp);
-    return NativeGetPropertyAttributes(cx, obj.as<js::NativeObject>(), id, attrsp);
+    return NativeGetPropertyAttributes(cx, obj.as<NativeObject>(), id, attrsp);
 }
 
 #endif /* vm_NativeObject_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -443,17 +443,17 @@ DynamicWithObject::create(JSContext *cx,
     if (!shape)
         return nullptr;
 
     RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
                                                                   gc::DefaultHeap, shape, type)));
     if (!obj)
         return nullptr;
 
-    JSObject *thisp = JSObject::thisObject(cx, object);
+    JSObject *thisp = GetThisObject(cx, object);
     if (!thisp)
         return nullptr;
 
     obj->as<ScopeObject>().setEnclosingScope(enclosing);
     obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
     obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
     obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
 
@@ -561,24 +561,24 @@ with_SetElement(JSContext *cx, HandleObj
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return SetElement(cx, actual, actual, index, vp, strict);
 }
 
 static bool
 with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
-    return JSObject::getGenericAttributes(cx, actual, id, attrsp);
+    return GetPropertyAttributes(cx, actual, id, attrsp);
 }
 
 static bool
 with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
-    return JSObject::setGenericAttributes(cx, actual, id, attrsp);
+    return SetPropertyAttributes(cx, actual, id, attrsp);
 }
 
 static bool
 with_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return DeleteProperty(cx, actual, id, succeeded);
 }