Bug 638054 - Remove the guts of the watch/unwatch implementation. r=jorendorff
authorTom Schuster <evilpies@gmail.com>
Tue, 24 Oct 2017 20:48:14 +0200
changeset 387884 e4f864ad5779d943ce2e562764c183b1a100bba3
parent 387883 3f8f9e5f28583ef7bb1e46647540da4309155e16
child 387885 31bc65f1acab36ec5abbb9b29974e279b622dff6
push id96520
push userevilpies@gmail.com
push dateTue, 24 Oct 2017 18:48:31 +0000
treeherdermozilla-inbound@a124f4901430 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
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 the guts of the watch/unwatch implementation. r=jorendorff
js/rust/src/jsglue.cpp
js/src/builtin/Object.cpp
js/src/builtin/TypedObject.cpp
js/src/gc/Marking.cpp
js/src/gc/RootMarking.cpp
js/src/jit/CacheIR.cpp
js/src/jit/VMFunctions.cpp
js/src/js.msg
js/src/jsapi-tests/testGCGrayMarking.cpp
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsversion.h
js/src/jswatchpoint.cpp
js/src/jswatchpoint.h
js/src/jswrapper.h
js/src/moz.build
js/src/proxy/SecurityWrapper.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.cpp
js/src/vm/NativeObject.h
js/src/vm/Runtime.cpp
js/src/vm/Shape.h
js/src/vm/TypeInference.cpp
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -85,18 +85,16 @@ struct ProxyTraps {
     bool (*defaultValue)(JSContext *cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
     void (*trace)(JSTracer *trc, JSObject *proxy);
     void (*finalize)(JSFreeOp *fop, JSObject *proxy);
     size_t (*objectMoved)(JSObject *proxy, JSObject *old);
 
     bool (*isCallable)(JSObject *obj);
     bool (*isConstructor)(JSObject *obj);
 
-    // watch
-    // unwatch
     // getElements
 
     // weakmapKeyDelegate
     // isScripted
 };
 
 static int HandlerFamily;
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -861,119 +861,16 @@ obj_assign(JSContext* cx, unsigned argc,
             return false;
     }
 
     // Step 5.
     args.rval().setObject(*to);
     return true;
 }
 
