Bug 749371 - Break JS_ClearScope into two not-as-bad functions (r=mrbkap)
authorLuke Wagner <luke@mozilla.com>
Thu, 23 Aug 2012 11:23:28 -0700
changeset 103602 104671eaadb84738adcb5b5b4a03379367ad9367
parent 103601 dbeed4d1e2045bba919ea4284307bf2aac061893
child 103603 e208bf8354c950401a9b2457e80d32921eb75478
push id14072
push userlwagner@mozilla.com
push dateMon, 27 Aug 2012 21:48:54 +0000
treeherdermozilla-inbound@e208bf8354c9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs749371
milestone17.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 749371 - Break JS_ClearScope into two not-as-bad functions (r=mrbkap)
ipc/testshell/XPCShellEnvironment.cpp
js/src/builtin/ParallelArray.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsclass.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsproxy.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/vm/ScopeObject.cpp
js/xpconnect/loader/mozJSComponentLoader.h
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/dom_quickstubs.qsconf
js/xpconnect/src/xpcprivate.h
js/xpconnect/tests/unit/test_unload.js
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -966,17 +966,17 @@ XPCShellEnvironment::XPCShellEnvironment
 
 XPCShellEnvironment::~XPCShellEnvironment()
 {
     if (mCx) {
         JS_BeginRequest(mCx);
 
         JSObject* global = GetGlobalObject();
         if (global) {
-            JS_ClearScope(mCx, global);
+            JS_SetAllNonReservedSlotsToUndefined(mCx, global);
         }
         mGlobalHolder.Release();
 
         JSRuntime *rt = JS_GetRuntime(mCx);
         JS_GC(rt);
 
         mCxStack = nullptr;
 
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -799,17 +799,16 @@ Class ParallelArrayObject::class_ = {
         setElementAttributes,
         setSpecialAttributes,
         deleteProperty,
         deleteElement,
         deleteSpecial,
         enumerate,
         NULL,                // typeof
         NULL,                // thisObject
-        NULL,                // clear
     }
 };
 
 JSObject *
 ParallelArrayObject::initClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4478,36 +4478,74 @@ JS_DeleteElement(JSContext *cx, JSObject
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteProperty(JSContext *cx, JSObject *objArg, const char *name)
 {
     jsval junk;
     return JS_DeleteProperty2(cx, objArg, name, &junk);
 }
 
+static Shape *
+LastConfigurableShape(JSObject *obj)
+{
+    for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
+        Shape *shape = &r.front();
+        if (shape->configurable())
+            return shape;
+    }
+    return NULL;
+}
+
 JS_PUBLIC_API(void)
-JS_ClearScope(JSContext *cx, JSObject *objArg)
+JS_ClearNonGlobalObject(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    ClearOp clearOp = obj->getOps()->clear;
-    if (clearOp)
-        clearOp(cx, obj);
-
-    if (obj->isNative())
-        js_ClearNative(cx, obj);
-
-    /* Clear cached class objects on the global object. */
-    if (obj->isGlobal())
-        obj->asGlobal().clear(cx);
-
-    js_InitRandom(cx);
+    JS_ASSERT(!obj->isGlobal());
+
+    if (!obj->isNative())
+        return;
+
+    /* Remove all configurable properties from obj. */
+    while (Shape *shape = LastConfigurableShape(obj)) {
+        if (!obj->removeProperty(cx, shape->propid()))
+            return;
+    }
+
+    /* Set all remaining writable plain data properties to undefined. */
+    for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
+        Shape *shape = &r.front();
+        if (shape->isDataDescriptor() &&
+            shape->writable() &&
+            shape->hasDefaultSetter() &&
+            shape->hasSlot()) {
+            obj->nativeSetSlot(shape->slot(), UndefinedValue());
+        }
+    }
+}
+
+JS_PUBLIC_API(void)
+JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg)
+{
+    RootedObject obj(cx, objArg);
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    if (!obj->isNative())
+        return;
+
+    Class *clasp = obj->getClass();
+    unsigned numReserved = JSCLASS_RESERVED_SLOTS(clasp);
+    unsigned numSlots = obj->slotSpan();
+    for (unsigned i = numReserved; i < numSlots; i++)
+        obj->setSlot(i, UndefinedValue());
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4772,18 +4772,29 @@ extern JS_PUBLIC_API(JSBool)
 JS_SetElement(JSContext *cx, JSObject *obj, uint32_t index, jsval *vp);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DeleteElement2(JSContext *cx, JSObject *obj, uint32_t index, jsval *rval);
 
-extern JS_PUBLIC_API(void)
-JS_ClearScope(JSContext *cx, JSObject *obj);
+/*
+ * Remove all configurable properties from the given (non-global) object and
+ * assign undefined to all writable data properties.
+ */
+JS_PUBLIC_API(void)
+JS_ClearNonGlobalObject(JSContext *cx, JSObject *objArg);
+
+/*
+ * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
+ * done for all slots, regardless of the associated property descriptor.
+ */
+JS_PUBLIC_API(void)
+JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg);
 
 extern JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj);
 
 /*
  * Create an object to iterate over enumerable properties of obj, in arbitrary
  * property definition order.  NB: This differs from longstanding for..in loop
  * order, which uses order of property definition in obj.
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1189,17 +1189,16 @@ Class js::ArrayClass = {
         array_setElementAttributes,
         array_setSpecialAttributes,
         array_deleteProperty,
         array_deleteElement,
         array_deleteSpecial,
         NULL,       /* enumerate      */
         array_typeOf,
         NULL,       /* thisObject     */
