Bug 281102 - Allow many Java proxies per XPCOM object. r=darin

Original committer: pedemont%us.ibm.com
Original revision: 1.22
Original date: 2005/02/24 21:53:45
This commit is contained in:
pedemont%us.ibm.com 2006-09-27 15:18:10 +00:00
Родитель 9c86335cf7
Коммит 9099d9e10a
2 изменённых файлов: 222 добавлений и 106 удалений

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

@ -334,26 +334,8 @@ SetupParams(JNIEnv *env, const jobject aParam, const nsXPTParamInfo &aParamInfo,
env->GetObjectArrayElement((jobjectArray) aParam, 0); env->GetObjectArrayElement((jobjectArray) aParam, 0);
} }
void* xpcom_obj; nsISupports* xpcom_obj;
if (java_obj) { if (java_obj) {
// Check if we already have a corresponding XPCOM object
jboolean isProxy = env->CallStaticBooleanMethod(xpcomJavaProxyClass,
isXPCOMJavaProxyMID,
java_obj);
if (env->ExceptionCheck()) {
rv = NS_ERROR_FAILURE;
break;
}
void* inst;
if (isProxy) {
rv = GetXPCOMInstFromProxy(env, java_obj, &inst);
if (NS_FAILED(rv))
break;
} else {
inst = gBindings->GetXPCOMObject(env, java_obj);
}
// Get IID for this param // Get IID for this param
nsID iid; nsID iid;
rv = GetIIDForMethodParam(aIInfo, aMethodInfo, aParamInfo, rv = GetIIDForMethodParam(aIInfo, aMethodInfo, aParamInfo,
@ -362,53 +344,57 @@ SetupParams(JNIEnv *env, const jobject aParam, const nsXPTParamInfo &aParamInfo,
if (NS_FAILED(rv)) if (NS_FAILED(rv))
break; break;
PRBool isWeakRef = iid.Equals(NS_GET_IID(nsIWeakReference)); // If the requested interface is nsIWeakReference, then we look for or
// create a stub for the nsISupports interface. Then we create a weak
// reference from that stub.
PRBool isWeakRef;
if (iid.Equals(NS_GET_IID(nsIWeakReference))) {
isWeakRef = PR_TRUE;
iid = NS_GET_IID(nsISupports);
} else {
isWeakRef = PR_FALSE;
}
if (inst == nsnull && !isWeakRef) { PRBool isXPTCStub;
// If there is not corresponding XPCOM object, then that means that the rv = GetNewOrUsedXPCOMObject(env, java_obj, iid, &xpcom_obj,
// parameter is non-generated class (that is, it is not one of our &isXPTCStub);
// Java stubs that represent an exising XPCOM object). So we need to
// create an XPCOM stub, that can route any method calls to the class.
// Get interface info for class
nsCOMPtr<nsIInterfaceInfoManager> iim = XPTI_GetInterfaceInfoManager();
nsCOMPtr<nsIInterfaceInfo> iinfo;
rv = iim->GetInfoForIID(&iid, getter_AddRefs(iinfo));
if (NS_FAILED(rv)) if (NS_FAILED(rv))
break; break;
// Create XPCOM stub // If the function expects a weak reference, then we need to
nsJavaXPTCStub* xpcomStub = new nsJavaXPTCStub(env, java_obj, iinfo);
if (!xpcomStub) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
inst = SetAsXPTCStub(xpcomStub);
gBindings->AddBinding(env, java_obj, inst);
}
if (isWeakRef) {
// If the function expects an weak reference, then we need to
// create it here. // create it here.
nsJavaXPTCStubWeakRef* weakref = if (isWeakRef) {
new nsJavaXPTCStubWeakRef(env, java_obj); if (isXPTCStub) {
nsJavaXPTCStub* stub = NS_STATIC_CAST(nsJavaXPTCStub*,
NS_STATIC_CAST(nsXPTCStubBase*,
xpcom_obj));
nsJavaXPTCStubWeakRef* weakref;
weakref = new nsJavaXPTCStubWeakRef(env, java_obj, stub);
if (!weakref) { if (!weakref) {
rv = NS_ERROR_OUT_OF_MEMORY; rv = NS_ERROR_OUT_OF_MEMORY;
break; break;
} }
NS_ADDREF(weakref); xpcom_obj = weakref;
xpcom_obj = (void*) weakref; NS_ADDREF(xpcom_obj);
aVariant.SetValIsAllocated(); aVariant.SetValIsAllocated();
} else { // if is native XPCOM object
} else if (IsXPTCStub(inst)) { nsCOMPtr<nsISupportsWeakReference> supportsweak =
nsJavaXPTCStub* xpcomStub = GetXPTCStubAddr(inst); do_QueryInterface(xpcom_obj);
NS_ADDREF(xpcomStub); if (supportsweak) {
xpcom_obj = (void*) xpcomStub; nsWeakPtr weakref;
aVariant.SetValIsAllocated(); supportsweak->GetWeakReference(getter_AddRefs(weakref));
xpcom_obj = weakref;
NS_ADDREF(xpcom_obj);
} else { } else {
JavaXPCOMInstance* xpcomInst = (JavaXPCOMInstance*) inst; xpcom_obj = nsnull;
xpcom_obj = (void*) xpcomInst->GetInstance(); }
}
} else if (isXPTCStub) {
aVariant.SetValIsAllocated();
} else { // if is native XPCOM object
xpcom_obj->Release();
} }
} else { } else {
xpcom_obj = nsnull; xpcom_obj = nsnull;
@ -682,7 +668,7 @@ FinalizeParams(JNIEnv *env, const jobject aParam,
case nsXPTType::T_INTERFACE: case nsXPTType::T_INTERFACE:
case nsXPTType::T_INTERFACE_IS: case nsXPTType::T_INTERFACE_IS:
{ {
void* xpcom_obj = aVariant.val.p; nsISupports* xpcom_obj = NS_STATIC_CAST(nsISupports*, aVariant.val.p);
if (aParamInfo.IsOut() && aParam) { // 'inout' & 'out' if (aParamInfo.IsOut() && aParam) { // 'inout' & 'out'
jobject java_obj = nsnull; jobject java_obj = nsnull;
@ -694,9 +680,13 @@ FinalizeParams(JNIEnv *env, const jobject aParam,
break; break;
// Get matching Java object for given xpcom object // Get matching Java object for given xpcom object
rv = gBindings->GetJavaObject(env, xpcom_obj, iid, PR_TRUE, &java_obj); PRBool isNewProxy;
rv = GetNewOrUsedJavaObject(env, xpcom_obj, iid, &java_obj,
&isNewProxy);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
break; break;
if (isNewProxy)
NS_RELEASE(xpcom_obj); // Java proxy owns ref to object
} }
// put new Java object into output array // put new Java object into output array
@ -706,8 +696,7 @@ FinalizeParams(JNIEnv *env, const jobject aParam,
// If VAL_IS_ALLOCD is set, that means that an XPCOM object was created // If VAL_IS_ALLOCD is set, that means that an XPCOM object was created
// is SetupParams that now needs to be released. // is SetupParams that now needs to be released.
if (xpcom_obj && aVariant.IsValAllocated()) { if (xpcom_obj && aVariant.IsValAllocated()) {
nsISupports* variant = NS_STATIC_CAST(nsISupports*, xpcom_obj); NS_RELEASE(xpcom_obj);
NS_RELEASE(variant);
} }
} }
break; break;
@ -877,7 +866,8 @@ SetRetval(JNIEnv *env, const nsXPTParamInfo &aParamInfo,
case nsXPTType::T_INTERFACE: case nsXPTType::T_INTERFACE:
case nsXPTType::T_INTERFACE_IS: case nsXPTType::T_INTERFACE_IS:
{ {
if (aVariant.val.p) { nsISupports* xpcom_obj = NS_STATIC_CAST(nsISupports*, aVariant.val.p);
if (xpcom_obj) {
nsID iid; nsID iid;
rv = GetIIDForMethodParam(aIInfo, aMethodInfo, aParamInfo, aMethodIndex, rv = GetIIDForMethodParam(aIInfo, aMethodInfo, aParamInfo, aMethodIndex,
aDispatchParams, PR_TRUE, iid); aDispatchParams, PR_TRUE, iid);
@ -886,13 +876,15 @@ SetRetval(JNIEnv *env, const nsXPTParamInfo &aParamInfo,
// Get matching Java object for given xpcom object // Get matching Java object for given xpcom object
jobject java_obj; jobject java_obj;
rv = gBindings->GetJavaObject(env, aVariant.val.p, iid, PR_TRUE, PRBool isNewProxy;
&java_obj); rv = GetNewOrUsedJavaObject(env, xpcom_obj, iid, &java_obj,
&isNewProxy);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
break; break;
if (isNewProxy)
xpcom_obj->Release(); // Java proxy owns ref to object
// If returned object is an nsJavaXPTCStub, release it. // If returned object is an nsJavaXPTCStub, release it.
nsISupports* xpcom_obj = NS_STATIC_CAST(nsISupports*, aVariant.val.p);
nsJavaXPTCStub* stub = nsnull; nsJavaXPTCStub* stub = nsnull;
xpcom_obj->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub); xpcom_obj->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub);
if (stub) { if (stub) {
@ -1079,7 +1071,7 @@ JAVAPROXY_NATIVE(callXPCOMMethod) (JNIEnv *env, jclass that, jobject aJavaProxy,
#ifdef DEBUG_JAVAXPCOM #ifdef DEBUG_JAVAXPCOM
const char* ifaceName; const char* ifaceName;
iinfo->GetNameShared(&ifaceName); iinfo->GetNameShared(&ifaceName);
LOG(("=> Calling %s::%s()\n", ifaceName, methodInfo->GetName())); LOG(("===> (XPCOM) %s::%s()\n", ifaceName, methodInfo->GetName()));
#endif #endif
// Convert the Java params // Convert the Java params
@ -1199,6 +1191,7 @@ JAVAPROXY_NATIVE(callXPCOMMethod) (JNIEnv *env, jclass that, jobject aJavaProxy,
ThrowException(env, invokeResult, message.get()); ThrowException(env, invokeResult, message.get());
} }
LOG(("<=== (XPCOM) %s::%s()\n", ifaceName, methodInfo->GetName()));
return result; return result;
} }
@ -1247,8 +1240,16 @@ CreateJavaProxy(JNIEnv* env, nsISupports* aXPCOMObject, const nsIID& aIID,
} }
if (java_obj) { if (java_obj) {
#ifdef DEBUG_JAVAXPCOM
char* iid_str = aIID.ToString();
LOG(("+ CreateJavaProxy (Java=%08x | XPCOM=%08x | IID=%s)\n",
env->CallIntMethod(java_obj, hashCodeMID),
(int) aXPCOMObject, iid_str));
PR_Free(iid_str);
#endif
// Associate XPCOM object with Java proxy // Associate XPCOM object with Java proxy
rv = gBindings->AddBinding(env, java_obj, inst); rv = gNativeToJavaProxyMap->Add(env, aXPCOMObject, aIID, java_obj);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
*aResult = java_obj; *aResult = java_obj;
return NS_OK; return NS_OK;
@ -1278,6 +1279,17 @@ GetXPCOMInstFromProxy(JNIEnv* env, jobject aJavaObject, void** aResult)
} }
*aResult = NS_REINTERPRET_CAST(void*, xpcom_obj); *aResult = NS_REINTERPRET_CAST(void*, xpcom_obj);
#ifdef DEBUG_JAVAXPCOM
JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, *aResult);
nsIID* iid;
inst->InterfaceInfo()->GetInterfaceIID(&iid);
char* iid_str = iid->ToString();
LOG(("< GetXPCOMInstFromProxy (Java=%08x | XPCOM=%08x | IID=%s)\n",
env->CallIntMethod(aJavaObject, hashCodeMID),
(int) inst->GetInstance(), iid_str));
PR_Free(iid_str);
nsMemory::Free(iid);
#endif
return NS_OK; return NS_OK;
} }
@ -1287,15 +1299,6 @@ GetXPCOMInstFromProxy(JNIEnv* env, jobject aJavaObject, void** aResult)
extern "C" JX_EXPORT void JNICALL extern "C" JX_EXPORT void JNICALL
JAVAPROXY_NATIVE(finalizeProxy) (JNIEnv *env, jclass that, jobject aJavaProxy) JAVAPROXY_NATIVE(finalizeProxy) (JNIEnv *env, jclass that, jobject aJavaProxy)
{ {
#ifdef DEBUG_JAVAXPCOM
jstring string = nsnull;
string = (jstring) env->CallStaticObjectMethod(xpcomJavaProxyClass,
proxyToStringMID, aJavaProxy);
const char* javaObjectName = env->GetStringUTFChars(string, nsnull);
LOG(("*** Finalize(java_obj=%s)\n", javaObjectName));
env->ReleaseStringUTFChars(string, javaObjectName);
#endif
// Due to Java's garbage collection, this finalize statement may get called // Due to Java's garbage collection, this finalize statement may get called
// after FreeJavaGlobals(). So check to make sure that everything is still // after FreeJavaGlobals(). So check to make sure that everything is still
// initialized. // initialized.
@ -1307,7 +1310,20 @@ JAVAPROXY_NATIVE(finalizeProxy) (JNIEnv *env, jclass that, jobject aJavaProxy)
nsresult rv = GetXPCOMInstFromProxy(env, aJavaProxy, &xpcom_obj); nsresult rv = GetXPCOMInstFromProxy(env, aJavaProxy, &xpcom_obj);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj); JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj);
gBindings->RemoveBinding(env, aJavaProxy, nsnull); nsIID* iid;
rv = inst->InterfaceInfo()->GetInterfaceIID(&iid);
if (NS_SUCCEEDED(rv)) {
rv = gNativeToJavaProxyMap->Remove(env, inst->GetInstance(), *iid);
#ifdef DEBUG_JAVAXPCOM
char* iid_str = iid->ToString();
LOG(("- Finalize (Java=%08x | XPCOM=%08x | IID=%s)\n",
env->CallIntMethod(aJavaProxy, hashCodeMID),
(int) inst->GetInstance(), iid_str));
PR_Free(iid_str);
#endif
nsMemory::Free(iid);
}
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to RemoveJavaProxy");
delete inst; delete inst;
} }
} }

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

@ -43,9 +43,12 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsString.h" #include "nsString.h"
#include "pldhash.h" #include "pldhash.h"
#include "nsJavaXPTCStub.h"
#include "nsAutoLock.h" #include "nsAutoLock.h"
//#define DEBUG_JAVAXPCOM //#define DEBUG_JAVAXPCOM
//#define DEBUG_JAVAXPCOM_REFCNT
#ifdef DEBUG_JAVAXPCOM #ifdef DEBUG_JAVAXPCOM
#define LOG(x) printf x #define LOG(x) printf x
#else #else
@ -62,6 +65,7 @@
/********************* /*********************
* Java JNI globals * Java JNI globals
*********************/ *********************/
extern jclass booleanClass; extern jclass booleanClass;
extern jclass charClass; extern jclass charClass;
extern jclass byteClass; extern jclass byteClass;
@ -101,11 +105,24 @@ extern jmethodID getNameMID;
extern jmethodID proxyToStringMID; extern jmethodID proxyToStringMID;
#endif #endif
class nsJavaXPCOMBindings; class NativeToJavaProxyMap;
extern nsJavaXPCOMBindings* gBindings; extern NativeToJavaProxyMap* gNativeToJavaProxyMap;
class JavaToXPTCStubMap;
extern JavaToXPTCStubMap* gJavaToXPTCStubMap;
extern PRLock* gJavaXPCOMLock; extern PRLock* gJavaXPCOMLock;
/**
* Initialize global structures used by Javaconnect.
* @param env Java environment pointer
* @return PR_TRUE if Javaconnect is initialized; PR_FALSE if an error occurred
*/
PRBool InitializeJavaGlobals(JNIEnv *env); PRBool InitializeJavaGlobals(JNIEnv *env);
/**
* Frees global structures that were allocated by InitializeJavaGlobals().
* @param env Java environment pointer
*/
void FreeJavaGlobals(JNIEnv* env); void FreeJavaGlobals(JNIEnv* env);
@ -129,42 +146,125 @@ private:
/************************************** /**************************************
* Java<->XPCOM binding stores * Java<->XPCOM object mappings
**************************************/ **************************************/
// This class is used to store the associations between existing Java object
// and XPCOM objects. /**
class nsJavaXPCOMBindings * Maps native XPCOM objects to their associated Java proxy object.
*/
class NativeToJavaProxyMap
{ {
public: protected:
nsJavaXPCOMBindings() struct ProxyList
: mJAVAtoXPCOMBindings(nsnull) {
, mXPCOMtoJAVABindings(nsnull) ProxyList(const jobject aRef, const nsIID& aIID, ProxyList* aList)
: javaObject(aRef)
, iid(aIID)
, next(aList)
{ } { }
~nsJavaXPCOMBindings();
// Initializes internal structures. const jobject javaObject;
NS_IMETHOD Init(); const nsIID iid;
ProxyList* next;
};
// Associates the given Java object with the given XPCOM object struct Entry : public PLDHashEntryHdr
NS_IMETHOD AddBinding(JNIEnv* env, jobject aJavaStub, void* aXPCOMObject); {
nsISupports* key;
ProxyList* list;
};
// Given either a Java object or XPCOM object, removes the association public:
// between the two. NativeToJavaProxyMap()
NS_IMETHOD RemoveBinding(JNIEnv* env, jobject aJavaObject, void* aXPCOMObject); : mHashTable(nsnull)
{ }
// Given a Java object, returns the associated XPCOM object. ~NativeToJavaProxyMap();
void* GetXPCOMObject(JNIEnv* env, jobject aJavaObject);
// Given an XPCOM object, returns the associated Java Object. If a Java nsresult Init();
// object doesn't exist, then create a Java proxy for the XPCOM object.
NS_IMETHOD GetJavaObject(JNIEnv* env, void* aXPCOMObject, const nsIID& aIID, nsresult Add(JNIEnv* env, nsISupports* aXPCOMObject, const nsIID& aIID,
PRBool aDoReleaseObject, jobject* aResult); jobject aProxy);
private:
PLDHashTable* mJAVAtoXPCOMBindings; nsresult Find(JNIEnv* env, nsISupports* aNativeObject, const nsIID& aIID,
PLDHashTable* mXPCOMtoJAVABindings; jobject* aResult);
nsresult Remove(JNIEnv* env, nsISupports* aNativeObject, const nsIID& aIID);
protected:
PLDHashTable* mHashTable;
};
/**
* Maps Java objects to their associated nsJavaXPTCStub.
*/
class JavaToXPTCStubMap
{
protected:
struct Entry : public PLDHashEntryHdr
{
jint key;
nsJavaXPTCStub* xptcstub;
};
public:
JavaToXPTCStubMap()
: mHashTable(nsnull)
{ }
~JavaToXPTCStubMap();
nsresult Init();
nsresult Add(JNIEnv* env, jobject aJavaObject, nsJavaXPTCStub* aProxy);
nsresult Find(JNIEnv* env, jobject aJavaObject, const nsIID& aIID,
nsJavaXPTCStub** aResult);
nsresult Remove(JNIEnv* env, jobject aJavaObject);
protected:
PLDHashTable* mHashTable;
}; };
/*******************************
* Helper functions
*******************************/
/**
* Finds the associated Java object for the given XPCOM object and IID. If no
* such Java object exists, then it creates one.
*
* @param env Java environment pointer
* @param aXPCOMObject XPCOM object for which to find/create Java object
* @param aIID desired interface IID for Java object
* @param aResult on success, holds reference to Java object
* @param aIsNewProxy on success, holds PR_TRUE if aResult points to newlyXPTCStub;
* created Java proxy; PR_FALSE otherwise
*
* @return NS_OK if succeeded; all other return values are error codes.
*/
nsresult GetNewOrUsedJavaObject(JNIEnv* env, nsISupports* aXPCOMObject,
const nsIID& aIID, jobject* aResult,
PRBool* aIsNewProxy);
/**
* Finds the associated XPCOM object for the given Java object and IID. If no
* such XPCOM object exists, then it creates one.
*
* @param env Java environment pointer
* @param aJavaObject Java object for which to find/create XPCOM object
* @param aIID desired interface IID for XPCOM object
* @param aResult on success, holds AddRef'd reference to XPCOM object
* @param aIsXPTCStub on success, holds PR_TRUE if aResult points to XPTCStub;
* PR_FALSE if aResult points to native XPCOM object
*
* @return NS_OK if succeeded; all other return values are error codes.
*/
nsresult GetNewOrUsedXPCOMObject(JNIEnv* env, jobject aJavaObject,
const nsIID& aIID, nsISupports** aResult,
PRBool* aIsXPTCStub);
nsresult GetIIDForMethodParam(nsIInterfaceInfo *iinfo, nsresult GetIIDForMethodParam(nsIInterfaceInfo *iinfo,
const nsXPTMethodInfo *methodInfo, const nsXPTMethodInfo *methodInfo,