Bug 638054 - Remove watch class-hook and proxy trap r=jorendorff,bz
authorTom Schuster <evilpies@gmail.com>
Tue, 24 Oct 2017 20:48:14 +0200
changeset 387883 3f8f9e5f28583ef7bb1e46647540da4309155e16
parent 387882 a79c43e48bacf3e0e7f7a3790a0ed115dd3d4c1d
child 387884 e4f864ad5779d943ce2e562764c183b1a100bba3
push id32738
push userarchaeopteryx@coole-files.de
push dateTue, 24 Oct 2017 21:58:00 +0000
treeherdermozilla-central@a124f4901430 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff, bz
bugs638054
milestone58.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 638054 - Remove watch class-hook and proxy trap r=jorendorff,bz
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/DOMJSProxyHandler.cpp
dom/bindings/DOMJSProxyHandler.h
js/public/Class.h
js/public/Proxy.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/proxy/BaseProxyHandler.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
js/src/vm/EnvironmentObject.cpp
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1120,21 +1120,16 @@ public:
 
   bool isCallable(JSObject *obj) const override {
     return false;
   }
   bool isConstructor(JSObject *obj) const override {
     return false;
   }
 
-  bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
-             JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const override;
-  bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
-               JS::Handle<jsid> id) const override;
-
   static const nsOuterWindowProxy singleton;
 
 protected:
   static nsGlobalWindow* GetOuterWindow(JSObject *proxy)
   {
     nsGlobalWindow* outerWindow = nsGlobalWindow::FromSupports(
       static_cast<nsISupports*>(js::GetProxyReservedSlot(proxy, 0).toPrivate()));
     MOZ_ASSERT_IF(outerWindow, outerWindow->IsOuterWindow());
@@ -1473,30 +1468,16 @@ nsOuterWindowProxy::AppendIndexedPropert
     if (!props.append(INT_TO_JSID(i))) {
       return false;
     }
   }
 
   return true;
 }
 
-bool
-nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy,
-                          JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const
-{
-  return js::WatchGuts(cx, proxy, id, callable);
-}
-
-bool
-nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
-                            JS::Handle<jsid> id) const
-{
-  return js::UnwatchGuts(cx, proxy, id);
-}
-
 size_t
 nsOuterWindowProxy::objectMoved(JSObject *obj, JSObject *old) const
 {
   nsGlobalWindow* outerWindow = GetOuterWindow(obj);
   if (outerWindow) {
     outerWindow->UpdateWrapper(obj, old);
   }
   return 0;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2062,18 +2062,16 @@ const js::ClassOps sBoringInterfaceObjec
 const js::ObjectOps sInterfaceObjectClassObjectOps = {
   nullptr, /* lookupProperty */
   nullptr, /* defineProperty */
   nullptr, /* hasProperty */
   nullptr, /* getProperty */
   nullptr, /* setProperty */
   nullptr, /* getOwnPropertyDescriptor */
   nullptr, /* deleteProperty */
-  nullptr, /* watch */
-  nullptr, /* unwatch */
   nullptr, /* getElements */
   InterfaceObjectToString, /* funToString */
 };
 
 bool
 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
                        bool* found, JS::MutableHandle<JS::Value> vp)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13551,19 +13551,19 @@ class CGDictionary(CGThing):
         else:
             # The data is inside the Optional<>
             memberData = "%s.InternalValue()" % memberLoc
 
         # If you have to change this list (which you shouldn't!), make sure it
         # continues to match the list in test_Object.prototype_props.html
         if (member.identifier.name in
             ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
-             "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
-             "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
-             "__lookupGetter__", "__lookupSetter__", "__proto__"]):
+             "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
+             "__defineGetter__", "__defineSetter__", "__lookupGetter__",
+             "__lookupSetter__", "__proto__"]):
             raise TypeError("'%s' member of %s dictionary shadows "
                             "a property of Object.prototype, and Xrays to "
                             "Object can't handle that.\n"
                             "%s" %
                             (member.identifier.name,
                              self.dictionary.identifier.name,
                              member.location))
 
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -229,29 +229,16 @@ DOMProxyHandler::delete_(JSContext* cx, 
   if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
     return JS_DeletePropertyById(cx, expando, id, result);
   }
 
   return result.succeed();
 }
 
 bool
-BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-                           JS::Handle<JSObject*> callable) const
-{
-  return js::WatchGuts(cx, proxy, id, callable);
-}
-
-bool
-BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const
-{
-  return js::UnwatchGuts(cx, proxy, id);
-}
-
-bool
 BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx,
                                      JS::Handle<JSObject*> proxy,
                                      JS::AutoIdVector& props) const
 {
   return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
 }
 
 bool
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -61,21 +61,16 @@ public:
 
   // We override getOwnEnumerablePropertyKeys() and implement it directly
   // instead of using the default implementation, which would call
   // ownPropertyKeys and then filter out the non-enumerable ones. This avoids
   // unnecessary work during enumeration.
   virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
                                             JS::AutoIdVector &props) const override;
 
-  bool watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-             JS::Handle<JSObject*> callable) const override;
-  bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy,
-               JS::Handle<jsid> id) const override;
-
 protected:
   // Hook for subclasses to implement shared ownPropertyKeys()/keys()
   // functionality.  The "flags" argument is either JSITER_OWNONLY (for keys())
   // or JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS (for
   // ownPropertyKeys()).
   virtual bool ownPropNames(JSContext* cx, JS::Handle<JSObject*> proxy,
                             unsigned flags,
                             JS::AutoIdVector& props) const = 0;
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -532,22 +532,16 @@ typedef bool
                   JS::HandleValue receiver, JS::ObjectOpResult& result);
 typedef bool
 (* GetOwnPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::MutableHandle<JS::PropertyDescriptor> desc);
 typedef bool
 (* DeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::ObjectOpResult& result);
 
-typedef bool
-(* WatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
-
-typedef bool
-(* UnwatchOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
-
 class JS_FRIEND_API(ElementAdder)
 {
   public:
     enum GetBehavior {
         // Check if the element exists before performing the Get and preserve
         // holes.
         CheckHasElemPreserveHoles,
 
@@ -736,18 +730,16 @@ struct JS_STATIC_CLASS ObjectOps
 {
     LookupPropertyOp lookupProperty;
     DefinePropertyOp defineProperty;
     HasPropertyOp    hasProperty;
     GetPropertyOp    getProperty;
     SetPropertyOp    setProperty;
     GetOwnPropertyOp getOwnPropertyDescriptor;
     DeletePropertyOp deleteProperty;
-    WatchOp          watch;
-    UnwatchOp        unwatch;
     GetElementsOp    getElements;
     JSFunToStringOp  funToString;
 };
 
 #define JS_NULL_OBJECT_OPS nullptr
 
 } // namespace js
 
@@ -891,18 +883,18 @@ struct JS_STATIC_CLASS Class
     const ClassSpec* spec;
     const ClassExtension* ext;
     const ObjectOps* oOps;
 
     /*
      * 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 and
-     * unwatch (don't do this).
+     * this) or by overriding all the ObjectOps except getElements
+     * (don't do this).
      */
     static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
 
     bool isNative() const {
         return !(flags & NON_NATIVE);
     }
 
     bool hasPrivate() const {
@@ -969,18 +961,16 @@ struct JS_STATIC_CLASS Class
     DefinePropertyOp getOpsDefineProperty() const { return oOps ? oOps->defineProperty : nullptr; }
     HasPropertyOp    getOpsHasProperty()    const { return oOps ? oOps->hasProperty    : nullptr; }
     GetPropertyOp    getOpsGetProperty()    const { return oOps ? oOps->getProperty    : nullptr; }
     SetPropertyOp    getOpsSetProperty()    const { return oOps ? oOps->setProperty    : nullptr; }
     GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
                                             const { return oOps ? oOps->getOwnPropertyDescriptor
                                                                                      : nullptr; }
     DeletePropertyOp getOpsDeleteProperty() const { return oOps ? oOps->deleteProperty : nullptr; }
-    WatchOp          getOpsWatch()          const { return oOps ? oOps->watch          : nullptr; }
-    UnwatchOp        getOpsUnwatch()        const { return oOps ? oOps->unwatch        : nullptr; }
     GetElementsOp    getOpsGetElements()    const { return oOps ? oOps->getElements    : nullptr; }
     JSFunToStringOp  getOpsFunToString()    const { return oOps ? oOps->funToString    : nullptr; }
 };
 
 static_assert(offsetof(JSClassOps, addProperty) == offsetof(ClassOps, addProperty),
               "ClassOps and JSClassOps must be consistent");
 static_assert(offsetof(JSClassOps, delProperty) == offsetof(ClassOps, delProperty),
               "ClassOps and JSClassOps must be consistent");
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -339,22 +339,16 @@ class JS_FRIEND_API(BaseProxyHandler)
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject*, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
     //       in the external APIs that handle proxies.
     virtual bool isCallable(JSObject* obj) const;
     virtual bool isConstructor(JSObject* obj) const;
 
-    // These two hooks must be overridden, or not overridden, in tandem -- no
-    // overriding just one!
-    virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
-                       JS::HandleObject callable) const;
-    virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const;
-
     virtual bool getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
                              ElementAdder* adder) const;
 
     /* See comment for weakmapKeyDelegateOp in js/Class.h. */
     virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const;
     virtual bool isScripted() const { return false; }
 };
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3032,34 +3032,28 @@ js::UnwatchGuts(JSContext* cx, JS::Handl
     if (WatchpointMap* wpmap = cx->compartment()->watchpointMap)
         wpmap->unwatch(obj, id);
     return true;
 }
 
 bool
 js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
 {
-    if (WatchOp op = obj->getOpsWatch())
-        return op(cx, obj, id, callable);
-
     if (!obj->isNative() || obj->is<TypedArrayObject>()) {
         JS_ReportErrorNumberASCII(cx, 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->getOpsUnwatch())
-        return op(cx, obj, id);
-
     return UnwatchGuts(cx, obj, id);
 }
 
 /* * */
 
 extern bool
 PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
                      js::PinningBehavior pin = js::DoNotPinAtom);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -113,18 +113,16 @@ class JSObject : public js::gc::Cell
     js::LookupPropertyOp getOpsLookupProperty() const { return getClass()->getOpsLookupProperty(); }
     js::DefinePropertyOp getOpsDefineProperty() const { return getClass()->getOpsDefineProperty(); }
     js::HasPropertyOp    getOpsHasProperty()    const { return getClass()->getOpsHasProperty(); }
     js::GetPropertyOp    getOpsGetProperty()    const { return getClass()->getOpsGetProperty(); }
     js::SetPropertyOp    getOpsSetProperty()    const { return getClass()->getOpsSetProperty(); }
     js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
                                                 const { return getClass()->getOpsGetOwnPropertyDescriptor(); }
     js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); }