-        NULL,       /* clear          */
     }
 };
 
 Class js::SlowArrayClass = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     slowarray_addProperty,
     JS_PropertyStub,         /* delProperty */
--- a/js/src/jsclass.h
+++ b/js/src/jsclass.h
@@ -205,18 +205,16 @@ typedef JSBool
 typedef JSBool
 (* DeleteSpecialOp)(JSContext *cx, HandleObject obj, HandleSpecialId sid, MutableHandleValue vp, JSBool strict);
 typedef JSType
 (* TypeOfOp)(JSContext *cx, HandleObject obj);
 
 typedef JSObject *
 (* ObjectOp)(JSContext *cx, HandleObject obj);
 typedef void
-(* ClearOp)(JSContext *cx, HandleObject obj);
-typedef void
 (* FinalizeOp)(FreeOp *fop, JSObject *obj);
 
 #define JS_CLASS_MEMBERS                                                      \
     const char          *name;                                                \
     uint32_t            flags;                                                \
                                                                               \
     /* Mandatory non-null function pointer members. */                        \
     JSPropertyOp        addProperty;                                          \
@@ -290,23 +288,22 @@ struct ObjectOps
     SpecialAttributesOp setSpecialAttributes;
     DeletePropertyOp    deleteProperty;
     DeleteElementOp     deleteElement;
     DeleteSpecialOp     deleteSpecial;
 
     JSNewEnumerateOp    enumerate;
     TypeOfOp            typeOf;
     ObjectOp            thisObject;
-    ClearOp             clear;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
     {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,   \
      NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,        \
-     NULL,NULL,NULL,NULL,NULL}
+     NULL,NULL,NULL,NULL}
 
 struct Class
 {
     JS_CLASS_MEMBERS;
     ClassExtension      ext;
     ObjectOps           ops;
     uint8_t             pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) -
                             sizeof(ClassExtension) - sizeof(ObjectOps)];
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5439,49 +5439,16 @@ js_GetObjectSlotName(JSTracer *trc, char
         } else if (JSID_IS_ATOM(propid)) {
             PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
         } else {
             JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
         }
     }
 }
 
