зеркало из https://github.com/mozilla/gecko-dev.git
Bug 934421 - Postbarrier the keys of the plugin wrapper table r=terrence r=jschoenick
This commit is contained in:
Родитель
c9dde06da3
Коммит
f463fd7786
|
@ -23,8 +23,10 @@
|
|||
#include "prmem.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsPluginInstanceOwner.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
|
||||
|
||||
#define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
|
||||
|
||||
|
@ -35,6 +37,21 @@ using namespace mozilla;
|
|||
using mozilla::plugins::PluginScriptableObjectParent;
|
||||
using mozilla::plugins::ParentNPObject;
|
||||
|
||||
struct JSObjWrapperHasher : public js::DefaultHasher<nsJSObjWrapperKey>
|
||||
{
|
||||
typedef nsJSObjWrapperKey Key;
|
||||
typedef Key Lookup;
|
||||
|
||||
static uint32_t hash(const Lookup &l) {
|
||||
return HashGeneric(l.mJSObj, l.mNpp);
|
||||
}
|
||||
|
||||
static void rekey(Key &k, const Key& newKey) {
|
||||
MOZ_ASSERT(k.mNpp == newKey.mNpp);
|
||||
k.mJSObj = newKey.mJSObj;
|
||||
}
|
||||
};
|
||||
|
||||
// Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
|
||||
// will be one wrapper per JSObject per plugin instance, i.e. if two
|
||||
// plugins access the JSObject x, two wrappers for x will be
|
||||
|
@ -42,7 +59,11 @@ using mozilla::plugins::ParentNPObject;
|
|||
// when a plugin is torn down in case there's a leak in the plugin (we
|
||||
// don't want to leak the world just because a plugin leaks an
|
||||
// NPObject).
|
||||
static PLDHashTable sJSObjWrappers;
|
||||
typedef js::HashMap<nsJSObjWrapperKey,
|
||||
nsJSObjWrapper*,
|
||||
JSObjWrapperHasher,
|
||||
js::SystemAllocPolicy> JSObjWrapperTable;
|
||||
static JSObjWrapperTable sJSObjWrappers;
|
||||
|
||||
// Hash of NPObject wrappers that wrap NPObjects as JSObjects.
|
||||
static PLDHashTable sNPObjWrappers;
|
||||
|
@ -237,18 +258,16 @@ OnWrapperDestroyed()
|
|||
NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
|
||||
|
||||
if (--sWrapperCount == 0) {
|
||||
if (sJSObjWrappers.ops) {
|
||||
NS_ASSERTION(sJSObjWrappers.entryCount == 0, "Uh, hash not empty?");
|
||||
if (sJSObjWrappers.initialized()) {
|
||||
MOZ_ASSERT(sJSObjWrappers.count() == 0);
|
||||
|
||||
// No more wrappers, and our hash was initialized. Finish the
|
||||
// hash to prevent leaking it.
|
||||
PL_DHashTableFinish(&sJSObjWrappers);
|
||||
|
||||
sJSObjWrappers.ops = nullptr;
|
||||
sJSObjWrappers.finish();
|
||||
}
|
||||
|
||||
if (sNPObjWrappers.ops) {
|
||||
NS_ASSERTION(sNPObjWrappers.entryCount == 0, "Uh, hash not empty?");
|
||||
MOZ_ASSERT(sNPObjWrappers.entryCount == 0);
|
||||
|
||||
// No more wrappers, and our hash was initialized. Finish the
|
||||
// hash to prevent leaking it.
|
||||
|
@ -480,9 +499,8 @@ ReportExceptionIfPending(JSContext *cx)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
nsJSObjWrapper::nsJSObjWrapper(NPP npp)
|
||||
: nsJSObjWrapperKey(nullptr, npp)
|
||||
: mNpp(npp)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsJSObjWrapper);
|
||||
OnWrapperCreated();
|
||||
|
@ -498,6 +516,15 @@ nsJSObjWrapper::~nsJSObjWrapper()
|
|||
OnWrapperDestroyed();
|
||||
}
|
||||
|
||||
void
|
||||
nsJSObjWrapper::ClearJSObject() {
|
||||
// Unroot the object's JSObject
|
||||
JS_RemoveObjectRootRT(sJSRuntime, &mJSObj);
|
||||
|
||||
// Forget our reference to the JSObject.
|
||||
mJSObj = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
NPObject *
|
||||
nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
|
||||
|
@ -523,18 +550,13 @@ nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
|
|||
nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
|
||||
|
||||
if (jsnpobj && jsnpobj->mJSObj) {
|
||||
// Unroot the object's JSObject
|
||||
JS_RemoveObjectRootRT(sJSRuntime, &jsnpobj->mJSObj);
|
||||
|
||||
if (sJSObjWrappers.ops) {
|
||||
// Remove the wrapper from the hash
|
||||
// Remove the wrapper from the hash
|
||||
MOZ_ASSERT(sJSObjWrappers.initialized());
|
||||
nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
|
||||
sJSObjWrappers.remove(key);
|
||||
|
||||
nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
|
||||
PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_REMOVE);
|
||||
}
|
||||
|
||||
// Forget our reference to the JSObject.
|
||||
jsnpobj->mJSObj = nullptr;
|
||||
jsnpobj->ClearJSObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -915,34 +937,23 @@ nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
|
|||
}
|
||||
|
||||
|
||||
class JSObjWrapperHashEntry : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
nsJSObjWrapper *mJSObjWrapper;
|
||||
};
|
||||
|
||||
|
||||
static PLDHashNumber
|
||||
JSObjWrapperHash(PLDHashTable *table, const void *key)
|
||||
{
|
||||
const nsJSObjWrapperKey *e = static_cast<const nsJSObjWrapperKey *>(key);
|
||||
return HashGeneric(e->mJSObj, e->mNpp);
|
||||
/*
|
||||
* This function is called during minor GCs for each key in the sJSObjWrappers
|
||||
* table that has been moved.
|
||||
*/
|
||||
static void
|
||||
JSObjWrapperKeyMarkCallback(JSTracer *trc, void *key, void *data) {
|
||||
JSObject *obj = static_cast<JSObject*>(key);
|
||||
nsJSObjWrapper* wrapper = static_cast<nsJSObjWrapper*>(data);
|
||||
JSObject *prior = obj;
|
||||
JS_CallObjectTracer(trc, &obj, "sJSObjWrappers key object");
|
||||
NPP npp = wrapper->mNpp;
|
||||
nsJSObjWrapperKey oldKey(prior, npp);
|
||||
nsJSObjWrapperKey newKey(obj, npp);
|
||||
sJSObjWrappers.rekeyIfMoved(oldKey, newKey);
|
||||
}
|
||||
|
||||
static bool
|
||||
JSObjWrapperHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
|
||||
const void *key)
|
||||
{
|
||||
const nsJSObjWrapperKey *objWrapperKey =
|
||||
static_cast<const nsJSObjWrapperKey *>(key);
|
||||
const JSObjWrapperHashEntry *e =
|
||||
static_cast<const JSObjWrapperHashEntry *>(entry);
|
||||
|
||||
return (e->mJSObjWrapper->mJSObj == objWrapperKey->mJSObj &&
|
||||
e->mJSObjWrapper->mNpp == objWrapperKey->mNpp);
|
||||
}
|
||||
|
||||
|
||||
// Look up or create an NPObject that wraps the JSObject obj.
|
||||
|
||||
// static
|
||||
|
@ -990,22 +1001,9 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
|
|||
return _retainobject(npobj);
|
||||
}
|
||||
|
||||
if (!sJSObjWrappers.ops) {
|
||||
if (!sJSObjWrappers.initialized()) {
|
||||
// No hash yet (or any more), initialize it.
|
||||
|
||||
static const PLDHashTableOps ops =
|
||||
{
|
||||
PL_DHashAllocTable,
|
||||
PL_DHashFreeTable,
|
||||
JSObjWrapperHash,
|
||||
JSObjWrapperHashMatchEntry,
|
||||
PL_DHashMoveEntryStub,
|
||||
PL_DHashClearEntryStub,
|
||||
PL_DHashFinalizeStub
|
||||
};
|
||||
|
||||
if (!PL_DHashTableInit(&sJSObjWrappers, &ops, nullptr,
|
||||
sizeof(JSObjWrapperHashEntry), 16)) {
|
||||
if (!sJSObjWrappers.init(16)) {
|
||||
NS_ERROR("Error initializing PLDHashTable!");
|
||||
|
||||
return nullptr;
|
||||
|
@ -1014,18 +1012,13 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
|
|||
|
||||
nsJSObjWrapperKey key(obj, npp);
|
||||
|
||||
JSObjWrapperHashEntry *entry = static_cast<JSObjWrapperHashEntry *>
|
||||
(PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_ADD));
|
||||
JSObjWrapperTable::AddPtr p = sJSObjWrappers.lookupForAdd(key);
|
||||
|
||||
if (!entry) {
|
||||
// Out of memory.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObjWrapper) {
|
||||
if (p/* && p->value*/) {
|
||||
MOZ_ASSERT(p->value);
|
||||
// Found a live nsJSObjWrapper, return it.
|
||||
|
||||
return _retainobject(entry->mJSObjWrapper);
|
||||
return _retainobject(p->value);
|
||||
}
|
||||
|
||||
// No existing nsJSObjWrapper, create one.
|
||||
|
@ -1034,16 +1027,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
|
|||
(nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
|
||||
|
||||
if (!wrapper) {
|
||||
// OOM? Remove the stale entry from the hash.
|
||||
|
||||
PL_DHashTableRawRemove(&sJSObjWrappers, entry);
|
||||
|
||||
// Out of memory, entry not yet added to table.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wrapper->mJSObj = obj;
|
||||
|
||||
entry->mJSObjWrapper = wrapper;
|
||||
if (!sJSObjWrappers.add(p, key, wrapper)) {
|
||||
// Out of memory, free the wrapper we created.
|
||||
_releaseobject(wrapper);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_ASSERTION(wrapper->mNpp == npp, "nsJSObjWrapper::mNpp not initialized!");
|
||||
|
||||
|
@ -1052,13 +1046,15 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
|
|||
if (!::JS_AddNamedObjectRoot(cx, &wrapper->mJSObj, "nsJSObjWrapper::mJSObject")) {
|
||||
NS_ERROR("Failed to root JSObject!");
|
||||
|
||||
sJSObjWrappers.remove(key);
|
||||
_releaseobject(wrapper);
|
||||
|
||||
PL_DHashTableRawRemove(&sJSObjWrappers, entry);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add postbarrier for the hashtable key
|
||||
JS_StoreObjectPostBarrierCallback(cx, JSObjWrapperKeyMarkCallback, obj, wrapper);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
|
@ -1812,35 +1808,6 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
|
|||
}
|
||||
|
||||
|
||||
// PLDHashTable enumeration callbacks for destruction code.
|
||||
static PLDHashOperator
|
||||
JSObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
uint32_t number, void *arg)
|
||||
{
|
||||
JSObjWrapperHashEntry *entry = (JSObjWrapperHashEntry *)hdr;
|
||||
|
||||
nsJSObjWrapper *npobj = entry->mJSObjWrapper;
|
||||
|
||||
if (npobj->mNpp == arg) {
|
||||
// Prevent invalidate() and _releaseobject() from touching the hash
|
||||
// we're enumerating.
|
||||
const PLDHashTableOps *ops = table->ops;
|
||||
table->ops = nullptr;
|
||||
|
||||
if (npobj->_class && npobj->_class->invalidate) {
|
||||
npobj->_class->invalidate(npobj);
|
||||
}
|
||||
|
||||
_releaseobject(npobj);
|
||||
|
||||
table->ops = ops;
|
||||
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Struct for passing an NPP and a JSContext to
|
||||
// NPObjWrapperPluginDestroyedCallback
|
||||
struct NppAndCx
|
||||
|
@ -1888,7 +1855,7 @@ NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
|||
|
||||
::JS_SetPrivate(entry->mJSObj, nullptr);
|
||||
|
||||
table->ops = ops;
|
||||
table->ops = ops;
|
||||
|
||||
if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
|
||||
OnWrapperDestroyed();
|
||||
|
@ -1904,9 +1871,16 @@ NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
|||
void
|
||||
nsJSNPRuntime::OnPluginDestroy(NPP npp)
|
||||
{
|
||||
if (sJSObjWrappers.ops) {
|
||||
PL_DHashTableEnumerate(&sJSObjWrappers,
|
||||
JSObjWrapperPluginDestroyedCallback, npp);
|
||||
if (sJSObjWrappers.initialized()) {
|
||||
for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
|
||||
nsJSObjWrapper *npobj = e.front().value;
|
||||
MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
|
||||
if (npobj->mNpp == npp) {
|
||||
npobj->ClearJSObject();
|
||||
_releaseobject(npobj);
|
||||
e.removeFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the safe JSContext here as we're not always able to find the
|
||||
|
|
|
@ -25,20 +25,30 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
JSObject *mJSObj;
|
||||
bool operator==(const nsJSObjWrapperKey& other) const {
|
||||
return mJSObj == other.mJSObj && mNpp == other.mNpp;
|
||||
}
|
||||
bool operator!=(const nsJSObjWrapperKey& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
JSObject * mJSObj;
|
||||
const NPP mNpp;
|
||||
};
|
||||
|
||||
extern const JSClass sNPObjectJSWrapperClass;
|
||||
|
||||
class nsJSObjWrapper : public NPObject,
|
||||
public nsJSObjWrapperKey
|
||||
class nsJSObjWrapper : public NPObject
|
||||
{
|
||||
public:
|
||||
JSObject *mJSObj; /* Added as a GC root. */
|
||||
const NPP mNpp;
|
||||
|
||||
static NPObject *GetNewOrUsed(NPP npp, JSContext *cx,
|
||||
JS::Handle<JSObject*> obj);
|
||||
|
||||
void ClearJSObject();
|
||||
|
||||
protected:
|
||||
nsJSObjWrapper(NPP npp);
|
||||
~nsJSObjWrapper();
|
||||
|
|
Загрузка…
Ссылка в новой задаче