Bug 650161 - Use the object moved hook to update the wrapper cache r=terrence r=bz r=bholley

This commit is contained in:
Jon Coppeard 2014-09-17 15:35:11 +01:00
parent 51efcd8004
commit c60aa1033f
12 changed files with 212 additions and 57 deletions

View File

@ -668,10 +668,12 @@ public:
unsigned flags,
JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
static void ObjectMoved(JSObject *obj, const JSObject *old);
static const nsOuterWindowProxy singleton;
protected:
nsGlobalWindow* GetWindow(JSObject *proxy) const
static nsGlobalWindow* GetWindow(JSObject *proxy)
{
return nsGlobalWindow::FromSupports(
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
@ -703,7 +705,8 @@ const js::Class OuterWindowProxyClass =
nullptr, /* outerObject */
js::proxy_innerObject,
nullptr, /* iteratorObject */
false /* isWrappedNative */
false, /* isWrappedNative */
nsOuterWindowProxy::ObjectMoved
));
bool
@ -1023,6 +1026,15 @@ nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
return js::UnwatchGuts(cx, proxy, id);
}
void
nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
{
nsGlobalWindow* global = GetWindow(obj);
if (global) {
global->UpdateWrapper(obj, old);
}
}
const nsOuterWindowProxy
nsOuterWindowProxy::singleton;

View File

@ -8,6 +8,7 @@
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Assertions.h"
#include "js/Class.h"
#include "js/Id.h" // must come before js/RootingAPI.h
#include "js/Value.h" // must come before js/RootingAPI.h
#include "js/RootingAPI.h"
@ -44,6 +45,14 @@ class XPCWrappedNativeScope;
*
* The finalizer for the wrapper clears the cache.
*
* A compacting GC can move the wrapper object. Pointers to moved objects are
* usually found and updated by tracing the heap, however non-preserved wrappers
* are weak references and are not traced, so another approach is
* necessary. Instead a class hook (objectMovedOp) is provided that is called
* when an object is moved and is responsible for ensuring pointers are
* updated. It does this by calling UpdateWrapper() on the wrapper
* cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
*
* A number of the methods are implemented in nsWrapperCacheInlines.h because we
* have to include some JS headers that don't play nicely with the rest of the
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
@ -89,6 +98,8 @@ public:
{
MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
MOZ_ASSERT(js::HasObjectMovedOp(aWrapper),
"Object has not provided the hook to update the wrapper if it is moved");
SetWrapperJSObject(aWrapper);
}
@ -104,6 +115,18 @@ public:
SetWrapperJSObject(nullptr);
}
/**
* Update the wrapper if the object it contains is moved.
*
* This method must be called from the objectMovedOp class extension hook for
* any wrapper cached object.
*/
void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
{
MOZ_ASSERT(mWrapper == aOldObject);
mWrapper = aNewObject;
}
bool PreservingWrapper()
{
return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
@ -162,7 +185,7 @@ public:
}
}
/*
/*
* The following methods for getting and manipulating flags allow the unused
* bits of mFlags to be used by derived classes.
*/

View File

@ -1213,6 +1213,22 @@ ClearWrapper(T* p, void*)
ClearWrapper(p, cache);
}
template<class T>
inline void
UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
{
cache->UpdateWrapper(obj, old);
}
template<class T>
inline void
UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old)
{
nsWrapperCache* cache;
CallQueryInterface(p, &cache);
UpdateWrapper(p, cache, obj, old);
}
// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
// Return true if we successfully preserved the wrapper, or there is no wrapper
// to preserve. In the latter case we don't need to preserve the wrapper, because

View File