-    js::WatchOp          getOpsWatch()          const { return getClass()->getOpsWatch(); }
-    js::UnwatchOp        getOpsUnwatch()        const { return getClass()->getOpsUnwatch(); }
     js::GetElementsOp    getOpsGetElements()    const { return getClass()->getOpsGetElements(); }
     JSFunToStringOp      getOpsFunToString()    const { return getClass()->getOpsFunToString(); }
 
     js::ObjectGroup* group() const {
         MOZ_ASSERT(!hasLazyGroup());
         return groupRaw();
     }
 
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -404,30 +404,16 @@ BaseProxyHandler::setPrototype(JSContext
 bool
 BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const
 {
     *succeeded = false;
     return true;
 }
 
 bool
-BaseProxyHandler::watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable) const
-{
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
-                              proxy->getClass()->name);
-    return false;
-}
-
-bool
-BaseProxyHandler::unwatch(JSContext* cx, HandleObject proxy, HandleId id) const
-{
-    return true;
-}
-
-bool
 BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
                               ElementAdder* adder) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
 
     return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
 }
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -620,32 +620,16 @@ Proxy::boxedValue_unbox(JSContext* cx, H
     if (!CheckRecursionLimit(cx))
         return false;
     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
 }
 
 JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
 
 /* static */ bool
-Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
-{
-    if (!CheckRecursionLimit(cx))
-        return false;
-    return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
-}
-
-/* static */ bool
-Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
-{
-    if (!CheckRecursionLimit(cx))
-        return false;
-    return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
-}
-
-/* static */ bool
 Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
                    ElementAdder* adder)
 {
     if (!CheckRecursionLimit(cx))
         return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
                            /* mayThrow = */ true);
@@ -818,17 +802,16 @@ const ClassExtension js::ProxyClassExten
 const ObjectOps js::ProxyObjectOps = {
     proxy_LookupProperty,
     Proxy::defineProperty,
     Proxy::has,
     Proxy::get,
     Proxy::set,
     Proxy::getOwnPropertyDescriptor,
     proxy_DeleteProperty,
-    Proxy::watch, Proxy::unwatch,
     Proxy::getElements,
     Proxy::fun_toString
 };
 
 const Class js::ProxyObject::proxyClass =
     PROXY_CLASS_DEF("Proxy",
                     JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
                     JSCLASS_HAS_RESERVED_SLOTS(2));
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -62,19 +62,16 @@ class Proxy
     static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
     static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
     static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
     static const char* className(JSContext* cx, HandleObject proxy);
     static JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource);
     static RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy);
     static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
 
