RetroZilla/extensions/java/xpcom/nsJavaXPCOMBindingUtils.cpp
2015-10-20 23:03:22 -04:00

1120 lines
34 KiB
C++

/* ***** 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) 2005
* 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 "nsJavaWrapper.h"
#include "jni.h"
#include "nsIInterfaceInfoManager.h"
#include "nsILocalFile.h"
#include "nsEventQueueUtils.h"
#include "nsProxyRelease.h"
/* Java JNI globals */
JavaVM* gCachedJVM = nsnull;
jclass systemClass = nsnull;
jclass booleanClass = nsnull;
jclass charClass = nsnull;
jclass byteClass = nsnull;
jclass shortClass = nsnull;
jclass intClass = nsnull;
jclass longClass = nsnull;
jclass floatClass = nsnull;
jclass doubleClass = nsnull;
jclass stringClass = nsnull;
jclass nsISupportsClass = nsnull;
jclass xpcomExceptionClass = nsnull;
jclass xpcomJavaProxyClass = nsnull;
jclass weakReferenceClass = nsnull;
jclass javaXPCOMUtilsClass = nsnull;
jmethodID hashCodeMID = nsnull;
jmethodID booleanValueMID = nsnull;
jmethodID booleanInitMID = nsnull;
jmethodID charValueMID = nsnull;
jmethodID charInitMID = nsnull;
jmethodID byteValueMID = nsnull;
jmethodID byteInitMID = nsnull;
jmethodID shortValueMID = nsnull;
jmethodID shortInitMID = nsnull;
jmethodID intValueMID = nsnull;
jmethodID intInitMID = nsnull;
jmethodID longValueMID = nsnull;
jmethodID longInitMID = nsnull;
jmethodID floatValueMID = nsnull;
jmethodID floatInitMID = nsnull;
jmethodID doubleValueMID = nsnull;
jmethodID doubleInitMID = nsnull;
jmethodID createProxyMID = nsnull;
jmethodID isXPCOMJavaProxyMID = nsnull;
jmethodID getNativeXPCOMInstMID = nsnull;
jmethodID weakReferenceConstructorMID = nsnull;
jmethodID getReferentMID = nsnull;
jmethodID clearReferentMID = nsnull;
jmethodID findClassInLoaderMID = nsnull;
#ifdef DEBUG_JAVAXPCOM
jmethodID getNameMID = nsnull;
jmethodID proxyToStringMID = nsnull;
#endif
NativeToJavaProxyMap* gNativeToJavaProxyMap = nsnull;
JavaToXPTCStubMap* gJavaToXPTCStubMap = nsnull;
PRBool gJavaXPCOMInitialized = PR_FALSE;
PRLock* gJavaXPCOMLock = nsnull;
static const char* kJavaKeywords[] = {
"abstract", "default" , "if" , "private" , "throw" ,
"boolean" , "do" , "implements", "protected" , "throws" ,
"break" , "double" , "import", "public" , "transient" ,
"byte" , "else" , "instanceof", "return" , "try" ,
"case" , "extends" , "int" , "short" , "void" ,
"catch" , "final" , "interface" , "static" , "volatile" ,
"char" , "finally" , "long" , "super" , "while" ,
"class" , "float" , "native" , "switch" ,
"const" , "for" , "new" , "synchronized",
"continue", "goto" , "package" , "this" ,
/* added in Java 1.2 */
"strictfp",
/* added in Java 1.4 */
"assert" ,
/* added in Java 5.0 */
"enum" ,
/* Java constants */
"true" , "false" , "null" ,
/* java.lang.Object methods *
* - don't worry about "toString", since it does the same thing *
* as Object's "toString" */
"clone" , "equals" , "finalize" , "getClass" , "hashCode" ,
"notify" , "notifyAll", /*"toString" ,*/ "wait"
};
nsTHashtable<nsDepCharHashKey>* gJavaKeywords = nsnull;
/******************************
* InitializeJavaGlobals
******************************/
PRBool
InitializeJavaGlobals(JNIEnv *env)
{
if (gJavaXPCOMInitialized)
return PR_TRUE;
// Save pointer to JavaVM, which is valid across threads.
jint rc = env->GetJavaVM(&gCachedJVM);
if (rc != 0) {
NS_WARNING("Failed to get JavaVM");
goto init_error;
}
jclass clazz;
if (!(clazz = env->FindClass("java/lang/System")) ||
!(systemClass = (jclass) env->NewGlobalRef(clazz)) ||
!(hashCodeMID = env->GetStaticMethodID(clazz, "identityHashCode",
"(Ljava/lang/Object;)I")))
{
NS_WARNING("Problem creating java.lang.System globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Boolean")) ||
!(booleanClass = (jclass) env->NewGlobalRef(clazz)) ||
!(booleanValueMID = env->GetMethodID(clazz, "booleanValue", "()Z")) ||
!(booleanInitMID = env->GetMethodID(clazz, "<init>", "(Z)V")))
{
NS_WARNING("Problem creating java.lang.Boolean globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Character")) ||
!(charClass = (jclass) env->NewGlobalRef(clazz)) ||
!(charValueMID = env->GetMethodID(clazz, "charValue", "()C")) ||
!(charInitMID = env->GetMethodID(clazz, "<init>", "(C)V")))
{
NS_WARNING("Problem creating java.lang.Character globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Byte")) ||
!(byteClass = (jclass) env->NewGlobalRef(clazz)) ||
!(byteValueMID = env->GetMethodID(clazz, "byteValue", "()B")) ||
!(byteInitMID = env->GetMethodID(clazz, "<init>", "(B)V")))
{
NS_WARNING("Problem creating java.lang.Byte globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Short")) ||
!(shortClass = (jclass) env->NewGlobalRef(clazz)) ||
!(shortValueMID = env->GetMethodID(clazz, "shortValue", "()S")) ||
!(shortInitMID = env->GetMethodID(clazz, "<init>", "(S)V")))
{
NS_WARNING("Problem creating java.lang.Short globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Integer")) ||
!(intClass = (jclass) env->NewGlobalRef(clazz)) ||
!(intValueMID = env->GetMethodID(clazz, "intValue", "()I")) ||
!(intInitMID = env->GetMethodID(clazz, "<init>", "(I)V")))
{
NS_WARNING("Problem creating java.lang.Integer globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Long")) ||
!(longClass = (jclass) env->NewGlobalRef(clazz)) ||
!(longValueMID = env->GetMethodID(clazz, "longValue", "()J")) ||
!(longInitMID = env->GetMethodID(clazz, "<init>", "(J)V")))
{
NS_WARNING("Problem creating java.lang.Long globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Float")) ||
!(floatClass = (jclass) env->NewGlobalRef(clazz)) ||
!(floatValueMID = env->GetMethodID(clazz, "floatValue", "()F")) ||
!(floatInitMID = env->GetMethodID(clazz, "<init>", "(F)V")))
{
NS_WARNING("Problem creating java.lang.Float globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/Double")) ||
!(doubleClass = (jclass) env->NewGlobalRef(clazz)) ||
!(doubleValueMID = env->GetMethodID(clazz, "doubleValue", "()D")) ||
!(doubleInitMID = env->GetMethodID(clazz, "<init>", "(D)V")))
{
NS_WARNING("Problem creating java.lang.Double globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/String")) ||
!(stringClass = (jclass) env->NewGlobalRef(clazz)))
{
NS_WARNING("Problem creating java.lang.String globals");
goto init_error;
}
if (!(clazz = env->FindClass("org/mozilla/interfaces/nsISupports")) ||
!(nsISupportsClass = (jclass) env->NewGlobalRef(clazz)))
{
NS_WARNING("Problem creating org.mozilla.interfaces.nsISupports globals");
goto init_error;
}
if (!(clazz = env->FindClass("org/mozilla/xpcom/XPCOMException")) ||
!(xpcomExceptionClass = (jclass) env->NewGlobalRef(clazz)))
{
NS_WARNING("Problem creating org.mozilla.xpcom.XPCOMException globals");
goto init_error;
}
if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/XPCOMJavaProxy")) ||
!(xpcomJavaProxyClass = (jclass) env->NewGlobalRef(clazz)) ||
!(createProxyMID = env->GetStaticMethodID(clazz, "createProxy",
"(Ljava/lang/Class;J)Ljava/lang/Object;")) ||
!(isXPCOMJavaProxyMID = env->GetStaticMethodID(clazz, "isXPCOMJavaProxy",
"(Ljava/lang/Object;)Z")) ||
!(getNativeXPCOMInstMID = env->GetStaticMethodID(xpcomJavaProxyClass,
"getNativeXPCOMInstance",
"(Ljava/lang/Object;)J")))
{
NS_WARNING("Problem creating org.mozilla.xpcom.internal.XPCOMJavaProxy globals");
goto init_error;
}
if (!(clazz = env->FindClass("java/lang/ref/WeakReference")) ||
!(weakReferenceClass = (jclass) env->NewGlobalRef(clazz)) ||
!(weakReferenceConstructorMID = env->GetMethodID(weakReferenceClass,
"<init>","(Ljava/lang/Object;)V")) ||
!(getReferentMID = env->GetMethodID(weakReferenceClass,
"get", "()Ljava/lang/Object;")) ||
!(clearReferentMID = env->GetMethodID(weakReferenceClass,
"clear", "()V")))
{
NS_WARNING("Problem creating java.lang.ref.WeakReference globals");
goto init_error;
}
if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/JavaXPCOMMethods")) ||
!(javaXPCOMUtilsClass = (jclass) env->NewGlobalRef(clazz)) ||
!(findClassInLoaderMID = env->GetStaticMethodID(clazz,
"findClassInLoader",
"(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Class;")))
{
NS_WARNING("Problem creating org.mozilla.xpcom.internal.JavaXPCOMMethods globals");
goto init_error;
}
#ifdef DEBUG_JAVAXPCOM
if (!(clazz = env->FindClass("java/lang/Class")) ||
!(getNameMID = env->GetMethodID(clazz, "getName","()Ljava/lang/String;")))
{
NS_WARNING("Problem creating java.lang.Class globals");
goto init_error;
}
if (!(proxyToStringMID = env->GetStaticMethodID(xpcomJavaProxyClass,
"proxyToString",
"(Ljava/lang/Object;)Ljava/lang/String;")))
{
NS_WARNING("Problem creating proxyToString global");
goto init_error;
}
#endif
gNativeToJavaProxyMap = new NativeToJavaProxyMap();
if (!gNativeToJavaProxyMap || NS_FAILED(gNativeToJavaProxyMap->Init())) {
NS_WARNING("Problem creating NativeToJavaProxyMap");
goto init_error;
}
gJavaToXPTCStubMap = new JavaToXPTCStubMap();
if (!gJavaToXPTCStubMap || NS_FAILED(gJavaToXPTCStubMap->Init())) {
NS_WARNING("Problem creating JavaToXPTCStubMap");
goto init_error;
}
{
nsresult rv = NS_OK;
PRUint32 size = NS_ARRAY_LENGTH(kJavaKeywords);
gJavaKeywords = new nsTHashtable<nsDepCharHashKey>();
if (!gJavaKeywords || NS_FAILED(gJavaKeywords->Init(size))) {
NS_WARNING("Failed to init JavaKeywords HashSet");
goto init_error;
}
for (PRUint32 i = 0; i < size && NS_SUCCEEDED(rv); i++) {
if (!gJavaKeywords->PutEntry(kJavaKeywords[i])) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
}
if (NS_FAILED(rv)) {
NS_WARNING("Failed to populate JavaKeywords hash");
goto init_error;
}
}
gJavaXPCOMLock = PR_NewLock();
gJavaXPCOMInitialized = PR_TRUE;
return PR_TRUE;
init_error:
// If we encounter an error during initialization, then free any globals that
// were allocated, and return false.
FreeJavaGlobals(env);
return PR_FALSE;
}
/*************************
* FreeJavaGlobals
*************************/
void
FreeJavaGlobals(JNIEnv* env)
{
PRLock* tempLock = nsnull;
if (gJavaXPCOMLock) {
PR_Lock(gJavaXPCOMLock);
// null out global lock so no one else can use it
tempLock = gJavaXPCOMLock;
gJavaXPCOMLock = nsnull;
}
gJavaXPCOMInitialized = PR_FALSE;
// Free the mappings first, since that process depends on some of the Java
// globals that are freed later.
if (gNativeToJavaProxyMap) {
gNativeToJavaProxyMap->Destroy(env);
delete gNativeToJavaProxyMap;
gNativeToJavaProxyMap = nsnull;
}
if (gJavaToXPTCStubMap) {
gJavaToXPTCStubMap->Destroy();
delete gJavaToXPTCStubMap;
gJavaToXPTCStubMap = nsnull;
}
// Free remaining Java globals
if (systemClass) {
env->DeleteGlobalRef(systemClass);
systemClass = nsnull;
}
if (booleanClass) {
env->DeleteGlobalRef(booleanClass);
booleanClass = nsnull;
}
if (charClass) {
env->DeleteGlobalRef(charClass);
charClass = nsnull;
}
if (byteClass) {
env->DeleteGlobalRef(byteClass);
byteClass = nsnull;
}
if (shortClass) {
env->DeleteGlobalRef(shortClass);
shortClass = nsnull;
}
if (intClass) {
env->DeleteGlobalRef(intClass);
intClass = nsnull;
}
if (longClass) {
env->DeleteGlobalRef(longClass);
longClass = nsnull;
}
if (floatClass) {
env->DeleteGlobalRef(floatClass);
floatClass = nsnull;
}
if (doubleClass) {
env->DeleteGlobalRef(doubleClass);
doubleClass = nsnull;
}
if (stringClass) {
env->DeleteGlobalRef(stringClass);
stringClass = nsnull;
}
if (nsISupportsClass) {
env->DeleteGlobalRef(nsISupportsClass);
nsISupportsClass = nsnull;
}
if (xpcomExceptionClass) {
env->DeleteGlobalRef(xpcomExceptionClass);
xpcomExceptionClass = nsnull;
}
if (xpcomJavaProxyClass) {
env->DeleteGlobalRef(xpcomJavaProxyClass);
xpcomJavaProxyClass = nsnull;
}
if (weakReferenceClass) {
env->DeleteGlobalRef(weakReferenceClass);
weakReferenceClass = nsnull;
}
if (gJavaKeywords) {
delete gJavaKeywords;
gJavaKeywords = nsnull;
}
if (tempLock) {
PR_Unlock(tempLock);
PR_DestroyLock(tempLock);
}
}
/**************************************
* Java<->XPCOM object mappings
**************************************/
static PLDHashTableOps hash_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashGetKeyStub,
PL_DHashVoidPtrKeyStub,
PL_DHashMatchEntryStub,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub
};
// NativeToJavaProxyMap: The common case is that each XPCOM object will have
// one Java proxy. But there are instances where there will be multiple Java
// proxies for a given XPCOM object, each representing a different interface.
// So we optimize the common case by using a hash table. Then, if there are
// multiple Java proxies, we cycle through the linked list, comparing IIDs.
nsresult
NativeToJavaProxyMap::Init()
{
mHashTable = PL_NewDHashTable(&hash_ops, nsnull, sizeof(Entry), 16);
if (!mHashTable)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
PLDHashOperator
DestroyJavaProxyMappingEnum(PLDHashTable* aTable, PLDHashEntryHdr* aHeader,
PRUint32 aNumber, void* aData)
{
JNIEnv* env = NS_STATIC_CAST(JNIEnv*, aData);
NativeToJavaProxyMap::Entry* entry =
NS_STATIC_CAST(NativeToJavaProxyMap::Entry*, aHeader);
// first, delete XPCOM instances from the Java proxies
nsresult rv;
NativeToJavaProxyMap::ProxyList* item = entry->list;
while(item != nsnull) {
void* xpcom_obj;
jobject javaObject = env->CallObjectMethod(item->javaObject, getReferentMID);
rv = GetXPCOMInstFromProxy(env, javaObject, &xpcom_obj);
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get XPCOM instance from Java proxy");
if (NS_SUCCEEDED(rv)) {
JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj);
#ifdef DEBUG_JAVAXPCOM
char* iid_str = item->iid.ToString();
LOG(("- NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
javaObject),
(PRUint32) entry, iid_str));
PR_Free(iid_str);
#endif
delete inst; // releases native XPCOM object
}
NativeToJavaProxyMap::ProxyList* next = item->next;
env->CallVoidMethod(item->javaObject, clearReferentMID);
env->DeleteGlobalRef(item->javaObject);
delete item;
item = next;
}
return PL_DHASH_REMOVE;
}
nsresult
NativeToJavaProxyMap::Destroy(JNIEnv* env)
{
// This is only called from FreeGlobals(), which already holds the lock.
// nsAutoLock lock(gJavaXPCOMLock);
PL_DHashTableEnumerate(mHashTable, DestroyJavaProxyMappingEnum, env);
PL_DHashTableDestroy(mHashTable);
mHashTable = nsnull;
return NS_OK;
}
nsresult
NativeToJavaProxyMap::Add(JNIEnv* env, nsISupports* aXPCOMObject,
const nsIID& aIID, jobject aProxy)
{
nsAutoLock lock(gJavaXPCOMLock);
Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
aXPCOMObject,
PL_DHASH_ADD));
if (!e)
return NS_ERROR_FAILURE;
jobject ref = nsnull;
jobject weakRefObj = env->NewObject(weakReferenceClass,
weakReferenceConstructorMID, aProxy);
if (weakRefObj)
ref = env->NewGlobalRef(weakRefObj);
if (!ref)
return NS_ERROR_OUT_OF_MEMORY;
// Add Java proxy weak reference ref to start of list
ProxyList* item = new ProxyList(ref, aIID, e->list);
e->key = aXPCOMObject;
e->list = item;
#ifdef DEBUG_JAVAXPCOM
char* iid_str = aIID.ToString();
LOG(("+ NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID, aProxy),
(PRUint32) aXPCOMObject, iid_str));
PR_Free(iid_str);
#endif
return NS_OK;
}
nsresult
NativeToJavaProxyMap::Find(JNIEnv* env, nsISupports* aNativeObject,
const nsIID& aIID, jobject* aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (!aResult)
return NS_ERROR_FAILURE;
nsAutoLock lock(gJavaXPCOMLock);
*aResult = nsnull;
Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
aNativeObject,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_FREE(e))
return NS_OK;
ProxyList* item = e->list;
while (item != nsnull && *aResult == nsnull) {
if (item->iid.Equals(aIID)) {
jobject referentObj = env->CallObjectMethod(item->javaObject,
getReferentMID);
if (!env->IsSameObject(referentObj, NULL)) {
*aResult = referentObj;
#ifdef DEBUG_JAVAXPCOM
char* iid_str = aIID.ToString();
LOG(("< NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
*aResult),
(PRUint32) aNativeObject, iid_str));
PR_Free(iid_str);
#endif
}
}
item = item->next;
}
return NS_OK;
}
nsresult
NativeToJavaProxyMap::Remove(JNIEnv* env, nsISupports* aNativeObject,
const nsIID& aIID)
{
// This is only called from finalizeProxy(), which already holds the lock.
// nsAutoLock lock(gJavaXPCOMLock);
Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
aNativeObject,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_FREE(e)) {
NS_WARNING("XPCOM object not found in hash table");
return NS_ERROR_FAILURE;
}
ProxyList* item = e->list;
ProxyList* last = e->list;
while (item != nsnull) {
if (item->iid.Equals(aIID)) {
#ifdef DEBUG_JAVAXPCOM
char* iid_str = aIID.ToString();
LOG(("- NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID,
item->javaObject),
(PRUint32) aNativeObject, iid_str));
PR_Free(iid_str);
#endif
env->CallVoidMethod(item->javaObject, clearReferentMID);
env->DeleteGlobalRef(item->javaObject);
if (item == e->list) {
e->list = item->next;
if (e->list == nsnull)
PL_DHashTableOperate(mHashTable, aNativeObject, PL_DHASH_REMOVE);
} else {
last->next = item->next;
}
delete item;
return NS_OK;
}
last = item;
item = item->next;
}
NS_WARNING("Java proxy matching given IID not found");
return NS_ERROR_FAILURE;
}
nsresult
JavaToXPTCStubMap::Init()
{
mHashTable = PL_NewDHashTable(&hash_ops, nsnull, sizeof(Entry), 16);
if (!mHashTable)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
PLDHashOperator
DestroyXPTCMappingEnum(PLDHashTable* aTable, PLDHashEntryHdr* aHeader,
PRUint32 aNumber, void* aData)
{
JavaToXPTCStubMap::Entry* entry =
NS_STATIC_CAST(JavaToXPTCStubMap::Entry*, aHeader);
// The XPTC stub will be released by the XPCOM side, if it hasn't been
// already. We just need to delete the Java global ref held by the XPTC stub,
// so the Java garbage collector can handle the Java object when necessary.
entry->xptcstub->DeleteStrongRef();
return PL_DHASH_REMOVE;
}
nsresult
JavaToXPTCStubMap::Destroy()
{
// This is only called from FreeGlobals(), which already holds the lock.
// nsAutoLock lock(gJavaXPCOMLock);
PL_DHashTableEnumerate(mHashTable, DestroyXPTCMappingEnum, nsnull);
PL_DHashTableDestroy(mHashTable);
mHashTable = nsnull;
return NS_OK;
}
nsresult
JavaToXPTCStubMap::Add(jint aJavaObjectHashCode, nsJavaXPTCStub* aProxy)
{
nsAutoLock lock(gJavaXPCOMLock);
Entry* e = NS_STATIC_CAST(Entry*,
PL_DHashTableOperate(mHashTable,
NS_INT32_TO_PTR(aJavaObjectHashCode),
PL_DHASH_ADD));
if (!e)
return NS_ERROR_FAILURE;
NS_ASSERTION(e->key == nsnull,
"XPTCStub for given Java object already exists in hash table");
e->key = aJavaObjectHashCode;
e->xptcstub = aProxy;
#ifdef DEBUG_JAVAXPCOM
nsIInterfaceInfo* iface_info;
aProxy->GetInterfaceInfo(&iface_info);
nsIID* iid;
iface_info->GetInterfaceIID(&iid);
char* iid_str = iid->ToString();
LOG(("+ JavaToXPTCStubMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) aJavaObjectHashCode, (PRUint32) aProxy, iid_str));
PR_Free(iid_str);
nsMemory::Free(iid);
NS_RELEASE(iface_info);
#endif
return NS_OK;
}
nsresult
JavaToXPTCStubMap::Find(jint aJavaObjectHashCode, const nsIID& aIID,
nsJavaXPTCStub** aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (!aResult)
return NS_ERROR_FAILURE;
nsAutoLock lock(gJavaXPCOMLock);
*aResult = nsnull;
Entry* e = NS_STATIC_CAST(Entry*,
PL_DHashTableOperate(mHashTable,
NS_INT32_TO_PTR(aJavaObjectHashCode),
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_FREE(e))
return NS_OK;
nsresult rv = e->xptcstub->QueryInterface(aIID, (void**) aResult);
#ifdef DEBUG_JAVAXPCOM
if (NS_SUCCEEDED(rv)) {
char* iid_str = aIID.ToString();
LOG(("< JavaToXPTCStubMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
(PRUint32) aJavaObjectHashCode, (PRUint32) *aResult, iid_str));
PR_Free(iid_str);
}
#endif
// NS_NOINTERFACE is not an error condition
if (rv == NS_NOINTERFACE)
rv = NS_OK;
return rv;
}
nsresult
JavaToXPTCStubMap::Remove(jint aJavaObjectHashCode)
{
PL_DHashTableOperate(mHashTable, NS_INT32_TO_PTR(aJavaObjectHashCode),
PL_DHASH_REMOVE);
#ifdef DEBUG_JAVAXPCOM
LOG(("- JavaToXPTCStubMap (Java=%08x)\n", (PRUint32) aJavaObjectHashCode));
#endif
return NS_OK;
}
/**********************************************************
* JavaXPCOMInstance
*********************************************************/
JavaXPCOMInstance::JavaXPCOMInstance(nsISupports* aInstance,
nsIInterfaceInfo* aIInfo)
: mInstance(aInstance)
, mIInfo(aIInfo)
{
NS_ADDREF(mInstance);
NS_ADDREF(mIInfo);
}
JavaXPCOMInstance::~JavaXPCOMInstance()
{
// Need to release these objects on the main thread.
nsCOMPtr<nsIEventQueue> eventQ;
nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQ));
if (NS_SUCCEEDED(rv)) {
rv = NS_ProxyRelease(eventQ, mInstance);
rv += NS_ProxyRelease(eventQ, mIInfo);
}
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to release using NS_ProxyRelease");
}
/*******************************
* Helper functions
*******************************/
nsresult
GetNewOrUsedJavaObject(JNIEnv* env, nsISupports* aXPCOMObject,
const nsIID& aIID, jobject aObjectLoader,
jobject* aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (!aResult)
return NS_ERROR_NULL_POINTER;
nsresult rv;
nsJavaXPTCStub* stub = nsnull;
aXPCOMObject->QueryInterface(NS_GET_IID(nsJavaXPTCStub), (void**) &stub);
if (stub) {
// Get Java object directly from nsJavaXPTCStub
*aResult = stub->GetJavaObject();
NS_ASSERTION(*aResult != nsnull, "nsJavaXPTCStub w/o matching Java object");
NS_RELEASE(stub);
return NS_OK;
}
// Get the root nsISupports of the xpcom object
nsCOMPtr<nsISupports> rootObject = do_QueryInterface(aXPCOMObject, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Get associated Java object from hash table
rv = gNativeToJavaProxyMap->Find(env, rootObject, aIID, aResult);
NS_ENSURE_SUCCESS(rv, rv);
if (*aResult)
return NS_OK;
// No Java object is associated with the given XPCOM object, so we
// create a Java proxy.
return CreateJavaProxy(env, rootObject, aIID, aObjectLoader, aResult);
}
nsresult
GetNewOrUsedXPCOMObject(JNIEnv* env, jobject aJavaObject, const nsIID& aIID,
nsISupports** aResult)
{
NS_PRECONDITION(aResult != nsnull, "null ptr");
if (!aResult)
return NS_ERROR_NULL_POINTER;
nsresult rv;
*aResult = nsnull;
// Check if the given Java object is actually one of our Java proxies. If so,
// then we query the associated XPCOM object directly from the proxy.
// If Java object is not a proxy, then we try to find associated XPCOM object
// in the mapping table.
jboolean isProxy = env->CallStaticBooleanMethod(xpcomJavaProxyClass,
isXPCOMJavaProxyMID,
aJavaObject);
if (env->ExceptionCheck())
return NS_ERROR_FAILURE;
if (isProxy) {
void* inst;
rv = GetXPCOMInstFromProxy(env, aJavaObject, &inst);
NS_ENSURE_SUCCESS(rv, rv);
nsISupports* rootObject =
NS_STATIC_CAST(JavaXPCOMInstance*, inst)->GetInstance();
rv = rootObject->QueryInterface(aIID, (void**) aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsJavaXPTCStub* stub;
jint hash = env->CallStaticIntMethod(systemClass, hashCodeMID, aJavaObject);
rv = gJavaToXPTCStubMap->Find(hash, aIID, &stub);
NS_ENSURE_SUCCESS(rv, rv);
if (stub) {
// stub is already AddRef'd and QI'd
*aResult = NS_STATIC_CAST(nsISupports*,
NS_STATIC_CAST(nsXPTCStubBase*, stub));
return NS_OK;
}
// If there is no corresponding XPCOM object, then that means that the
// parameter is a non-generated class (that is, it is not one of our
// 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(&aIID, getter_AddRefs(iinfo));
NS_ENSURE_SUCCESS(rv, rv);
// Create XPCOM stub
stub = new nsJavaXPTCStub(aJavaObject, iinfo);
if (!stub) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = gJavaToXPTCStubMap->Add(hash, stub);
if (NS_FAILED(rv)) {
delete stub;
return rv;
}
NS_ADDREF(stub);
*aResult = NS_STATIC_CAST(nsISupports*,
NS_STATIC_CAST(nsXPTCStubBase*, stub));
return NS_OK;
}
nsresult
GetIIDForMethodParam(nsIInterfaceInfo *iinfo, const nsXPTMethodInfo *methodInfo,
const nsXPTParamInfo &paramInfo, PRUint8 paramType,
PRUint16 methodIndex, nsXPTCMiniVariant *dispatchParams,
PRBool isFullVariantArray, nsID &result)
{
nsresult rv;
switch (paramType)
{
case nsXPTType::T_INTERFACE:
rv = iinfo->GetIIDForParamNoAlloc(methodIndex, &paramInfo, &result);
break;
case nsXPTType::T_INTERFACE_IS:
{
PRUint8 argnum;
rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, &paramInfo,
&argnum);
if (NS_FAILED(rv))
break;
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) {
rv = NS_ERROR_UNEXPECTED;
break;
}
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
*******************************/
JNIEnv*
GetJNIEnv()
{
JNIEnv* env;
jint rc = gCachedJVM->GetEnv((void**) &env, JNI_VERSION_1_2);
NS_ASSERTION(rc == JNI_OK && env != nsnull,
"Current thread not attached to given JVM instance");
return env;
}
void
ThrowException(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.
if (env->ExceptionCheck())
return;
// If the error code we get is for an Out Of Memory error, try to throw an
// OutOfMemoryError. The JVM may have enough memory to create this error.
if (aErrorCode == NS_ERROR_OUT_OF_MEMORY) {
jclass clazz = env->FindClass("java/lang/OutOfMemoryError");
if (clazz) {
env->ThrowNew(clazz, aMessage);
}
env->DeleteLocalRef(clazz);
return;
}
// If the error was not handled above, then create an XPCOMException with the
// given error code and message.
// Create parameters and method signature. Max of 2 params. The error code
// comes before the message string.
PRInt64 errorCode = aErrorCode ? aErrorCode : NS_ERROR_FAILURE;
nsCAutoString methodSig("(J");
jstring message = nsnull;
if (aMessage) {
message = env->NewStringUTF(aMessage);
if (!message) {
return;
}
methodSig.AppendLiteral("Ljava/lang/String;");
}
methodSig.AppendLiteral(")V");
// In some instances (such as in shutdownXPCOM() and termEmbedding()), we
// will need to throw an exception when JavaXPCOM has already been
// terminated. In such a case, 'xpcomExceptionClass' will be null. So we
// reset it temporarily in order to throw the appropriate exception.
if (xpcomExceptionClass == nsnull) {
xpcomExceptionClass = env->FindClass("org/mozilla/xpcom/XPCOMException");
if (!xpcomExceptionClass) {
return;
}
}
// create exception object
jthrowable throwObj = nsnull;
jmethodID mid = env->GetMethodID(xpcomExceptionClass, "<init>",
methodSig.get());
if (mid) {
throwObj = (jthrowable) env->NewObject(xpcomExceptionClass, mid, errorCode,
message);
}
NS_ASSERTION(throwObj, "Failed to create XPCOMException object");
// throw exception
if (throwObj) {
env->Throw(throwObj);
}
}
nsAString*
jstring_to_nsAString(JNIEnv* env, jstring aString)
{
const PRUnichar* buf = nsnull;
if (aString) {
buf = env->GetStringChars(aString, nsnull);
if (!buf)
return nsnull; // exception already thrown
}
nsString* str = new nsString(buf);
if (aString) {
env->ReleaseStringChars(aString, buf);
} else {
str->SetIsVoid(PR_TRUE);
}
// returns string, or nsnull if 'new' failed
return str;
}
nsACString*
jstring_to_nsACString(JNIEnv* env, jstring aString)
{
const char* buf = nsnull;
if (aString) {
buf = env->GetStringUTFChars(aString, nsnull);
if (!buf)
return nsnull; // exception already thrown
}
nsCString* str = new nsCString(buf);
if (aString) {
env->ReleaseStringUTFChars(aString, buf);
} else {
str->SetIsVoid(PR_TRUE);
}
// returns string, or nsnull if 'new' failed
return str;
}
nsresult
File_to_nsILocalFile(JNIEnv* env, jobject aFile, nsILocalFile** aLocalFile)
{
nsresult rv = NS_ERROR_FAILURE;
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 != nsnull && !env->ExceptionCheck())
rv = NS_OK;
}
}
if (NS_SUCCEEDED(rv)) {
nsAString* path = jstring_to_nsAString(env, pathName);
if (!path)
rv = NS_ERROR_OUT_OF_MEMORY;
if (NS_SUCCEEDED(rv)) {
rv = NS_NewLocalFile(*path, false, aLocalFile);
delete path;
}
}
return rv;
}