diff options
Diffstat (limited to 'src/libs/xpcom18a4/java/src/nsJavaWrapper.cpp')
-rw-r--r-- | src/libs/xpcom18a4/java/src/nsJavaWrapper.cpp | 2020 |
1 files changed, 2020 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/java/src/nsJavaWrapper.cpp b/src/libs/xpcom18a4/java/src/nsJavaWrapper.cpp new file mode 100644 index 00000000..a046c8a3 --- /dev/null +++ b/src/libs/xpcom18a4/java/src/nsJavaWrapper.cpp @@ -0,0 +1,2020 @@ +/* ***** 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) 2006 + * 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 "nsJavaInterfaces.h" +#include "nsJavaWrapper.h" +#include "nsJavaXPTCStub.h" +#include "nsJavaXPCOMBindingUtils.h" +#include "jni.h" +#include "xptcall.h" +#include "nsIInterfaceInfoManager.h" +#include "nsString.h" +#include "nsCRT.h" +#include "prmem.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" + +static nsID nullID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; + +#ifdef VBOX +#include "nsIThread.h" +static nsresult +NS_InvokeByIndex(nsISupports* that, PRUint32 methodIndex, + PRUint32 paramCount, nsXPTCVariant* params) +{ + return XPTC_InvokeByIndex(that, methodIndex, paramCount, params); +} + +#endif + +nsresult +CreateJavaArray(JNIEnv* env, PRUint8 aType, PRUint32 aSize, const nsID& aIID, + jobject* aResult) +{ + jobject array = nsnull; + switch (aType) + { + case nsXPTType::T_I8: + array = env->NewByteArray(aSize); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U8: + array = env->NewShortArray(aSize); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U16: + array = env->NewIntArray(aSize); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U32: + array = env->NewLongArray(aSize); + break; + + case nsXPTType::T_FLOAT: + array = env->NewFloatArray(aSize); + break; + + // XXX how do we handle unsigned 64-bit values? + case nsXPTType::T_U64: + case nsXPTType::T_DOUBLE: + array = env->NewDoubleArray(aSize); + break; + + case nsXPTType::T_BOOL: + array = env->NewBooleanArray(aSize); + break; + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + array = env->NewCharArray(aSize); + break; + + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + case nsXPTType::T_IID: + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + array = env->NewObjectArray(aSize, stringClass, nsnull); + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + NS_ASSERTION(iim, "Failed to get InterfaceInfoManager"); + if (!iim) + return NS_ERROR_FAILURE; + + // Get interface info for given IID + nsCOMPtr<nsIInterfaceInfo> info; + nsresult rv = iim->GetInfoForIID(&aIID, getter_AddRefs(info)); + if (NS_FAILED(rv)) + return rv; + + // Get interface name + const char* iface_name; + rv = info->GetNameShared(&iface_name); + if (NS_FAILED(rv)) + return rv; + + // Create proper Java interface name + nsCAutoString class_name("org/mozilla/interfaces/"); + class_name.AppendASCII(iface_name); + jclass ifaceClass = env->FindClass(class_name.get()); + if (!ifaceClass) + return NS_ERROR_FAILURE; + + array = env->NewObjectArray(aSize, ifaceClass, nsnull); + break; + } + + case nsXPTType::T_VOID: + array = env->NewLongArray(aSize); + break; + + default: + NS_WARNING("unknown type"); + return NS_ERROR_FAILURE; + } + + if (!array) + return NS_ERROR_OUT_OF_MEMORY; + + *aResult = array; + return NS_OK; +} + +nsresult +GetNativeArrayElement(PRUint8 aType, void* aArray, PRUint32 aIndex, + nsXPTCVariant* aResult) +{ + switch (aType) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + aResult->val.u8 = static_cast<PRUint8*>(aArray)[aIndex]; + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + aResult->val.u16 = static_cast<PRUint16*>(aArray)[aIndex]; + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + aResult->val.u32 = static_cast<PRUint32*>(aArray)[aIndex]; + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + aResult->val.u64 = static_cast<PRUint64*>(aArray)[aIndex]; + break; + + case nsXPTType::T_FLOAT: + aResult->val.f = static_cast<float*>(aArray)[aIndex]; + break; + + case nsXPTType::T_DOUBLE: + aResult->val.d = static_cast<double*>(aArray)[aIndex]; + break; + + case nsXPTType::T_BOOL: + aResult->val.b = static_cast<PRBool*>(aArray)[aIndex]; + break; + + case nsXPTType::T_CHAR: + aResult->val.c = static_cast<char*>(aArray)[aIndex]; + break; + + case nsXPTType::T_WCHAR: + aResult->val.wc = static_cast<PRUnichar*>(aArray)[aIndex]; + break; + + case nsXPTType::T_CHAR_STR: + aResult->val.p = static_cast<char**>(aArray)[aIndex]; + break; + + case nsXPTType::T_WCHAR_STR: + aResult->val.p = static_cast<PRUnichar**>(aArray)[aIndex]; + break; + + case nsXPTType::T_IID: + aResult->val.p = static_cast<nsID**>(aArray)[aIndex]; + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + aResult->val.p = static_cast<nsISupports**>(aArray)[aIndex]; + break; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + aResult->val.p = static_cast<nsString**>(aArray)[aIndex]; + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + aResult->val.p = static_cast<nsCString**>(aArray)[aIndex]; + break; + + case nsXPTType::T_VOID: + aResult->val.p = static_cast<void**>(aArray)[aIndex]; + break; + + default: + NS_WARNING("unknown type"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +CreateNativeArray(PRUint8 aType, PRUint32 aSize, void** aResult) +{ + void* array = nsnull; + switch (aType) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + array = PR_Malloc(aSize * sizeof(PRUint8)); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + array = PR_Malloc(aSize * sizeof(PRUint16)); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + array = PR_Malloc(aSize * sizeof(PRUint32)); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + array = PR_Malloc(aSize * sizeof(PRUint64)); + break; + + case nsXPTType::T_FLOAT: + array = PR_Malloc(aSize * sizeof(float)); + break; + + case nsXPTType::T_DOUBLE: + array = PR_Malloc(aSize * sizeof(double)); + break; + + case nsXPTType::T_BOOL: + array = PR_Malloc(aSize * sizeof(PRBool)); + break; + + case nsXPTType::T_CHAR: + array = PR_Malloc(aSize * sizeof(char)); + break; + + case nsXPTType::T_WCHAR: + array = PR_Malloc(aSize * sizeof(PRUnichar)); + break; + + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + case nsXPTType::T_IID: + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + array = PR_Malloc(aSize * sizeof(void*)); + break; + + case nsXPTType::T_VOID: + array = PR_Malloc(aSize * sizeof(void*)); + break; + + default: + NS_WARNING("unknown type"); + return NS_ERROR_FAILURE; + } + + if (!array) + return NS_ERROR_OUT_OF_MEMORY; + + *aResult = array; + return NS_OK; +} + +/** + * Handle 'in' and 'inout' params. + */ +nsresult +SetupParams(JNIEnv *env, const jobject aParam, PRUint8 aType, PRBool aIsOut, + const nsID& aIID, PRUint8 aArrayType, PRUint32 aArraySize, + PRBool aIsArrayElement, PRUint32 aIndex, nsXPTCVariant &aVariant) +{ + nsresult rv = NS_OK; + + switch (aType) + { + case nsXPTType::T_I8: + { + LOG(("byte\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.i8 = env->CallByteMethod(aParam, byteValueMID); + } else { // 'inout' & 'array' + jbyte value; + if (aParam) { + env->GetByteArrayRegion((jbyteArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.i8 = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<PRInt8*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_I16: + case nsXPTType::T_U8: // C++ unsigned octet <=> Java short + { + LOG(("short\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + jshort value = env->CallShortMethod(aParam, shortValueMID); + if (aType == nsXPTType::T_I16) + aVariant.val.i16 = value; + else + aVariant.val.u8 = value; + } else { // 'inout' & 'array' + jshort value; + if (aParam) { + env->GetShortArrayRegion((jshortArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + if (aType == nsXPTType::T_I16) + aVariant.val.i16 = value; + else + aVariant.val.u8 = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + if (aType == nsXPTType::T_I16) + static_cast<PRInt16*>(aVariant.val.p)[aIndex] = value; + else + static_cast<PRUint8*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_I32: + case nsXPTType::T_U16: // C++ unsigned short <=> Java int + { + LOG(("int\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + jint value = env->CallIntMethod(aParam, intValueMID); + if (aType == nsXPTType::T_I32) + aVariant.val.i32 = value; + else + aVariant.val.u16 = value; + } else { // 'inout' & 'array' + jint value; + if (aParam) { + env->GetIntArrayRegion((jintArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + if (aType == nsXPTType::T_I32) + aVariant.val.i32 = value; + else + aVariant.val.u16 = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + if (aType == nsXPTType::T_I32) + static_cast<PRInt32*>(aVariant.val.p)[aIndex] = value; + else + static_cast<PRUint16*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_I64: + case nsXPTType::T_U32: // C++ unsigned int <=> Java long + { + LOG(("long\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + jlong value = env->CallLongMethod(aParam, longValueMID); + if (aType == nsXPTType::T_I64) + aVariant.val.i64 = value; + else + aVariant.val.u32 = value; + } else { // 'inout' & 'array' + jlong value; + if (aParam) { + env->GetLongArrayRegion((jlongArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + if (aType == nsXPTType::T_I64) + aVariant.val.i64 = value; + else + aVariant.val.u32 = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + if (aType == nsXPTType::T_I64) + static_cast<PRInt64*>(aVariant.val.p)[aIndex] = value; + else + static_cast<PRUint32*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_FLOAT: + { + LOG(("float\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.f = env->CallFloatMethod(aParam, floatValueMID); + } else { // 'inout' & 'array' + jfloat value; + if (aParam) { + env->GetFloatArrayRegion((jfloatArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.f = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<float*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + // XXX how do we handle unsigned 64-bit value? + case nsXPTType::T_U64: // C++ unsigned long <=> Java double + case nsXPTType::T_DOUBLE: + { + LOG(("double\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + jdouble value = env->CallDoubleMethod(aParam, doubleValueMID); + if (aType == nsXPTType::T_DOUBLE) + aVariant.val.d = value; + else + aVariant.val.u64 = static_cast<PRUint64>(value); + } else { // 'inout' & 'array' + jdouble value; + if (aParam) { + env->GetDoubleArrayRegion((jdoubleArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + if (aType == nsXPTType::T_DOUBLE) + aVariant.val.d = value; + else + aVariant.val.u64 = static_cast<PRUint64>(value); + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + if (aType == nsXPTType::T_DOUBLE) + static_cast<double*>(aVariant.val.p)[aIndex] = value; + else + static_cast<PRUint64*>(aVariant.val.p)[aIndex] = + static_cast<PRUint64>(value); + } + } + break; + } + + case nsXPTType::T_BOOL: + { + LOG(("boolean\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.b = env->CallBooleanMethod(aParam, booleanValueMID); + } else { // 'inout' & 'array' + jboolean value; + if (aParam) { + env->GetBooleanArrayRegion((jbooleanArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.b = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<PRBool*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_CHAR: + { + LOG(("char\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.c = env->CallCharMethod(aParam, charValueMID); + } else { // 'inout' & 'array' + jchar value; + if (aParam) { + env->GetCharArrayRegion((jcharArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.c = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<char*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_WCHAR: + { + LOG(("char\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.wc = env->CallCharMethod(aParam, charValueMID); + } else { // 'inout' & 'array' + jchar value; + if (aParam) { + env->GetCharArrayRegion((jcharArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.wc = value; + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<PRUnichar*>(aVariant.val.p)[aIndex] = value; + } + } + break; + } + + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + { + LOG(("String\n")); + jstring data = nsnull; + if (!aIsOut && !aIsArrayElement) { // 'in' + data = (jstring) aParam; + } else if (aParam) { // 'inout' & 'array' + data = (jstring) env->GetObjectArrayElement((jobjectArray) aParam, + aIndex); + } + + void* buf = nsnull; + if (data) { + jsize uniLength = env->GetStringLength(data); + if (uniLength > 0) { + if (aType == nsXPTType::T_CHAR_STR) { + jsize utf8Length = env->GetStringUTFLength(data); + buf = nsMemory::Alloc((utf8Length + 1) * sizeof(char)); + if (!buf) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + char* char_str = static_cast<char*>(buf); + env->GetStringUTFRegion(data, 0, uniLength, char_str); + char_str[utf8Length] = '\0'; + + } else { // if T_WCHAR_STR + buf = nsMemory::Alloc((uniLength + 1) * sizeof(jchar)); + if (!buf) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + jchar* jchar_str = static_cast<jchar*>(buf); + env->GetStringRegion(data, 0, uniLength, jchar_str); + jchar_str[uniLength] = '\0'; + } + } else { + // create empty string + buf = nsMemory::Alloc(2); + if (!buf) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + ((jchar*)buf)[0] = '\0'; + } + } + + if (!aIsArrayElement) { // 'in' & 'inout' + aVariant.val.p = buf; + if (aIsOut) { // 'inout' + aVariant.ptr = &aVariant.val; + aVariant.SetPtrIsData(); + } + } else { // 'array' + if (aType == nsXPTType::T_CHAR_STR) { + char* str = static_cast<char*>(buf); + static_cast<char**>(aVariant.val.p)[aIndex] = str; + } else { + PRUnichar* str = static_cast<PRUnichar*>(buf); + static_cast<PRUnichar**>(aVariant.val.p)[aIndex] = str; + } + } + break; + } + + case nsXPTType::T_IID: + { + LOG(("String(IID)\n")); + jstring data = nsnull; + if (!aIsOut && !aIsArrayElement) { // 'in' + data = (jstring) aParam; + } else if (aParam) { // 'inout' & 'array' + data = (jstring) env->GetObjectArrayElement((jobjectArray) aParam, + aIndex); + } + + nsID* iid = new nsID; + if (!iid) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + if (data) { + // extract IID string from Java string + const char* str = env->GetStringUTFChars(data, nsnull); + if (!str) { + delete iid; + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + // parse string into IID object + iid->Parse(str); + env->ReleaseStringUTFChars(data, str); + } else { + *iid = nullID; + } + + if (!aIsArrayElement) { // 'in' & 'inout' + aVariant.val.p = iid; + if (aIsOut) { // 'inout' + aVariant.ptr = &aVariant.val; + aVariant.SetPtrIsData(); + } + } else { // 'array' + static_cast<nsID**>(aVariant.val.p)[aIndex] = iid; + } + break; + } + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + LOG(("nsISupports\n")); + jobject java_obj = nsnull; + if (!aIsOut && !aIsArrayElement) { // 'in' + java_obj = (jobject) aParam; + } else if (aParam) { // 'inout' & 'array' + java_obj = (jobject) env->GetObjectArrayElement((jobjectArray) aParam, + aIndex); + } + + void* xpcom_obj; + if (java_obj) { + // If the requested interface is nsIWeakReference, then we look for or + // create a stub for the nsISupports interface. Then we create a weak + // reference from that stub. + PRBool isWeakRef; + nsID iid; + if (aIID.Equals(NS_GET_IID(nsIWeakReference))) { + isWeakRef = PR_TRUE; + iid = NS_GET_IID(nsISupports); + } else { + isWeakRef = PR_FALSE; + iid = aIID; + } + + rv = JavaObjectToNativeInterface(env, java_obj, iid, &xpcom_obj); + if (NS_FAILED(rv)) + break; + NS_ENSURE_TRUE(xpcom_obj, NS_ERROR_FAILURE); + nsISupports *xpcom_nat_obj = (nsISupports*) xpcom_obj; + rv = xpcom_nat_obj->QueryInterface(iid, &xpcom_obj); + NS_IF_RELEASE(xpcom_nat_obj); + if (NS_FAILED(rv)) + break; + + // If the function expects a weak reference, then we need to + // create it here. + if (isWeakRef) { + nsISupports* isupports = (nsISupports*) xpcom_obj; + nsCOMPtr<nsISupportsWeakReference> supportsweak = + do_QueryInterface(isupports); + if (supportsweak) { + nsWeakPtr weakref; + supportsweak->GetWeakReference(getter_AddRefs(weakref)); + NS_RELEASE(isupports); + xpcom_obj = weakref; + NS_ADDREF((nsISupports*) xpcom_obj); + } else { + xpcom_obj = nsnull; + } + } + } else { + xpcom_obj = nsnull; + } + + if (!aIsArrayElement) { // 'in' & 'inout' + aVariant.val.p = xpcom_obj; + aVariant.SetValIsInterface(); + if (aIsOut) { // 'inout' + aVariant.ptr = &aVariant.val; + aVariant.SetPtrIsData(); + } + } else { // 'array' + static_cast<void**>(aVariant.val.p)[aIndex] = xpcom_obj; + } + break; + } + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + LOG(("String\n")); + // Expecting only 'in' and 'in dipper' + NS_PRECONDITION(!aIsOut, "unexpected param descriptor"); + if (aIsOut) { + rv = NS_ERROR_UNEXPECTED; + break; + } + + jstring jstr = static_cast<jstring>(aParam); + nsAString* str = jstring_to_nsAString(env, jstr); + if (!str) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + aVariant.val.p = str; + aVariant.SetValIsDOMString(); + break; + } + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + LOG(("StringUTF\n")); + // Expecting only 'in' and 'in dipper' + NS_PRECONDITION(!aIsOut, "unexpected param descriptor"); + if (aIsOut) { + rv = NS_ERROR_UNEXPECTED; + break; + } + + jstring jstr = static_cast<jstring>(aParam); + nsACString* str = jstring_to_nsACString(env, jstr); + if (!str) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + aVariant.val.p = str; + if (aType == nsXPTType::T_CSTRING) { + aVariant.SetValIsCString(); + } else { + aVariant.SetValIsUTF8String(); + } + break; + } + + // handle "void *" as an "long" in Java + case nsXPTType::T_VOID: + { + LOG(("long (void*)\n")); + if (!aIsOut && !aIsArrayElement) { // 'in' + aVariant.val.p = + reinterpret_cast<void*>(env->CallLongMethod(aParam, longValueMID)); + } else { // 'inout' & 'array' + jlong value; + if (aParam) { + env->GetLongArrayRegion((jlongArray) aParam, aIndex, 1, &value); + } + + if (aIsOut) { // 'inout' + if (aParam) { + aVariant.val.p = reinterpret_cast<void*>(value); + aVariant.ptr = &aVariant.val; + } else { + aVariant.ptr = nsnull; + } + aVariant.SetPtrIsData(); + } else { // 'array' + static_cast<void**>(aVariant.val.p)[aIndex] = + reinterpret_cast<void*>(value); + } + } + break; + } + + case nsXPTType::T_ARRAY: + { + jobject sourceArray = nsnull; + if (!aIsOut) { // 'in' + sourceArray = aParam; + } else if (aParam) { // 'inout' + jobjectArray array = static_cast<jobjectArray>(aParam); + sourceArray = env->GetObjectArrayElement(array, 0); + } + + if (sourceArray) { + rv = CreateNativeArray(aArrayType, aArraySize, &aVariant.val.p); + + for (PRUint32 i = 0; i < aArraySize && NS_SUCCEEDED(rv); i++) { + rv = SetupParams(env, sourceArray, aArrayType, PR_FALSE, aIID, 0, 0, + PR_TRUE, i, aVariant); + } + } + + if (aIsOut) { // 'inout' + aVariant.ptr = &aVariant.val.p; + aVariant.SetPtrIsData(); + } + break; + } + + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + { + NS_PRECONDITION(!aIsArrayElement, "sized string array not supported"); + + LOG(("Sized string\n")); + jstring data = nsnull; + if (!aIsOut) { // 'in' + data = (jstring) aParam; + } else if (aParam) { // 'inout' + data = (jstring) env->GetObjectArrayElement((jobjectArray) aParam, + aIndex); + } + + PRUint32 length = 0; + if (data) { + if (aType == nsXPTType::T_PSTRING_SIZE_IS) { + length = env->GetStringUTFLength(data); + } else { + length = env->GetStringLength(data); + } + if (length > aArraySize) { + rv = NS_ERROR_ILLEGAL_VALUE; + break; + } + } + + PRUint32 size_of_char = (aType == nsXPTType::T_PSTRING_SIZE_IS) ? + sizeof(char) : sizeof(jchar); + PRUint32 allocLength = (aArraySize + 1) * size_of_char; + void* buf = nsMemory::Alloc(allocLength); + if (!buf) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + + if (data) { + if (aType == nsXPTType::T_PSTRING_SIZE_IS) { + const char* str = env->GetStringUTFChars(data, nsnull); + if (!str) { + nsMemory::Free(buf); + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + memcpy(buf, str, length); + env->ReleaseStringUTFChars(data, str); + } else { + jchar* jchar_str = static_cast<jchar*>(buf); + env->GetStringRegion(data, 0, length, jchar_str); + } + } + + aVariant.val.p = buf; + if (aIsOut) { // 'inout' + aVariant.ptr = &aVariant.val; + aVariant.SetPtrIsData(); + } + + break; + } + + default: + NS_WARNING("unexpected parameter type"); + return NS_ERROR_UNEXPECTED; + } + + return rv; +} + +/** + * Does any cleanup from objects created in SetupParams, as well as converting + * any out params to Java. + * + * NOTE: If aInvokeResult is an error condition, then we just do cleanup in + * this function. + */ +nsresult +FinalizeParams(JNIEnv *env, const nsXPTParamInfo &aParamInfo, PRUint8 aType, + nsXPTCVariant &aVariant, const nsID& aIID, + PRBool aIsArrayElement, PRUint8 aArrayType, PRUint32 aArraySize, + PRUint32 aIndex, nsresult aInvokeResult, jobject* aParam) +{ + nsresult rv = NS_OK; + + switch (aType) + { + case nsXPTType::T_I8: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jbyte value = aVariant.val.i8; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(byteClass, byteInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetByteArrayRegion((jbyteArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_I16: + case nsXPTType::T_U8: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jshort value = (aType == nsXPTType::T_I16) ? aVariant.val.i16 : + aVariant.val.u8; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(shortClass, shortInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetShortArrayRegion((jshortArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_I32: + case nsXPTType::T_U16: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jint value = (aType == nsXPTType::T_I32) ? aVariant.val.i32 : + aVariant.val.u16; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(intClass, intInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetIntArrayRegion((jintArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_I64: + case nsXPTType::T_U32: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jlong value = (aType == nsXPTType::T_I64) ? aVariant.val.i64 : + aVariant.val.u32; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(longClass, longInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetLongArrayRegion((jlongArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_FLOAT: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jfloat value = aVariant.val.f; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(floatClass, floatInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetFloatArrayRegion((jfloatArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + // XXX how do we handle unsigned 64-bit values? + case nsXPTType::T_U64: + case nsXPTType::T_DOUBLE: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jdouble value = (aType == nsXPTType::T_DOUBLE) ? aVariant.val.d : + aVariant.val.u64; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(doubleClass, doubleInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetDoubleArrayRegion((jdoubleArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_BOOL: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jboolean value = aVariant.val.b; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(booleanClass, booleanInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetBooleanArrayRegion((jbooleanArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jchar value; + if (aType == nsXPTType::T_CHAR) + value = aVariant.val.c; + else + value = aVariant.val.wc; + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(charClass, charInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetCharArrayRegion((jcharArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + { + if ((aParamInfo.IsOut() || aIsArrayElement) && + NS_SUCCEEDED(aInvokeResult)) + { + // create new string from data + jstring str = nsnull; + if (aVariant.val.p) { + if (aType == nsXPTType::T_CHAR_STR) { + str = env->NewStringUTF((const char*) aVariant.val.p); + } else { + PRUint32 length = nsCRT::strlen((const PRUnichar*) aVariant.val.p); + str = env->NewString((const jchar*) aVariant.val.p, length); + } + if (!str) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = str; + } else if (*aParam) { + // put new string into output array + env->SetObjectArrayElement((jobjectArray) *aParam, aIndex, str); + } + } + + // cleanup + if (aVariant.val.p) + nsMemory::Free(aVariant.val.p); + break; + } + + case nsXPTType::T_IID: + { + nsID* iid = static_cast<nsID*>(aVariant.val.p); + + if ((aParamInfo.IsOut() || aIsArrayElement) && + NS_SUCCEEDED(aInvokeResult)) + { + // Create the string from nsID + jstring str = nsnull; + if (iid) { + char iid_str[NSID_LENGTH]; + iid->ToProvidedString(iid_str); + str = env->NewStringUTF(iid_str); + if (!str) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = str; + } else if (*aParam) { + // put new string into output array + env->SetObjectArrayElement((jobjectArray) *aParam, aIndex, str); + } + } + + // Ordinarily, we would delete 'iid' here. But we cannot do that until + // we've handled all of the params. See comment in CallXPCOMMethod. + // We can safely delete array elements, though. + if (aIsArrayElement) + delete iid; + + break; + } + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + nsISupports* xpcom_obj = static_cast<nsISupports*>(aVariant.val.p); + + if ((aParamInfo.IsOut() || aIsArrayElement) && + NS_SUCCEEDED(aInvokeResult)) + { + jobject java_obj = nsnull; + if (xpcom_obj) { + // Get matching Java object for given xpcom object + rv = NativeInterfaceToJavaObject(env, xpcom_obj, aIID, nsnull, + &java_obj); + if (NS_FAILED(rv)) + break; + } + + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = java_obj; + } else if (*aParam) { + // put new Java object into output array + env->SetObjectArrayElement((jobjectArray) *aParam, aIndex, java_obj); + } + } + + // cleanup + NS_IF_RELEASE(xpcom_obj); + break; + } + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + NS_PRECONDITION(aParamInfo.IsIn(), "unexpected param descriptor"); + if (!aParamInfo.IsIn()) { + rv = NS_ERROR_UNEXPECTED; + break; + } + + nsString* str = static_cast<nsString*>(aVariant.val.p); + if (NS_SUCCEEDED(aInvokeResult) && aParamInfo.IsDipper()) { + // Create Java string from returned nsString + jstring jstr = nsnull; + if (str && !str->IsVoid()) { + jstr = env->NewString((const jchar*) str->get(), str->Length()); + if (!jstr) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + + *aParam = jstr; + } + + // cleanup + if (str) { + delete str; + } + break; + } + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + NS_PRECONDITION(aParamInfo.IsIn(), "unexpected param descriptor"); + if (!aParamInfo.IsIn()) { + rv = NS_ERROR_UNEXPECTED; + break; + } + + nsCString* str = static_cast<nsCString*>(aVariant.val.p); + if (NS_SUCCEEDED(aInvokeResult) && aParamInfo.IsDipper()) { + // Create Java string from returned nsString + jstring jstr = nsnull; + if (str && !str->IsVoid()) { + jstr = env->NewStringUTF((const char*) str->get()); + if (!jstr) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + + *aParam = jstr; + } + + // cleanup + if (str) { + delete str; + } + break; + } + + case nsXPTType::T_VOID: + { + if (NS_SUCCEEDED(aInvokeResult)) { + jlong value = reinterpret_cast<jlong>(aVariant.val.p); + if (aParamInfo.IsRetval() && !aIsArrayElement) { + *aParam = env->NewObject(longClass, longInitMID, value); + } else if ((aParamInfo.IsOut() || aIsArrayElement) && *aParam) { + env->SetLongArrayRegion((jlongArray) *aParam, aIndex, 1, &value); + } + } + break; + } + + case nsXPTType::T_ARRAY: + { + if (aParamInfo.IsOut() && NS_SUCCEEDED(aInvokeResult)) { + // Create Java array from returned native array + jobject jarray = nsnull; + if (aVariant.val.p) { + rv = CreateJavaArray(env, aArrayType, aArraySize, aIID, &jarray); + if (NS_FAILED(rv)) + break; + + nsXPTCVariant var; + for (PRUint32 i = 0; i < aArraySize && NS_SUCCEEDED(rv); i++) { + rv = GetNativeArrayElement(aArrayType, aVariant.val.p, i, &var); + if (NS_SUCCEEDED(rv)) { + rv = FinalizeParams(env, aParamInfo, aArrayType, var, aIID, + PR_TRUE, 0, 0, i, aInvokeResult, &jarray); + } + } + } + + if (aParamInfo.IsRetval()) { + *aParam = jarray; + } else if (*aParam) { + // put new Java array into output array + env->SetObjectArrayElement((jobjectArray) *aParam, 0, jarray); + } + } + + // cleanup + // If this is not an out param or if the invokeResult is a failure case, + // then the array elements have not been cleaned up. Do so now. + if (!aParamInfo.IsOut() || (NS_FAILED(aInvokeResult) && aVariant.val.p)) { + nsXPTCVariant var; + for (PRUint32 i = 0; i < aArraySize; i++) { + rv = GetNativeArrayElement(aArrayType, aVariant.val.p, i, &var); + if (NS_SUCCEEDED(rv)) { + FinalizeParams(env, aParamInfo, aArrayType, var, aIID, PR_TRUE, + 0, 0, i, NS_ERROR_FAILURE, nsnull); + } + } + } + PR_Free(aVariant.val.p); + break; + } + + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + { + NS_PRECONDITION(!aIsArrayElement, "sized string array not supported"); + + if ((aParamInfo.IsOut()) && NS_SUCCEEDED(aInvokeResult)) + { + // create new string from data + jstring str = nsnull; + if (aVariant.val.p) { + if (aType == nsXPTType::T_PSTRING_SIZE_IS) { + PRUint32 len = (aArraySize + 1) * sizeof(char); + char* buf = (char*) nsMemory::Alloc(len); + if (buf) { + memcpy(buf, aVariant.val.p, len); + buf[aArraySize] = '\0'; + str = env->NewStringUTF((const char*) buf); + nsMemory::Free(buf); + } + } else { + str = env->NewString((const jchar*) aVariant.val.p, aArraySize); + } + if (!str) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + + if (aParamInfo.IsRetval()) { + *aParam = str; + } else if (*aParam) { + // put new string into output array + env->SetObjectArrayElement((jobjectArray) *aParam, aIndex, str); + } + } + + // cleanup + if (aVariant.val.p) + nsMemory::Free(aVariant.val.p); + break; + } + + default: + NS_WARNING("unexpected parameter type"); + return NS_ERROR_UNEXPECTED; + } + + // Check for Java exception, but don't overwrite pre-existing error code. + if (NS_SUCCEEDED(rv) && env->ExceptionCheck()) + rv = NS_ERROR_FAILURE; + + return rv; +} + +nsresult +QueryAttributeInfo(nsIInterfaceInfo* aIInfo, const char* aMethodName, + PRBool aCapitalizedAttr, PRUint16* aMethodIndex, + const nsXPTMethodInfo** aMethodInfo) + +{ + nsresult rv = NS_ERROR_FAILURE; + + // An 'attribute' will start with either "get" or "set". But first, + // we check the length, in order to skip over method names that match exactly + // "get" or "set". + if (strlen(aMethodName) > 3) { + if (strncmp("get", aMethodName, 3) == 0) { + char* getterName = strdup(aMethodName + 3); + if (!aCapitalizedAttr) { + getterName[0] = tolower(getterName[0]); + } + rv = aIInfo->GetMethodInfoForName(getterName, aMethodIndex, aMethodInfo); + free(getterName); + } else if (strncmp("set", aMethodName, 3) == 0) { + char* setterName = strdup(aMethodName + 3); + if (!aCapitalizedAttr) { + setterName[0] = tolower(setterName[0]); + } + rv = aIInfo->GetMethodInfoForName(setterName, aMethodIndex, aMethodInfo); + if (NS_SUCCEEDED(rv)) { + // If this succeeded, GetMethodInfoForName will have returned the + // method info for the 'getter'. We want the 'setter', so increase + // method index by one ('setter' immediately follows the 'getter'), + // and get its method info. + (*aMethodIndex)++; + rv = aIInfo->GetMethodInfo(*aMethodIndex, aMethodInfo); + if (NS_SUCCEEDED(rv)) { + // Double check that this methodInfo matches the given method. + if (!(*aMethodInfo)->IsSetter() || + strcmp(setterName, (*aMethodInfo)->name) != 0) { + rv = NS_ERROR_FAILURE; + } + } + } + free(setterName); + } + } + + return rv; +} + +/** + * Given an interface info struct and a method name, returns the method info + * and index, if that method exists. + * + * Most method names are lower case. Unfortunately, the method names of some + * interfaces (such as nsIAppShell) start with a capital letter. This function + * will try all of the permutations. + */ +nsresult +QueryMethodInfo(nsIInterfaceInfo* aIInfo, const char* aMethodName, + PRUint16* aMethodIndex, const nsXPTMethodInfo** aMethodInfo) +{ + // Skip over any leading underscores, since these are methods that conflicted + // with existing Java keywords + const char* methodName = aMethodName; + if (methodName[0] == '_') { + methodName++; + } + + // The common case is that the method name is lower case, so we check + // that first. + nsresult rv; + rv = aIInfo->GetMethodInfoForName(methodName, aMethodIndex, aMethodInfo); + if (NS_SUCCEEDED(rv)) + return rv; + + // If there is no method called <aMethodName>, then maybe it is an + // 'attribute'. + rv = QueryAttributeInfo(aIInfo, methodName, PR_FALSE, aMethodIndex, + aMethodInfo); + if (NS_SUCCEEDED(rv)) + return rv; + + // If we get here, then maybe the method name is capitalized. + char* name = strdup(methodName); + name[0] = toupper(name[0]); + rv = aIInfo->GetMethodInfoForName(name, aMethodIndex, aMethodInfo); + free(name); + if (NS_SUCCEEDED(rv)) + return rv; + + // If there is no method called <aMethodName>, then maybe it is an + // 'attribute'. + rv = QueryAttributeInfo(aIInfo, methodName, PR_TRUE, aMethodIndex, + aMethodInfo); + + return rv; +} + +#ifdef VBOX +#include <nsIExceptionService.h> +#include <iprt/err.h> +#include "nspr.h" + +static void makeErrorMessage(nsresult r, char* msg, size_t msgSize) +{ + bool gotMsg = false; + + if (!gotMsg) + { + nsresult rc; + nsCOMPtr <nsIExceptionService> es; + es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc); + if (NS_SUCCEEDED (rc)) + { + nsCOMPtr <nsIExceptionManager> em; + rc = es->GetCurrentExceptionManager (getter_AddRefs (em)); + if (NS_SUCCEEDED (rc)) + { + nsCOMPtr <nsIException> ex; + rc = em->GetCurrentException(getter_AddRefs (ex)); + if (NS_SUCCEEDED (rc) && ex) + { + nsXPIDLCString emsg; + ex->GetMessage(getter_Copies(emsg)); + PR_snprintf(msg, msgSize, "%s", + emsg.get()); + gotMsg = true; + } + } + } + } + + if (!gotMsg) + { + const RTCOMERRMSG* pMsg = RTErrCOMGet(r); + if (strncmp(pMsg->pszMsgFull, "Unknown", 7) != 0) + { + PR_snprintf(msg, msgSize, "%s (%s)", + pMsg->pszMsgFull, pMsg->pszDefine); + gotMsg = true; + } + } + + if (!gotMsg) + { + PR_snprintf(msg, msgSize, "Error 0x%x in module 0x%x", + NS_ERROR_GET_CODE(r), NS_ERROR_GET_MODULE(r)); + } +} +#endif + +/** + * org.mozilla.xpcom.XPCOMJavaProxy.internal.callXPCOMMethod + */ +extern "C" NS_EXPORT jobject JNICALL +JAVAPROXY_NATIVE(callXPCOMMethod) (JNIEnv *env, jclass that, jobject aJavaProxy, + jstring aMethodName, jobjectArray aParams) +{ + nsresult rv; + + // Get native XPCOM instance + void* xpcom_obj; + rv = GetXPCOMInstFromProxy(env, aJavaProxy, &xpcom_obj); + if (NS_FAILED(rv)) { + ThrowException(env, 0, "Failed to get matching XPCOM object"); + return nsnull; + } + JavaXPCOMInstance* inst = static_cast<JavaXPCOMInstance*>(xpcom_obj); + + // Get method info + PRUint16 methodIndex; + const nsXPTMethodInfo* methodInfo; + nsIInterfaceInfo* iinfo = inst->InterfaceInfo(); + const char* methodName = env->GetStringUTFChars(aMethodName, nsnull); + rv = QueryMethodInfo(iinfo, methodName, &methodIndex, &methodInfo); + env->ReleaseStringUTFChars(aMethodName, methodName); + + if (NS_FAILED(rv)) { + ThrowException(env, rv, "GetMethodInfoForName failed"); + return nsnull; + } + +#ifdef DEBUG_JAVAXPCOM + const char* ifaceName; + iinfo->GetNameShared(&ifaceName); + LOG(("===> (XPCOM) %s::%s()\n", ifaceName, methodInfo->GetName())); +#endif + + // Convert the Java params + PRUint8 paramCount = methodInfo->GetParamCount(); + nsXPTCVariant* params = nsnull; + if (paramCount) + { + params = new nsXPTCVariant[paramCount]; + if (!params) { + ThrowException(env, NS_ERROR_OUT_OF_MEMORY, "Can't create params array"); + return nsnull; + } + memset(params, 0, paramCount * sizeof(nsXPTCVariant)); + + PRBool foundDependentParam = PR_FALSE; + for (PRUint8 i = 0; i < paramCount && NS_SUCCEEDED(rv); i++) + { + LOG(("\t Param %d: ", i)); + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + params[i].type = paramInfo.GetType(); + + if (params[i].type.IsDependent() && paramInfo.IsIn()) { + foundDependentParam = PR_TRUE; + continue; + } + + if (paramInfo.IsIn()) { + PRUint8 type = params[i].type.TagPart(); + + // get IID for interface params + nsID iid; + if (type == nsXPTType::T_INTERFACE) { + rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type, + methodIndex, params, PR_TRUE, iid); + } + + if (NS_SUCCEEDED(rv)) { + jobject param = nsnull; + if (aParams && !paramInfo.IsRetval()) { + param = env->GetObjectArrayElement(aParams, i); + } + rv = SetupParams(env, param, type, paramInfo.IsOut(), iid, 0, 0, + PR_FALSE, 0, params[i]); + } + } else { + LOG(("out/retval\n")); + params[i].ptr = &(params[i].val); + params[i].SetPtrIsData(); + } + } + + // Handle any dependent params by doing a second pass + if (foundDependentParam) { + + for (PRUint8 j = 0; j < paramCount && NS_SUCCEEDED(rv); j++) { + + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(j); + params[j].type = paramInfo.GetType(); + + if (!params[j].type.IsDependent()) + continue; + + if (paramInfo.IsIn()) { + PRUint8 type = params[j].type.TagPart(); + + // is paramater an array or sized string? + PRUint8 arrayType = 0; + PRUint32 arraySize = 0; + PRBool isArray = params[j].type.IsArray(); + PRBool isSizedString = isArray ? PR_FALSE : + type == nsXPTType::T_PSTRING_SIZE_IS || + type == nsXPTType::T_PWSTRING_SIZE_IS; + + if (isArray) { + // get array type + nsXPTType xpttype; + rv = iinfo->GetTypeForParam(methodIndex, ¶mInfo, 1, &xpttype); + if (NS_FAILED(rv)) + break; + arrayType = xpttype.TagPart(); + // IDL 'octet' arrays are not 'promoted' to short, but kept as 'byte'; + // therefore, treat as a signed 8bit value + if (arrayType == nsXPTType::T_U8) + arrayType = nsXPTType::T_I8; + } + + if (isArray || isSizedString) { + // get size of array or string + PRUint8 argnum; + rv = iinfo->GetSizeIsArgNumberForParam(methodIndex, ¶mInfo, 0, + &argnum); + if (NS_FAILED(rv)) + break; + arraySize = params[argnum].val.u32; + } + + // get IID for interface params + nsID iid; + if (type == nsXPTType::T_INTERFACE_IS || + (type == nsXPTType::T_ARRAY && + (arrayType == nsXPTType::T_INTERFACE || + arrayType == nsXPTType::T_INTERFACE_IS))) + { + PRUint8 paramType = type == nsXPTType::T_ARRAY ? arrayType : type; + rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, paramType, + methodIndex, params, PR_TRUE, iid); + } + + if (NS_SUCCEEDED(rv)) { + jobject param = nsnull; + if (aParams && !paramInfo.IsRetval()) { + param = env->GetObjectArrayElement(aParams, j); + } + rv = SetupParams(env, param, type, paramInfo.IsOut(), iid, arrayType, + arraySize, PR_FALSE, 0, params[j]); + } + } + } + } + + if (NS_FAILED(rv)) { + ThrowException(env, rv, "SetupParams failed"); + return nsnull; + } + } + + // Call the XPCOM method + const nsIID* iid; + iinfo->GetIIDShared(&iid); + nsISupports* realObject; + rv = inst->GetInstance()->QueryInterface(*iid, (void**) &realObject); + if (NS_FAILED(rv)) { + ThrowException(env, rv, "Failed to get real XPCOM object"); + return nsnull; + } + nsresult invokeResult = NS_InvokeByIndex(realObject, methodIndex, + paramCount, params); + NS_RELEASE(realObject); + + // Clean up params + jobject result = nsnull; + for (PRUint8 i = 0; i < paramCount && NS_SUCCEEDED(rv); i++) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i); + PRUint8 type = paramInfo.GetType().TagPart(); + + // is paramater an array or sized string? + PRUint8 arrayType = 0; + PRUint32 arraySize = 0; + PRBool isArray = params[i].type.IsArray(); + PRBool isSizedString = isArray ? PR_FALSE : + type == nsXPTType::T_PSTRING_SIZE_IS || + type == nsXPTType::T_PWSTRING_SIZE_IS; + + if (isArray) { + // get array type + nsXPTType array_xpttype; + rv = iinfo->GetTypeForParam(methodIndex, ¶mInfo, 1, &array_xpttype); + if (NS_FAILED(rv)) + break; + arrayType = array_xpttype.TagPart(); + // IDL 'octet' arrays are not 'promoted' to short, but kept as 'byte'; + // therefore, treat as a signed 8bit value + if (arrayType == nsXPTType::T_U8) + arrayType = nsXPTType::T_I8; + } + + if (isArray || isSizedString) { + // get size of array + PRUint8 argnum; + rv = iinfo->GetSizeIsArgNumberForParam(methodIndex, ¶mInfo, 0, + &argnum); + if (NS_FAILED(rv)) + break; + arraySize = params[argnum].val.u32; + } + + // get IID for interface params + nsID iid; + if (type == nsXPTType::T_INTERFACE || type == nsXPTType::T_INTERFACE_IS || + (type == nsXPTType::T_ARRAY && (arrayType == nsXPTType::T_INTERFACE || + arrayType == nsXPTType::T_INTERFACE_IS))) + { + PRUint8 paramType = type == nsXPTType::T_ARRAY ? arrayType : type; + rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, paramType, + methodIndex, params, PR_TRUE, iid); + if (NS_FAILED(rv)) + break; + } + + jobject* javaElement; + jobject element = nsnull; + if (!paramInfo.IsRetval()) { + element = env->GetObjectArrayElement(aParams, i); + javaElement = &element; + } else { + javaElement = &result; + } + rv = FinalizeParams(env, paramInfo, type, params[i], iid, PR_FALSE, + arrayType, arraySize, 0, invokeResult, javaElement); + } + + // Normally, we would delete any created nsID object in the above loop. + // However, GetIIDForMethodParam may need some of the nsID params when it's + // looking for the IID of an INTERFACE_IS. Therefore, we can't delete it + // until we've gone through the 'Finalize' loop once and created the result. + for (PRUint8 j = 0; j < paramCount; j++) + { + const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(j); + const nsXPTType &type = paramInfo.GetType(); + if (type.TagPart() == nsXPTType::T_IID) { + nsID* iid = (nsID*) params[j].val.p; + delete iid; + } + } + + if (params) { + delete params; + } + + // If the XPCOM method invocation failed, we don't immediately throw an + // exception and return so that we can clean up any parameters. + if (NS_FAILED(invokeResult)) { + nsCAutoString message("The function \""); + message.AppendASCII(methodInfo->GetName()); + message.AppendLiteral("\" returned an error condition"); +#ifdef VBOX + char vboxMsg[1024]; + message.AppendLiteral(": \""); + makeErrorMessage(invokeResult, vboxMsg, sizeof vboxMsg); + message.AppendASCII(vboxMsg); + message.AppendLiteral("\""); +#endif + ThrowException(env, invokeResult, message.get()); + } + if (NS_FAILED(rv)) { + ThrowException(env, rv, "FinalizeParams failed"); + return nsnull; + } + + LOG(("<=== (XPCOM) %s::%s()\n", ifaceName, methodInfo->GetName())); + return result; +} + +nsresult +GetNewOrUsedJavaWrapper(JNIEnv* env, nsISupports* aXPCOMObject, + const nsIID& aIID, jobject aObjectLoader, + jobject* aResult) +{ + NS_PRECONDITION(aResult != nsnull, "null ptr"); + if (!aResult) + return NS_ERROR_NULL_POINTER; + + // Get the root nsISupports of the xpcom object + nsresult rv; + 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. + + nsCOMPtr<nsIInterfaceInfoManager> + iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); + NS_ASSERTION(iim, "Failed to get InterfaceInfoManager"); + if (!iim) + return NS_ERROR_FAILURE; + + // Get interface info for class + nsCOMPtr<nsIInterfaceInfo> info; + rv = iim->GetInfoForIID(&aIID, getter_AddRefs(info)); + if (NS_FAILED(rv)) + return rv; + + // Wrap XPCOM object (addrefs rootObject) + JavaXPCOMInstance* inst = new JavaXPCOMInstance(rootObject, info); + if (!inst) + return NS_ERROR_OUT_OF_MEMORY; + + // Get interface name + const char* iface_name; + rv = info->GetNameShared(&iface_name); + + if (NS_SUCCEEDED(rv)) { + jobject java_obj = nsnull; + + // Create proper Java interface name + nsCAutoString class_name("org.mozilla.interfaces."); + class_name.AppendASCII(iface_name); + jclass ifaceClass = FindClassInLoader(env, aObjectLoader, class_name.get()); + + if (ifaceClass) { + java_obj = env->CallStaticObjectMethod(xpcomJavaProxyClass, + createProxyMID, ifaceClass, + reinterpret_cast<jlong>(inst)); + if (env->ExceptionCheck()) + java_obj = nsnull; + } + + if (java_obj) { +#ifdef DEBUG_JAVAXPCOM + char* iid_str = aIID.ToString(); + LOG(("+ CreateJavaProxy (Java=%08x | XPCOM=%08x | IID=%s)\n", + (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID, + java_obj), + (PRUint32) rootObject, iid_str)); + PR_Free(iid_str); +#endif + + // Associate XPCOM object with Java proxy + rv = gNativeToJavaProxyMap->Add(env, rootObject, aIID, java_obj); + if (NS_SUCCEEDED(rv)) { + *aResult = java_obj; + return NS_OK; + } + } else { + rv = NS_ERROR_FAILURE; + } + } + + // If there was an error, clean up. + delete inst; + return rv; +} + +nsresult +GetXPCOMInstFromProxy(JNIEnv* env, jobject aJavaObject, void** aResult) +{ + NS_PRECONDITION(aResult != nsnull, "null ptr"); + if (!aResult) + return NS_ERROR_NULL_POINTER; + + jlong xpcom_obj = env->CallStaticLongMethod(xpcomJavaProxyClass, + getNativeXPCOMInstMID, aJavaObject); + + if (!xpcom_obj || env->ExceptionCheck()) { + return NS_ERROR_FAILURE; + } + + *aResult = reinterpret_cast<void*>(xpcom_obj); +#ifdef DEBUG_JAVAXPCOM + JavaXPCOMInstance* inst = static_cast<JavaXPCOMInstance*>(*aResult); + nsIID* iid; + inst->InterfaceInfo()->GetInterfaceIID(&iid); + char* iid_str = iid->ToString(); + LOG(("< GetXPCOMInstFromProxy (Java=%08x | XPCOM=%08x | IID=%s)\n", + (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID, + aJavaObject), + (PRUint32) inst->GetInstance(), iid_str)); + PR_Free(iid_str); + nsMemory::Free(iid); +#endif + return NS_OK; +} + +/** + * org.mozilla.xpcom.internal.XPCOMJavaProxy.finalizeProxy + */ +extern "C" NS_EXPORT void JNICALL +JAVAPROXY_NATIVE(finalizeProxy) (JNIEnv *env, jclass that, jobject aJavaProxy) +{ +#ifdef DEBUG_JAVAXPCOM + PRUint32 xpcom_addr = 0; +#endif + + // Due to Java's garbage collection, this finalize statement may get called + // after FreeJavaGlobals(). So check to make sure that everything is still + // initialized. + if (gJavaXPCOMLock) { + nsAutoLock lock(gJavaXPCOMLock); + + // If may be possible for the lock to be acquired here when FreeGlobals is + // in the middle of running. If so, then this thread will sleep until + // FreeGlobals releases its lock. At that point, we resume this thread + // here, but JavaXPCOM may no longer be initialized. So we need to check + // that everything is legit after acquiring the lock. + if (gJavaXPCOMInitialized) { + // Get native XPCOM instance + void* xpcom_obj; + nsresult rv = GetXPCOMInstFromProxy(env, aJavaProxy, &xpcom_obj); + if (NS_SUCCEEDED(rv)) { + JavaXPCOMInstance* inst = static_cast<JavaXPCOMInstance*>(xpcom_obj); +#ifdef DEBUG_JAVAXPCOM + xpcom_addr = reinterpret_cast<PRUint32>(inst->GetInstance()); +#endif + nsIID* iid; + rv = inst->InterfaceInfo()->GetInterfaceIID(&iid); + if (NS_SUCCEEDED(rv)) { + rv = gNativeToJavaProxyMap->Remove(env, inst->GetInstance(), *iid); + nsMemory::Free(iid); + } + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to RemoveJavaProxy"); + // Release gJavaXPCOMLock before deleting inst (see bug 340022) + lock.unlock(); + delete inst; + } + } + } + +#ifdef DEBUG_JAVAXPCOM + LOG(("- Finalize (Java=%08x | XPCOM=%08x)\n", + (PRUint32) env->CallStaticIntMethod(systemClass, hashCodeMID, + aJavaProxy), + xpcom_addr)); +#endif +} + +/** + * org.mozilla.xpcom.XPCOMJavaProxy.isSameXPCOMObject + */ +extern "C" NS_EXPORT jboolean JNICALL +JAVAPROXY_NATIVE(isSameXPCOMObject) (JNIEnv *env, jclass that, + jobject aProxy1, jobject aProxy2) +{ + void* xpcom_obj1; + nsresult rv = GetXPCOMInstFromProxy(env, aProxy1, &xpcom_obj1); + if (NS_SUCCEEDED(rv)) { + void* xpcom_obj2; + rv = GetXPCOMInstFromProxy(env, aProxy2, &xpcom_obj2); + if (NS_SUCCEEDED(rv)) { + JavaXPCOMInstance* inst1 = static_cast<JavaXPCOMInstance*>(xpcom_obj1); + JavaXPCOMInstance* inst2 = static_cast<JavaXPCOMInstance*>(xpcom_obj2); + if (inst1->GetInstance() == inst2->GetInstance()) { + return JNI_TRUE; + } + } + } + return JNI_FALSE; +} + +/** + * org.mozilla.xpcom.ProfileLock.release + */ +extern "C" NS_EXPORT void JNICALL +LOCKPROXY_NATIVE(release) (JNIEnv *env, jclass that, jlong aLockObject) +{ + // Need to release object on the main thread. + nsresult rv = NS_ERROR_FAILURE; +#ifdef VBOX + rv = NS_ProxyRelease(do_GetMainThreadQueue().get(), reinterpret_cast<nsISupports*>(aLockObject)); +#else + nsCOMPtr<nsIThread> thread = do_GetMainThread(); + if (thread) { + rv = NS_ProxyRelease(thread, reinterpret_cast<nsISupports*>(aLockObject)); + } +#endif + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to release using NS_ProxyRelease"); +} |