-#if JS_HAS_OBJ_WATCHPOINT
-
-bool
-js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old,
-                 JS::Value* nvp, void* closure)
-{
-    RootedObject obj(cx, obj_);
-    RootedId id(cx, id_);
-
-    /* Avoid recursion on (obj, id) already being watched on cx. */
-    AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
-    if (resolving.alreadyStarted())
-        return true;
-
-    FixedInvokeArgs<3> args(cx);
-
-    args[0].set(IdToValue(id));
-    args[1].set(old);
-    args[2].set(*nvp);
-
-    RootedValue callable(cx, ObjectValue(*static_cast<JSObject*>(closure)));
-    RootedValue thisv(cx, ObjectValue(*obj));
-    RootedValue rv(cx);
-    if (!Call(cx, callable, thisv, args, &rv))
-        return false;
-
-    *nvp = rv;
-    return true;
-}
-
-static bool
-obj_watch(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    if (!cx->compartment()->warnedAboutObjectWatch) {
-        if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
-                                               JSMSG_OBJECT_WATCH_DEPRECATED))
-        {
-            return false;
-        }
-        cx->compartment()->warnedAboutObjectWatch = true;
-    }
-
-    if (args.length() <= 1) {
-        ReportMissingArg(cx, args.calleev(), 1);
-        return false;
-    }
-
-    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 (!WatchProperty(cx, obj, propid, callable))
-        return false;
-
-    args.rval().setUndefined();
-    return true;
-}
-
-static bool
-obj_unwatch(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    if (!cx->compartment()->warnedAboutObjectWatch) {
-        if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
-                                               JSMSG_OBJECT_WATCH_DEPRECATED))
-        {
-            return false;
-        }
-        cx->compartment()->warnedAboutObjectWatch = true;
-    }
-
-    RootedId id(cx);
-    if (args.length() != 0) {
-        if (!ValueToId<CanGC>(cx, args[0], &id))
-            return false;
-    } else {
-        id = JSID_VOID;
-    }
-
-    if (!UnwatchProperty(cx, obj, id))
-        return false;
-
-    args.rval().setUndefined();
-    return true;
-}
-
-#endif /* JS_HAS_OBJ_WATCHPOINT */
-
 /* ES5 15.2.4.6. */
 static bool
 obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     if (args.length() < 1 || !args[0].isObject()) {
@@ -1622,20 +1519,16 @@ ProtoSetter(JSContext* cx, unsigned argc
 
 static const JSFunctionSpec object_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,             obj_toSource,                0,0),
 #endif
     JS_INLINABLE_FN(js_toString_str,   obj_toString,                0,0, ObjectToString),
     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
     JS_SELF_HOSTED_FN(js_valueOf_str,  "Object_valueOf",            0,0),
-#if JS_HAS_OBJ_WATCHPOINT
-    JS_FN(js_watch_str,                obj_watch,                   2,0),
-    JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
-#endif
     JS_SELF_HOSTED_FN(js_hasOwnProperty_str, "Object_hasOwnProperty", 1,0),
     JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
     JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
 #if JS_OLD_GETTER_SETTER_METHODS
     JS_SELF_HOSTED_FN(js_defineGetter_str, "ObjectDefineGetter",    2,0),
     JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter",    2,0),
     JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter",    1,0),
     JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter",    1,0),
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2216,17 +2216,16 @@ OutlineTransparentTypedObject::getOrCrea
 const ObjectOps TypedObject::objectOps_ = {
     TypedObject::obj_lookupProperty,
     TypedObject::obj_defineProperty,
     TypedObject::obj_hasProperty,
     TypedObject::obj_getProperty,
     TypedObject::obj_setProperty,
     TypedObject::obj_getOwnPropertyDescriptor,
     TypedObject::obj_deleteProperty,
-    nullptr, nullptr, /* watch/unwatch */
     nullptr,   /* getElements */
     nullptr, /* thisValue */
 };
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved, flag)   \
     static const ClassOps Name##ClassOps = {             \
         nullptr,        /* addProperty */                \
         nullptr,        /* delProperty */                \
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -3353,19 +3353,18 @@ FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(I
  * To prevent this badness, we unmark the gray bit of an object when it is
  * accessed by callers outside XPConnect. This would cause the object to go
  * black in step 2 above. This must be done on everything reachable from the
  * object being returned. The following code takes care of the recursive
  * re-coloring.
  *
  * There is an additional complication for certain kinds of edges that are not
  * contained explicitly in the source object itself, such as from a weakmap key
- * to its value, and from an object being watched by a watchpoint to the
- * watchpoint's closure. These "implicit edges" are represented in some other
- * container object, such as the weakmap or the watchpoint itself. In these
+ * to its value. These "implicit edges" are represented in some other
+ * container object, such as the weakmap itself. In these
  * cases, calling unmark gray on an object won't find all of its children.
  *
  * Handling these implicit edges has two parts:
  * - A special pass enumerating all of the containers that know about the
  *   implicit edges to fix any black-gray edges that have been created. This
  *   is implemented in nsXPConnect::FixWeakMappingGrayBits.
  * - To prevent any incorrectly gray objects from escaping to live JS outside
  *   of the containers, we must add unmark-graying read barriers to these
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -9,17 +9,16 @@
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsprf.h"
 #include "jstypes.h"
-#include "jswatchpoint.h"
 
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "jit/MacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2809,18 +2809,16 @@ SetPropIRGenerator::tryAttachStub()
     bool nameOrSymbol;
     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
         cx_->clearPendingException();
         return false;
     }
 
     if (lhsVal_.isObject()) {
         RootedObject obj(cx_, &lhsVal_.toObject());
-        if (obj->watched())
-            return false;
 
         ObjOperandId objId = writer.guardIsObject(objValId);
         if (IsPropertySetOp(JSOp(*pc_))) {
             if (tryAttachMegamorphicSetElement(obj, objId, rhsValId))
                 return true;
         }
         if (nameOrSymbol) {
             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
@@ -3766,18 +3764,16 @@ SetPropIRGenerator::tryAttachAddSlotStub
         cx_->clearPendingException();
         return false;
     }
 
     if (!lhsVal_.isObject() || !nameOrSymbol)
         return false;
 
     RootedObject obj(cx_, &lhsVal_.toObject());
-    if (obj->watched())
-        return false;
 
     PropertyResult prop;
     JSObject* holder;
     if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
         return false;
     if (obj != holder)
         return false;
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1704,18 +1704,17 @@ SetNativeDataProperty(JSContext* cx, JSO
 
     if (MOZ_UNLIKELY(!obj->isNative()))
         return false;
 
     NativeObject* nobj = &obj->as<NativeObject>();
     Shape* shape = nobj->lastProperty()->search(cx, NameToId(name));
     if (!shape ||
         !shape->isDataProperty() ||
-        !shape->writable() ||
-        nobj->watched())
+        !shape->writable())
     {
         return false;
     }
 
     if (NeedsTypeBarrier && !HasTypePropertyId(nobj, NameToId(name), *val))
         return false;
 
     nobj->setSlot(shape->slot(), *val);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -40,17 +40,16 @@
  */
 
 MSG_DEF(JSMSG_NOT_AN_ERROR,            0, JSEXN_ERR, "<Error #0 is reserved>")
 MSG_DEF(JSMSG_NOT_DEFINED,             1, JSEXN_REFERENCEERR, "{0} is not defined")
 MSG_DEF(JSMSG_MORE_ARGS_NEEDED,        3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}")
 MSG_DEF(JSMSG_INCOMPATIBLE_PROTO,      3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
 MSG_DEF(JSMSG_NO_CONSTRUCTOR,          1, JSEXN_TYPEERR, "{0} has no constructor")
 MSG_DEF(JSMSG_BAD_SORT_ARG,            0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
-MSG_DEF(JSMSG_CANT_WATCH,              1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}")
 MSG_DEF(JSMSG_READ_ONLY,               1, JSEXN_TYPEERR, "{0} is read-only")
 MSG_DEF(JSMSG_CANT_DELETE,             1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
 MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY,     0, JSEXN_TYPEERR, "can't delete non-configurable array element")
 MSG_DEF(JSMSG_NOT_FUNCTION,            1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         1, JSEXN_TYPEERR, "{0} is not a constructor")
 MSG_DEF(JSMSG_CANT_CONVERT_TO,         2, JSEXN_TYPEERR, "can't convert {0} to {1}")
 MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] property is not a function")
 MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
@@ -65,17 +64,16 @@ MSG_DEF(JSMSG_REDECLARED_PREV,         2
 MSG_DEF(JSMSG_REDECLARED_VAR,          2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
 MSG_DEF(JSMSG_UNDECLARED_VAR,          1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
 MSG_DEF(JSMSG_GETTER_ONLY,             1, JSEXN_TYPEERR, "setting getter-only property {0}")
 MSG_DEF(JSMSG_OVERWRITING_ACCESSOR,    1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
 MSG_DEF(JSMSG_UNDEFINED_PROP,          1, JSEXN_REFERENCEERR, "reference to undefined property {0}")
 MSG_DEF(JSMSG_INVALID_MAP_ITERABLE,    1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
 MSG_DEF(JSMSG_NESTING_GENERATOR,       0, JSEXN_TYPEERR, "already executing generator")
 MSG_DEF(JSMSG_INCOMPATIBLE_METHOD,     3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
-MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead")
 MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,      1, JSEXN_TYPEERR, "bad surrogate character {0}")
 MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE,     1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
 MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR,     1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
 MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW,     1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
 MSG_DEF(JSMSG_BAD_GENERATOR_YIELD,     1, JSEXN_TYPEERR, "yield from closing generator {0}")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,      0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,         2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,         2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -50,17 +50,16 @@ BEGIN_TEST(testGCGrayMarking)
     JSAutoCompartment ac(cx, global1);
 
     InitGrayRootTracer();
 
     bool ok =
         TestMarking() &&
         TestWeakMaps() &&
         TestUnassociatedWeakMaps() &&
-        TestWatchpoints() &&
         TestCCWs() &&
         TestGrayUnmarking();
 
     global1 = nullptr;
     global2 = nullptr;
     RemoveGrayRootTracer();
 
     return ok;
@@ -427,73 +426,16 @@ TestUnassociatedWeakMaps()
     blackRoot = nullptr;
     grayRoots.grayRoot1 = nullptr;
     grayRoots.grayRoot2 = nullptr;
 
     return true;
 }
 
 bool
-TestWatchpoints()
-{
-    JSObject* watched = AllocPlainObject();
-    CHECK(watched);
-
-    JSObject* closure = AllocPlainObject();
-    CHECK(closure);
-
-    {
-        RootedObject obj(cx, watched);
-        RootedObject callable(cx, closure);
-        RootedId id(cx, INT_TO_JSID(0));
-        CHECK(JS_DefinePropertyById(cx, obj, id, JS::TrueHandleValue, 0));
-        CHECK(js::WatchGuts(cx, obj, id, callable));
-    }
-
-    // Test that a watchpoint marks the callable black if the watched object is
-    // black.
-
-    RootedObject blackRoot(cx, watched);
-    grayRoots.grayRoot1 = nullptr;
-    JS_GC(cx);
-    CHECK(IsMarkedBlack(watched));
-    CHECK(IsMarkedBlack(closure));
-
-    // Test that a watchpoint marks the callable gray if the watched object is
-    // gray.
-
-    blackRoot = nullptr;
-    grayRoots.grayRoot1 = watched;
-    JS_GC(cx);
-    CHECK(IsMarkedGray(watched));
-    CHECK(IsMarkedGray(closure));
-
-    // Test that ExposeToActiveJS *doesn't* unmark through watchpoints.  We
-    // could make this work, but it's currently handled by the CC fixup.
-
-    CHECK(IsMarkedGray(watched));
-    CHECK(IsMarkedGray(closure));
-    JS::ExposeObjectToActiveJS(watched);
-    CHECK(IsMarkedBlack(watched));
-    CHECK(IsMarkedGray(closure));
-
-    {
-        RootedObject obj(cx, watched);
-        RootedId id(cx, INT_TO_JSID(0));
-        CHECK(js::UnwatchGuts(cx, obj, id));
-    }
-
-    blackRoot = nullptr;
-    grayRoots.grayRoot1 = nullptr;
-    grayRoots.grayRoot2 = nullptr;
-
-    return true;
-}
-
-bool
 TestCCWs()
 {
     JSObject* target = AllocPlainObject();
     CHECK(target);
 
     // Test getting a new wrapper doesn't return a gray wrapper.
 
     RootedObject blackRoot(cx, target);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -34,17 +34,16 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "json.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
-#include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/Eval.h"
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/Promise.h"
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -38,17 +38,16 @@
 #include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
-#include "jswatchpoint.h"
 #include "jswin.h"
 
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
 #include "js/CharacterEncoding.h"
 #include "vm/ErrorReporting.h"
 #include "vm/HelperThreads.h"
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -10,17 +10,16 @@
 #include "mozilla/MemoryReporting.h"
 
 #include <stddef.h>
 
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsiter.h"
-#include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitOptions.h"
 #include "js/Date.h"
 #include "js/Proxy.h"
@@ -55,17 +54,16 @@ JSCompartment::JSCompartment(Zone* zone,
     isSystem_(false),
     isAtomsCompartment_(false),
     isSelfHosting(false),
     marked(true),
     warnedAboutDateToLocaleFormat(false),
     warnedAboutExprClosure(false),
     warnedAboutForEach(false),
     warnedAboutLegacyGenerator(false),
-    warnedAboutObjectWatch(false),
     warnedAboutStringGenericsMethods(0),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     globalHolds(0),
     performanceMonitoring(runtime_),
@@ -82,17 +80,16 @@ JSCompartment::JSCompartment(Zone* zone,
     innerViews(zone),
     lazyArrayBuffers(nullptr),
     wasm(zone),
     nonSyntacticLexicalEnvironments_(nullptr),
     gcIncomingGrayPointers(nullptr),
     debugModeBits(0),
     validAccessPtr(nullptr),
     randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
-    watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     scriptNameMap(nullptr),
     debugScriptMap(nullptr),
     debugEnvs(nullptr),
     enumerators(nullptr),
     compartmentStats_(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
@@ -113,17 +110,16 @@ JSCompartment::~JSCompartment()
     reportTelemetry();
 
     // Write the code coverage information in a file.
     JSRuntime* rt = runtimeFromActiveCooperatingThread();
     if (rt->lcovOutput().isEnabled())
         rt->lcovOutput().writeLCovResult(lcovOutput);
 
     js_delete(jitCompartment_);
-    js_delete(watchpointMap);
     js_delete(scriptCountsMap);
     js_delete(scriptNameMap);
     js_delete(debugScriptMap);
     js_delete(debugEnvs);
     js_delete(objectMetadataTable);
     js_delete(lazyArrayBuffers);
     js_delete(nonSyntacticLexicalEnvironments_);
     js_free(enumerators);
@@ -772,22 +768,16 @@ JSCompartment::traceRoots(JSTracer* trc,
             TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
     }
 
     // Nothing below here needs to be treated as a root if we aren't marking
     // this zone for a collection.
     if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollectingFromAnyThread())
         return;
 
-    // During a GC, these are treated as weak pointers.
-    if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
-        if (watchpointMap)
-            watchpointMap->trace(trc);
-    }
-
     /* Mark debug scopes, if present */
     if (debugEnvs)
         debugEnvs->trace(trc);
 
     if (lazyArrayBuffers)
         lazyArrayBuffers->trace(trc);
 
     if (objectMetadataTable)
@@ -820,19 +810,16 @@ JSCompartment::traceRoots(JSTracer* trc,
 
     if (nonSyntacticLexicalEnvironments_)
         nonSyntacticLexicalEnvironments_->trace(trc);
 }
 
 void
 JSCompartment::finishRoots()
 {
-    if (watchpointMap)
-        watchpointMap->clear();
-
     if (debugEnvs)
         debugEnvs->finish();
 
     if (lazyArrayBuffers)
         lazyArrayBuffers->clear();
 
     if (objectMetadataTable)
         objectMetadataTable->clear();
@@ -940,23 +927,16 @@ JSCompartment::sweepCrossCompartmentWrap
 
 void
 JSCompartment::sweepVarNames()
 {
     varNames_.sweep();
 }
 
 void
-JSCompartment::sweepWatchpoints()
-{
-    if (watchpointMap)
-        watchpointMap->sweep();
-}
-
-void
 JSCompartment::sweepMapAndSetObjectsAfterMinorGC()
 {
     auto fop = runtime_->defaultFreeOp();
 
     for (auto mapobj : mapsWithNurseryMemory)
         MapObject::sweepAfterMinorGC(fop, mapobj);
     mapsWithNurseryMemory.clearAndFree();
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -541,17 +541,16 @@ struct IteratorHashPolicy
     static bool match(PropertyIteratorObject* obj, const Lookup& lookup);
 };
 
 } /* namespace js */
 
 namespace js {
 class DebugEnvironments;
 class ObjectWeakMap;
-class WatchpointMap;
 class WeakMapBase;
 } // namespace js
 
 struct JSCompartment
 {
     const JS::CompartmentCreationOptions creationOptions_;
     JS::CompartmentBehaviors behaviors_;
 
@@ -622,17 +621,16 @@ struct JSCompartment
 
   public:
     bool                         isSelfHosting;
     bool                         marked;
     bool                         warnedAboutDateToLocaleFormat : 1;
     bool                         warnedAboutExprClosure : 1;
     bool                         warnedAboutForEach : 1;
     bool                         warnedAboutLegacyGenerator : 1;
-    bool                         warnedAboutObjectWatch : 1;
     uint32_t                     warnedAboutStringGenericsMethods;
 
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
 #endif
 
     void mark() { marked = true; }
 
@@ -963,17 +961,16 @@ struct JSCompartment
     void sweepGlobalObject();
     void sweepSelfHostingScriptSource();
     void sweepJitCompartment(js::FreeOp* fop);
     void sweepRegExps();
     void sweepDebugEnvironments();
     void sweepNativeIterators();
     void sweepTemplateObjects();
     void sweepVarNames();
-    void sweepWatchpoints();
 
     void purge();
     void clearTables();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupAfterMovingGC();
     void fixupGlobal();
     void fixupScriptMapsAfterMovingGC();
@@ -1152,18 +1149,16 @@ struct JSCompartment
     bool ensureDelazifyScriptsForDebugger(JSContext* cx);
 
     void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
 
   private:
     void sweepBreakpoints(js::FreeOp* fop);
 
   public:
-    js::WatchpointMap* watchpointMap;
-
     js::ScriptCountsMap* scriptCountsMap;
     js::ScriptNameMap* scriptNameMap;
 
     js::DebugScriptMap* debugScriptMap;
 
     /* Bookkeeping information for debug scope objects. */
     js::DebugEnvironments* debugEnvs;
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -10,17 +10,16 @@
 
 #include <stdint.h>
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
-#include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
 #include "gc/GCInternals.h"
 #include "js/Proxy.h"
 #include "proxy/DeadObjectProxy.h"
@@ -569,17 +568,16 @@ JS_IsScriptSourceObject(JSObject* obj)
 {
     return obj->is<ScriptSourceObject>();
 }
 
 void
 js::TraceWeakMaps(WeakMapTracer* trc)
 {
     WeakMapBase::traceAllMappings(trc);
-    WatchpointMap::traceAll(trc);
 }
 
 extern JS_FRIEND_API(bool)
 js::AreGCGrayBitsValid(JSRuntime* rt)
 {
     return rt->gc.areGrayBitsValid();
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2257,46 +2257,21 @@ JS_GetDataViewByteLength(JSObject* obj);
  *
  * |*isSharedMemory| will be set to true if the DataView maps a SharedArrayBuffer,
  * otherwise to false.
  */
 JS_FRIEND_API(void*)
 JS_GetDataViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoRequireNoGC&);
 
 namespace js {
-
-/**
- * Add a watchpoint -- in the Object.prototype.watch sense -- to |obj| for the
- * property |id|, using the callable object |callable| as the function to be
- * called for notifications.
- *
- * This is an internal function exposed -- temporarily -- only so that DOM
- * proxies can be watchable.  Don't use it!  We'll soon kill off the
- * Object.prototype.{,un}watch functions, at which point this will go too.
- */
-extern JS_FRIEND_API(bool)
-WatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
-
-/**
- * Remove a watchpoint -- in the Object.prototype.watch sense -- from |obj| for
- * the property |id|.
- *
- * This is an internal function exposed -- temporarily -- only so that DOM
- * proxies can be watchable.  Don't use it!  We'll soon kill off the
- * Object.prototype.{,un}watch functions, at which point this will go too.
- */
-extern JS_FRIEND_API(bool)
-UnwatchGuts(JSContext* cx, JS::HandleObject obj, JS::HandleId id);
-
 namespace jit {
 
 enum class InlinableNative : uint16_t;
 
 } // namespace jit
-
 } // namespace js
 
 /**
  * A class, expected to be passed by value, which represents the CallArgs for a
  * JSJitGetterOp.
  */
 class JSJitGetterCallArgs : protected JS::MutableHandleValue
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -210,17 +210,16 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
-#include "jswatchpoint.h"
 #include "jsweakmap.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 
 #include "gc/FindSCCs.h"
 #include "gc/GCInternals.h"
 #include "gc/GCTrace.h"
@@ -2728,20 +2727,16 @@ GCRuntime::updateZonePointersToRelocated
     // as much as possible.
     updateAllCellPointers(&trc, zone);
 
     // Mark roots to update them.
     {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_ROOTS);
 
         WeakMapBase::traceZone(zone, &trc);
-        for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
-            if (c->watchpointMap)
-                c->watchpointMap->trace(&trc);
-        }
     }
 
     // Sweep everything to fix up weak pointers.
     rt->gc.sweepZoneAfterCompacting(zone);
 
     // Call callbacks to get the rest of the system to fixup other untraced pointers.
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         callWeakPointerCompartmentCallbacks(comp);
@@ -2772,17 +2767,16 @@ GCRuntime::updateRuntimePointersToReloca
 
         // Mark all gray roots, making sure we call the trace callback to get the
         // current set.
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&trc, grayRootTracer.data);
     }
 
     // Sweep everything to fix up weak pointers.
-    WatchpointMap::sweepAll(rt);
     Debugger::sweepAll(rt->defaultFreeOp());
     jit::JitRuntime::SweepJitcodeGlobalTable(rt);
     for (JS::detail::WeakCacheBase* cache : rt->weakCaches())
         cache->sweep();
 
     // Type inference may put more blocks here to free.
     blocksToFreeAfterSweeping.ref().freeAll();
 
@@ -4368,20 +4362,16 @@ GCRuntime::markWeakReferences(gcstats::P
     MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
 
     for (;;) {
         bool markedAny = false;
         if (!marker.isWeakMarkingTracer()) {
             for (ZoneIterT zone(rt); !zone.done(); zone.next())
                 markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
         }
-        for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) {
-            if (c->watchpointMap)
-                markedAny |= c->watchpointMap->markIteratively(&marker);
-        }
         markedAny |= Debugger::markIteratively(&marker);
         markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
 
         if (!markedAny)
             break;
 
         auto unlimited = SliceBudget::unlimited();
         MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
@@ -5278,17 +5268,16 @@ SweepMisc(JSRuntime* runtime)
 {
     for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next()) {
         c->sweepGlobalObject();
         c->sweepTemplateObjects();
         c->sweepSavedStacks();
         c->sweepTemplateLiteralMap();
         c->sweepSelfHostingScriptSource();
         c->sweepNativeIterators();
-        c->sweepWatchpoints();
     }
 }
 
 static void
 SweepCompressionTasks(JSRuntime* runtime)
 {
     AutoLockHelperThreadState lock;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -28,17 +28,16 @@
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
-#include "jswatchpoint.h"
 #include "jswin.h"
 #include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
@@ -1078,23 +1077,17 @@ js::CreateThisForFunction(JSContext* cx,
 
     return obj;
 }
 
 /* static */ bool
 JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                                HandleValue receiver, ObjectOpResult& result)
 {
-    RootedValue value(cx, v);
-    if (MOZ_UNLIKELY(obj->watched())) {
-        WatchpointMap* wpmap = cx->compartment()->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value))
-            return false;
-    }
-    return obj->getOpsSetProperty()(cx, obj, id, value, receiver, result);
+    return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
 }
 
 /* static */ bool
 JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
                               HandleValue receiver, ObjectOpResult& result)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
@@ -2991,72 +2984,16 @@ js::GetPropertyDescriptor(JSContext* cx,
         if (!GetPrototype(cx, pobj, &pobj))
             return false;
     }
 
     MOZ_ASSERT(!desc.object());
     return true;
 }
 
-bool
-js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
-{
-    RootedObject obj(cx, ToWindowIfWindowProxy(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;
-
-        MarkTypePropertyNonData(cx, obj, id);
-    }
-
-    WatchpointMap* wpmap = cx->compartment()->watchpointMap;
-    if (!wpmap) {
-        wpmap = cx->zone()->new_<WatchpointMap>();
-        if (!wpmap || !wpmap->init()) {
-            ReportOutOfMemory(cx);
-            js_delete(wpmap);
-            return false;
-        }
-        cx->compartment()->watchpointMap = wpmap;
-    }
-
-    return wpmap->watch(cx, obj, id, js::WatchHandler, 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, ToWindowIfWindowProxy(origObj));
-    if (WatchpointMap* wpmap = cx->compartment()->watchpointMap)
-        wpmap->unwatch(obj, id);
-    return true;
-}
-
-bool
-js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject 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)
-{
-    return UnwatchGuts(cx, obj, id);
-}
-
 /* * */
 
 extern bool
 PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id,
                      js::PinningBehavior pin = js::DoNotPinAtom);
 
 static bool
 DefineFunctionFromSpec(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs, unsigned flags,
@@ -3617,17 +3554,16 @@ JSObject::dump(js::GenericPrinter& out) 
 
     out.put("flags:");
     if (obj->isDelegate()) out.put(" delegate");
     if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) out.put(" not_extensible");
     if (obj->maybeHasInterestingSymbolProperty()) out.put(" maybe_has_interesting_symbol");
     if (obj->isBoundFunction()) out.put(" bound_function");
     if (obj->isQualifiedVarObj()) out.put(" varobj");
     if (obj->isUnqualifiedVarObj()) out.put(" unqualified_varobj");