@ -17,6 +17,7 @@ AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
ADDPROPERTY_HOOK_NAME = '_addProperty'
FINALIZE_HOOK_NAME = '_finalize'
OBJECT_MOVED_HOOK_NAME = '_objectMoved'
CONSTRUCT_HOOK_NAME = '_constructor'
LEGACYCALLER_HOOK_NAME = '_legacycaller'
HASINSTANCE_HOOK_NAME = '_hasInstance'
@ -364,48 +365,61 @@ class CGDOMJSClass(CGThing):
def define(self):
traceHook = 'nullptr'
callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
classFlags = "JSCLASS_IS_DOMJSCLASS | "
classExtensionAndObjectOps = """\
JS_NULL_CLASS_EXT,
JS_NULL_OBJECT_OPS
"""
classExtensionAndObjectOps = fill(
"""
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
${objectMoved} /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
""",
objectMoved=objectMovedHook)
if self.descriptor.isGlobal():
classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
traceHook = "JS_GlobalObjectTraceHook"
reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
if not self.descriptor.workers:
classExtensionAndObjectOps = """\
{
nsGlobalWindow::OuterObject, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr /* weakmapKeyDelegateOp */
},
{
nullptr, /* lookupGeneric */
nullptr, /* lookupProperty */
nullptr, /* lookupElement */
nullptr, /* defineGeneric */
nullptr, /* defineProperty */
nullptr, /* defineElement */
nullptr, /* getGeneric */
nullptr, /* getProperty */
nullptr, /* getElement */
nullptr, /* setGeneric */
nullptr, /* setProperty */
nullptr, /* setElement */
nullptr, /* getGenericAttributes */
nullptr, /* setGenericAttributes */
nullptr, /* deleteGeneric */
nullptr, /* watch */
nullptr, /* unwatch */
nullptr, /* slice */
nullptr, /* enumerate */
JS_ObjectToOuterObject /* thisObject */
}
"""
classExtensionAndObjectOps = fill(
"""
{
nsGlobalWindow::OuterObject, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
${objectMoved} /* objectMovedOp */
},
{
nullptr, /* lookupGeneric */
nullptr, /* lookupProperty */
nullptr, /* lookupElement */
nullptr, /* defineGeneric */
nullptr, /* defineProperty */
nullptr, /* defineElement */
nullptr, /* getGeneric */
nullptr, /* getProperty */
nullptr, /* getElement */
nullptr, /* setGeneric */
nullptr, /* setProperty */
nullptr, /* setElement */
nullptr, /* getGenericAttributes */
nullptr, /* setGenericAttributes */
nullptr, /* deleteGeneric */
nullptr, /* watch */
nullptr, /* unwatch */
nullptr, /* slice */
nullptr, /* enumerate */
JS_ObjectToOuterObject /* thisObject */
}
""",
objectMoved=objectMovedHook)
else:
classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
reservedSlots = slotCount
@ -481,17 +495,24 @@ class CGDOMProxyJSClass(CGThing):
# HTMLAllCollection. So just hardcode it here.
if self.descriptor.interface.identifier.name == "HTMLAllCollection":
flags.append("JSCLASS_EMULATES_UNDEFINED")
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
return fill(
"""
static const DOMJSClass Class = {
PROXY_CLASS_DEF("${name}",
0, /* extra slots */
${flags}),
PROXY_CLASS_WITH_EXT("${name}",
0, /* extra slots */
${flags},
PROXY_MAKE_EXT(nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
${objectMoved})),
$*{descriptor}
};
""",
name=self.descriptor.interface.identifier.name,
flags=" | ".join(flags),
objectMoved=objectMovedHook,
descriptor=DOMClass(self.descriptor))
@ -1549,11 +1570,28 @@ class CGClassFinalizeHook(CGAbstractClassHook):
return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
class CGClassObjectMovedHook(CGAbstractClassHook):
"""
A hook for objectMovedOp, used to update the wrapper cache when an object it
is holding moves.
"""
def __init__(self, descriptor):
args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
'void', args)
def generate_code(self):
assert self.descriptor.wrapperCache
return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
"self").define()
def JSNativeArguments():
return [Argument('JSContext*', 'cx'),
Argument('unsigned', 'argc'),
Argument('JS::Value*', 'vp')]
class CGClassConstructor(CGAbstractStaticMethod):
"""
JS-visible constructor for our objects
@ -10919,6 +10957,9 @@ class CGDescriptor(CGThing):
# wants a custom hook.
cgThings.append(CGClassFinalizeHook(descriptor))
if descriptor.concrete and descriptor.wrapperCache:
cgThings.append(CGClassObjectMovedHook(descriptor))
if len(descriptor.permissions):
for (k, v) in sorted(descriptor.permissions.items()):
perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")

View File

@ -23,7 +23,8 @@ const js::Class OuterWrapperClass =
nullptr, /* outerObject */
js::proxy_innerObject,
nullptr, /* iteratorObject */
false /* isWrappedNative */
false, /* isWrappedNative */
nullptr /* objectMoved */
));
static JSObject *

View File

@ -252,14 +252,14 @@ namespace js {
* allow for potention JSClass extensions.
*/
#define PROXY_MAKE_EXT(outerObject, innerObject, iteratorObject, \
isWrappedNative) \
isWrappedNative, objectMoved) \
{ \
outerObject, \
innerObject, \
iteratorObject, \
isWrappedNative, \
js::proxy_WeakmapKeyDelegate, \
js::proxy_ObjectMoved \
objectMoved \
}
#define PROXY_CLASS_WITH_EXT(name, extraSlots, flags, ext) \
@ -313,7 +313,8 @@ namespace js {
nullptr, /* outerObject */ \
nullptr, /* innerObject */ \
nullptr, /* iteratorObject */ \
false /* isWrappedNative */ \
false, /* isWrappedNative */ \
js::proxy_ObjectMoved \
))
/*

View File

@ -2374,7 +2374,6 @@ MovingTracer::Sweep(JSTracer *jstrc)
// TODO: Should possibly just call PurgeRuntime() here.
rt->newObjectCache.purge();
rt->nativeIterCache.purge();
rt->regExpTestCache.purge();
}
/*

View File

@ -49,6 +49,11 @@ public:
ClearWrapper();
}
void ObjectMoved(JSObject *obj, const JSObject *old)
{
UpdateWrapper(obj, old);
}
private:
virtual ~SandboxPrivate() { }

View File

@ -310,7 +310,7 @@ sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
}
static void
sandbox_finalize(JSFreeOp *fop, JSObject *obj)
sandbox_finalize(js::FreeOp *fop, JSObject *obj)
{
nsIScriptObjectPrincipal *sop =
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
@ -324,6 +324,15 @@ sandbox_finalize(JSFreeOp *fop, JSObject *obj)
DestroyProtoAndIfaceCache(obj);
}
static void
sandbox_moved(JSObject *obj, const JSObject *old)
{
nsIScriptObjectPrincipal *sop =
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
MOZ_ASSERT(sop);
static_cast<SandboxPrivate *>(sop)->ObjectMoved(obj, old);
}
static bool
sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
{
@ -443,22 +452,42 @@ sandbox_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleV
#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
static const JSClass SandboxClass = {
static const js::Class SandboxClass = {
"Sandbox",
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC,
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
sandbox_moved /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
};
// Note to whomever comes here to remove addProperty hooks: billm has promised
// to do the work for this class.
static const JSClass SandboxWriteToProtoClass = {
static const js::Class SandboxWriteToProtoClass = {
"Sandbox",
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
sandbox_addProperty, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC,
{
nullptr, /* outerObject */
nullptr, /* innerObject */
nullptr, /* iteratorObject */
false, /* isWrappedNative */
nullptr, /* weakmapKeyDelegateOp */
sandbox_moved /* objectMovedOp */
},
JS_NULL_OBJECT_OPS
};
static const JSFunctionSpec SandboxFunctions[] = {
@ -471,7 +500,7 @@ static const JSFunctionSpec SandboxFunctions[] = {
bool
xpc::IsSandbox(JSObject *obj)
{
const JSClass *clasp = GetObjectJSClass(obj);
const Class *clasp = GetObjectClass(obj);
return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
}
@ -861,11 +890,11 @@ xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prin
compartmentOptions.setAddonId(addonId);
const JSClass *clasp = options.writeToGlobalPrototype
? &SandboxWriteToProtoClass
: &SandboxClass;
const Class *clasp = options.writeToGlobalPrototype
? &SandboxWriteToProtoClass
: &SandboxClass;
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, clasp,
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
principal, compartmentOptions));
if (!sandbox)
return NS_ERROR_FAILURE;

View File

@ -991,6 +991,19 @@ XPCWrappedNative::FlatJSObjectFinalized()
Release();
}
void
XPCWrappedNative::FlatJSObjectMoved(JSObject *obj, const JSObject *old)
{
MOZ_ASSERT(mFlatJSObject == old);
nsWrapperCache *cache = nullptr;
CallQueryInterface(mIdentity, &cache);
if (cache)
cache->UpdateWrapper(obj, old);
mFlatJSObject = obj;
}
void
XPCWrappedNative::SystemIsBeingShutDown()
{

View File

@ -572,6 +572,17 @@ WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType)
wrapper->FlatJSObjectFinalized();
}
static void
WrappedNativeObjectMoved(JSObject *obj, const JSObject *old)
{
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
if (!p)
return;
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
wrapper->FlatJSObjectMoved(obj, old);
}
static void
XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
{
@ -658,7 +669,9 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
nullptr, // outerObject
nullptr, // innerObject
nullptr, // iteratorObject
true, // isWrappedNative
true, // isWrappedNative
nullptr, // weakmapKeyDelegateOp
WrappedNativeObjectMoved
},
// ObjectOps
@ -1165,6 +1178,7 @@ XPCNativeScriptableShared::PopulateJSClass()
mJSClass.base.trace = XPCWrappedNative::Trace;
mJSClass.base.ext.isWrappedNative = true;
mJSClass.base.ext.objectMovedOp = WrappedNativeObjectMoved;
}
/***************************************************************************/

View File

@ -2141,6 +2141,7 @@ public:
nsresult RescueOrphans();
void FlatJSObjectFinalized();
void FlatJSObjectMoved(JSObject *obj, const JSObject *old);
void SystemIsBeingShutDown();