Preserve identity for deep wrapping (bug 610390, r=mrbkap, a=b8).

This commit is contained in:
Andreas Gal 2010-12-03 19:42:11 -08:00
parent b8cc189151
commit f7f43f70b7
8 changed files with 198 additions and 8 deletions

View File

@ -83,6 +83,7 @@ class XPCNativeScriptableSharedMap;
class XPCWrappedNativeProtoMap;
class XPCNativeWrapperMap;
class WrappedNative2WrapperMap;
class JSObject2JSObjectMap;
class nsXPCComponents;
class nsXPCComponents_Interfaces;

View File

@ -240,6 +240,12 @@ ContextCallback(JSContext *cx, uintN operation)
return JS_TRUE;
}
xpc::CompartmentPrivate::~CompartmentPrivate()
{
if (waiverWrapperMap)
delete waiverWrapperMap;
}
static JSBool
CompartmentCallback(JSContext *cx, JSCompartment *compartment, uintN op)
{
@ -545,6 +551,27 @@ DoDeferredRelease(nsTArray<T> &array)
}
}
static JSDHashOperator
SweepWaiverWrappers(JSDHashTable *table, JSDHashEntryHdr *hdr,
uint32 number, void *arg)
{
JSObject *key = ((JSObject2JSObjectMap::Entry *)hdr)->key;
JSObject *value = ((JSObject2JSObjectMap::Entry *)hdr)->value;
if(IsAboutToBeFinalized(key) || IsAboutToBeFinalized(value))
return JS_DHASH_REMOVE;
return JS_DHASH_NEXT;
}
static PLDHashOperator
SweepCompartment(nsCStringHashKey& aKey, JSCompartment *compartment, void *aClosure)
{
xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
JS_GetCompartmentPrivate((JSContext *)aClosure, compartment);
if (priv->waiverWrapperMap)
priv->waiverWrapperMap->Enumerate(SweepWaiverWrappers, nsnull);
return PL_DHASH_NEXT;
}
// static
JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
{
@ -597,9 +624,14 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
Enumerate(WrappedJSDyingJSObjectFinder, &data);
}
// Find dying scopes...
// Find dying scopes.
XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self);
// Sweep compartments.
self->GetCompartmentMap().EnumerateRead(
(XPCCompartmentMap::EnumReadFunction)
SweepCompartment, cx);
self->mDoingFinalization = JS_TRUE;
break;
}

View File

@ -763,3 +763,18 @@ WrappedNative2WrapperMap::AddLink(JSObject* wrappedObject, Link* oldLink)
}
/***************************************************************************/
// implement JSObject2JSObjectMap...
struct JSDHashTableOps
JSObject2JSObjectMap::sOps = {
JS_DHashAllocTable,
JS_DHashFreeTable,
JS_DHashVoidPtrKeyStub,
JS_DHashMatchEntryStub,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
nsnull
};
/***************************************************************************/

View File

@ -710,4 +710,80 @@ private:
JSDHashTable *mTable;
};
class JSObject2JSObjectMap
{
static struct JSDHashTableOps sOps;
public:
struct Entry : public JSDHashEntryHdr
{
JSObject* key;
JSObject* value;
};
static JSObject2JSObjectMap* newMap(int size)
{
JSObject2JSObjectMap* map = new JSObject2JSObjectMap(size);
if(map && map->mTable)
return map;
delete map;
return nsnull;
}
inline JSObject* Find(JSObject* key)
{
NS_PRECONDITION(key, "bad param");
Entry* entry = (Entry*)
JS_DHashTableOperate(mTable, key, JS_DHASH_LOOKUP);
if(JS_DHASH_ENTRY_IS_FREE(entry))
return nsnull;
return entry->value;
}
// Note: If the entry already exists, return the old value.
inline JSObject* Add(JSObject *key, JSObject *value)
{
NS_PRECONDITION(key,"bad param");
Entry* entry = (Entry*)
JS_DHashTableOperate(mTable, key, JS_DHASH_ADD);
if(!entry)
return nsnull;
if(entry->key)
return entry->value;
entry->key = key;
entry->value = value;
return value;
}
inline void Remove(JSObject* key)
{
NS_PRECONDITION(key,"bad param");
JS_DHashTableOperate(mTable, key, JS_DHASH_REMOVE);
}
inline uint32 Count() {return mTable->entryCount;}
inline uint32 Enumerate(JSDHashEnumerator f, void *arg)
{
return JS_DHashTableEnumerate(mTable, f, arg);
}
~JSObject2JSObjectMap()
{
if(mTable)
JS_DHashTableDestroy(mTable);
}
private:
JSObject2JSObjectMap(int size)
{
mTable = JS_NewDHashTable(&sOps, nsnull, sizeof(Entry), size);
}
JSObject2JSObjectMap(); // no implementation
private:
JSDHashTable *mTable;
};
#endif /* xpcmaps_h___ */