-    if (obj->watched()) out.put(" watched");
     if (obj->isIteratedSingleton()) out.put(" iterated_singleton");
     if (obj->isNewGroupUnknown()) out.put(" new_type_unknown");
     if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto");
     if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable())
         out.put(" immutable_prototype");
 
     if (obj->isNative()) {
         const NativeObject* nobj = &obj->as<NativeObject>();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -180,21 +180,16 @@ class JSObject : public js::gc::Cell
     inline bool isDelegate() const;
     static bool setDelegate(JSContext* cx, JS::HandleObject obj) {
         return setFlags(cx, obj, js::BaseShape::DELEGATE, GENERATE_SHAPE);
     }
 
     inline bool isBoundFunction() const;
     inline bool hasSpecialEquality() const;
 
-    inline bool watched() const;
-    static bool setWatched(JSContext* cx, JS::HandleObject obj) {
-        return setFlags(cx, obj, js::BaseShape::WATCHED, GENERATE_SHAPE);
-    }
-
     // A "qualified" varobj is the object on which "qualified" variable
     // declarations (i.e., those defined with "var") are kept.
     //
     // Conceptually, when a var binding is defined, it is defined on the
     // innermost qualified varobj on the scope chain.
     //
     // Function scopes (CallObjects) are qualified varobjs, and there can be
     // no other qualified varobj that is more inner for var bindings in that
@@ -986,31 +981,16 @@ enum DefineAsIntrinsic {
     NotIntrinsic,
     AsIntrinsic
 };
 
 extern bool
 DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
                 DefineAsIntrinsic intrinsic);
 
