vendor/tracemonkey/xpconnect/src/xpcwrappednative.cpp in johnson-2.0.0.pre0 vs vendor/tracemonkey/xpconnect/src/xpcwrappednative.cpp in johnson-2.0.0.pre1
- old
+ new
@@ -44,10 +44,11 @@
#include "xpcprivate.h"
#include "nsCRT.h"
#include "XPCNativeWrapper.h"
#include "XPCWrapper.h"
#include "nsWrapperCache.h"
+#include "xpclog.h"
/***************************************************************************/
NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
@@ -66,24 +67,25 @@
{
XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
if(!tmp->IsValid())
return NS_OK;
-#ifdef DEBUG_CC
- char name[72];
- XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
- if(si)
- JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
- si->GetJSClass()->name);
- else
- JS_snprintf(name, sizeof(name), "XPCWrappedNative");
+ if (NS_UNLIKELY(cb.WantDebugInfo())) {
+ char name[72];
+ XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
+ if(si)
+ JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
+ si->GetJSClass()->name);
+ else
+ JS_snprintf(name, sizeof(name), "XPCWrappedNative");
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(XPCWrappedNative),
- name);
-#else
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get());
-#endif
+ cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
+ sizeof(XPCWrappedNative), name);
+ } else {
+ cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
+ sizeof(XPCWrappedNative), "XPCWrappedNative");
+ }
if(tmp->mRefCnt.get() > 1) {
// If our refcount is > 1, our reference to the flat JS object is
// considered "strong", and we're going to traverse it.
@@ -278,10 +280,17 @@
#define DEBUG_TrackWrapperCall(wrapper, mode) ((void)0)
#define DEBUG_TrackShutdownWrapper(wrapper) ((void)0)
#endif
/***************************************************************************/
+static nsresult
+FinishCreate(XPCCallContext& ccx,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ nsWrapperCache *cache,
+ XPCWrappedNative* wrapper,
+ XPCWrappedNative** resultWrapper);
// static
nsresult
XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx,
nsISupports* Object,
@@ -335,29 +344,41 @@
// This would cause the mSet to get collected and we'd later crash. I've
// *seen* this happen.
AutoMarkingWrappedNativePtr wrapper(ccx);
Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
- { // scoped lock
- XPCAutoLock lock(mapLock);
- wrapper = map->Find(identity);
- if(wrapper)
- wrapper->AddRef();
- }
-
- if(wrapper)
+ if(!cache)
{
- if(Interface && !wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv))
+ { // scoped lock
+ XPCAutoLock lock(mapLock);
+ wrapper = map->Find(identity);
+ if(wrapper)
+ wrapper->AddRef();
+ }
+
+ if(wrapper)
{
- NS_RELEASE(wrapper);
- NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
- return rv;
+ if(Interface &&
+ !wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv))
+ {
+ NS_RELEASE(wrapper);
+ NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+ DEBUG_CheckWrapperThreadSafety(wrapper);
+ *resultWrapper = wrapper;
+ return NS_OK;
}
- DEBUG_CheckWrapperThreadSafety(wrapper);
- *resultWrapper = wrapper;
- return NS_OK;
}
+#ifdef DEBUG
+ else if(!cache->GetWrapper())
+ { // scoped lock
+ XPCAutoLock lock(mapLock);
+ NS_ASSERTION(!map->Find(identity),
+ "There's a wrapper in the hashtable but it wasn't cached?");
+ }
+#endif
// There is a chance that the object wants to have the self-same JSObject
// reflection regardless of the scope into which we are reflecting it.
// Many DOM objects require this. The scriptable helper specifies this
// in preCreate by indicating a 'parent' of a particular scope.
@@ -406,10 +427,11 @@
jsval newParentVal = JSVAL_NULL;
XPCMarkableJSVal newParentVal_markable(&newParentVal);
AutoMarkingJSVal newParentVal_automarker(ccx, &newParentVal_markable);
JSBool chromeOnly = JS_FALSE;
+ JSBool crossDoubleWrapped = JS_FALSE;
if(sciWrapper.GetFlags().WantPreCreate())
{
JSObject* plannedParent = parent;
nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
@@ -436,10 +458,32 @@
// Take the performance hit of checking the hashtable again in case
// the preCreate call caused the wrapper to get created through some
// interesting path (the DOM code tends to make this happen sometimes).
+ if(cache)
+ {
+ JSObject *cached = cache->GetWrapper();
+ if(cached)
+ {
+ if(IS_SLIM_WRAPPER(cached))
+ {
+ nsRefPtr<XPCWrappedNative> morphed;
+ if(!XPCWrappedNative::Morph(ccx, cached, Interface, cache,
+ getter_AddRefs(morphed)))
+ return NS_ERROR_FAILURE;
+
+ wrapper = morphed.forget().get();
+ } else {
+ wrapper =
+ static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(cached));
+ if(wrapper)
+ wrapper->AddRef();
+ }
+ }
+ }
+ else
{ // scoped lock
XPCAutoLock lock(mapLock);
wrapper = map->Find(identity);
if(wrapper)
wrapper->AddRef();
@@ -456,10 +500,25 @@
DEBUG_CheckWrapperThreadSafety(wrapper);
*resultWrapper = wrapper;
return NS_OK;
}
}
+ else
+ {
+ if(nsXPCWrappedJSClass::IsWrappedJS(Object))
+ {
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedjs(do_QueryInterface(Object));
+ JSObject *obj;
+ wrappedjs->GetJSObject(&obj);
+ if((obj->isSystem() ||
+ JS_GetGlobalForObject(ccx, obj)->isSystem()) &&
+ !Scope->GetGlobalJSObject()->isSystem())
+ {
+ crossDoubleWrapped = JS_TRUE;
+ }
+ }
+ }
AutoMarkingWrappedNativeProtoPtr proto(ccx);
// If there is ClassInfo (and we are not building a wrapper for the
// nsIClassInfo interface) then we use a wrapper that needs a prototype.
@@ -474,11 +533,11 @@
if(!proto)
return NS_ERROR_FAILURE;
proto->CacheOffsets(identity);
- wrapper = new XPCWrappedNative(identity, proto);
+ wrapper = new XPCWrappedNative(identity.get(), proto);
if(!wrapper)
return NS_ERROR_FAILURE;
}
else
{
@@ -490,40 +549,57 @@
set = XPCNativeSet::GetNewOrUsed(ccx, nsnull, iface, 0);
if(!set)
return NS_ERROR_FAILURE;
- wrapper = new XPCWrappedNative(identity, Scope, set);
+ wrapper = new XPCWrappedNative(identity.get(), Scope, set);
if(!wrapper)
return NS_ERROR_FAILURE;
DEBUG_ReportShadowedMembers(set, wrapper, nsnull);
}
+ // The strong reference was taken over by the wrapper, so make the nsCOMPtr
+ // forget about it.
+ // Note that identity is null from here on!
+ identity.forget();
+
NS_ADDREF(wrapper);
NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(parent),
"XPCNativeWrapper being used to parent XPCWrappedNative?");
-
+
if(!wrapper->Init(ccx, parent, isGlobal, &sciWrapper))
{
NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
if(Interface && !wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv))
{
- // Second reference will be released by the FlatJSObject's finializer.
+ // Second reference will be released by the FlatJSObject's finalizer.
wrapper->Release();
NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
return rv;
}
if(chromeOnly)
wrapper->SetNeedsChromeWrapper();
+ if(crossDoubleWrapped)
+ wrapper->SetIsDoubleWrapper();
+ return FinishCreate(ccx, Scope, Interface, cache, wrapper, resultWrapper);
+}
+static nsresult
+FinishCreate(XPCCallContext& ccx,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ nsWrapperCache *cache,
+ XPCWrappedNative* wrapper,
+ XPCWrappedNative** resultWrapper)
+{
#if DEBUG_xpc_leaks
{
char* s = wrapper->ToString(ccx);
NS_ASSERTION(wrapper->GetFlatJSObject(), "eh?");
printf("Created wrapped native %s, flat JSObject is %p\n",
@@ -531,10 +607,13 @@
if(s)
JS_smprintf_free(s);
}
#endif
+ XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
+ Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
+
// Redundant wrapper must be killed outside of the map lock.
XPCWrappedNative* wrapperToKill = nsnull;
{ // scoped lock
XPCAutoLock lock(mapLock);
@@ -561,19 +640,25 @@
// Second reference will be released by the FlatJSObject's finializer.
wrapperToKill->Release();
}
else if(wrapper)
{
- if(cache)
- cache->SetWrapper(wrapper);
+ JSObject *flat = wrapper->GetFlatJSObject();
+ NS_ASSERTION(!cache || !cache->GetWrapper() ||
+ flat == cache->GetWrapper(),
+ "This object has a cached wrapper that's different from "
+ "the JSObject held by its native wrapper?");
+ if(cache && !cache->GetWrapper())
+ cache->SetWrapper(flat);
+
// Our newly created wrapper is the one that we just added to the table.
// All is well. Call PostCreate as necessary.
XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
if(si && si->GetFlags().WantPostCreate())
{
- rv = si->GetCallback()->
+ nsresult rv = si->GetCallback()->
PostCreate(wrapper, ccx, wrapper->GetFlatJSObject());
if(NS_FAILED(rv))
{
// PostCreate failed and that's Very Bad. We'll remove it from
// the map and mark it as invalid, but the PostCreate function
@@ -612,10 +697,81 @@
return NS_OK;
}
// static
nsresult
+XPCWrappedNative::Morph(XPCCallContext& ccx,
+ JSObject* existingJSObject,
+ XPCNativeInterface* Interface,
+ nsWrapperCache *cache,
+ XPCWrappedNative** resultWrapper)
+{
+ NS_ASSERTION(IS_SLIM_WRAPPER(existingJSObject),
+ "Trying to morph a JSObject that's not a slim wrapper?");
+
+ nsISupports *identity =
+ static_cast<nsISupports*>(xpc_GetJSPrivate(existingJSObject));
+ XPCWrappedNativeProto *proto = GetSlimWrapperProto(existingJSObject);
+
+ // We use an AutoMarkingPtr here because it is possible for JS gc to happen
+ // after we have Init'd the wrapper but *before* we add it to the hashtable.
+ // This would cause the mSet to get collected and we'd later crash. I've
+ // *seen* this happen.
+ AutoMarkingWrappedNativePtr wrapper(ccx);
+
+#if DEBUG
+ // FIXME Can't assert this until
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=343141 is fixed.
+#if 0
+ if(proto->GetScriptableInfo()->GetFlags().WantPreCreate())
+ {
+ JSObject* parent = JS_GetParent(ccx, existingJSObject);
+ JSObject* plannedParent = parent;
+ nsresult rv =
+ proto->GetScriptableInfo()->GetCallback()->PreCreate(identity, ccx,
+ parent,
+ &parent);
+ if(NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(parent == plannedParent,
+ "PreCreate returned a different parent");
+ }
+#endif
+#endif
+
+ wrapper = new XPCWrappedNative(dont_AddRef(identity), proto);
+ if(!wrapper)
+ return NS_ERROR_FAILURE;
+
+ NS_ADDREF(wrapper);
+
+ NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(STOBJ_GET_PARENT(
+ existingJSObject)),
+ "XPCNativeWrapper being used to parent XPCWrappedNative?");
+
+ if(!wrapper->Init(ccx, existingJSObject))
+ {
+ NS_RELEASE(wrapper);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if(Interface && !wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv))
+ {
+ // Second reference will be released by the FlatJSObject's finalizer.
+ wrapper->Release();
+ NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+
+ return FinishCreate(ccx, wrapper->GetScope(), Interface, cache, wrapper,
+ resultWrapper);
+}
+
+// static
+nsresult
XPCWrappedNative::GetUsedOnly(XPCCallContext& ccx,
nsISupports* Object,
XPCWrappedNativeScope* Scope,
XPCNativeInterface* Interface,
XPCWrappedNative** resultWrapper)
@@ -625,11 +781,18 @@
XPCWrappedNative* wrapper;
nsWrapperCache* cache = nsnull;
CallQueryInterface(Object, &cache);
if(cache)
{
- wrapper = static_cast<XPCWrappedNative*>(cache->GetWrapper());
+ JSObject *flat = cache->GetWrapper();
+ if(flat && IS_SLIM_WRAPPER(flat) && !MorphSlimWrapper(ccx, flat))
+ return NS_ERROR_FAILURE;
+
+ wrapper = flat ?
+ static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(flat)) :
+ nsnull;
+
if(!wrapper)
{
*resultWrapper = nsnull;
return NS_OK;
}
@@ -677,38 +840,38 @@
*resultWrapper = wrapper;
return NS_OK;
}
// This ctor is used if this object will have a proto.
-XPCWrappedNative::XPCWrappedNative(nsISupports* aIdentity,
+XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
XPCWrappedNativeProto* aProto)
: mMaybeProto(aProto),
mSet(aProto->GetSet()),
mFlatJSObject((JSObject*)JSVAL_ONE), // non-null to pass IsValid() test
mScriptableInfo(nsnull),
- mWrapper(nsnull)
+ mWrapperWord(0)
{
- NS_ADDREF(mIdentity = aIdentity);
+ mIdentity = aIdentity.get();
NS_ASSERTION(mMaybeProto, "bad ctor param");
NS_ASSERTION(mSet, "bad ctor param");
DEBUG_TrackNewWrapper(this);
}
// This ctor is used if this object will NOT have a proto.
-XPCWrappedNative::XPCWrappedNative(nsISupports* aIdentity,
+XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
XPCWrappedNativeScope* aScope,
XPCNativeSet* aSet)
: mMaybeScope(TagScope(aScope)),
mSet(aSet),
mFlatJSObject((JSObject*)JSVAL_ONE), // non-null to pass IsValid() test
mScriptableInfo(nsnull),
- mWrapper(nsnull)
+ mWrapperWord(0)
{
- NS_ADDREF(mIdentity = aIdentity);
+ mIdentity = aIdentity.get();
NS_ASSERTION(aScope, "bad ctor param");
NS_ASSERTION(aSet, "bad ctor param");
DEBUG_TrackNewWrapper(this);
@@ -781,11 +944,11 @@
JSUint32 flags;
rv = helper->GetScriptableFlags(&flags);
if(NS_FAILED(rv))
flags = 0;
- sciProto->SetCallback(helper);
+ sciProto->SetCallback(helper.forget());
sciProto->SetFlags(flags);
}
}
return NS_OK;
}
@@ -820,11 +983,11 @@
JSUint32 flags;
nsresult rv = helper->GetScriptableFlags(&flags);
if(NS_FAILED(rv))
flags = 0;
- sciWrapper->SetCallback(helper);
+ sciWrapper->SetCallback(helper.forget());
sciWrapper->SetFlags(flags);
// A whole series of assertions to catch bad uses of scriptable flags on
// the siWrapper...
@@ -895,12 +1058,17 @@
JS_CALL_OBJECT_TRACER(trc, otherWrapper,
"XPCWrappedNative::mOtherWrapper");
}
}
+#ifdef DEBUG_slimwrappers
+static PRUint32 sMorphedSlimWrappers;
+#endif
+
JSBool
-XPCWrappedNative::Init(XPCCallContext& ccx, JSObject* parent, JSBool isGlobal,
+XPCWrappedNative::Init(XPCCallContext& ccx,
+ JSObject* parent, JSBool isGlobal,
const XPCNativeScriptableCreateInfo* sci)
{
// setup our scriptable info...
if(sci->GetCallback())
@@ -968,10 +1136,42 @@
mFlatJSObject = xpc_NewSystemInheritingJSObject(ccx, jsclazz, protoJSObject,
parent);
if(!mFlatJSObject)
return JS_FALSE;
+ return FinishInit(ccx);
+}
+
+JSBool
+XPCWrappedNative::Init(XPCCallContext &ccx, JSObject *existingJSObject)
+{
+ mScriptableInfo = GetProto()->GetScriptableInfo();
+ JSClass* jsclazz = mScriptableInfo->GetJSClass();
+
+ // Morph the existing object.
+#ifdef DEBUG
+ JSObject* protoJSObject = GetProto()->GetJSProtoObject();
+ NS_ASSERTION(protoJSObject->map->ops == jsclazz->getObjectOps(ccx, jsclazz),
+ "Ugh, we can't deal with that!");
+#endif
+
+ mFlatJSObject = existingJSObject;
+
+ // Make sure we preserve any flags borrowing bits in classword.
+ mFlatJSObject->classword ^= (jsuword)OBJ_GET_CLASS(ccx, mFlatJSObject);
+ mFlatJSObject->classword |= (jsuword)jsclazz;
+
+ SLIM_LOG(("----- %i morphed slim wrapper (mFlatJSObject: %p, %p)\n",
+ ++sMorphedSlimWrappers, mFlatJSObject,
+ static_cast<nsISupports*>(xpc_GetJSPrivate(mFlatJSObject))));
+
+ return FinishInit(ccx);
+}
+
+JSBool
+XPCWrappedNative::FinishInit(XPCCallContext &ccx)
+{
// In the current JS engine JS_SetPrivate can't fail. But if it *did*
// fail then we would not receive our finalizer call and would not be
// able to properly cleanup. So, if it fails we null out mFlatJSObject
// to indicate the invalid state of this object and return false.
if(!JS_SetPrivate(ccx, mFlatJSObject, this))
@@ -984,12 +1184,13 @@
// Since this reference will push the refcount to 2 it will also root
// mFlatJSObject;
NS_ASSERTION(1 == mRefCnt, "unexpected refcount value");
NS_ADDREF(this);
- if(si && si->GetFlags().WantCreate() &&
- NS_FAILED(si->GetCallback()->Create(this, ccx, mFlatJSObject)))
+ if(mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() &&
+ NS_FAILED(mScriptableInfo->GetCallback()->Create(this, ccx,
+ mFlatJSObject)))
{
return JS_FALSE;
}
#ifdef XPC_CHECK_WRAPPER_THREADSAFETY
@@ -998,10 +1199,13 @@
if(HasProto() && GetProto()->ClassIsMainThreadOnly() && !NS_IsMainThread())
DEBUG_ReportWrapperThreadSafetyError(ccx,
"MainThread only wrapper created on the wrong thread", this);
#endif
+ // A hack for bug 517665, increase the probability for GC.
+ ccx.GetJSContext()->updateMallocCounter(2 * sizeof(XPCWrappedNative));
+
return JS_TRUE;
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
@@ -1250,47 +1454,69 @@
if(!iface)
return NS_ERROR_FAILURE;
nsresult rv;
- XPCWrappedNative* wrapper;
- rv = XPCWrappedNative::GetUsedOnly(ccx, aCOMObj, aOldScope, iface, &wrapper);
- if(NS_FAILED(rv))
- return rv;
+ nsRefPtr<XPCWrappedNative> wrapper;
+ JSObject *flat;
+ nsWrapperCache* cache = nsnull;
+ CallQueryInterface(aCOMObj, &cache);
+ if(cache)
+ {
+ flat = cache->GetWrapper();
+ if(flat && !IS_SLIM_WRAPPER(flat))
+ wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(flat));
+
+ }
+ else
+ {
+ rv = XPCWrappedNative::GetUsedOnly(ccx, aCOMObj, aOldScope, iface,
+ getter_AddRefs(wrapper));
+ if(NS_FAILED(rv))
+ return rv;
- if(!wrapper || !wrapper->IsValid())
+ flat = wrapper->GetFlatJSObject();
+ }
+
+ if(!flat)
{
- NS_IF_RELEASE(wrapper);
*aWrapper = nsnull;
return NS_OK;
}
// ReparentWrapperIfFound is really only meant to be called from DOM code
// which must happen only on the main thread. Bail if we're on some other
// thread or have a non-main-thread-only wrapper.
if (!XPCPerThreadData::IsMainThread(ccx) ||
- (wrapper->GetProto() &&
+ (wrapper &&
+ wrapper->GetProto() &&
!wrapper->GetProto()->ClassIsMainThreadOnly())) {
- NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
if(aOldScope != aNewScope)
{
// Oh, so now we need to move the wrapper to a different scope.
// First notify any XOWs.
- nsXPConnect* xpc = nsXPConnect::GetXPConnect();
- xpc->UpdateXOWs(ccx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE);
+ if(wrapper)
+ {
+ nsXPConnect* xpc = nsXPConnect::GetXPConnect();
+ xpc->UpdateXOWs(ccx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE);
+ }
AutoMarkingWrappedNativeProtoPtr oldProto(ccx);
AutoMarkingWrappedNativeProtoPtr newProto(ccx);
- if(wrapper->HasProto())
- {
+ if(!wrapper)
+ oldProto = GetSlimWrapperProto(flat);
+ else if(wrapper->HasProto())
oldProto = wrapper->GetProto();
+
+ if(oldProto)
+ {
XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
XPCNativeScriptableCreateInfo ci(*info);
newProto =
XPCWrappedNativeProto::GetNewOrUsed(ccx, aNewScope,
oldProto->GetClassInfo(),
@@ -1298,91 +1524,102 @@
!oldProto->IsShared(),
(info->GetJSClass()->flags & JSCLASS_IS_GLOBAL),
oldProto->GetOffsetsMasked());
if(!newProto)
{
- NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
}
- if(!XPC_XOW_WrapperMoved(ccx, wrapper, aNewScope))
+ if(wrapper)
{
- NS_RELEASE(wrapper);
- return NS_ERROR_FAILURE;
- }
+ if(!XPC_XOW_WrapperMoved(ccx, wrapper, aNewScope))
+ {
+ return NS_ERROR_FAILURE;
+ }
- Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
- Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
+ Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
+ Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
- { // scoped lock
- XPCAutoLock lock(aOldScope->GetRuntime()->GetMapLock());
+ { // scoped lock
+ XPCAutoLock lock(aOldScope->GetRuntime()->GetMapLock());
- oldMap->Remove(wrapper);
+ oldMap->Remove(wrapper);
- if(wrapper->HasProto())
- wrapper->SetProto(newProto);
+ if(wrapper->HasProto())
+ wrapper->SetProto(newProto);
- // If the wrapper has no scriptable or it has a non-shared
- // scriptable, then we don't need to mess with it.
- // Otherwise...
+ // If the wrapper has no scriptable or it has a non-shared
+ // scriptable, then we don't need to mess with it.
+ // Otherwise...
- if(wrapper->mScriptableInfo &&
- wrapper->mScriptableInfo == oldProto->GetScriptableInfo())
- {
- // The new proto had better have the same JSClass stuff as the
- // old one! We maintain a runtime wide unique map of this stuff.
- // So, if these don't match then the caller is doing something
- // bad here.
+ if(wrapper->mScriptableInfo &&
+ wrapper->mScriptableInfo == oldProto->GetScriptableInfo())
+ {
+ // The new proto had better have the same JSClass stuff as
+ // the old one! We maintain a runtime wide unique map of
+ // this stuff. So, if these don't match then the caller is
+ // doing something bad here.
- NS_ASSERTION(
- oldProto->GetScriptableInfo()->GetScriptableShared() ==
- newProto->GetScriptableInfo()->GetScriptableShared(),
- "Changing proto is also changing JSObject Classname or "
- "helper's nsIXPScriptable flags. This is not allowed!");
+ NS_ASSERTION(
+ oldProto->GetScriptableInfo()->GetScriptableShared() ==
+ newProto->GetScriptableInfo()->GetScriptableShared(),
+ "Changing proto is also changing JSObject Classname or "
+ "helper's nsIXPScriptable flags. This is not allowed!");
- wrapper->mScriptableInfo = newProto->GetScriptableInfo();
- }
+ wrapper->mScriptableInfo = newProto->GetScriptableInfo();
+ }
- NS_ASSERTION(!newMap->Find(wrapper->GetIdentityObject()),
- "wrapper already in new scope!");
+ NS_ASSERTION(!newMap->Find(wrapper->GetIdentityObject()),
+ "wrapper already in new scope!");
- (void) newMap->Add(wrapper);
- }
+ (void) newMap->Add(wrapper);
+ }
- // We only try to fixup the __proto__ JSObject if the wrapper
- // is directly using that of its XPCWrappedNativeProto.
+ // We only try to fixup the __proto__ JSObject if the wrapper
+ // is directly using that of its XPCWrappedNativeProto.
- if(wrapper->HasProto() &&
- STOBJ_GET_PROTO(wrapper->GetFlatJSObject()) ==
- oldProto->GetJSProtoObject())
+ if(wrapper->HasProto() &&
+ STOBJ_GET_PROTO(flat) == oldProto->GetJSProtoObject())
+ {
+ if(!JS_SetPrototype(ccx, flat, newProto->GetJSProtoObject()))
+ {
+ // this is bad, very bad
+ NS_ERROR("JS_SetPrototype failed");
+ return NS_ERROR_FAILURE;
+ }
+ }
+ else
+ {
+ NS_WARNING("Moving XPConnect wrappedNative to new scope, "
+ "but can't fixup __proto__");
+ }
+ }
+ else
{
- if(!JS_SetPrototype(ccx, wrapper->GetFlatJSObject(),
- newProto->GetJSProtoObject()))
+ if(!JS_SetReservedSlot(ccx, flat, 0,
+ PRIVATE_TO_JSVAL(newProto.get())) ||
+ !JS_SetPrototype(ccx, flat, newProto->GetJSProtoObject()))
{
// this is bad, very bad
+ JS_SetReservedSlot(ccx, flat, 0, JSVAL_NULL);
NS_ERROR("JS_SetPrototype failed");
- NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
}
- else
- {
- NS_WARNING("Moving XPConnect wrappedNative to new scope, "
- "but can't fixup __proto__");
- }
}
// Now we can just fix up the parent and return the wrapper
- if(!JS_SetParent(ccx, wrapper->GetFlatJSObject(), aNewParent))
+ if(!JS_SetParent(ccx, flat, aNewParent))
{
- NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
- *aWrapper = wrapper;
+ *aWrapper = nsnull;
+ wrapper.swap(*aWrapper);
+
return NS_OK;
}
#define IS_TEAROFF_CLASS(clazz) \
((clazz) == &XPC_WN_Tearoff_JSClass)
@@ -1417,10 +1654,14 @@
NS_ASSERTION(STOBJ_GET_PARENT(funObjParent), "funobj's parent (proto) is global");
proto = (XPCWrappedNativeProto*) xpc_GetJSPrivate(funObjParent);
if(proto)
protoClassInfo = proto->GetClassInfo();
}
+ else if(IS_SLIM_WRAPPER_CLASS(funObjParentClass))
+ {
+ NS_ERROR("function object has slim wrapper!");
+ }
else if(IS_WRAPPER_CLASS(funObjParentClass))
{
cur = funObjParent;
goto return_wrapper;
}
@@ -1441,10 +1682,26 @@
{
// this is on two lines to make the compiler happy given the goto.
JSClass* clazz;
clazz = STOBJ_GET_CLASS(cur);
+ if(IS_SLIM_WRAPPER_CLASS(clazz))
+ {
+ if(proto)
+ {
+ XPCWrappedNativeProto* wrapper_proto =
+ GetSlimWrapperProto(cur);
+ if(proto != wrapper_proto &&
+ (proto->GetScope() != wrapper_proto->GetScope() ||
+ !protoClassInfo || !wrapper_proto ||
+ protoClassInfo != wrapper_proto->GetClassInfo()))
+ continue;
+ }
+ if(pobj2)
+ *pobj2 = cur;
+ return nsnull;
+ }
if(IS_WRAPPER_CLASS(clazz))
{
return_wrapper:
XPCWrappedNative* wrapper =
(XPCWrappedNative*) xpc_GetJSPrivate(cur);
@@ -2174,15 +2431,19 @@
if((paramInfo.IsOut() || paramInfo.IsDipper()) &&
!paramInfo.IsRetval()) {
NS_ASSERTION(i < argc || paramInfo.IsOptional(),
"Expected either enough arguments or an optional argument");
jsval arg = i < argc ? argv[i] : JSVAL_NULL;
- if(JSVAL_IS_PRIMITIVE(arg) ||
- !JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(arg),
- rt->GetStringID(XPCJSRuntime::IDX_VALUE),
- &src))
+ if((JSVAL_IS_PRIMITIVE(arg) ||
+ !JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(arg),
+ rt->GetStringID(XPCJSRuntime::IDX_VALUE),
+ &src))
+ && i < argc)
{
+ // Explicitly passed in unusable value for out param. Note that
+ // if i >= argc we already know that |arg| is JSVAL_NULL, and
+ // that's ok.
ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, ccx);
goto done;
}
}
@@ -2355,14 +2616,18 @@
if(!paramInfo.IsRetval()) {
NS_ASSERTION(i < argc || paramInfo.IsOptional(),
"Expected either enough arguments or an optional argument");
jsval arg = i < argc ? argv[i] : JSVAL_NULL;
- if(JSVAL_IS_PRIMITIVE(arg) ||
- !JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(arg),
- rt->GetStringID(XPCJSRuntime::IDX_VALUE), &src))
+ if((JSVAL_IS_PRIMITIVE(arg) ||
+ !JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(arg),
+ rt->GetStringID(XPCJSRuntime::IDX_VALUE), &src))
+ && i < argc)
{
+ // Explicitly passed in unusable value for out param.
+ // Note that if i >= argc we already know that |arg| is
+ // JSVAL_NULL, and that's ok.
ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, ccx);
goto done;
}
}
@@ -2513,11 +2778,12 @@
¶m_iid))
goto done;
if(isArray)
{
- if(!XPCConvert::NativeArray2JS(ccx, &v, (const void**)&dp->val,
+ XPCLazyCallContext lccx(ccx);
+ if(!XPCConvert::NativeArray2JS(lccx, &v, (const void**)&dp->val,
datum_type, ¶m_iid,
array_count, ccx.GetCurrentJSObject(),
&err))
{
// XXX need exception scheme for arrays to indicate bad element
@@ -3477,6 +3743,120 @@
{
NS_ASSERTION(0, "bad param");
return nsnull;
}
return new XPCJSObjectHolder(ccx, obj);
+}
+
+JSBool
+MorphSlimWrapper(JSContext *cx, JSObject *obj)
+{
+ SLIM_LOG(("***** morphing from MorphSlimToWrapper (%p, %p)\n",
+ obj, static_cast<nsISupports*>(xpc_GetJSPrivate(obj))));
+
+ XPCCallContext ccx(JS_CALLER, cx);
+
+ nsISupports* object = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
+ nsWrapperCache *cache = nsnull;
+ CallQueryInterface(object, &cache);
+ nsRefPtr<XPCWrappedNative> wn;
+ nsresult rv = XPCWrappedNative::Morph(ccx, obj, nsnull, cache,
+ getter_AddRefs(wn));
+ return NS_SUCCEEDED(rv);
+}
+
+#ifdef DEBUG_slimwrappers
+static PRUint32 sSlimWrappers;
+#endif
+
+JSBool
+ConstructSlimWrapper(XPCCallContext &ccx, nsISupports *p, nsWrapperCache *cache,
+ XPCWrappedNativeScope* xpcScope, jsval *rval)
+{
+ nsCOMPtr<nsISupports> identityObj = do_QueryInterface(p);
+
+ nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(p);
+ if(!classInfo)
+ return JS_FALSE;
+
+ // XXX Sucks a bit that we have to get the proto before we know whether we
+ // can create a slim wrapper.
+ XPCNativeScriptableCreateInfo sciProto;
+ nsresult rv = XPCWrappedNative::GatherProtoScriptableCreateInfo(classInfo,
+ &sciProto);
+ NS_ENSURE_SUCCESS(rv, JS_FALSE);
+
+ JSObject* parent = xpcScope->GetGlobalJSObject();
+ if(!sciProto.GetFlags().WantPreCreate())
+ {
+ SLIM_LOG_NOT_CREATED(ccx, identityObj,
+ "scriptable helper has no PreCreate hook");
+
+ return JS_FALSE;
+ }
+
+ JSObject* plannedParent = parent;
+ rv = sciProto.GetCallback()->PreCreate(identityObj, ccx, parent,
+ &parent);
+ if(rv != NS_SUCCESS_ALLOW_SLIM_WRAPPERS)
+ {
+ SLIM_LOG_NOT_CREATED(ccx, identityObj, "PreCreate hook refused");
+
+ return JS_FALSE;
+ }
+
+ if(parent != plannedParent)
+ {
+ XPCWrappedNativeScope *newXpcScope =
+ XPCWrappedNativeScope::FindInJSObjectScope(ccx, parent);
+ if(newXpcScope != xpcScope)
+ {
+ SLIM_LOG_NOT_CREATED(ccx, identityObj, "crossing origins");
+
+ return JS_FALSE;
+ }
+ }
+
+ // The PreCreate hook could have forced the creation of a wrapper, need
+ // to check for that here and return early.
+ JSObject* wrapper = cache->GetWrapper();
+ if(wrapper)
+ {
+ *rval = OBJECT_TO_JSVAL(wrapper);
+
+ return JS_TRUE;
+ }
+
+ AutoMarkingWrappedNativeProtoPtr xpcproto(ccx);
+ JSBool isGlobal = JS_FALSE;
+ xpcproto = XPCWrappedNativeProto::GetNewOrUsed(ccx, xpcScope, classInfo,
+ &sciProto, JS_FALSE,
+ isGlobal);
+ if(!xpcproto)
+ return JS_FALSE;
+
+ xpcproto->CacheOffsets(identityObj);
+
+ XPCNativeScriptableInfo* si = xpcproto->GetScriptableInfo();
+ JSClass* jsclazz = si->GetSlimJSClass();
+ if(!jsclazz->addProperty)
+ return JS_FALSE;
+
+ wrapper = xpc_NewSystemInheritingJSObject(ccx, jsclazz,
+ xpcproto->GetJSProtoObject(),
+ parent);
+ if(!JS_SetPrivate(ccx, wrapper, identityObj) ||
+ !JS_SetReservedSlot(ccx, wrapper, 0, PRIVATE_TO_JSVAL(xpcproto.get())))
+ return JS_FALSE;
+
+ // Transfer ownership to the wrapper's private.
+ identityObj.forget();
+
+ cache->SetWrapper(wrapper);
+
+ SLIM_LOG(("+++++ %i created slim wrapper (%p, %p, %p)\n", ++sSlimWrappers,
+ wrapper, p, xpcScope));
+
+ *rval = OBJECT_TO_JSVAL(wrapper);
+
+ return JS_TRUE;
}