diff --git a/extensions/java/xpcom/src/nsJavaXPCOMBindingUtils.cpp b/extensions/java/xpcom/src/nsJavaXPCOMBindingUtils.cpp index 18e258d996d..f2b163107a2 100644 --- a/extensions/java/xpcom/src/nsJavaXPCOMBindingUtils.cpp +++ b/extensions/java/xpcom/src/nsJavaXPCOMBindingUtils.cpp @@ -1,610 +1,610 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Java XPCOM Bindings. - * - * The Initial Developer of the Original Code is - * IBM Corporation. - * Portions created by the Initial Developer are Copyright (C) 2004 - * IBM Corporation. All Rights Reserved. - * - * Contributor(s): - * Javier Pedemonte (jhpedemonte@gmail.com) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - - -#include "nsJavaXPCOMBindingUtils.h" -#include "nsJavaXPTCStub.h" -#include "jni.h" -#include "nsIInterfaceInfoManager.h" -#include "pldhash.h" -#include "nsILocalFile.h" -#include "nsEventQueueUtils.h" -#include "nsProxyRelease.h" - - -/* Java JNI globals */ -jclass intClass = nsnull; -jclass intArrayClass = nsnull; -jclass stringClass = nsnull; -jclass nsISupportsClass = nsnull; -jclass xpcomExceptionClass = nsnull; - -jmethodID hashCodeMID = nsnull; -jmethodID booleanValueMID = nsnull; -jmethodID charValueMID = nsnull; -jmethodID byteValueMID = nsnull; -jmethodID shortValueMID = nsnull; -jmethodID intValueMID = nsnull; -jmethodID longValueMID = nsnull; -jmethodID floatValueMID = nsnull; -jmethodID doubleValueMID = nsnull; - -#ifdef DEBUG -jmethodID getNameMID = nsnull; -#endif - - -/************************************** - * Java<->XPCOM binding stores - **************************************/ -class JavaXPCOMBindingEntry : public PLDHashEntryHdr -{ -public: - // mKey will either be a Java hash of the Java object, or the address of - // the XPCOM object, depending on which hash table this entry is used in. - const void* mKey; - jobject mJavaObject; - void* mXPCOMInstance; -}; - -static PLDHashTable *gJAVAtoXPCOMBindings = nsnull; -static PLDHashTable *gXPCOMtoJAVABindings = nsnull; - -PR_STATIC_CALLBACK(PRBool) -InitJavaXPCOMBindingEntry(PLDHashTable *table, PLDHashEntryHdr *entry, - const void *key) -{ - JavaXPCOMBindingEntry *e = - NS_CONST_CAST(JavaXPCOMBindingEntry *, - NS_STATIC_CAST(const JavaXPCOMBindingEntry *, entry)); - - e->mKey = key; - - return PR_TRUE; -} - -void -AddJavaXPCOMBinding(JNIEnv* env, jobject aJavaObject, void* aXPCOMObject) -{ - // We use a Java hash of the Java object as a key since the JVM can return - // different "addresses" for the same Java object, but the hash code (the - // result of calling |hashCode()| on the Java object) will always be the same. - jint hash = env->CallIntMethod(aJavaObject, hashCodeMID); - jweak java_ref = env->NewWeakGlobalRef(aJavaObject); - - JavaXPCOMBindingEntry *entry = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gJAVAtoXPCOMBindings, - NS_INT32_TO_PTR(hash), - PL_DHASH_ADD)); - entry->mJavaObject = java_ref; - entry->mXPCOMInstance = aXPCOMObject; - - // We want the hash key to be the actual XPCOM object - void* xpcomObjKey = nsnull; - if (IsXPTCStub(aXPCOMObject)) - xpcomObjKey = GetXPTCStubAddr(aXPCOMObject); - else - xpcomObjKey = ((JavaXPCOMInstance*) aXPCOMObject)->GetInstance(); - - entry = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gXPCOMtoJAVABindings, xpcomObjKey, - PL_DHASH_ADD)); - entry->mJavaObject = java_ref; - entry->mXPCOMInstance = aXPCOMObject; - - LOG(("+ Adding Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x) weakref=0x%08x\n", - hash, (int) xpcomObjKey, (PRUint32) java_ref)); -} - -void -RemoveJavaXPCOMBinding(JNIEnv* env, jobject aJavaObject, void* aXPCOMObject) -{ - // We either get a Java or an XPCOM object. So find the other object. - jint hash = 0; - JavaXPCOMBindingEntry* entry = nsnull; - - if (aJavaObject) { - hash = env->CallIntMethod(aJavaObject, hashCodeMID); - JavaXPCOMBindingEntry* e = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gJAVAtoXPCOMBindings, - NS_INT32_TO_PTR(hash), - PL_DHASH_LOOKUP)); - if (PL_DHASH_ENTRY_IS_BUSY(e)) - entry = e; - } else { - JavaXPCOMBindingEntry* e = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gXPCOMtoJAVABindings, aXPCOMObject, - PL_DHASH_LOOKUP)); - if (PL_DHASH_ENTRY_IS_BUSY(e)) - entry = e; - } - NS_ASSERTION(entry != nsnull, "Failed to find matching entry"); - - if (entry) { - jobject jweakref = entry->mJavaObject; - void* xpcom_obj = entry->mXPCOMInstance; - - // Get keys. For Java, we use the hash key of the Java object. For XPCOM, - // we get the 'actual' object (either the nsJavaXPTCStub or the instance - // that is wrapped by JavaXPCOMInstance). - void* xpcomObjKey = nsnull; - if (hash == 0) - hash = env->CallIntMethod(jweakref, hashCodeMID); - if (aXPCOMObject) { - xpcomObjKey = aXPCOMObject; - } else { - if (IsXPTCStub(xpcom_obj)) - xpcomObjKey = GetXPTCStubAddr(xpcom_obj); - else - xpcomObjKey = ((JavaXPCOMInstance*) xpcom_obj)->GetInstance(); - } - - NS_ASSERTION(env->IsSameObject(jweakref, aJavaObject), - "Weakref does not point to expected Java object"); - NS_ASSERTION(!env->IsSameObject(jweakref, NULL), - "Weakref refers to garbage collected Java object. No longer valid"); - - // Remove both instances from stores - PL_DHashTableOperate(gJAVAtoXPCOMBindings, NS_INT32_TO_PTR(hash), - PL_DHASH_REMOVE); - PL_DHashTableOperate(gXPCOMtoJAVABindings, xpcomObjKey, PL_DHASH_REMOVE); - LOG(("- Removing Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x) weakref=0x%08x\n", - hash, (int) xpcomObjKey, (PRUint32) jweakref)); - - env->DeleteWeakGlobalRef(NS_STATIC_CAST(jweak, jweakref)); - } -} - -void* -GetMatchingXPCOMObject(JNIEnv* env, jobject aJavaObject) -{ - jint hash = env->CallIntMethod(aJavaObject, hashCodeMID); - - JavaXPCOMBindingEntry *entry = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gJAVAtoXPCOMBindings, - NS_INT32_TO_PTR(hash), - PL_DHASH_LOOKUP)); - - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { -#ifdef DEBUG - void* xpcomObjKey = nsnull; - if (IsXPTCStub(entry->mXPCOMInstance)) - xpcomObjKey = GetXPTCStubAddr(entry->mXPCOMInstance); - else - xpcomObjKey = ((JavaXPCOMInstance*) entry->mXPCOMInstance)->GetInstance(); - LOG(("< Get Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x)\n", - hash, (int) xpcomObjKey)); -#endif - return entry->mXPCOMInstance; - } - - return nsnull; -} - -jobject -GetMatchingJavaObject(JNIEnv* env, void* aXPCOMObject) -{ - jobject java_obj = nsnull; - nsISupports* xpcom_obj = NS_STATIC_CAST(nsISupports*, aXPCOMObject); - - nsJavaXPTCStub* stub = nsnull; - xpcom_obj->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub); - if (stub) { - java_obj = stub->GetJavaObject(); - NS_ASSERTION(java_obj != nsnull, "nsJavaXPTCStub w/o matching Java object"); - NS_RELEASE(stub); - } - - if (java_obj == nsnull) { - JavaXPCOMBindingEntry *entry = - NS_STATIC_CAST(JavaXPCOMBindingEntry*, - PL_DHashTableOperate(gXPCOMtoJAVABindings, aXPCOMObject, - PL_DHASH_LOOKUP)); - - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { - java_obj = entry->mJavaObject; - } - } - - if (java_obj) { - LOG(("< Get Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x)\n", - env->CallIntMethod(java_obj, hashCodeMID), (int) aXPCOMObject)); - } - - return java_obj; -} - - -/****************************** - * InitializeJavaGlobals - ******************************/ -PRBool gInitialized = PR_FALSE; - -PRBool -InitializeJavaGlobals(JNIEnv *env) -{ - if (gInitialized) - return PR_TRUE; - - jclass clazz; - - if (!(clazz = env->FindClass("java/lang/Object")) || - !(hashCodeMID = env->GetMethodID(clazz, "hashCode","()I"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Boolean")) || - !(booleanValueMID = env->GetMethodID(clazz,"booleanValue","()Z"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Character")) || - !(charValueMID = env->GetMethodID(clazz,"charValue","()C"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Byte")) || - !(byteValueMID = env->GetMethodID(clazz,"byteValue","()B"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Short")) || - !(shortValueMID = env->GetMethodID(clazz,"shortValue","()S"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Integer")) || - !(intClass = (jclass) env->NewGlobalRef(clazz)) || - !(clazz = env->FindClass("[I")) || - !(intArrayClass = (jclass) env->NewGlobalRef(clazz)) || - !(intValueMID = env->GetMethodID(intClass,"intValue","()I"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Long")) || - !(longValueMID = env->GetMethodID(clazz,"longValue","()J"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Float")) || - !(floatValueMID = env->GetMethodID(clazz,"floatValue","()F"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/Double")) || - !(doubleValueMID = env->GetMethodID(clazz,"doubleValue","()D"))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("java/lang/String")) || - !(stringClass = (jclass) env->NewGlobalRef(clazz))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("org/mozilla/xpcom/nsISupports")) || - !(nsISupportsClass = (jclass) env->NewGlobalRef(clazz))) - { - return PR_FALSE; - } - - if (!(clazz = env->FindClass("org/mozilla/xpcom/XPCOMException")) || - !(xpcomExceptionClass = (jclass) env->NewGlobalRef(clazz))) - { - return PR_FALSE; - } - -#ifdef DEBUG - if (!(clazz = env->FindClass("java/lang/Class")) || - !(getNameMID = env->GetMethodID(clazz, "getName","()Ljava/lang/String;"))) { - return PR_FALSE; - } -#endif - - static PLDHashTableOps java_to_xpcom_hash_ops = - { - PL_DHashAllocTable, - PL_DHashFreeTable, - PL_DHashGetKeyStub, - PL_DHashVoidPtrKeyStub, - PL_DHashMatchEntryStub, - PL_DHashMoveEntryStub, - PL_DHashClearEntryStub, - PL_DHashFinalizeStub, - InitJavaXPCOMBindingEntry - }; - - gJAVAtoXPCOMBindings = PL_NewDHashTable(&java_to_xpcom_hash_ops, nsnull, - sizeof(JavaXPCOMBindingEntry), 16); - if (!gJAVAtoXPCOMBindings) { - return PR_FALSE; - } - - static PLDHashTableOps xpcom_to_java_hash_ops = - { - PL_DHashAllocTable, - PL_DHashFreeTable, - PL_DHashGetKeyStub, - PL_DHashVoidPtrKeyStub, - PL_DHashMatchEntryStub, - PL_DHashMoveEntryStub, - PL_DHashClearEntryStub, - PL_DHashFinalizeStub, - InitJavaXPCOMBindingEntry - }; - - gXPCOMtoJAVABindings = PL_NewDHashTable(&xpcom_to_java_hash_ops, nsnull, - sizeof(JavaXPCOMBindingEntry), 16); - if (!gXPCOMtoJAVABindings) { - return PR_FALSE; - } - - gInitialized = PR_TRUE; - - return PR_TRUE; -} - -/************************* - * FreeJavaGlobals - *************************/ -void -FreeJavaGlobals(JNIEnv* env) -{ - if (!gInitialized) - return; - - env->DeleteGlobalRef(intClass); - env->DeleteGlobalRef(intArrayClass); - env->DeleteGlobalRef(stringClass); - env->DeleteGlobalRef(nsISupportsClass); - env->DeleteGlobalRef(xpcomExceptionClass); - - PL_DHashTableDestroy(gJAVAtoXPCOMBindings); - PL_DHashTableDestroy(gXPCOMtoJAVABindings); - - gInitialized = PR_FALSE; -} - - -/********************************************************** - * JavaXPCOMInstance - *********************************************************/ -JavaXPCOMInstance::JavaXPCOMInstance(nsISupports* aInstance, - nsIInterfaceInfo* aIInfo) - : mInstance(aInstance), - mIInfo(aIInfo) -{ - NS_ADDREF(mInstance); -} - -JavaXPCOMInstance::~JavaXPCOMInstance() -{ - nsCOMPtr eventQ; - nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQ)); - if (NS_SUCCEEDED(rv)) - rv = NS_ProxyRelease(eventQ, mInstance); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get MainEventQ"); -} - -JavaXPCOMInstance* -CreateJavaXPCOMInstance(nsISupports* aXPCOMObject, const nsIID* aIID) -{ - JavaXPCOMInstance* inst = nsnull; - - // Get interface info for class - nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); - NS_ASSERTION(iim != nsnull, "Failed to get InterfaceInfoManager"); - if (iim) { - nsCOMPtr info; - iim->GetInfoForIID(aIID, getter_AddRefs(info)); - - // Wrap XPCOM object - inst = new JavaXPCOMInstance(aXPCOMObject, info); - } - - return inst; -} - - -nsresult -GetIIDForMethodParam(nsIInterfaceInfo *iinfo, - const nsXPTMethodInfo *methodInfo, - const nsXPTParamInfo ¶mInfo, - PRUint16 methodIndex, - nsXPTCMiniVariant *dispatchParams, - PRBool isFullVariantArray, - nsID &result) -{ - nsresult rv; - - switch (paramInfo.GetType().TagPart()) - { - case nsXPTType::T_INTERFACE: - rv = iinfo->GetIIDForParamNoAlloc(methodIndex, ¶mInfo, &result); - break; - - case nsXPTType::T_INTERFACE_IS: - { - PRUint8 argnum; - rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, ¶mInfo, &argnum); - if (NS_FAILED(rv)) - return rv; - - const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum); - const nsXPTType& arg_type = arg_param.GetType(); - - // The xpidl compiler ensures this. We reaffirm it for safety. - if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID) - return NS_ERROR_UNEXPECTED; - - nsID *p = nsnull; - if (isFullVariantArray) { - p = (nsID *) ((nsXPTCVariant*) dispatchParams)[argnum].val.p; - } else { - p = (nsID *) dispatchParams[argnum].val.p; - } - if (!p) - return NS_ERROR_UNEXPECTED; - - result = *p; - break; - } - - default: - rv = NS_ERROR_UNEXPECTED; - } - return rv; -} - - -/******************************* - * JNI helper functions - *******************************/ - -void -ThrowXPCOMException(JNIEnv* env, const nsresult aErrorCode, - const char* aMessage) -{ - // Only throw this exception if one hasn't already been thrown, so we don't - // mask a previous exception/error. - jthrowable throwObj = env->ExceptionOccurred(); - if (throwObj != nsnull) - return; - - // Create parameters and method signature. Max of 2 params. The error code - // comes before the message string. - PRUint32 index = 0; - jvalue* args = new jvalue[2]; - nsCAutoString methodSig("("); - if (aErrorCode) { - args[index++].j = aErrorCode; - methodSig.Append("J"); - } - if (aMessage) { - args[index].l = env->NewStringUTF(aMessage); - methodSig.Append("Ljava/lang/String;"); - } - methodSig.Append(")V"); - - // create exception object - jmethodID mid = env->GetMethodID(xpcomExceptionClass, "", - methodSig.get()); - if (mid) { - throwObj = (jthrowable) env->NewObjectA(xpcomExceptionClass, mid, args); - } - NS_ASSERTION(throwObj, "Failed to create XPCOMException object"); - - // throw exception - if (throwObj) { - env->Throw(throwObj); - } - - // cleanup - delete[] args; -} - -nsAString* -jstring_to_nsAString(JNIEnv* env, jstring aString) -{ - jboolean isCopy = JNI_FALSE; - const PRUnichar* buf = nsnull; - if (aString) { - buf = env->GetStringChars(aString, &isCopy); - } - - nsString* str = new nsString(buf); - if (isCopy) { - env->ReleaseStringChars(aString, buf); - } - - return str; -} - -nsACString* -jstring_to_nsACString(JNIEnv* env, jstring aString) -{ - jboolean isCopy = JNI_FALSE; - const char* buf = nsnull; - if (aString) { - buf = env->GetStringUTFChars(aString, &isCopy); - } - - nsCString* str = new nsCString(buf); - if (isCopy) { - env->ReleaseStringUTFChars(aString, buf); - } - - return str; -} - -nsresult -File_to_nsILocalFile(JNIEnv* env, jobject aFile, nsILocalFile** aLocalFile) -{ - jstring pathName = nsnull; - jclass clazz = env->FindClass("java/io/File"); - if (clazz) { - jmethodID pathMID = env->GetMethodID(clazz, "getCanonicalPath", - "()Ljava/lang/String;"); - if (pathMID) { - pathName = (jstring) env->CallObjectMethod(aFile, pathMID); - } - } - - if (pathName) { - nsAString* path = jstring_to_nsAString(env, pathName); - nsresult rv = NS_NewLocalFile(*path, false, aLocalFile); - delete path; - return rv; - } - - return NS_ERROR_FAILURE; -} - +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Java XPCOM Bindings. + * + * The Initial Developer of the Original Code is + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Javier Pedemonte (jhpedemonte@gmail.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#include "nsJavaXPCOMBindingUtils.h" +#include "nsJavaXPTCStub.h" +#include "jni.h" +#include "nsIInterfaceInfoManager.h" +#include "pldhash.h" +#include "nsILocalFile.h" +#include "nsEventQueueUtils.h" +#include "nsProxyRelease.h" + + +/* Java JNI globals */ +jclass intClass = nsnull; +jclass intArrayClass = nsnull; +jclass stringClass = nsnull; +jclass nsISupportsClass = nsnull; +jclass xpcomExceptionClass = nsnull; + +jmethodID hashCodeMID = nsnull; +jmethodID booleanValueMID = nsnull; +jmethodID charValueMID = nsnull; +jmethodID byteValueMID = nsnull; +jmethodID shortValueMID = nsnull; +jmethodID intValueMID = nsnull; +jmethodID longValueMID = nsnull; +jmethodID floatValueMID = nsnull; +jmethodID doubleValueMID = nsnull; + +#ifdef DEBUG +jmethodID getNameMID = nsnull; +#endif + + +/************************************** + * Java<->XPCOM binding stores + **************************************/ +class JavaXPCOMBindingEntry : public PLDHashEntryHdr +{ +public: + // mKey will either be a Java hash of the Java object, or the address of + // the XPCOM object, depending on which hash table this entry is used in. + const void* mKey; + jobject mJavaObject; + void* mXPCOMInstance; +}; + +static PLDHashTable *gJAVAtoXPCOMBindings = nsnull; +static PLDHashTable *gXPCOMtoJAVABindings = nsnull; + +PR_STATIC_CALLBACK(PRBool) +InitJavaXPCOMBindingEntry(PLDHashTable *table, PLDHashEntryHdr *entry, + const void *key) +{ + JavaXPCOMBindingEntry *e = + NS_CONST_CAST(JavaXPCOMBindingEntry *, + NS_STATIC_CAST(const JavaXPCOMBindingEntry *, entry)); + + e->mKey = key; + + return PR_TRUE; +} + +void +AddJavaXPCOMBinding(JNIEnv* env, jobject aJavaObject, void* aXPCOMObject) +{ + // We use a Java hash of the Java object as a key since the JVM can return + // different "addresses" for the same Java object, but the hash code (the + // result of calling |hashCode()| on the Java object) will always be the same. + jint hash = env->CallIntMethod(aJavaObject, hashCodeMID); + jweak java_ref = env->NewWeakGlobalRef(aJavaObject); + + JavaXPCOMBindingEntry *entry = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gJAVAtoXPCOMBindings, + NS_INT32_TO_PTR(hash), + PL_DHASH_ADD)); + entry->mJavaObject = java_ref; + entry->mXPCOMInstance = aXPCOMObject; + + // We want the hash key to be the actual XPCOM object + void* xpcomObjKey = nsnull; + if (IsXPTCStub(aXPCOMObject)) + xpcomObjKey = GetXPTCStubAddr(aXPCOMObject); + else + xpcomObjKey = ((JavaXPCOMInstance*) aXPCOMObject)->GetInstance(); + + entry = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gXPCOMtoJAVABindings, xpcomObjKey, + PL_DHASH_ADD)); + entry->mJavaObject = java_ref; + entry->mXPCOMInstance = aXPCOMObject; + + LOG(("+ Adding Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x) weakref=0x%08x\n", + hash, (int) xpcomObjKey, (PRUint32) java_ref)); +} + +void +RemoveJavaXPCOMBinding(JNIEnv* env, jobject aJavaObject, void* aXPCOMObject) +{ + // We either get a Java or an XPCOM object. So find the other object. + jint hash = 0; + JavaXPCOMBindingEntry* entry = nsnull; + + if (aJavaObject) { + hash = env->CallIntMethod(aJavaObject, hashCodeMID); + JavaXPCOMBindingEntry* e = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gJAVAtoXPCOMBindings, + NS_INT32_TO_PTR(hash), + PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(e)) + entry = e; + } else { + JavaXPCOMBindingEntry* e = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gXPCOMtoJAVABindings, aXPCOMObject, + PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(e)) + entry = e; + } + NS_ASSERTION(entry != nsnull, "Failed to find matching entry"); + + if (entry) { + jobject jweakref = entry->mJavaObject; + void* xpcom_obj = entry->mXPCOMInstance; + + // Get keys. For Java, we use the hash key of the Java object. For XPCOM, + // we get the 'actual' object (either the nsJavaXPTCStub or the instance + // that is wrapped by JavaXPCOMInstance). + void* xpcomObjKey = nsnull; + if (hash == 0) + hash = env->CallIntMethod(jweakref, hashCodeMID); + if (aXPCOMObject) { + xpcomObjKey = aXPCOMObject; + } else { + if (IsXPTCStub(xpcom_obj)) + xpcomObjKey = GetXPTCStubAddr(xpcom_obj); + else + xpcomObjKey = ((JavaXPCOMInstance*) xpcom_obj)->GetInstance(); + } + + NS_ASSERTION(env->IsSameObject(jweakref, aJavaObject), + "Weakref does not point to expected Java object"); + NS_ASSERTION(!env->IsSameObject(jweakref, NULL), + "Weakref refers to garbage collected Java object. No longer valid"); + + // Remove both instances from stores + PL_DHashTableOperate(gJAVAtoXPCOMBindings, NS_INT32_TO_PTR(hash), + PL_DHASH_REMOVE); + PL_DHashTableOperate(gXPCOMtoJAVABindings, xpcomObjKey, PL_DHASH_REMOVE); + LOG(("- Removing Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x) weakref=0x%08x\n", + hash, (int) xpcomObjKey, (PRUint32) jweakref)); + + env->DeleteWeakGlobalRef(NS_STATIC_CAST(jweak, jweakref)); + } +} + +void* +GetMatchingXPCOMObject(JNIEnv* env, jobject aJavaObject) +{ + jint hash = env->CallIntMethod(aJavaObject, hashCodeMID); + + JavaXPCOMBindingEntry *entry = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gJAVAtoXPCOMBindings, + NS_INT32_TO_PTR(hash), + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { +#ifdef DEBUG + void* xpcomObjKey = nsnull; + if (IsXPTCStub(entry->mXPCOMInstance)) + xpcomObjKey = GetXPTCStubAddr(entry->mXPCOMInstance); + else + xpcomObjKey = ((JavaXPCOMInstance*) entry->mXPCOMInstance)->GetInstance(); + LOG(("< Get Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x)\n", + hash, (int) xpcomObjKey)); +#endif + return entry->mXPCOMInstance; + } + + return nsnull; +} + +jobject +GetMatchingJavaObject(JNIEnv* env, void* aXPCOMObject) +{ + jobject java_obj = nsnull; + nsISupports* xpcom_obj = NS_STATIC_CAST(nsISupports*, aXPCOMObject); + + nsJavaXPTCStub* stub = nsnull; + xpcom_obj->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub); + if (stub) { + java_obj = stub->GetJavaObject(); + NS_ASSERTION(java_obj != nsnull, "nsJavaXPTCStub w/o matching Java object"); + NS_RELEASE(stub); + } + + if (java_obj == nsnull) { + JavaXPCOMBindingEntry *entry = + NS_STATIC_CAST(JavaXPCOMBindingEntry*, + PL_DHashTableOperate(gXPCOMtoJAVABindings, aXPCOMObject, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(entry)) { + java_obj = entry->mJavaObject; + } + } + + if (java_obj) { + LOG(("< Get Java<->XPCOM binding (Java=0x%08x | XPCOM=0x%08x)\n", + env->CallIntMethod(java_obj, hashCodeMID), (int) aXPCOMObject)); + } + + return java_obj; +} + + +/****************************** + * InitializeJavaGlobals + ******************************/ +PRBool gInitialized = PR_FALSE; + +PRBool +InitializeJavaGlobals(JNIEnv *env) +{ + if (gInitialized) + return PR_TRUE; + + jclass clazz; + + if (!(clazz = env->FindClass("java/lang/Object")) || + !(hashCodeMID = env->GetMethodID(clazz, "hashCode","()I"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Boolean")) || + !(booleanValueMID = env->GetMethodID(clazz,"booleanValue","()Z"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Character")) || + !(charValueMID = env->GetMethodID(clazz,"charValue","()C"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Byte")) || + !(byteValueMID = env->GetMethodID(clazz,"byteValue","()B"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Short")) || + !(shortValueMID = env->GetMethodID(clazz,"shortValue","()S"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Integer")) || + !(intClass = (jclass) env->NewGlobalRef(clazz)) || + !(clazz = env->FindClass("[I")) || + !(intArrayClass = (jclass) env->NewGlobalRef(clazz)) || + !(intValueMID = env->GetMethodID(intClass,"intValue","()I"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Long")) || + !(longValueMID = env->GetMethodID(clazz,"longValue","()J"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Float")) || + !(floatValueMID = env->GetMethodID(clazz,"floatValue","()F"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/Double")) || + !(doubleValueMID = env->GetMethodID(clazz,"doubleValue","()D"))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("java/lang/String")) || + !(stringClass = (jclass) env->NewGlobalRef(clazz))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("org/mozilla/xpcom/nsISupports")) || + !(nsISupportsClass = (jclass) env->NewGlobalRef(clazz))) + { + return PR_FALSE; + } + + if (!(clazz = env->FindClass("org/mozilla/xpcom/XPCOMException")) || + !(xpcomExceptionClass = (jclass) env->NewGlobalRef(clazz))) + { + return PR_FALSE; + } + +#ifdef DEBUG + if (!(clazz = env->FindClass("java/lang/Class")) || + !(getNameMID = env->GetMethodID(clazz, "getName","()Ljava/lang/String;"))) { + return PR_FALSE; + } +#endif + + static PLDHashTableOps java_to_xpcom_hash_ops = + { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashVoidPtrKeyStub, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + PL_DHashClearEntryStub, + PL_DHashFinalizeStub, + InitJavaXPCOMBindingEntry + }; + + gJAVAtoXPCOMBindings = PL_NewDHashTable(&java_to_xpcom_hash_ops, nsnull, + sizeof(JavaXPCOMBindingEntry), 16); + if (!gJAVAtoXPCOMBindings) { + return PR_FALSE; + } + + static PLDHashTableOps xpcom_to_java_hash_ops = + { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashVoidPtrKeyStub, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + PL_DHashClearEntryStub, + PL_DHashFinalizeStub, + InitJavaXPCOMBindingEntry + }; + + gXPCOMtoJAVABindings = PL_NewDHashTable(&xpcom_to_java_hash_ops, nsnull, + sizeof(JavaXPCOMBindingEntry), 16); + if (!gXPCOMtoJAVABindings) { + return PR_FALSE; + } + + gInitialized = PR_TRUE; + + return PR_TRUE; +} + +/************************* + * FreeJavaGlobals + *************************/ +void +FreeJavaGlobals(JNIEnv* env) +{ + if (!gInitialized) + return; + + env->DeleteGlobalRef(intClass); + env->DeleteGlobalRef(intArrayClass); + env->DeleteGlobalRef(stringClass); + env->DeleteGlobalRef(nsISupportsClass); + env->DeleteGlobalRef(xpcomExceptionClass); + + PL_DHashTableDestroy(gJAVAtoXPCOMBindings); + PL_DHashTableDestroy(gXPCOMtoJAVABindings); + + gInitialized = PR_FALSE; +} + + +/********************************************************** + * JavaXPCOMInstance + *********************************************************/ +JavaXPCOMInstance::JavaXPCOMInstance(nsISupports* aInstance, + nsIInterfaceInfo* aIInfo) + : mInstance(aInstance), + mIInfo(aIInfo) +{ + NS_ADDREF(mInstance); +} + +JavaXPCOMInstance::~JavaXPCOMInstance() +{ + nsCOMPtr eventQ; + nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQ)); + if (NS_SUCCEEDED(rv)) + rv = NS_ProxyRelease(eventQ, mInstance); + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get MainEventQ"); +} + +JavaXPCOMInstance* +CreateJavaXPCOMInstance(nsISupports* aXPCOMObject, const nsIID* aIID) +{ + JavaXPCOMInstance* inst = nsnull; + + // Get interface info for class + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + NS_ASSERTION(iim != nsnull, "Failed to get InterfaceInfoManager"); + if (iim) { + nsCOMPtr info; + iim->GetInfoForIID(aIID, getter_AddRefs(info)); + + // Wrap XPCOM object + inst = new JavaXPCOMInstance(aXPCOMObject, info); + } + + return inst; +} + + +nsresult +GetIIDForMethodParam(nsIInterfaceInfo *iinfo, + const nsXPTMethodInfo *methodInfo, + const nsXPTParamInfo ¶mInfo, + PRUint16 methodIndex, + nsXPTCMiniVariant *dispatchParams, + PRBool isFullVariantArray, + nsID &result) +{ + nsresult rv; + + switch (paramInfo.GetType().TagPart()) + { + case nsXPTType::T_INTERFACE: + rv = iinfo->GetIIDForParamNoAlloc(methodIndex, ¶mInfo, &result); + break; + + case nsXPTType::T_INTERFACE_IS: + { + PRUint8 argnum; + rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, ¶mInfo, &argnum); + if (NS_FAILED(rv)) + return rv; + + const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum); + const nsXPTType& arg_type = arg_param.GetType(); + + // The xpidl compiler ensures this. We reaffirm it for safety. + if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID) + return NS_ERROR_UNEXPECTED; + + nsID *p = nsnull; + if (isFullVariantArray) { + p = (nsID *) ((nsXPTCVariant*) dispatchParams)[argnum].val.p; + } else { + p = (nsID *) dispatchParams[argnum].val.p; + } + if (!p) + return NS_ERROR_UNEXPECTED; + + result = *p; + break; + } + + default: + rv = NS_ERROR_UNEXPECTED; + } + return rv; +} + + +/******************************* + * JNI helper functions + *******************************/ + +void +ThrowXPCOMException(JNIEnv* env, const nsresult aErrorCode, + const char* aMessage) +{ + // Only throw this exception if one hasn't already been thrown, so we don't + // mask a previous exception/error. + jthrowable throwObj = env->ExceptionOccurred(); + if (throwObj != nsnull) + return; + + // Create parameters and method signature. Max of 2 params. The error code + // comes before the message string. + PRUint32 index = 0; + jvalue* args = new jvalue[2]; + nsCAutoString methodSig("("); + if (aErrorCode) { + args[index++].j = aErrorCode; + methodSig.Append("J"); + } + if (aMessage) { + args[index].l = env->NewStringUTF(aMessage); + methodSig.Append("Ljava/lang/String;"); + } + methodSig.Append(")V"); + + // create exception object + jmethodID mid = env->GetMethodID(xpcomExceptionClass, "", + methodSig.get()); + if (mid) { + throwObj = (jthrowable) env->NewObjectA(xpcomExceptionClass, mid, args); + } + NS_ASSERTION(throwObj, "Failed to create XPCOMException object"); + + // throw exception + if (throwObj) { + env->Throw(throwObj); + } + + // cleanup + delete[] args; +} + +nsAString* +jstring_to_nsAString(JNIEnv* env, jstring aString) +{ + jboolean isCopy = JNI_FALSE; + const PRUnichar* buf = nsnull; + if (aString) { + buf = env->GetStringChars(aString, &isCopy); + } + + nsString* str = new nsString(buf); + if (isCopy) { + env->ReleaseStringChars(aString, buf); + } + + return str; +} + +nsACString* +jstring_to_nsACString(JNIEnv* env, jstring aString) +{ + jboolean isCopy = JNI_FALSE; + const char* buf = nsnull; + if (aString) { + buf = env->GetStringUTFChars(aString, &isCopy); + } + + nsCString* str = new nsCString(buf); + if (isCopy) { + env->ReleaseStringUTFChars(aString, buf); + } + + return str; +} + +nsresult +File_to_nsILocalFile(JNIEnv* env, jobject aFile, nsILocalFile** aLocalFile) +{ + jstring pathName = nsnull; + jclass clazz = env->FindClass("java/io/File"); + if (clazz) { + jmethodID pathMID = env->GetMethodID(clazz, "getCanonicalPath", + "()Ljava/lang/String;"); + if (pathMID) { + pathName = (jstring) env->CallObjectMethod(aFile, pathMID); + } + } + + if (pathName) { + nsAString* path = jstring_to_nsAString(env, pathName); + nsresult rv = NS_NewLocalFile(*path, false, aLocalFile); + delete path; + return rv; + } + + return NS_ERROR_FAILURE; +} +