-/*
- * 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);
-
 /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
 extern bool
 ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
 
 inline bool
 ToPrimitive(JSContext* cx, MutableHandleValue vp)
 {
     if (vp.isPrimitive())
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -444,22 +444,16 @@ JSObject::nonProxyIsExtensible() const
 
 inline bool
 JSObject::isBoundFunction() const
 {
     return is<JSFunction>() && as<JSFunction>().isBoundFunction();
 }
 
 inline bool
-JSObject::watched() const
-{
-    return hasAllFlags(js::BaseShape::WATCHED);
-}
-
-inline bool
 JSObject::isDelegate() const
 {
     return hasAllFlags(js::BaseShape::DELEGATE);
 }
 
 inline bool
 JSObject::hasUncacheableProto() const
 {
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -7,17 +7,16 @@
 #ifndef jsversion_h
 #define jsversion_h
 
 /*
  * JS Capability Macros.
  */
 #define JS_HAS_STR_HTML_HELPERS 1       /* (no longer used) */
 #define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
-#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */
 #define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */
 #define JS_HAS_CATCH_GUARD      1       /* has exception handling catch guard */
 #define JS_HAS_UNEVAL           1       /* has uneval() top-level function */
 #define JS_HAS_CONST            1       /* (no longer used) */
 #define JS_HAS_FUN_EXPR_STMT    1       /* (no longer used) */
 #define JS_HAS_FOR_EACH_IN      1       /* has for each (lhs in iterable) */
 #define JS_HAS_GENERATORS       1       /* (no longer used) */
 #define JS_HAS_BLOCK_SCOPE      1       /* (no longer used) */
