/* ***** 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 "nsEventQueueUtils.h" #include "nsProxyRelease.h" static nsID nullID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; 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 iim = XPTI_GetInterfaceInfoManager(); NS_ASSERTION(iim != nsnull, "Failed to get InterfaceInfoManager"); if (!iim) return NS_ERROR_FAILURE; // Get interface info for given IID nsCOMPtr 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 = NS_STATIC_CAST(PRUint8*, aArray)[aIndex]; break; case nsXPTType::T_I16: case nsXPTType::T_U16: aResult->val.u16 = NS_STATIC_CAST(PRUint16*, aArray)[aIndex]; break; case nsXPTType::T_I32: case nsXPTType::T_U32: aResult->val.u32 = NS_STATIC_CAST(PRUint32*, aArray)[aIndex]; break; case nsXPTType::T_I64: case nsXPTType::T_U64: aResult->val.u64 = NS_STATIC_CAST(PRUint64*, aArray)[aIndex]; break; case nsXPTType::T_FLOAT: aResult->val.f = NS_STATIC_CAST(float*, aArray)[aIndex]; break; case nsXPTType::T_DOUBLE: aResult->val.d = NS_STATIC_CAST(double*, aArray)[aIndex]; break; case nsXPTType::T_BOOL: aResult->val.b = NS_STATIC_CAST(PRBool*, aArray)[aIndex]; break; case nsXPTType::T_CHAR: aResult->val.c = NS_STATIC_CAST(char*, aArray)[aIndex]; break; case nsXPTType::T_WCHAR: aResult->val.wc = NS_STATIC_CAST(PRUnichar*, aArray)[aIndex]; break; case nsXPTType::T_CHAR_STR: aResult->val.p = NS_STATIC_CAST(char**, aArray)[aIndex]; break; case nsXPTType::T_WCHAR_STR: aResult->val.p = NS_STATIC_CAST(PRUnichar**, aArray)[aIndex]; break; case nsXPTType::T_IID: aResult->val.p = NS_STATIC_CAST(nsID**, aArray)[aIndex]; break; case nsXPTType::T_INTERFACE: case nsXPTType::T_INTERFACE_IS: aResult->val.p = NS_STATIC_CAST(nsISupports**, aArray)[aIndex]; break; case nsXPTType::T_ASTRING: case nsXPTType::T_DOMSTRING: aResult->val.p = NS_STATIC_CAST(nsString**, aArray)[aIndex]; break; case nsXPTType::T_UTF8STRING: case nsXPTType::T_CSTRING: aResult->val.p = NS_STATIC_CAST(nsCString**, aArray)[aIndex]; break; case nsXPTType::T_VOID: aResult->val.p = NS_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' NS_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) NS_STATIC_CAST(PRInt16*, aVariant.val.p)[aIndex] = value; else NS_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) NS_STATIC_CAST(PRInt32*, aVariant.val.p)[aIndex] = value; else NS_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) NS_STATIC_CAST(PRInt64*, aVariant.val.p)[aIndex] = value; else NS_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' NS_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 = NS_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 = NS_STATIC_CAST(PRUint64, value); aVariant.ptr = &aVariant.val; } else { aVariant.ptr = nsnull; } aVariant.SetPtrIsData(); } else { // 'array' if (aType == nsXPTType::T_DOUBLE) NS_STATIC_CAST(double*, aVariant.val.p)[aIndex] = value; else NS_STATIC_CAST(PRUint64*, aVariant.val.p)[aIndex] = NS_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' NS_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' NS_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' NS_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 = NS_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 = NS_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 = NS_STATIC_CAST(char*, buf); NS_STATIC_CAST(char**, aVariant.val.p)[aIndex] = str; } else { PRUnichar* str = NS_STATIC_CAST(PRUnichar*, buf); NS_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) { 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' NS_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); } nsISupports* 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 = GetNewOrUsedXPCOMObject(env, java_obj, iid, &xpcom_obj); if (NS_FAILED(rv)) break; // If the function expects a weak reference, then we need to // create it here. if (isWeakRef) { nsCOMPtr supportsweak = do_QueryInterface(xpcom_obj); if (supportsweak) { nsWeakPtr weakref; supportsweak->GetWeakReference(getter_AddRefs(weakref)); NS_RELEASE(xpcom_obj); xpcom_obj = weakref; NS_ADDREF(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' NS_STATIC_CAST(nsISupports**, 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 = NS_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 = NS_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 = NS_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 = NS_REINTERPRET_CAST(void*, value); aVariant.ptr = &aVariant.val; } else { aVariant.ptr = nsnull; } aVariant.SetPtrIsData(); } else { // 'array' NS_STATIC_CAST(void**, aVariant.val.p)[aIndex] = NS_REINTERPRET_CAST(void*, value); } } break; } case nsXPTType::T_ARRAY: { jobject sourceArray = nsnull; if (!aIsOut) { // 'in' sourceArray = aParam; } else if (aParam) { // 'inout' jobjectArray array = NS_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 = NS_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 = NS_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 = iid->ToString(); if (iid_str) { str = env->NewStringUTF(iid_str); } if (!iid_str || !str) { rv = NS_ERROR_OUT_OF_MEMORY; break; } PR_Free(iid_str); } 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 = NS_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 = GetNewOrUsedJavaObject(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 = NS_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 = NS_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 = NS_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 , 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 , then maybe it is an // 'attribute'. rv = QueryAttributeInfo(aIInfo, methodName, PR_TRUE, aMethodIndex, aMethodInfo); return rv; } /** * 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 = NS_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 = XPTC_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; if (!paramInfo.IsRetval()) { jobject 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"); 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 CreateJavaProxy(JNIEnv* env, nsISupports* aXPCOMObject, const nsIID& aIID, jobject aObjectLoader, jobject* aResult) { NS_PRECONDITION(aResult != nsnull, "null ptr"); if (!aResult) return NS_ERROR_NULL_POINTER; nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); NS_ASSERTION(iim != nsnull, "Failed to get InterfaceInfoManager"); if (!iim) return NS_ERROR_FAILURE; // Get interface info for class nsCOMPtr info; nsresult rv = iim->GetInfoForIID(&aIID, getter_AddRefs(info)); if (NS_FAILED(rv)) return rv; // Wrap XPCOM object (addrefs aXPCOMObject) JavaXPCOMInstance* inst = new JavaXPCOMInstance(aXPCOMObject, 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, NS_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) aXPCOMObject, iid_str)); PR_Free(iid_str); #endif // Associate XPCOM object with Java proxy rv = gNativeToJavaProxyMap->Add(env, aXPCOMObject, 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 = NS_REINTERPRET_CAST(void*, xpcom_obj); #ifdef DEBUG_JAVAXPCOM JavaXPCOMInstance* inst = NS_STATIC_CAST(JavaXPCOMInstance*, *aResult); nsIID* iid; inst->InterfaceInfo()->GetInterfaceIID(&iid); char* iid_str = iid->ToString(); LOG(("< GetXPCOMInstFromProxy (Java=%08x | XPCOM=%08x | IID=%s)\n", (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 = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj); #ifdef DEBUG_JAVAXPCOM xpcom_addr = NS_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 = NS_STATIC_CAST(JavaXPCOMInstance*, xpcom_obj1); JavaXPCOMInstance* inst2 = NS_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. nsCOMPtr eventQ; nsresult rv = NS_GetMainEventQ(getter_AddRefs(eventQ)); if (NS_SUCCEEDED(rv)) { rv = NS_ProxyRelease(eventQ, NS_REINTERPRET_CAST(nsISupports*, aLockObject)); } NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to release using NS_ProxyRelease"); }