View File

@ -240,6 +240,7 @@ void DEBUG_CheckWrapperThreadSafety(const XPCWrappedNative* wrapper);
#define XPC_NATIVE_JSCLASS_MAP_SIZE 32
#define XPC_THIS_TRANSLATOR_MAP_SIZE 8
#define XPC_NATIVE_WRAPPER_MAP_SIZE 16
#define XPC_WRAPPER_MAP_SIZE 16
/***************************************************************************/
// data declarations...
@ -323,7 +324,7 @@ typedef nsDataHashtableMT<nsISupportsHashKey, JSCompartment *> XPCMTCompartmentM
typedef nsDataHashtable<xpc::PtrAndPrincipalHashKey, JSCompartment *> XPCCompartmentMap;
/***************************************************************************/
// useful macros...
// Useful macros...
#define XPC_STRING_GETTER_BODY(dest, src) \
NS_ENSURE_ARG_POINTER(dest); \
@ -4484,22 +4485,28 @@ struct CompartmentPrivate
: key(key),
ptr(nsnull),
wantXrays(wantXrays),
cycleCollectionEnabled(cycleCollectionEnabled)
cycleCollectionEnabled(cycleCollectionEnabled),
waiverWrapperMap(nsnull)
{
}
CompartmentPrivate(nsISupports *ptr, bool wantXrays, bool cycleCollectionEnabled)
: key(nsnull),
ptr(ptr),
wantXrays(wantXrays),
cycleCollectionEnabled(cycleCollectionEnabled)
cycleCollectionEnabled(cycleCollectionEnabled),
waiverWrapperMap(nsnull)
{
}
~CompartmentPrivate();
// NB: key and ptr are mutually exclusive.
nsAutoPtr<PtrAndPrincipalHashKey> key;
nsCOMPtr<nsISupports> ptr;
bool wantXrays;
bool cycleCollectionEnabled;
JSObject2JSObjectMap *waiverWrapperMap;
};
inline bool

View File

@ -58,6 +58,7 @@ _CHROME_FILES = \
test_bug517163.xul \
test_bug571849.xul \
test_bug601803.xul \
test_bug610390.xul \
$(NULL)
# Disabled until this test gets updated to test the new proxy based

View File

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=610390
-->
<window title="Mozilla Bug 610390"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<iframe type="content"
src="data:text/html,&lt;script&gt;var x=3&lt;/script&gt;"
onload="go()"
id="ifr">
</iframe>
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
SimpleTest.waitForExplicitFinish();
function go() {
var w = $('ifr').contentWindow;
is(w.wrappedJSObject, w.wrappedJSObject, "wrappedJSObject identity not maintained");
SimpleTest.finish();
}
]]></script>
</window>

View File

@ -48,6 +48,7 @@
#include "XPCWrapper.h"
#include "xpcprivate.h"
#include "xpcmaps.h"
namespace xpc {
@ -307,10 +308,33 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp)
obj = GetCurrentOuter(cx, obj);
{
js::SwitchToCompartment sc(cx, obj->compartment());
obj = JSWrapper::New(cx, obj, NULL, obj->getGlobal(), &WaiveXrayWrapperWrapper);
if (!obj)
return false;
// See if we already have a waiver wrapper for this object.
CompartmentPrivate *priv =
(CompartmentPrivate *)JS_GetCompartmentPrivate(cx, obj->compartment());
JSObject *wobj = nsnull;
if (priv && priv->waiverWrapperMap)
wobj = priv->waiverWrapperMap->Find(obj);
// No wrapper yet, make one.
if (!wobj) {
js::SwitchToCompartment sc(cx, obj->compartment());
wobj = JSWrapper::New(cx, obj, NULL, obj->getGlobal(), &WaiveXrayWrapperWrapper);
if (!wobj)
return false;
// Add the new wrapper so we find it next time.
if (priv) {
if (!priv->waiverWrapperMap) {
priv->waiverWrapperMap = JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_SIZE);
if (!priv->waiverWrapperMap)
return false;
}
if (!priv->waiverWrapperMap->Add(obj, wobj))
return false;
}
}
obj = wobj;
}
*vp = OBJECT_TO_JSVAL(obj);