deleted file mode 100644
--- a/js/src/jswatchpoint.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jswatchpoint.h"
-
-#include "jsatom.h"
-#include "jscompartment.h"
-#include "jsfriendapi.h"
-
-#include "gc/Marking.h"
-#include "vm/Shape.h"
-
-#include "jsgcinlines.h"
-
-using namespace js;
-using namespace js::gc;
-
-inline HashNumber
-WatchKeyHasher::hash(const Lookup& key)
-{
-    return MovableCellHasher<PreBarrieredObject>::hash(key.object) ^ HashId(key.id);
-}
-
-namespace {
-
-class AutoEntryHolder {
-    typedef WatchpointMap::Map Map;
-    Generation gen;
-    Map& map;
-    Map::Ptr p;
-    RootedObject obj;
-    RootedId id;
-
-  public:
-    AutoEntryHolder(JSContext* cx, Map& map, Map::Ptr p)
-      : gen(map.generation()), map(map), p(p), obj(cx, p->key().object), id(cx, p->key().id)
-    {
-        MOZ_ASSERT(!p->value().held);
-        p->value().held = true;
-    }
-
-    ~AutoEntryHolder() {
-        if (gen != map.generation())
-            p = map.lookup(WatchKey(obj, id));
-        if (p)
-            p->value().held = false;
-    }
-};
-
-} /* anonymous namespace */
-
-bool
-WatchpointMap::init()
-{
-    return map.init();
-}
-
-bool
-WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id,
-                     JSWatchPointHandler handler, HandleObject closure)
-{
-    MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
-
-    if (!JSObject::setWatched(cx, obj))
-        return false;
-
-    Watchpoint w(handler, closure, false);
-    if (!map.put(WatchKey(obj, id), w)) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-    /*
-     * For generational GC, we don't need to post-barrier writes to the
-     * hashtable here because we mark all watchpoints as part of root marking in
-     * markAll().
-     */
-    return true;
-}
-
-void
-WatchpointMap::unwatch(JSObject* obj, jsid id)
-{
-    if (Map::Ptr p = map.lookup(WatchKey(obj, id)))
-        map.remove(p);
-}
-
-void
-WatchpointMap::unwatchObject(JSObject* obj)
-{
-    for (Map::Enum e(map); !e.empty(); e.popFront()) {
-        Map::Entry& entry = e.front();
-        if (entry.key().object == obj)
-            e.removeFront();
-    }
-}
-
-void
-WatchpointMap::clear()
-{
-    map.clear();
-}
-
-bool
-WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
-{
-    Map::Ptr p = map.lookup(WatchKey(obj, id));
-    if (!p || p->value().held)
-        return true;
-
-    AutoEntryHolder holder(cx, map, p);
-
-    /* Copy the entry, since GC would invalidate p. */
-    JSWatchPointHandler handler = p->value().handler;
-    RootedObject closure(cx, p->value().closure);
-
-    /* Determine the property's old value. */
-    Value old;
-    old.setUndefined();
-    if (obj->isNative()) {
-        NativeObject* nobj = &obj->as<NativeObject>();
-        if (Shape* shape = nobj->lookup(cx, id)) {
-            if (shape->isDataProperty())
-                old = nobj->getSlot(shape->slot());
-        }
-    }
-
-    // Read barrier to prevent an incorrectly gray closure from escaping the
-    // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
-    JS::ExposeObjectToActiveJS(closure);
-
-    /* Call the handler. */
-    return handler(cx, obj, id, old, vp.address(), closure);
-}
-
-bool
-WatchpointMap::markIteratively(GCMarker* marker)
-{
-    bool marked = false;
-    for (Map::Enum e(map); !e.empty(); e.popFront()) {
-        Map::Entry& entry = e.front();
-        auto& object = entry.mutableKey().object;
-        bool objectIsLive = IsMarked(marker->runtime(), &object);
-        if (objectIsLive || entry.value().held) {
-            if (!objectIsLive) {
-                TraceEdge(marker, &object, "held Watchpoint object");
-                marked = true;
-            }
-
-            auto& id = entry.mutableKey().id;
-            MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
-            TraceEdge(marker, &id, "WatchKey::id");
-
-            auto& closure = entry.value().closure;
-            if (closure && !IsMarked(marker->runtime(), &closure)) {
-                TraceEdge(marker, &closure, "Watchpoint::closure");
-                marked = true;
-            }
-        }
-    }
-    return marked;
-}
-
-void
-WatchpointMap::trace(JSTracer* trc)
-{
-    for (Map::Enum e(map); !e.empty(); e.popFront()) {
-        Map::Entry& entry = e.front();
-        auto& id = entry.mutableKey().id;
-        MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
-        TraceEdge(trc, &entry.mutableKey().object, "held Watchpoint object");
-        TraceEdge(trc, &id, "WatchKey::id");
-        TraceEdge(trc, &entry.value().closure, "Watchpoint::closure");
-    }
-}
-
-/* static */ void
-WatchpointMap::sweepAll(JSRuntime* rt)
-{
-    // This is called during compacting GC. Watchpoint closure pointers can be
-    // cross-compartment so we have to sweep all watchpoint maps, not just those
-    // owned by compartments we are compacting.
-    for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-        if (WatchpointMap* wpmap = c->watchpointMap)
-            wpmap->sweep();
-    }
-}
-
-void
-WatchpointMap::sweep()
-{
-    for (Map::Enum e(map); !e.empty(); e.popFront()) {
-        Map::Entry& entry = e.front();
-        if (IsAboutToBeFinalized(&entry.mutableKey().object)) {
-            MOZ_ASSERT(!entry.value().held);
-            e.removeFront();
-        }
-    }
-}
-
-void
-WatchpointMap::traceAll(WeakMapTracer* trc)
-{
-    JSRuntime* rt = trc->runtime;
-    for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
-        if (WatchpointMap* wpmap = comp->watchpointMap)
-            wpmap->trace(trc);
-    }
-}
-
-void
-WatchpointMap::trace(WeakMapTracer* trc)
-{
-    for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
-        Map::Entry& entry = r.front();
-        trc->trace(nullptr,
-                   JS::GCCellPtr(entry.key().object.get()),
-                   JS::GCCellPtr(entry.value().closure.get()));
-    }
-}
deleted file mode 100644
--- a/js/src/jswatchpoint.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jswatchpoint_h
-#define jswatchpoint_h
-
-#include "jsalloc.h"
-
-#include "gc/Barrier.h"
-#include "js/HashTable.h"
-
-namespace js {
-
-class GCMarker;
-struct WeakMapTracer;
-
-struct WatchKey {
-    WatchKey() {}
-    WatchKey(JSObject* obj, jsid id) : object(obj), id(id) {}
-    WatchKey(const WatchKey& key) : object(key.object.get()), id(key.id.get()) {}
-
-    // These are traced unconditionally during minor GC, so do not require
-    // post-barriers.
-    PreBarrieredObject object;
-    PreBarrieredId id;
-
-    bool operator!=(const WatchKey& other) const {
-        return object != other.object || id != other.id;
-    }
-};
-
-typedef bool
-(* JSWatchPointHandler)(JSContext* cx, JSObject* obj, jsid id, const JS::Value& old,
-                        JS::Value* newp, void* closure);
-
-struct Watchpoint {
-    JSWatchPointHandler handler;
-    PreBarrieredObject closure;  /* This is always marked in minor GCs and so doesn't require a postbarrier. */
-    bool held;  /* true if currently running handler */
-    Watchpoint(JSWatchPointHandler handler, JSObject* closure, bool held)
-      : handler(handler), closure(closure), held(held) {}
-};
-
-struct WatchKeyHasher
-{
-    typedef WatchKey Lookup;
-    static inline js::HashNumber hash(const Lookup& key);
-
-    static bool match(const WatchKey& k, const Lookup& l) {
-        return MovableCellHasher<PreBarrieredObject>::match(k.object, l.object) &&
-               DefaultHasher<PreBarrieredId>::match(k.id, l.id);
-    }
-
-    static void rekey(WatchKey& k, const WatchKey& newKey) {
-        k.object.unsafeSet(newKey.object);
-        k.id.unsafeSet(newKey.id);
-    }
-};
-
-class WatchpointMap {
-  public:
-    typedef HashMap<WatchKey, Watchpoint, WatchKeyHasher, SystemAllocPolicy> Map;
-
-    bool init();
-    bool watch(JSContext* cx, HandleObject obj, HandleId id,
-               JSWatchPointHandler handler, HandleObject closure);
-    void unwatch(JSObject* obj, jsid id);
-    void unwatchObject(JSObject* obj);
-    void clear();
-
-    bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
-
-    bool markIteratively(GCMarker* marker);
-    void trace(JSTracer* trc);
-
-    static void sweepAll(JSRuntime* rt);
-    void sweep();
-
-    static void traceAll(WeakMapTracer* trc);
-    void trace(WeakMapTracer* trc);
-
-  private:
-    Map map;
-};
-
-} // namespace js
-
-#endif /* jswatchpoint_h */
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -316,20 +316,16 @@ class JS_FRIEND_API(SecurityWrapper) : p
     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
     virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
     // against.
 