-    static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
-    static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
-
     static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
                             ElementAdder* adder);
 
     static void trace(JSTracer* trc, JSObject* obj);
 };
 
 bool
 proxy_Call(JSContext* cx, unsigned argc, Value* vp);
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -400,17 +400,16 @@ const Class VarEnvironmentObject::class_
 const ObjectOps ModuleEnvironmentObject::objectOps_ = {
     ModuleEnvironmentObject::lookupProperty,
     nullptr,                                             /* defineProperty */
     ModuleEnvironmentObject::hasProperty,
     ModuleEnvironmentObject::getProperty,
     ModuleEnvironmentObject::setProperty,
     ModuleEnvironmentObject::getOwnPropertyDescriptor,
     ModuleEnvironmentObject::deleteProperty,
-    nullptr, nullptr,                                    /* watch/unwatch */
     nullptr,                                             /* getElements */
     nullptr
 };
 
 const ClassOps ModuleEnvironmentObject::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* enumerate */
@@ -852,17 +851,16 @@ with_DeleteProperty(JSContext* cx, Handl
 static const ObjectOps WithEnvironmentObjectOps = {
     with_LookupProperty,
     with_DefineProperty,
     with_HasProperty,
     with_GetProperty,
     with_SetProperty,
     with_GetOwnPropertyDescriptor,
     with_DeleteProperty,
-    nullptr, nullptr,    /* watch/unwatch */
     nullptr,             /* getElements */
     nullptr,
 };
 
 const Class WithEnvironmentObject::class_ = {
     "With",
     JSCLASS_HAS_RESERVED_SLOTS(WithEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
@@ -1219,17 +1217,16 @@ lexicalError_DeleteProperty(JSContext* c
 static const ObjectOps RuntimeLexicalErrorObjectObjectOps = {
     lexicalError_LookupProperty,
     nullptr,             /* defineProperty */
     lexicalError_HasProperty,
     lexicalError_GetProperty,
     lexicalError_SetProperty,
     lexicalError_GetOwnPropertyDescriptor,
     lexicalError_DeleteProperty,
-    nullptr, nullptr,    /* watch/unwatch */
     nullptr,             /* getElements */
     nullptr,             /* this */
 };
 
 const Class RuntimeLexicalErrorObject::class_ = {
     "RuntimeLexicalError",
     JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -891,24 +891,16 @@ UnboxedPlainObject::obj_deleteProperty(J
                                        ObjectOpResult& result)
 {
     if (!convertToNative(cx, obj))
         return false;
     return DeleteProperty(cx, obj, id, result);
 }
 
 /* static */ bool
-UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
-{
-    if (!convertToNative(cx, obj))
-        return false;
-    return WatchProperty(cx, obj, id, callable);
-}
-
-/* static */ bool
 UnboxedPlainObject::newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                                  bool enumerableOnly)
 {
     // Ignore expando properties here, they are special-cased by the property
     // enumeration code.
 
     const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
     for (size_t i = 0; i < unboxed.length(); i++) {
@@ -941,18 +933,16 @@ static const ClassOps UnboxedPlainObject
 static const ObjectOps UnboxedPlainObjectObjectOps = {
     UnboxedPlainObject::obj_lookupProperty,
     UnboxedPlainObject::obj_defineProperty,
     UnboxedPlainObject::obj_hasProperty,
     UnboxedPlainObject::obj_getProperty,
     UnboxedPlainObject::obj_setProperty,
     UnboxedPlainObject::obj_getOwnPropertyDescriptor,
     UnboxedPlainObject::obj_deleteProperty,
-    UnboxedPlainObject::obj_watch,
-    nullptr,   /* No unwatch needed, as watch() converts the object to native */
     nullptr,   /* getElements */
     nullptr    /* funToString */
 };
 
 const Class UnboxedPlainObject::class_ = {
     js_Object_str,
     Class::NON_NATIVE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -255,17 +255,16 @@ class UnboxedPlainObject : public Unboxe
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<PropertyDescriptor> desc);
 
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
     static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                              bool enumerableOnly);
-    static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
     inline const UnboxedLayout& layout() const;
 
     const UnboxedLayout& layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
     uint8_t* data() {