Bug 934421 - Postbarrier the keys of the plugin wrapper table r=terrence r=jschoenick

This commit is contained in:
Jon Coppeard 2013-11-19 22:53:36 +00:00
Родитель c9dde06da3
Коммит f463fd7786
2 изменённых файлов: 96 добавлений и 112 удалений

Просмотреть файл

@ -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();