-    virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
-                       JS::HandleObject callable) const override;
-    virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
-
     /*
      * Allow our subclasses to select the superclass behavior they want without
      * needing to specify an exact superclass.
      */
     typedef Base Permissive;
     typedef SecurityWrapper<Base> Restrictive;
 };
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -286,17 +286,16 @@ UNIFIED_SOURCES += [
     'jsnativestack.cpp',
     'jsnum.cpp',
     'jsobj.cpp',
     'json.cpp',
     'jsopcode.cpp',
     'jsprf.cpp',
     'jsscript.cpp',
     'jsstr.cpp',
-    'jswatchpoint.cpp',
     'jsweakmap.cpp',
     'perf/jsperf.cpp',
     'proxy/BaseProxyHandler.cpp',
     'proxy/CrossCompartmentWrapper.cpp',
     'proxy/DeadObjectProxy.cpp',
     'proxy/OpaqueCrossCompartmentWrapper.cpp',
     'proxy/Proxy.cpp',
     'proxy/ScriptedProxyHandler.cpp',
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -119,29 +119,10 @@ SecurityWrapper<Base>::defineProperty(JS
         JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr,
                                JSMSG_ACCESSOR_DEF_DENIED, prop);
         return false;
     }
 
     return Base::defineProperty(cx, wrapper, id, desc, result);
 }
 