-static Shape *
-LastConfigurableShape(JSObject *obj)
-{
-    for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
-        Shape *shape = &r.front();
-        if (shape->configurable())
-            return shape;
-    }
-    return NULL;
-}
-
-bool
-js_ClearNative(JSContext *cx, JSObject *obj)
-{
-    /* Remove all configurable properties from obj. */
-    while (Shape *shape = LastConfigurableShape(obj)) {
-        if (!obj->removeProperty(cx, shape->propid()))
-            return false;
-    }
-
-    /* Set all remaining writable plain data properties to undefined. */
-    for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
-        Shape *shape = &r.front();
-        if (shape->isDataDescriptor() &&
-            shape->writable() &&
-            shape->hasDefaultSetter() &&
-            shape->hasSlot()) {
-            obj->nativeSetSlot(shape->slot(), UndefinedValue());
-        }
-    }
-    return true;
-}
-
 JSBool
 js_ReportGetterOnlyAssignment(JSContext *cx)
 {
     return JS_ReportErrorFlagsAndNumber(cx,
                                         JSREPORT_WARNING | JSREPORT_STRICT |
                                         JSREPORT_STRICT_MODE_ERROR,
                                         js_GetErrorMessage, NULL,
                                         JSMSG_GETTER_ONLY);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1391,19 +1391,16 @@ ToObjectFromStack(JSContext *cx, HandleV
     return ToObjectSlow(cx, vp, true);
 }
 
 } /* namespace js */
 
 extern void
 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize);
 
-extern bool
-js_ClearNative(JSContext *cx, JSObject *obj);
-
 extern JSBool
 js_ReportGetterOnlyAssignment(JSContext *cx);
 
 extern unsigned
 js_InferFlags(JSContext *cx, unsigned defaultFlags);
 
 /* Object constructor native. Exposed only so the JIT can know its address. */
 JSBool
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1781,17 +1781,16 @@ JS_FRIEND_DATA(Class) js::ObjectProxyCla
         proxy_SetElementAttributes,
         proxy_SetSpecialAttributes,
         proxy_DeleteProperty,
         proxy_DeleteElement,
         proxy_DeleteSpecial,
         NULL,                /* enumerate       */
         proxy_TypeOf,
         NULL,                /* thisObject      */
-        NULL,                /* clear           */
     }
 };
 
 JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = {
     "Proxy",
     Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
@@ -1839,17 +1838,16 @@ JS_FRIEND_DATA(Class) js::OuterWindowPro
         proxy_SetElementAttributes,
         proxy_SetSpecialAttributes,
         proxy_DeleteProperty,
         proxy_DeleteElement,
         proxy_DeleteSpecial,
         NULL,                /* enumerate       */
         NULL,                /* typeof          */
         NULL,                /* thisObject      */
-        NULL,                /* clear           */
     }
 };
 
 static JSBool
 proxy_Call(JSContext *cx, unsigned argc, Value *vp)
 {
     JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
     JS_ASSERT(proxy->isProxy());
@@ -1909,17 +1907,16 @@ JS_FRIEND_DATA(Class) js::FunctionProxyC
         proxy_SetElementAttributes,
         proxy_SetSpecialAttributes,
         proxy_DeleteProperty,
         proxy_DeleteElement,
         proxy_DeleteSpecial,
         NULL,                /* enumerate       */
         proxy_TypeOf,
         NULL,                /* thisObject      */
-        NULL,                /* clear           */
     }
 };
 
 JS_FRIEND_API(JSObject *)
 js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_, JSObject *proto_,
                    JSObject *parent_, JSObject *call_, JSObject *construct_)
 {
     RootedValue priv(cx, priv_);
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -2826,17 +2826,16 @@ Class js::ArrayBufferClass = {
         ArrayBufferObject::obj_setElementAttributes,
         ArrayBufferObject::obj_setSpecialAttributes,
         ArrayBufferObject::obj_deleteProperty,
         ArrayBufferObject::obj_deleteElement,
         ArrayBufferObject::obj_deleteSpecial,
         ArrayBufferObject::obj_enumerate,
         ArrayBufferObject::obj_typeOf,
         NULL,       /* thisObject      */
-        NULL,       /* clear           */
     }
 };
 
 JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
     JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
     JS_FS_END
 };
 
@@ -2998,17 +2997,16 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Flo
         _typedArray::obj_setElementAttributes,                                 \
         _typedArray::obj_setSpecialAttributes,                                 \
         _typedArray::obj_deleteProperty,                                       \
         _typedArray::obj_deleteElement,                                        \
         _typedArray::obj_deleteSpecial,                                        \
         _typedArray::obj_enumerate,                                            \
         _typedArray::obj_typeOf,                                               \
         NULL,                /* thisObject  */                                 \
