зеркало из https://github.com/mozilla/pjs.git
1022 строки
30 KiB
C++
1022 строки
30 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 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;
|
|
|
|
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;
|
|
|
|
#ifdef DEBUG_JAVAXPCOM
|
|
jmethodID getNameMID = nsnull;
|
|
jmethodID proxyToStringMID = nsnull;
|
|
#endif
|
|
|
|
NativeToJavaProxyMap* gNativeToJavaProxyMap = nsnull;
|
|
JavaToXPTCStubMap* gJavaToXPTCStubMap = nsnull;
|
|
|
|
PRBool gJavaXPCOMInitialized = PR_FALSE;
|
|
PRLock* gJavaXPCOMLock = nsnull;
|
|
|
|
|
|
/******************************
|
|
* JNI Load & Unload
|
|
******************************/
|
|
extern "C" JX_EXPORT jint JNICALL
|
|
JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
{
|
|
// Save pointer to JavaVM, which is valid across threads.
|
|
gCachedJVM = vm;
|
|
|
|
// Let the JVM know that we are using JDK 1.2 JNI features.
|
|
return JNI_VERSION_1_2;
|
|
}
|
|
|
|
extern "C" JX_EXPORT void JNICALL
|
|
JNI_OnUnload(JavaVM* vm, void* reserved)
|
|
{
|
|
}
|
|
|
|
/******************************
|
|
* InitializeJavaGlobals
|
|
******************************/
|
|
PRBool
|
|
InitializeJavaGlobals(JNIEnv *env)
|
|
{
|
|
if (gJavaXPCOMInitialized)
|
|
return PR_TRUE;
|
|
|
|
jclass clazz;
|
|
if (!(clazz = env->FindClass("java/lang/Object")) ||
|
|
!(hashCodeMID = env->GetMethodID(clazz, "hashCode","()I")))
|
|
{
|
|
NS_WARNING("Problem creating java.lang.Object 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/xpcom/nsISupports")) ||
|
|
!(nsISupportsClass = (jclass) env->NewGlobalRef(clazz)))
|
|
{
|
|
NS_WARNING("Problem creating org.mozilla.xpcom.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/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.XPCOMJavaProxy 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 (NS_FAILED(gNativeToJavaProxyMap->Init())) {
|
|
NS_WARNING("Problem creating NativeToJavaProxyMap");
|
|
goto init_error;
|
|
}
|
|
gJavaToXPTCStubMap = new JavaToXPTCStubMap();
|
|
if (NS_FAILED(gJavaToXPTCStubMap->Init())) {
|
|
NS_WARNING("Problem creating JavaToXPTCStubMap");
|
|
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)
|
|
{
|
|
PR_Lock(gJavaXPCOMLock);
|
|
|
|
// null out global lock so no one else can use it
|
|
PRLock* 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 (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;
|
|
}
|
|
|
|
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->NewLocalRef(item->javaObject);
|
|
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->CallIntMethod(javaObject, hashCodeMID),
|
|
(PRUint32) entry, iid_str));
|
|
PR_Free(iid_str);
|
|
#endif
|
|
delete inst; // releases native XPCOM object
|
|
}
|
|
|
|
NativeToJavaProxyMap::ProxyList* next = item->next;
|
|
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;
|
|
|
|
jweak ref = env->NewWeakGlobalRef(aProxy);
|
|
if (!ref)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
// Add Java proxy 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->CallIntMethod(aProxy, hashCodeMID),
|
|
(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 javaObject = env->NewLocalRef(item->javaObject);
|
|
if (javaObject) {
|
|
*aResult = javaObject;
|
|
#ifdef DEBUG_JAVAXPCOM
|
|
char* iid_str = aIID.ToString();
|
|
LOG(("< NativeToJavaProxyMap (Java=%08x | XPCOM=%08x | IID=%s)\n",
|
|
(PRUint32) env->CallIntMethod(*aResult, hashCodeMID),
|
|
(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->CallIntMethod(item->javaObject, hashCodeMID),
|
|
(PRUint32) aNativeObject, iid_str));
|
|
PR_Free(iid_str);
|
|
#endif
|
|
|
|
env->DeleteWeakGlobalRef(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(JNIEnv* env, jobject aJavaObject, nsJavaXPTCStub* aProxy)
|
|
{
|
|
nsAutoLock lock(gJavaXPCOMLock);
|
|
|
|
jint hash = env->CallIntMethod(aJavaObject, hashCodeMID);
|
|
Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
|
|
NS_INT32_TO_PTR(hash),
|
|
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 = hash;
|
|
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) hash, (PRUint32) aProxy, iid_str));
|
|
PR_Free(iid_str);
|
|
nsMemory::Free(iid);
|
|
NS_RELEASE(iface_info);
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
JavaToXPTCStubMap::Find(JNIEnv* env, jobject aJavaObject, const nsIID& aIID,
|
|
nsJavaXPTCStub** aResult)
|
|
{
|
|
NS_PRECONDITION(aResult != nsnull, "null ptr");
|
|
if (!aResult)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsAutoLock lock(gJavaXPCOMLock);
|
|
|
|
*aResult = nsnull;
|
|
jint hash = env->CallIntMethod(aJavaObject, hashCodeMID);
|
|
Entry* e = NS_STATIC_CAST(Entry*, PL_DHashTableOperate(mHashTable,
|
|
NS_INT32_TO_PTR(hash),
|
|
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) hash, (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(JNIEnv* env, jobject aJavaObject)
|
|
{
|
|
jint hash = env->CallIntMethod(aJavaObject, hashCodeMID);
|
|
PL_DHashTableOperate(mHashTable, NS_INT32_TO_PTR(hash), PL_DHASH_REMOVE);
|
|
|
|
#ifdef DEBUG_JAVAXPCOM
|
|
LOG(("- JavaToXPTCStubMap (Java=%08x)\n", (PRUint32) hash));
|
|
#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* 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 associated Java object from hash table
|
|
rv = gNativeToJavaProxyMap->Find(env, aXPCOMObject, aIID, aResult);
|
|
if (NS_FAILED(rv))
|
|
return 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, aXPCOMObject, aIID, aResult);
|
|
}
|
|
|
|
nsresult
|
|
GetNewOrUsedXPCOMObject(JNIEnv* env, jobject aJavaObject, const nsIID& aIID,
|
|
nsISupports** aResult, PRBool* aIsXPTCStub)
|
|
{
|
|
NS_PRECONDITION(aResult != nsnull && aIsXPTCStub != nsnull, "null ptr");
|
|
if (!aResult || !aIsXPTCStub)
|
|
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);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
*aResult = NS_STATIC_CAST(JavaXPCOMInstance*, inst)->GetInstance();
|
|
NS_ADDREF(*aResult);
|
|
*aIsXPTCStub = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
*aIsXPTCStub = PR_TRUE;
|
|
|
|
nsJavaXPTCStub* stub;
|
|
rv = gJavaToXPTCStubMap->Find(env, aJavaObject, aIID, &stub);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (stub) {
|
|
// stub is already AddRef'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));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// Create XPCOM stub
|
|
stub = new nsJavaXPTCStub(aJavaObject, iinfo);
|
|
if (!stub) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
rv = gJavaToXPTCStubMap->Add(env, aJavaObject, 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 ¶mInfo, PRUint8 paramType,
|
|
PRUint16 methodIndex, nsXPTCMiniVariant *dispatchParams,
|
|
PRBool isFullVariantArray, nsID &result)
|
|
{
|
|
nsresult rv;
|
|
|
|
switch (paramType)
|
|
{
|
|
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))
|
|
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.
|
|
PRUint32 index = 0;
|
|
jvalue* args = new jvalue[2];
|
|
if (!args) {
|
|
ThrowException(env, NS_ERROR_OUT_OF_MEMORY,
|
|
"Out of memory while throwing another exception");
|
|
return;
|
|
}
|
|
|
|
nsCAutoString methodSig("(");
|
|
if (aErrorCode) {
|
|
args[index++].j = aErrorCode;
|
|
methodSig.Append('J');
|
|
}
|
|
if (aMessage) {
|
|
args[index].l = env->NewStringUTF(aMessage);
|
|
if (args[index].l == nsnull) {
|
|
delete[] args;
|
|
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 Javaconnect 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->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)
|
|
{
|
|
const PRUnichar* buf = nsnull;
|
|
if (aString) {
|
|
buf = env->GetStringChars(aString, nsnull);
|
|
if (!buf)
|
|
return nsnull; // exception already thrown
|
|
}
|
|
|
|
nsString* str = new nsString(buf);
|
|
env->ReleaseStringChars(aString, buf);
|
|
|
|
// 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);
|
|
env->ReleaseStringUTFChars(aString, buf);
|
|
|
|
// 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;
|
|
}
|
|
|