diff --git a/extensions/java/xpcom/src/nsJavaXPTCStub.cpp b/extensions/java/xpcom/src/nsJavaXPTCStub.cpp new file mode 100644 index 000000000000..2640861ef96d --- /dev/null +++ b/extensions/java/xpcom/src/nsJavaXPTCStub.cpp @@ -0,0 +1,1111 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Java XPCOM Bindings. + * + * The Initial Developer of the Original Code is + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * Javier Pedemonte (jhpedemonte@gmail.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsJavaXPTCStub.h" +#include "nsJavaWrapper.h" +#include "nsJavaXPCOMBindingUtils.h" +#include "prmem.h" +#include "nsIInterfaceInfoManager.h" +#include "nsString.h" +#include "nsString.h" +#include "nsCRT.h" + + +nsJavaXPTCStub::nsJavaXPTCStub(JNIEnv* aJavaEnv, jobject aJavaObject, + nsIInterfaceInfo *aIInfo) + : mJavaEnv(aJavaEnv) + , mIInfo(aIInfo) + , mMaster(nsnull) +{ + mJavaObject = aJavaEnv->NewGlobalRef(aJavaObject); +} + +nsJavaXPTCStub::~nsJavaXPTCStub() +{ +#ifdef DEBUG + jboolean isCopy; + jclass clazz = mJavaEnv->GetObjectClass(mJavaObject); + jstring name = (jstring) mJavaEnv->CallObjectMethod(clazz, getNameMID); + const char* javaObjectName = mJavaEnv->GetStringUTFChars(name, &isCopy); + fprintf(stderr, "*** ~nsJavaXPTCStub(java_obj=%s)\n", javaObjectName); + if (isCopy) + mJavaEnv->ReleaseStringUTFChars(name, javaObjectName); +#endif + if (mMaster) + { + mMaster->mChildren.RemoveElement(this); + NS_RELEASE(mMaster); + } + + mJavaEnv->DeleteGlobalRef(mJavaObject); +} + +NS_IMPL_ADDREF(nsJavaXPTCStub) +NS_IMPL_RELEASE(nsJavaXPTCStub) + +NS_IMETHODIMP +nsJavaXPTCStub::QueryInterface(const nsID &aIID, void **aInstancePtr) +{ + LOG("JavaStub::QueryInterface()\n"); + nsJavaXPTCStub *master = mMaster ? mMaster : this; + + // always return the master stub for nsISupports + if (aIID.Equals(NS_GET_IID(nsISupports))) + { + *aInstancePtr = master; + NS_ADDREF(master); + return NS_OK; + } + + // does any existing stub support the requested IID? + nsJavaXPTCStub *stub = master->FindStubSupportingIID(aIID); + if (stub) + { + *aInstancePtr = stub; + NS_ADDREF(stub); + return NS_OK; + } + + // Query Java object + LOG("\tCalling Java object queryInterface\n"); + jclass clazz = mJavaEnv->GetObjectClass(mJavaObject); + char* sig = "(Ljava/lang/String;)Lorg/mozilla/xpcom/nsISupports;"; + jmethodID qiMID = mJavaEnv->GetMethodID(clazz, "queryInterface", sig); + NS_ASSERTION(qiMID, "Failed to get queryInterface method ID"); + + char* iid_str = aIID.ToString(); + jstring iid_jstr = mJavaEnv->NewStringUTF(iid_str); + PR_Free(iid_str); + jobject obj = mJavaEnv->CallObjectMethod(mJavaObject, qiMID, iid_jstr); + + // XXX Should we check here if the jobject we got back is the same as + // mJavaObject? Or to see if it already has a nsJavaXPTCStub associated + // with it? + + if (obj) { + // Get interface info for new java object + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + nsCOMPtr iinfo; + iim->GetInfoForIID(&aIID, getter_AddRefs(iinfo)); + + nsJavaXPTCStub* stub = new nsJavaXPTCStub(mJavaEnv, obj, iinfo); + + // add stub to the master's list of children, so we can preserve + // symmetry in future QI calls. each child owns his master, but + // the master only keeps weak references to its children. the + // children are responsible for removing themselves as they die. + NS_ADDREF(master); + stub->mMaster = master; + master->mChildren.AppendElement(stub); + + *aInstancePtr = stub; + NS_ADDREF(stub); + return NS_OK; + } + + *aInstancePtr = nsnull; + return NS_OK; +} + +PRBool +nsJavaXPTCStub::SupportsIID(const nsID &iid) +{ + PRBool match; + nsCOMPtr iter = mIInfo; + do + { + if (NS_SUCCEEDED(iter->IsIID(&iid, &match)) && match) + return PR_TRUE; + + nsCOMPtr parent; + iter->GetParent(getter_AddRefs(parent)); + iter = parent; + } + while (iter != nsnull); + + return PR_FALSE; +} + +nsJavaXPTCStub * +nsJavaXPTCStub::FindStubSupportingIID(const nsID &iid) +{ + NS_ASSERTION(mMaster == nsnull, "this is not a master stub"); + + if (SupportsIID(iid)) + return this; + + for (PRInt32 i = 0; i < mChildren.Count(); i++) + { + nsJavaXPTCStub *child = (nsJavaXPTCStub *) mChildren[i]; + if (child->SupportsIID(iid)) + return child; + } + return nsnull; +} + +NS_IMETHODIMP +nsJavaXPTCStub::GetInterfaceInfo(nsIInterfaceInfo **aInfo) +{ + NS_ADDREF(*aInfo = mIInfo); + return NS_OK; +} + +NS_IMETHODIMP +nsJavaXPTCStub::CallMethod(PRUint16 aMethodIndex, + const nsXPTMethodInfo *aMethodInfo, + nsXPTCMiniVariant *aParams) +{ + LOG("nsJavaXPTCStub::CallMethod [%s]\n", aMethodInfo->GetName()); + + nsresult rv = NS_OK; + + nsCAutoString methodSig("("); + + // Create jvalue array to hold Java params + PRUint8 paramCount = aMethodInfo->GetParamCount(); + jvalue* java_params = nsnull; + const nsXPTParamInfo* retvalInfo = nsnull; + if (paramCount) { + java_params = new jvalue[paramCount]; + + for (PRUint8 i = 0; i < paramCount && NS_SUCCEEDED(rv); i++) + { + const nsXPTParamInfo ¶mInfo = aMethodInfo->GetParam(i); + if (!paramInfo.IsRetval()) { + rv = SetupJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, + aParams[i], java_params[i], methodSig); + } else { + retvalInfo = ¶mInfo; + } + } + NS_ASSERTION(NS_SUCCEEDED(rv), "SetupJavaParams failed"); + } + + // Finish method signature + if (NS_SUCCEEDED(rv)) { + methodSig.Append(")"); + if (retvalInfo) { + nsCAutoString retvalSig; + rv = GetRetvalSig(retvalInfo, retvalSig); + methodSig.Append(retvalSig); + } else { + methodSig.Append("V"); + } + NS_ASSERTION(NS_SUCCEEDED(rv), "GetRetvalSig failed"); + } + + // Get Java method to call + jmethodID mid = nsnull; + if (NS_SUCCEEDED(rv)) { + nsCAutoString methodName(aMethodInfo->GetName()); + methodName.SetCharAt(tolower(methodName[0]), 0); + + jclass clazz = mJavaEnv->GetObjectClass(mJavaObject); + if (clazz) + mid = mJavaEnv->GetMethodID(clazz, methodName.get(), methodSig.get()); + NS_ASSERTION(mid, "Failed to get requested method for Java object"); + } + + // Call method + jvalue retval; + if (mid) { + if (!retvalInfo) { + mJavaEnv->CallVoidMethodA(mJavaObject, mid, java_params); + } else { + switch (retvalInfo->GetType().TagPart()) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + retval.b = mJavaEnv->CallByteMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + retval.s = mJavaEnv->CallShortMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + retval.i = mJavaEnv->CallIntMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_FLOAT: + retval.f = mJavaEnv->CallFloatMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_DOUBLE: + retval.d = mJavaEnv->CallDoubleMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_BOOL: + retval.z = mJavaEnv->CallBooleanMethodA(mJavaObject, mid, java_params); + break; + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + retval.c = mJavaEnv->CallCharMethodA(mJavaObject, mid, java_params); + 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: + retval.l = mJavaEnv->CallObjectMethodA(mJavaObject, mid, java_params); + break; + + default: + NS_WARNING("Unhandled retval type"); + break; + } + } + + // Check for exception from called Java function + jthrowable exp = mJavaEnv->ExceptionOccurred(); + if (exp) { +#ifdef DEBUG + mJavaEnv->ExceptionDescribe(); +#endif + mJavaEnv->ExceptionClear(); + rv = NS_ERROR_FAILURE; + } + } + + // Handle any inout params + if (NS_SUCCEEDED(rv)) { + for (PRUint8 i = 0; i < paramCount; i++) + { + const nsXPTParamInfo ¶mInfo = aMethodInfo->GetParam(i); + if (!paramInfo.IsRetval()) { + rv = FinalizeJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, + aParams[i], java_params[i]); + } else { + rv = FinalizeJavaParams(paramInfo, aMethodInfo, aMethodIndex, aParams, + aParams[i], retval); + } + } + NS_ASSERTION(NS_SUCCEEDED(rv), "FinalizeJavaParams/SetXPCOMRetval failed"); + } + + if (java_params) + delete [] java_params; + + return rv; +} + +nsresult +nsJavaXPTCStub::SetupJavaParams(const nsXPTParamInfo &aParamInfo, + const nsXPTMethodInfo* aMethodInfo, + PRUint16 aMethodIndex, + nsXPTCMiniVariant* aDispatchParams, + nsXPTCMiniVariant &aVariant, jvalue &aJValue, + nsACString &aMethodSig) +{ + nsresult rv; + const nsXPTType &type = aParamInfo.GetType(); + PRUint8 tag = type.TagPart(); + switch (tag) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + { + if (!aParamInfo.IsOut()) { + aJValue.b = aVariant.val.u8; + aMethodSig.Append("B"); + } else { + jbyteArray array = mJavaEnv->NewByteArray(1); + mJavaEnv->SetByteArrayRegion(array, 0, 1, (jbyte*) &(aVariant.val.u8)); + aJValue.l = array; + aMethodSig.Append("[B"); + } + } + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + { + if (!aParamInfo.IsOut()) { + aJValue.s = aVariant.val.u16; + aMethodSig.Append("S"); + } else { + jshortArray array = mJavaEnv->NewShortArray(1); + mJavaEnv->SetShortArrayRegion(array, 0, 1, + (jshort*) &(aVariant.val.u16)); + aJValue.l = array; + aMethodSig.Append("[S"); + } + } + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + { + if (!aParamInfo.IsOut()) { + aJValue.i = aVariant.val.u32; + aMethodSig.Append("I"); + } else { + jintArray array = mJavaEnv->NewIntArray(1); + mJavaEnv->SetIntArrayRegion(array, 0, 1, (jint*) &(aVariant.val.u32)); + aJValue.l = array; + aMethodSig.Append("[I"); + } + } + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + { + if (!aParamInfo.IsOut()) { + aJValue.j = aVariant.val.u64; + aMethodSig.Append("J"); + } else { + jlongArray array = mJavaEnv->NewLongArray(1); + mJavaEnv->SetLongArrayRegion(array, 0, 1, (jlong*) &(aVariant.val.u64)); + aJValue.l = array; + aMethodSig.Append("[J"); + } + } + break; + + case nsXPTType::T_FLOAT: + { + if (!aParamInfo.IsOut()) { + aJValue.f = aVariant.val.f; + aMethodSig.Append("F"); + } else { + jfloatArray array = mJavaEnv->NewFloatArray(1); + mJavaEnv->SetFloatArrayRegion(array, 0, 1, (jfloat*) &(aVariant.val.f)); + aJValue.l = array; + aMethodSig.Append("[F"); + } + } + break; + + case nsXPTType::T_DOUBLE: + { + if (!aParamInfo.IsOut()) { + aJValue.d = aVariant.val.d; + aMethodSig.Append("D"); + } else { + jdoubleArray array = mJavaEnv->NewDoubleArray(1); + mJavaEnv->SetDoubleArrayRegion(array, 0, 1, + (jdouble*) &(aVariant.val.d)); + aJValue.l = array; + aMethodSig.Append("[D"); + } + } + break; + + case nsXPTType::T_BOOL: + { + if (!aParamInfo.IsOut()) { + aJValue.z = aVariant.val.b; + aMethodSig.Append("Z"); + } else { + jbooleanArray array = mJavaEnv->NewBooleanArray(1); + mJavaEnv->SetBooleanArrayRegion(array, 0, 1, + (jboolean*) &(aVariant.val.b)); + aJValue.l = array; + aMethodSig.Append("[Z"); + } + } + break; + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + { + if (!aParamInfo.IsOut()) { + aJValue.c = aVariant.val.wc; + aMethodSig.Append("C"); + } else { + jcharArray array = mJavaEnv->NewCharArray(1); + mJavaEnv->SetCharArrayRegion(array, 0, 1, (jchar*) &(aVariant.val.wc)); + aJValue.l = array; + aMethodSig.Append("[C"); + } + } + break; + + case nsXPTType::T_CHAR_STR: + case nsXPTType::T_WCHAR_STR: + { + jobject str; + if (aVariant.val.p) { + if (tag == nsXPTType::T_CHAR_STR) { + str = mJavaEnv->NewStringUTF((const char*) aVariant.val.p); + } else { + const PRUnichar* buf = (const PRUnichar*) aVariant.val.p; + str = mJavaEnv->NewString(buf, nsCRT::strlen(buf)); + } + } else { + str = nsnull; + } + + if (!aParamInfo.IsOut()) { + aJValue.l = str; + aMethodSig.Append("Ljava/lang/String;"); + } else { + aJValue.l = mJavaEnv->NewObjectArray(1, stringClass, str); + aMethodSig.Append("[Ljava/lang/String;"); + } + } + break; + + case nsXPTType::T_IID: + { + jstring str = nsnull; + nsID* iid = (nsID*) aVariant.val.p; + if (iid) { + char* iid_str = iid->ToString(); + str = mJavaEnv->NewStringUTF(iid_str); + PR_Free(iid_str); + } + + if (!aParamInfo.IsOut()) { + aJValue.l = str; + aMethodSig.Append("Ljava/lang/String;"); + } else { + aJValue.l = mJavaEnv->NewObjectArray(1, stringClass, str); + aMethodSig.Append("[Ljava/lang/String;"); + } + } + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + jobject java_stub; + if (aVariant.val.p) { + java_stub = GetMatchingJavaObject(aVariant.val.p); + + if (java_stub == nsnull) { + // wrap xpcom instance + nsID iid; + JavaXPCOMInstance* inst; + rv = GetIIDForMethodParam(mIInfo, aMethodInfo, aParamInfo, + aMethodIndex, aDispatchParams, PR_FALSE, iid); + if (NS_FAILED(rv)) + return rv; + + inst = CreateJavaXPCOMInstance((nsISupports*) aVariant.val.p, &iid); + + if (inst) { + // create java stub + char* iface_name; + inst->InterfaceInfo()->GetName(&iface_name); + java_stub = CreateJavaWrapper(mJavaEnv, iface_name); + + if (java_stub) { + // Associate XPCOM object w/ Java stub + AddJavaXPCOMBinding(mJavaEnv, java_stub, inst); + } + } + } + } else { + java_stub = nsnull; + } + + if (!aParamInfo.IsOut()) { + aJValue.l = java_stub; + aMethodSig.Append("Lorg/mozilla/xpcom/nsISupports;"); + } else { + aJValue.l = mJavaEnv->NewObjectArray(1, nsISupportsClass, java_stub); + aMethodSig.Append("[Lorg/mozilla/xpcom/nsISupports;"); + } + } + break; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + jstring jstr = nsnull; + nsString* str = (nsString*) aVariant.val.p; + if (str) { + jstr = mJavaEnv->NewString(str->get(), str->Length()); + } + + if (!aParamInfo.IsOut()) { + aJValue.l = jstr; + aMethodSig.Append("Ljava/lang/String;"); + } else { + aJValue.l = mJavaEnv->NewObjectArray(1, stringClass, jstr); + aMethodSig.Append("[Ljava/lang/String;"); + } + } + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + jstring jstr = nsnull; + nsCString* str = (nsCString*) aVariant.val.p; + if (str) { + jstr = mJavaEnv->NewStringUTF(str->get()); + } + + if (!aParamInfo.IsOut()) { + aJValue.l = jstr; + aMethodSig.Append("Ljava/lang/String;"); + } else { + aJValue.l = mJavaEnv->NewObjectArray(1, stringClass, jstr); + aMethodSig.Append("[Ljava/lang/String;"); + } + } + break; + + // XXX How should this be handled? + case nsXPTType::T_VOID: + NS_WARNING("Unhandled T_VOID"); + return NS_ERROR_NOT_IMPLEMENTED; + break; + + case nsXPTType::T_ARRAY: + NS_WARNING("array types are not yet supported"); + return NS_ERROR_NOT_IMPLEMENTED; + break; + + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + default: + NS_WARNING("unexpected parameter type"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +nsresult +nsJavaXPTCStub::GetRetvalSig(const nsXPTParamInfo* aParamInfo, + nsACString &aRetvalSig) +{ + const nsXPTType &type = aParamInfo->GetType(); + PRUint8 tag = type.TagPart(); + switch (tag) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + aRetvalSig.Append("B"); + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + aRetvalSig.Append("S"); + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + aRetvalSig.Append("I"); + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + aRetvalSig.Append("J"); + break; + + case nsXPTType::T_FLOAT: + aRetvalSig.Append("F"); + break; + + case nsXPTType::T_DOUBLE: + aRetvalSig.Append("D"); + break; + + case nsXPTType::T_BOOL: + aRetvalSig.Append("Z"); + break; + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + aRetvalSig.Append("C"); + 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: + aRetvalSig.Append("Ljava/lang/String;"); + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + aRetvalSig.Append("Lorg/mozilla/xpcom/nsISupports;"); + break; + + // XXX How should this be handled? + case nsXPTType::T_VOID: + NS_WARNING("Unhandled T_VOID"); + return NS_ERROR_NOT_IMPLEMENTED; + break; + + case nsXPTType::T_ARRAY: + NS_WARNING("array types are not yet supported"); + return NS_ERROR_NOT_IMPLEMENTED; + break; + + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + default: + NS_WARNING("unexpected parameter type"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +nsresult +nsJavaXPTCStub::FinalizeJavaParams(const nsXPTParamInfo &aParamInfo, + const nsXPTMethodInfo* aMethodInfo, + PRUint16 aMethodIndex, + nsXPTCMiniVariant* aDispatchParams, + nsXPTCMiniVariant &aVariant, jvalue &aJValue) +{ + nsresult rv; + const nsXPTType &type = aParamInfo.GetType(); + + switch (type.TagPart()) + { + case nsXPTType::T_I8: + case nsXPTType::T_U8: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((PRUint8 *) aVariant.val.p) = aJValue.b; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jbyte* array = mJavaEnv->GetByteArrayElements((jbyteArray) aJValue.l, + &isCopy); + *((PRUint8 *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseByteArrayElements((jbyteArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_I16: + case nsXPTType::T_U16: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((PRUint16 *) aVariant.val.p) = aJValue.s; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jshort* array = + mJavaEnv->GetShortArrayElements((jshortArray) aJValue.l, + &isCopy); + *((PRUint16 *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseShortArrayElements((jshortArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_I32: + case nsXPTType::T_U32: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((PRUint32 *) aVariant.val.p) = aJValue.i; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jint* array = mJavaEnv->GetIntArrayElements((jintArray) aJValue.l, + &isCopy); + *((PRUint32 *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseIntArrayElements((jintArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_I64: + case nsXPTType::T_U64: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((PRUint64 *) aVariant.val.p) = aJValue.j; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jlong* array = mJavaEnv->GetLongArrayElements((jlongArray) aJValue.l, + &isCopy); + *((PRUint64 *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseLongArrayElements((jlongArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_FLOAT: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((float *) aVariant.val.p) = aJValue.f; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jfloat* array = + mJavaEnv->GetFloatArrayElements((jfloatArray) aJValue.l, + &isCopy); + *((float *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseFloatArrayElements((jfloatArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_DOUBLE: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((double *) aVariant.val.p) = aJValue.d; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jdouble* array = + mJavaEnv->GetDoubleArrayElements((jdoubleArray) aJValue.l, + &isCopy); + *((double *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseDoubleArrayElements((jdoubleArray) aJValue.l, + array, JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_BOOL: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + *((PRBool *) aVariant.val.p) = aJValue.z; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jboolean* array = + mJavaEnv->GetBooleanArrayElements((jbooleanArray) aJValue.l, + &isCopy); + *((PRBool *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseBooleanArrayElements((jbooleanArray) aJValue.l, + array, JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_CHAR: + case nsXPTType::T_WCHAR: + { + if (aJValue.l) { + if (aParamInfo.IsRetval()) { + if (type.TagPart() == nsXPTType::T_CHAR) + *((char *) aVariant.val.p) = aJValue.c; + else + *((PRUnichar *) aVariant.val.p) = aJValue.c; + } else if (aParamInfo.IsOut()) { + jboolean isCopy = PR_FALSE; + jchar* array = mJavaEnv->GetCharArrayElements((jcharArray) aJValue.l, + &isCopy); + if (type.TagPart() == nsXPTType::T_CHAR) + *((char *) aVariant.val.p) = array[0]; + else + *((PRUnichar *) aVariant.val.p) = array[0]; + if (isCopy) { + mJavaEnv->ReleaseCharArrayElements((jcharArray) aJValue.l, array, + JNI_ABORT); + } + } + } + } + break; + + case nsXPTType::T_CHAR_STR: + { + // XXX not sure how this should be handled + if (aJValue.l && aParamInfo.IsOut()) { + jstring str = nsnull; + if (aParamInfo.IsRetval()) { + str = (jstring) aJValue.l; + } else if (aParamInfo.IsOut()) { + str = (jstring) + mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (str) { + jboolean isCopy; + const char* char_ptr = mJavaEnv->GetStringUTFChars(str, &isCopy); + *((char **) aVariant.val.p) = strdup(char_ptr); + if (isCopy) { + mJavaEnv->ReleaseStringUTFChars(str, char_ptr); + } + } + } + } + break; + + case nsXPTType::T_WCHAR_STR: + { + // XXX not sure how this should be handled + if (aJValue.l && aParamInfo.IsOut()) { + jstring str = nsnull; + if (aParamInfo.IsRetval()) { + str = (jstring) aJValue.l; + } else if (aParamInfo.IsOut()) { + str = (jstring) + mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (str) { + jboolean isCopy; + const jchar* wchar_ptr = mJavaEnv->GetStringChars(str, &isCopy); + + PRUint32 length = nsCRT::strlen(wchar_ptr); + PRUnichar* string = (PRUnichar*) malloc(length + 1); // add one for null + memcpy(string, wchar_ptr, length * sizeof(PRUnichar)); + string[length] = 0; + *((PRUnichar **) aVariant.val.p) = string; + + if (isCopy) { + mJavaEnv->ReleaseStringChars(str, wchar_ptr); + } + } + } + } + break; + + case nsXPTType::T_IID: + { + if (aJValue.l && aParamInfo.IsOut()) { + jstring str = nsnull; + if (aParamInfo.IsRetval()) { + str = (jstring) aJValue.l; + } else { + str = (jstring) + mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (str) { + jboolean isCopy; + const char* char_ptr = mJavaEnv->GetStringUTFChars(str, &isCopy); + + if (aVariant.val.p) { + // If not null, then we just put in the new value. + nsID* iid = *((nsID**) aVariant.val.p); + iid->Parse(char_ptr); + } else { + // If the argument that was passed in was null, then we need to + // create a new nsID. + nsID* iid = new nsID; + iid->Parse(char_ptr); + *((nsID **) aVariant.val.p) = iid; + } + + if (isCopy) { + mJavaEnv->ReleaseStringUTFChars(str, char_ptr); + } + } + } + } + break; + + case nsXPTType::T_INTERFACE: + case nsXPTType::T_INTERFACE_IS: + { + if (aJValue.l && aParamInfo.IsOut()) { + jobject java_obj = nsnull; + if (aParamInfo.IsRetval()) { + java_obj = aJValue.l; + } else { + java_obj = mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (java_obj) { + void* inst = GetMatchingXPCOMObject(mJavaEnv, java_obj); + if (inst == nsnull) { + // Get IID for this param + nsID iid; + rv = GetIIDForMethodParam(mIInfo, aMethodInfo, aParamInfo, + aMethodIndex, aDispatchParams, PR_TRUE, + iid); + if (NS_FAILED(rv)) + return rv; + + // Get interface info for class + nsCOMPtr iim = XPTI_GetInterfaceInfoManager(); + nsCOMPtr iinfo; + iim->GetInfoForIID(&iid, getter_AddRefs(iinfo)); + + // Create XPCOM stub + nsJavaXPTCStub* xpcomStub = new nsJavaXPTCStub(mJavaEnv, java_obj, + iinfo); + NS_ADDREF(xpcomStub); + inst = SetAsXPTCStub(xpcomStub); + AddJavaXPCOMBinding(mJavaEnv, java_obj, inst); + } +#if 1 + // XXX This code is here to make sure that the nsJavaXPTCStub that is + // used has the correct (asked for) interface info. For example, in + // the Java code, the nsIWeakReference functions return the current + // Java object ('this'); so when we call GetMatchingXPCOMObject on + // that Java object, we get back a nsJavaXPTCStub that has the interface + // info for something other than nsIWeakReference. This will cause + // problems later when we try to call one of the nsIWeakReference + // functions on that Java object, but the nsJavaXPTCStub object doesn't + // know anything about nsIWeakReference. + else { + if (IsXPTCStub(inst)) { + // Get IID for this param + nsID iid; + rv = GetIIDForMethodParam(mIInfo, aMethodInfo, aParamInfo, + aMethodIndex, aDispatchParams, PR_TRUE, + iid); + if (NS_FAILED(rv)) + return rv; + + void* temp; + rv = GetXPTCStubAddr(inst)->QueryInterface(iid, &temp); + if (NS_SUCCEEDED(rv) && temp != GetXPTCStubAddr(inst)) + inst = SetAsXPTCStub((nsJavaXPTCStub*) temp); + } + } +#endif + + if (IsXPTCStub(inst)) + *((void **) aVariant.val.p) = (void*) GetXPTCStubAddr(inst); + else { + JavaXPCOMInstance* xpcomInst = (JavaXPCOMInstance*) inst; + *((void **) aVariant.val.p) = (void*) xpcomInst->GetInstance(); + } + } + } + } + break; + + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + { + if (aJValue.l && aParamInfo.IsOut()) { + jstring str = nsnull; + if (aParamInfo.IsRetval()) { + str = (jstring) aJValue.l; + } else { + str = (jstring) + mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (str) { + jboolean isCopy; + const jchar* wchar_ptr = mJavaEnv->GetStringChars(str, &isCopy); + + if (aVariant.val.p) { + ((nsString*) aVariant.val.p)->Assign(wchar_ptr); + } else { + nsString* embedStr = new nsString(wchar_ptr); + aVariant.val.p = embedStr; + } + + if (isCopy) { + mJavaEnv->ReleaseStringChars(str, wchar_ptr); + } + } + } + } + break; + + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + { + if (aJValue.l && aParamInfo.IsOut()) { + jstring str = nsnull; + if (aParamInfo.IsRetval()) { + str = (jstring) aJValue.l; + } else { + str = (jstring) + mJavaEnv->GetObjectArrayElement((jobjectArray) aJValue.l, 0); + } + + if (str) { + jboolean isCopy; + const char* char_ptr = mJavaEnv->GetStringUTFChars(str, &isCopy); + + if (aVariant.val.p) { + ((nsCString*) aVariant.val.p)->Assign(char_ptr); + } else { + nsCString* embedStr = new nsCString(char_ptr); + aVariant.val.p = embedStr; + } + + if (isCopy) { + mJavaEnv->ReleaseStringUTFChars(str, char_ptr); + } + } + } + } + break; + + case nsXPTType::T_VOID: + NS_WARNING("Unhandled T_VOID"); + return NS_ERROR_NOT_IMPLEMENTED; + break; + + default: + NS_WARNING("unexpected parameter type"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} +