-        NULL,                /* clear       */                                 \
     }                                                                          \
 }
 
 template<class ArrayType>
 static inline JSObject *
 InitTypedArrayClass(JSContext *cx)
 {
     Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -5131,21 +5131,16 @@ xml_trace(JSTracer *trc, JSObject *obj)
      * to fix somehow for generational.
      */
     if (xml) {
         MarkXMLUnbarriered(trc, &xml, "private");
         JS_ASSERT(xml == obj->getPrivate());
     }
 }
 
-static void
-xml_clear(JSContext *cx, HandleObject obj)
-{
-}
-
 static JSBool
 HasSimpleContent(JSXML *xml)
 {
     JSXML *kid;
     JSBool simple;
     uint32_t i, n;
 
 again:
@@ -5355,17 +5350,16 @@ JS_FRIEND_DATA(Class) js::XMLClass = {
         xml_setElementAttributes,
         xml_setSpecialAttributes,
         xml_deleteProperty,
         xml_deleteElement,
         xml_deleteSpecial,
         xml_enumerate,
         xml_typeOf,
         NULL,       /* thisObject     */
-        xml_clear
     }
 };
 
 static JSXML *
 StartNonListXMLMethod(JSContext *cx, jsval *vp, MutableHandleObject objp)
 {
     JSXML *xml;
     JSFunction *fun;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -545,17 +545,16 @@ Class js::WithClass = {
         with_SetElementAttributes,
         with_SetSpecialAttributes,
         with_DeleteProperty,
         with_DeleteElement,
         with_DeleteSpecial,
         with_Enumerate,
         with_TypeOf,
         with_ThisObject,
-        NULL,             /* clear */
     }
 };
 
 /*****************************************************************************/
 
 ClonedBlockObject *
 ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, StackFrame *fp)
 {
--- a/js/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -95,17 +95,17 @@ class mozJSComponentLoader : public mozi
         void Clear() {
             getfactoryobj = NULL;
 
             if (global) {
                 JSAutoRequest ar(sSelf->mContext);
 
                 JSAutoCompartment ac(sSelf->mContext, global);
 
-                JS_ClearScope(sSelf->mContext, global);
+                JS_SetAllNonReservedSlotsToUndefined(sSelf->mContext, global);
                 JS_RemoveObjectRoot(sSelf->mContext, &global);
             }
 
             if (location)
                 NS_Free(location);
 
             global = NULL;
             location = NULL;
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -1915,17 +1915,17 @@ main(int argc, char **argv, char **envp)
 
 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
             // test of late call and release (see below)
             nsCOMPtr<nsIJSContextStack> bogus;
             xpc->WrapJS(cx, glob, NS_GET_IID(nsIJSContextStack),
                         (void**) getter_AddRefs(bogus));
 #endif
             JS_DropPrincipals(rt, gJSPrincipals);
-            JS_ClearScope(cx, glob);
+            JS_SetAllNonReservedSlotsToUndefined(cx, glob);
             JS_GC(rt);
             JSContext *oldcx;
             cxstack->Pop(&oldcx);
             NS_ASSERTION(oldcx == cx, "JS thread context push/pop mismatch");
             cxstack = nullptr;
             JS_GC(rt);
         } //this scopes the JSAutoCrossCompartmentCall
         JS_EndRequest(cx);
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -833,17 +833,16 @@ XPCWrappedNativeJSClass XPC_WN_NoHelper_
         nullptr, // setElementAttributes
         nullptr, // setSpecialAttributes
         nullptr, // deleteProperty
         nullptr, // deleteElement
         nullptr, // deleteSpecial
         XPC_WN_JSOp_Enumerate,
         XPC_WN_JSOp_TypeOf_Object,
         XPC_WN_JSOp_ThisObject,
-        XPC_WN_JSOp_Clear
     }
   },
   0 // interfacesBitmap
 };
 
 
 /***************************************************************************/
 