-template <class Base>
-bool
-SecurityWrapper<Base>::watch(JSContext* cx, HandleObject proxy,
-                             HandleId id, HandleObject callable) const
-{
-    ReportAccessDenied(cx);
-    return false;
-}
-
-template <class Base>
-bool
-SecurityWrapper<Base>::unwatch(JSContext* cx, HandleObject proxy,
-                               HandleId id) const
-{
-    ReportAccessDenied(cx);
-    return false;
-}
-
-
 template class js::SecurityWrapper<Wrapper>;
 template class js::SecurityWrapper<CrossCompartmentWrapper>;
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -317,21 +317,21 @@ NativeObject::ensureDenseInitializedLeng
 DenseElementResult
 NativeObject::extendDenseElements(JSContext* cx,
                                   uint32_t requiredCapacity, uint32_t extra)
 {
     MOZ_ASSERT(!denseElementsAreCopyOnWrite());
     MOZ_ASSERT(!denseElementsAreFrozen());
 
     /*
-     * Don't grow elements for non-extensible objects or watched objects. Dense
-     * elements can be added/written with no extensible or watchpoint checks as
-     * long as there is capacity for them.
+     * Don't grow elements for non-extensible objects. Dense elements can be
+     * added/written with no extensible checks as long as there is capacity
+     * for them.
      */
-    if (!nonProxyIsExtensible() || watched()) {
+    if (!nonProxyIsExtensible()) {
         MOZ_ASSERT(getDenseCapacity() == 0);
         return DenseElementResult::Incomplete;
     }
 
     /*
      * Don't grow elements for objects which already have sparse indexes.
      * This avoids needing to count non-hole elements in willBeSparseElements
      * every time a new index is added.
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -5,18 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/NativeObject-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
-#include "jswatchpoint.h"
-
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
@@ -590,17 +588,17 @@ NativeObject::maybeDensifySparseElements
      * Only measure the number of indexed properties every log(n) times when
      * populating the object.
      */
     uint32_t slotSpan = obj->slotSpan();
     if (slotSpan != RoundUpPow2(slotSpan))
         return DenseElementResult::Incomplete;
 
     /* Watch for conditions under which an object's elements cannot be dense. */
-    if (!obj->nonProxyIsExtensible() || obj->watched())
+    if (!obj->nonProxyIsExtensible())
         return DenseElementResult::Incomplete;
 
     /*
      * The indexes in the object need to be sufficiently dense before they can
      * be converted to dense mode.
      */
     uint32_t numDenseElements = 0;
     uint32_t newInitializedLength = 0;
@@ -2732,27 +2730,19 @@ SetExistingProperty(JSContext* cx, Handl
     if (!js::CallSetter(cx, receiver, setter, v))
         return false;
 
     return result.succeed();
 }
 
 template <QualifiedBool IsQualified>
 bool
-js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
+js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
                       HandleValue receiver, ObjectOpResult& result)
 {
-    // Fire watchpoints, if any.
-    RootedValue v(cx, value);
-    if (MOZ_UNLIKELY(obj->watched())) {
-        WatchpointMap* wpmap = cx->compartment()->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v))
-            return false;
-    }
-
     // Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal
     // method for ordinary objects. We substitute our own names for these names
     // used in the spec: O -> pobj, P -> id, ownDesc -> shape.
     Rooted<PropertyResult> prop(cx);
     RootedNativeObject pobj(cx, obj);
 
     // This loop isn't explicit in the spec algorithm. See the comment on step
     // 4.c.i below. (There's a very similar loop in the NativeGetProperty
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -726,19 +726,16 @@ class NativeObject : public ShapedObject
     /*
      * The methods below shadow methods on JSObject and are more efficient for
      * known-native objects.
      */
     bool hasAllFlags(js::BaseShape::Flag flags) const {
         MOZ_ASSERT(flags);
         return shape_->hasAllObjectFlags(flags);
     }