@@ -1231,22 +1230,16 @@ XPC_WN_JSOp_TypeOf_Object(JSContext *cx,
 }
 
 JSType
 XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSHandleObject obj)
 {
     return JSTYPE_FUNCTION;
 }
 
-void
-XPC_WN_JSOp_Clear(JSContext *cx, JSHandleObject obj)
-{
-    // XXX Clear XrayWrappers?
-}
-
 namespace {
 
 NS_STACK_CLASS class AutoPopJSContext
 {
 public:
   AutoPopJSContext(XPCJSContextStack *stack)
   : mCx(nullptr), mStack(stack)
   {
@@ -1399,17 +1392,16 @@ XPCNativeScriptableShared::PopulateJSCla
 
     // Note that we *must* set the ObjectOps (even for the cases were it does
     // not do much) because with these dynamically generated JSClasses, the
     // code in XPCWrappedNative::GetWrappedNativeOfJSObject() needs to look
     // for that these callback pointers in order to identify that a given
     // JSObject represents a wrapper.
     js::ObjectOps *ops = &mJSClass.base.ops;
     ops->enumerate = XPC_WN_JSOp_Enumerate;
-    ops->clear = XPC_WN_JSOp_Clear;
     ops->thisObject = XPC_WN_JSOp_ThisObject;
 
     if (mFlags.WantCall() || mFlags.WantConstruct()) {
         ops->typeOf = XPC_WN_JSOp_TypeOf_Function;
         if (mFlags.WantCall())
             mJSClass.base.call = XPC_WN_Helper_Call;
         if (mFlags.WantConstruct())
             mJSClass.base.construct = XPC_WN_Helper_Construct;
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -86,18 +86,18 @@ XPCWrappedNativeScope::GetNewOrUsed(XPCC
 
     XPCWrappedNativeScope* scope = FindInJSObjectScope(ccx, aGlobal, true);
     if (!scope)
         scope = new XPCWrappedNativeScope(ccx, aGlobal, aNative);
     else {
         // We need to call SetGlobal in order to refresh our cached
         // mPrototypeJSObject and to clear mPrototypeNoHelper (so we get a new
         // new one if requested in the new scope) in the case where the global
-        // object is being reused (JS_ClearScope has been called).  NOTE: We are
-        // only called by nsXPConnect::InitClasses.
+        // object is being reused (JS_SetAllNonReservedSlotsToUndefined has
+        // been called).  NOTE: We are only called by nsXPConnect::InitClasses.
         scope->SetGlobal(ccx, aGlobal, aNative);
     }
     if (js::GetObjectClass(aGlobal)->flags & JSCLASS_XPCONNECT_GLOBAL)
         JS_SetReservedSlot(aGlobal,
                            JSCLASS_GLOBAL_SLOT_COUNT,
                            PRIVATE_TO_JSVAL(scope));
     return scope;
 }
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -558,17 +558,17 @@ nsIDOMHTMLDocument_Write_customMethodCal
     }
 
     rv = self->%s(arg0, cx);
 """
 
 nsIDOMStorage_Clear_customMethodCallCode = """
     rv = self->Clear();
     if (NS_SUCCEEDED(rv))
-        JS_ClearScope(cx, obj);
+        JS_ClearNonGlobalObject(cx, obj);
 """
 
 CUSTOM_QS = {
     'skipgen': True,
     'traceable': False
 }
 
 CUSTOM_QS_TN = {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1454,93 +1454,88 @@ XPC_WN_JSOp_Enumerate(JSContext *cx, JSH
                       jsval *statep, jsid *idp);
 
 extern JSType
 XPC_WN_JSOp_TypeOf_Object(JSContext *cx, JSHandleObject obj);
 
 extern JSType
 XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSHandleObject obj);
 
-extern void
-XPC_WN_JSOp_Clear(JSContext *cx, JSHandleObject obj);
-
 extern JSObject*
 XPC_WN_JSOp_ThisObject(JSContext *cx, JSHandleObject obj);
 
 // Macros to initialize Object or Function like XPC_WN classes
 #define XPC_WN_WithCall_ObjectOps                                             \
     {                                                                         \
-        nullptr, /* lookupGeneric */                                           \
-        nullptr, /* lookupProperty */                                          \
-        nullptr, /* lookupElement */                                           \
-        nullptr, /* lookupSpecial */                                           \
-        nullptr, /* defineGeneric */                                           \
-        nullptr, /* defineProperty */                                          \
-        nullptr, /* defineElement */                                           \
-        nullptr, /* defineSpecial */                                           \
-        nullptr, /* getGeneric    */                                           \
-        nullptr, /* getProperty    */                                          \
-        nullptr, /* getElement    */                                           \
-        nullptr, /* getElementIfPresent */                                     \
-        nullptr, /* getSpecial    */                                           \
-        nullptr, /* setGeneric    */                                           \
-        nullptr, /* setProperty    */                                          \
-        nullptr, /* setElement    */                                           \
-        nullptr, /* setSpecial    */                                           \
-        nullptr, /* getGenericAttributes  */                                   \
-        nullptr, /* getAttributes  */                                          \
-        nullptr, /* getElementAttributes  */                                   \
-        nullptr, /* getSpecialAttributes  */                                   \
-        nullptr, /* setGenericAttributes  */                                   \
-        nullptr, /* setAttributes  */                                          \
-        nullptr, /* setElementAttributes  */                                   \
-        nullptr, /* setSpecialAttributes  */                                   \
-        nullptr, /* deleteProperty */                                          \
-        nullptr, /* deleteElement */                                           \
-        nullptr, /* deleteSpecial */                                           \
+        nullptr, /* lookupGeneric */                                          \
+        nullptr, /* lookupProperty */                                         \
+        nullptr, /* lookupElement */                                          \
+        nullptr, /* lookupSpecial */                                          \
+        nullptr, /* defineGeneric */                                          \
+        nullptr, /* defineProperty */                                         \
+        nullptr, /* defineElement */                                          \
+        nullptr, /* defineSpecial */                                          \
+        nullptr, /* getGeneric    */                                          \
+        nullptr, /* getProperty    */                                         \
+        nullptr, /* getElement    */                                          \
+        nullptr, /* getElementIfPresent */                                    \
+        nullptr, /* getSpecial    */                                          \
+        nullptr, /* setGeneric    */                                          \
+        nullptr, /* setProperty    */                                         \
+        nullptr, /* setElement    */                                          \
+        nullptr, /* setSpecial    */                                          \
+        nullptr, /* getGenericAttributes  */                                  \
+        nullptr, /* getAttributes  */                                         \
+        nullptr, /* getElementAttributes  */                                  \
+        nullptr, /* getSpecialAttributes  */                                  \
+        nullptr, /* setGenericAttributes  */                                  \
+        nullptr, /* setAttributes  */                                         \
+        nullptr, /* setElementAttributes  */                                  \
+        nullptr, /* setSpecialAttributes  */                                  \
+        nullptr, /* deleteProperty */                                         \
+        nullptr, /* deleteElement */                                          \
+        nullptr, /* deleteSpecial */                                          \
         XPC_WN_JSOp_Enumerate,                                                \
         XPC_WN_JSOp_TypeOf_Function,                                          \
         XPC_WN_JSOp_ThisObject,                                               \
-        XPC_WN_JSOp_Clear                                                     \
     }
 
 #define XPC_WN_NoCall_ObjectOps                                               \
     {                                                                         \
-        nullptr, /* lookupGeneric */                                           \
-        nullptr, /* lookupProperty */                                          \
-        nullptr, /* lookupElement */                                           \
-        nullptr, /* lookupSpecial */                                           \
-        nullptr, /* defineGeneric */                                           \
-        nullptr, /* defineProperty */                                          \
-        nullptr, /* defineElement */                                           \
-        nullptr, /* defineSpecial */                                           \
-        nullptr, /* getGeneric    */                                           \
-        nullptr, /* getProperty    */                                          \
-        nullptr, /* getElement    */                                           \
-        nullptr, /* getElementIfPresent */                                     \
-        nullptr, /* getSpecial    */                                           \
-        nullptr, /* setGeneric    */                                           \
-        nullptr, /* setProperty    */                                          \
-        nullptr, /* setElement    */                                           \
-        nullptr, /* setSpecial    */                                           \
-        nullptr, /* getGenericAttributes  */                                   \
-        nullptr, /* getAttributes  */                                          \
-        nullptr, /* getElementAttributes  */                                   \
-        nullptr, /* getSpecialAttributes  */                                   \
-        nullptr, /* setGenericAttributes  */                                   \
-        nullptr, /* setAttributes  */                                          \
-        nullptr, /* setElementAttributes  */                                   \
-        nullptr, /* setSpecialAttributes  */                                   \
-        nullptr, /* deleteProperty */                                          \
-        nullptr, /* deleteElement */                                           \
-        nullptr, /* deleteSpecial */                                           \
+        nullptr, /* lookupGeneric */                                          \
+        nullptr, /* lookupProperty */                                         \
+        nullptr, /* lookupElement */                                          \
+        nullptr, /* lookupSpecial */                                          \
+        nullptr, /* defineGeneric */                                          \
+        nullptr, /* defineProperty */                                         \
+        nullptr, /* defineElement */                                          \
+        nullptr, /* defineSpecial */                                          \
+        nullptr, /* getGeneric    */                                          \
+        nullptr, /* getProperty    */                                         \
+        nullptr, /* getElement    */                                          \
+        nullptr, /* getElementIfPresent */                                    \
+        nullptr, /* getSpecial    */                                          \
+        nullptr, /* setGeneric    */                                          \
+        nullptr, /* setProperty    */                                         \
+        nullptr, /* setElement    */                                          \
+        nullptr, /* setSpecial    */                                          \
+        nullptr, /* getGenericAttributes  */                                  \
+        nullptr, /* getAttributes  */                                         \
+        nullptr, /* getElementAttributes  */                                  \
+        nullptr, /* getSpecialAttributes  */                                  \
+        nullptr, /* setGenericAttributes  */                                  \
+        nullptr, /* setAttributes  */                                         \
+        nullptr, /* setElementAttributes  */                                  \
+        nullptr, /* setSpecialAttributes  */                                  \
+        nullptr, /* deleteProperty */                                         \
+        nullptr, /* deleteElement */                                          \
+        nullptr, /* deleteSpecial */                                          \
         XPC_WN_JSOp_Enumerate,                                                \
         XPC_WN_JSOp_TypeOf_Object,                                            \
         XPC_WN_JSOp_ThisObject,                                               \
-        XPC_WN_JSOp_Clear                                                     \
     }
 
 // Maybe this macro should check for class->enumerate ==
 // XPC_WN_Shared_Proto_Enumerate or something rather than checking for
 // 4 classes?
 static inline bool IS_PROTO_CLASS(js::Class *clazz)
 {
     return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
--- a/js/xpconnect/tests/unit/test_unload.js
+++ b/js/xpconnect/tests/unit/test_unload.js
@@ -15,17 +15,14 @@ function run_test() {
   Components.utils.unload("resource://gre/modules/NetUtil.jsm");
 
   var scope3 = {};
   var global3 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope3);
 
   do_check_false(global1 === global3);
   do_check_false(scope1.NetUtil === scope3.NetUtil);
 
-  // Both instances should work
-  uri1 = scope1.NetUtil.newURI("http://www.example.com");
-  do_check_true(uri1 instanceof Components.interfaces.nsIURL);
-
-  var uri3 = scope3.NetUtil.newURI("http://www.example.com");
-  do_check_true(uri3 instanceof Components.interfaces.nsIURL);
-
-  do_check_true(uri1.equals(uri3));
+  // When the jsm was unloaded, the value of all its global's properties were
+  // set to undefined. While it must be safe (not crash) to call into the
+  // module, we expect it to throw an error (e.g., when trying to use Ci).
+  try { scope1.NetUtil.newURI("http://www.example.com"); } catch (e) {}
+  try { scope3.NetUtil.newURI("http://www.example.com"); } catch (e) {}
 }