-    bool watched() const {
-        return hasAllFlags(js::BaseShape::WATCHED);
-    }
     bool nonProxyIsExtensible() const {
         return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
     }
 
     /*
      * Whether there may be indexed properties on this object, excluding any in
      * the object's elements.
      */
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -24,17 +24,16 @@
 # include <sys/mman.h>
 #endif
 
 #include "jsatom.h"
 #include "jsgc.h"
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jsscript.h"
-#include "jswatchpoint.h"
 #include "jswin.h"
 #include "jswrapper.h"
 
 #include "builtin/Promise.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/JitCompartment.h"
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -476,17 +476,17 @@ class BaseShape : public gc::TenuredCell
          * JSObject::dump to dump it as part of object representation.
          */
 
         DELEGATE            =    0x8,
         NOT_EXTENSIBLE      =   0x10,
         INDEXED             =   0x20,
         HAS_INTERESTING_SYMBOL = 0x40,
         HAD_ELEMENTS_ACCESS =   0x80,
-        WATCHED             =  0x100,
+        // 0x100 is unused.
         ITERATED_SINGLETON  =  0x200,
         NEW_GROUP_UNKNOWN   =  0x400,
         UNCACHEABLE_PROTO   =  0x800,
         IMMUTABLE_PROTOTYPE = 0x1000,
 
         // See JSObject::isQualifiedVarObj().
         QUALIFIED_VAROBJ    = 0x2000,
 
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2719,24 +2719,16 @@ ObjectGroup::updateNewPropertyTypes(JSCo
             }
         }
     } else if (!JSID_IS_EMPTY(id)) {
         RootedId rootedId(cx, id);
         Shape* shape = obj->lookup(cx, rootedId);
         if (shape)
             UpdatePropertyType(cx, types, obj, shape, false);
     }
-
-    if (obj->watched()) {
-        /*
-         * Mark the property as non-data, to inhibit optimizations on it
-         * and avoid bypassing the watchpoint handler.
-         */
-        types->setNonDataProperty(cx);
-    }
 }
 
 void
 ObjectGroup::addDefiniteProperties(JSContext* cx, Shape* shape)
 {
     if (unknownProperties())
         return;