/* * 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 mozilla.org code. * * The Initial Developer of the Original Code is Frank * Mitchell. Portions created by Frank Mitchell are * Copyright (C) 1999 Frank Mitchell. All * Rights Reserved. * * Contributor(s): * Frank Mitchell (frank.mitchell@sun.com) */ #include #include #include #include #include "nscore.h" #include "nsIFactory.h" #include "nsIComponentManager.h" #include "nsIInterfaceInfo.h" #include "nsIInterfaceInfoManager.h" #include "xptinfo.h" #include "xptcall.h" #include "xpt_struct.h" #include "nsIAllocator.h" #include "nsSpecialSystemDirectory.h" #include "xpjava.h" #ifdef XP_PC #define XPCOM_DLL "xpcom32.dll" #define SAMPLE_DLL "sampl32.dll" #else #ifdef XP_MAC #define XPCOM_DLL "XPCOM_DLL" #define SAMPLE_DLL "SAMPL_DLL" #else #define XPCOM_DLL "libxpcom.so" #define SAMPLE_DLL "libsample.so" #endif #endif /* Global references to frequently used classes and objects */ static jclass classString; static jclass classByte; static jclass classShort; static jclass classInteger; static jclass classLong; static jclass classFloat; static jclass classDouble; static jclass classBoolean; static jclass classCharacter; /* Cached field and method IDs */ static jfieldID Byte_value_ID; static jfieldID Short_value_ID; static jfieldID Integer_value_ID; static jfieldID Long_value_ID; static jfieldID Float_value_ID; static jfieldID Double_value_ID; static jfieldID Boolean_value_ID; static jfieldID Character_value_ID; static jmethodID Byte_init_ID; static jmethodID Short_init_ID; static jmethodID Integer_init_ID; static jmethodID Long_init_ID; static jmethodID Float_init_ID; static jmethodID Double_init_ID; static jmethodID Boolean_init_ID; static jmethodID Character_init_ID; /* Global references to frequently used XPCOM objects */ static nsIInterfaceInfoManager *interfaceInfoManager; /* Flag to indicate caches filled */ static jboolean cache_initialized = JNI_FALSE; /* ---------- GENERAL API FUNCTIONS ------------ */ nsresult xpj_InitXPCOM() { nsresult res; #ifdef DEBUG_frankm cerr << "Initializing XPCOM" << endl; #endif // Autoregistration magic. Boogeda boogeda. res = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup, nsnull); #ifdef DEBUG_frankm cerr << "XPCOM Initialized" << endl; #endif return res; } nsresult xpj_GetMethodInfoByName(const nsID *iid, const char *methodname, PRBool wantSetter, const nsXPTMethodInfo **miptr, int *_retval) { nsresult res; // Get info nsIInterfaceInfo *info = nsnull; res = interfaceInfoManager->GetInfoForIID(iid, &info); if (NS_FAILED(res)) { cerr << "Failed to find info for " << iid->ToString() << endl; return res; } // Find info for command uint16 methodcount; info->GetMethodCount(&methodcount); int i; for (i = 0; i < methodcount; i++) { const nsXPTMethodInfo *mi; info->GetMethodInfo(i, &mi); // PENDING(frankm): match against name, get/set, *AND* param types if (strcmp(methodname, mi->GetName()) == 0) { PRBool setter = mi->IsSetter(); PRBool getter = mi->IsGetter(); if ((!getter && !setter) || (setter && wantSetter) || (getter && !wantSetter)) { *miptr = mi; *_retval = i; break; } } } if (i >= methodcount) { cerr << "Failed to find " << methodname << endl; *miptr = NULL; *_retval = -1; return NS_ERROR_FAILURE; } return res; } /* ---------- DISPATCH SUPPORT FUNCTIONS ------------ */ static int xpjd_BuildParamsForOffset(nsIInterfaceInfo *info, jint offset, jint arraysize, nsXPTCVariant **_retval) { const nsXPTMethodInfo *mi; nsresult res; // Find info for command uint16 methodcount; info->GetMethodCount(&methodcount); if (offset < methodcount) { info->GetMethodInfo(offset, &mi); } else { cerr << "Offset " << offset << " should be < " << methodcount << endl; return -1; } if (NS_FAILED(res)) { return -1; } // Make sure the array is large enough, and alloc more if // necessary. int paramcount = mi->GetParamCount(); if (paramcount > arraysize || *_retval == NULL) { *_retval = (nsXPTCVariant *) nsAllocator::Alloc(sizeof(nsXPTCVariant) * paramcount); } // Set flags, pointers, and types for array elements nsXPTCVariant *current = *_retval; for (int i = 0; i < paramcount; i++) { nsXPTParamInfo param = mi->GetParam(i); nsXPTType type = param.GetType(); current->type = type.TagPart(); current->flags = 0; if (!param.IsOut()) { current->flags = 0; current->ptr = 0; } else { // PTR_IS_DATA: ptr points to 'real' data in val current->flags = nsXPTCVariant::PTR_IS_DATA; if (type.IsPointer()) { current->val.p = NULL; current->ptr = ¤t->val.p; // PENDING: does this routine also alloc memory? // Or do we treat pointers to basic types like "out" // parameters? // What about an out parameter that is a } else { // PENDING: Will this work on all platforms? current->ptr = ¤t->val; } } current++; } return paramcount; } static jboolean JObjectToVariant(JNIEnv *env, nsXPTCVariant *current, const jobject elem, REFNSIID iid) { jboolean result = JNI_FALSE; /* * Match: * T_BOOL : Boolean * T_I8: Byte * T_I16: Short * T_I32: Integer * T_I64: Long * -- we may want to support widening casts * T_U: T_I type * T_FLOAT: Float * T_DOUBLE: Double (or Float?) * T_CHAR: Character if value is an ASCII value * T_WCHAR: Character * T_CHAR_STRING: String as UTF-8 chars * T_WCHAR_STRING: String as Unicode chars * T_BSTR: String as UTF-8 chars * T_INTERFACE(_IS): XPCOM pointer for an ComObject, if nsID matches */ // Integer code assumes that current->val.i# == current->val.u# // for assignment purposes switch(current->type) { case nsXPTType::T_I8: case nsXPTType::T_U8: if (env->IsInstanceOf(elem, classByte)) { current->val.i8 = env->GetByteField(elem, Byte_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_I16: case nsXPTType::T_U16: if (env->IsInstanceOf(elem, classShort)) { current->val.i16 = env->GetShortField(elem, Short_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_I32: case nsXPTType::T_U32: if (env->IsInstanceOf(elem, classInteger)) { current->val.i32 = env->GetShortField(elem, Integer_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_I64: case nsXPTType::T_U64: if (env->IsInstanceOf(elem, classLong)) { current->val.i64 = env->GetShortField(elem, Long_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_FLOAT: if (env->IsInstanceOf(elem, classFloat)) { current->val.f = env->GetFloatField(elem, Float_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_DOUBLE: if (env->IsInstanceOf(elem, classDouble)) { current->val.d = env->GetDoubleField(elem, Double_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_BOOL: if (env->IsInstanceOf(elem, classBoolean)) { current->val.b = env->GetBooleanField(elem, Boolean_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_CHAR: if (env->IsInstanceOf(elem, classCharacter)) { current->val.c = (char)env->GetCharField(elem, Character_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_WCHAR: if (env->IsInstanceOf(elem, classCharacter)) { current->val.wc = env->GetCharField(elem, Character_value_ID); result = JNI_TRUE; } break; case nsXPTType::T_CHAR_STR: if (env->IsInstanceOf(elem, classString)) { jstring string = (jstring)elem; jsize jstrlen = env->GetStringUTFLength(string); const char *utf = env->GetStringUTFChars(string, NULL); // PENDING: copying every time is wasteful, but // we need to release `utf' before invocation loses it. char *tmpstr = new char[jstrlen + 1]; strncpy(tmpstr, utf, jstrlen); tmpstr[jstrlen] = '\0'; current->val.p = tmpstr; current->flags |= nsXPTCVariant::VAL_IS_OWNED; env->ReleaseStringUTFChars(string, utf); result = JNI_TRUE; } break; case nsXPTType::T_WCHAR_STR: if (env->IsInstanceOf(elem, classString)) { jstring string = (jstring)elem; jsize jstrlen = env->GetStringLength(string); const jchar *wstr = env->GetStringChars(string, NULL); // PENDING: copying every time is wasteful, but // we need to release `wstr' before invocation loses it. PRUnichar *tmpstr = new PRUnichar[jstrlen + 1]; memcpy(tmpstr, wstr, jstrlen * sizeof(PRUnichar)); tmpstr[jstrlen] = '\0'; current->val.p = tmpstr; env->ReleaseStringChars(string, wstr); result = JNI_TRUE; } break; case nsXPTType::T_VOID: // PENDING: handle void ptrs break; case nsXPTType::T_IID: // PENDING: unwrap the ID break; case nsXPTType::T_INTERFACE: case nsXPTType::T_INTERFACE_IS: if (elem == NULL) { current->val.p = NULL; } else { xpjp_QueryInterfaceToXPCOM(env, elem, iid, &(current->val.p)); } current->flags |= nsXPTCVariant::VAL_IS_IFACE; break; case nsXPTType::T_BSTR: // Ignore for now break; } // VAL_IS_OWNED: val.p holds alloc'd ptr that must be freed // VAL_IS_IFACE: val.p holds interface ptr that must be released return result; } static nsresult JArrayToVariant(JNIEnv *env, int paramcount, nsXPTCVariant *params, const jobjectArray theJarray) { nsXPTCVariant *current = params; for (jsize i = 0; i < paramcount; i++, current++) { jobject elem = env->GetObjectArrayElement(theJarray, i); nsIID iid = NS_GET_IID(nsISupports); // PENDING: get the iid of the object if (elem == NULL) { continue; } if (JObjectToVariant(env, current, elem, iid) == JNI_FALSE) { // PENDING: throw an exception cerr << "Argument " << i << " is not of the correct type" << endl; } } return NS_OK; } static jobject VariantToJObject(JNIEnv *env, const nsXPTCVariant *current) { jobject result = NULL; // Integer code assumes that current->val.i# == current->val.u# // for assignment purposes switch(current->type) { case nsXPTType::T_I8: case nsXPTType::T_U8: result = env->NewObject(classByte, Byte_init_ID, (jbyte)current->val.i16); break; case nsXPTType::T_I16: case nsXPTType::T_U16: result = env->NewObject(classShort, Short_init_ID, (jshort)current->val.i16); break; case nsXPTType::T_I32: case nsXPTType::T_U32: result = env->NewObject(classInteger, Integer_init_ID, (jint)current->val.i32); break; case nsXPTType::T_I64: result = env->NewObject(classLong, Long_init_ID, (jlong)current->val.i64); break; case nsXPTType::T_FLOAT: result = env->NewObject(classFloat, Float_init_ID, (jfloat)current->val.f); break; case nsXPTType::T_DOUBLE: result = env->NewObject(classDouble, Double_init_ID, (jdouble)current->val.d); break; case nsXPTType::T_BOOL: result = env->NewObject(classBoolean, Boolean_init_ID, (jboolean)current->val.b); break; case nsXPTType::T_CHAR: result = env->NewObject(classCharacter, Character_init_ID, (jchar)current->val.c); break; case nsXPTType::T_WCHAR: result = env->NewObject(classCharacter, Character_init_ID, (jchar)current->val.wc); break; case nsXPTType::T_CHAR_STR: if (current->val.p != NULL) { result = env->NewStringUTF((const char *)current->val.p); } break; case nsXPTType::T_WCHAR_STR: if (current->val.p != NULL) { jsize len = 0; const jchar *wstr = (const jchar *)current->val.p; // PENDING: is this right? while (wstr[len] != 0) len++; result = env->NewString(wstr, len); } break; case nsXPTType::T_VOID: // Ignore for now break; case nsXPTType::T_IID: // Ignore for now if (current->val.p != NULL) { result = ID_NewJavaID(env, (const nsID *)current->val.p); } break; case nsXPTType::T_BSTR: // Ignore for now break; case nsXPTType::T_INTERFACE: // Ignore for now break; case nsXPTType::T_INTERFACE_IS: // Ignore for now break; } return result; } static nsresult VariantToJArray(JNIEnv *env, jobjectArray theJarray, int paramcount, nsXPTCVariant *params) { nsXPTCVariant *current = params; for (jsize i = 0; i < paramcount; i++, current++) { jobject elem = NULL; // env->GetObjectArrayElement(theJarray, i); jboolean isequal = JNI_FALSE; nsXPTCVariant currValue; if (!(current->flags & nsXPTCVariant::PTR_IS_DATA)) { continue; } if (elem != NULL) { nsIID iid = NS_GET_IID(nsISupports); // PENDING: get the iid of the object memcpy(&currValue, current, sizeof(nsXPTCVariant)); if (JObjectToVariant(env, &currValue, elem, iid) != JNI_FALSE) { isequal = (memcmp(&currValue, current, sizeof(nsXPTCVariant)) != 0); } } if (isequal) { // PENDING: what about casting to more specific interfaces? continue; } elem = VariantToJObject(env, current); env->SetObjectArrayElement(theJarray, i, elem); if (current->flags & nsXPTCVariant::VAL_IS_IFACE) { xpjp_SafeRelease((nsISupports*)current->val.p); } if (current->flags & nsXPTCVariant::VAL_IS_OWNED) { delete [] current->val.p; current->val.p = 0; // for cleanliness sake } } return NS_OK; } /* ---------- DISPATCH API FUNCTIONS ------------ */ jboolean xpjd_InitJavaCaches(JNIEnv *env) { if (cache_initialized) { return JNI_TRUE; } // For basic types classString = env->FindClass("java/lang/String"); if (classString == NULL) return JNI_FALSE; classString = (jclass)env->NewGlobalRef(classString); if (classString == NULL) return JNI_FALSE; classByte = env->FindClass("java/lang/Byte"); if (classByte == NULL) return JNI_FALSE; classByte = (jclass)env->NewGlobalRef(classByte); if (classByte == NULL) return JNI_FALSE; classShort = env->FindClass("java/lang/Short"); if (classShort == NULL) return JNI_FALSE; classShort = (jclass)env->NewGlobalRef(classShort); if (classShort == NULL) return JNI_FALSE; classInteger = env->FindClass("java/lang/Integer"); if (classInteger == NULL) return JNI_FALSE; classInteger = (jclass)env->NewGlobalRef(classInteger); if (classInteger == NULL) return JNI_FALSE; classLong = env->FindClass("java/lang/Long"); if (classLong == NULL) return JNI_FALSE; classLong = (jclass)env->NewGlobalRef(classLong); if (classLong == NULL) return JNI_FALSE; classFloat = env->FindClass("java/lang/Float"); if (classFloat == NULL) return JNI_FALSE; classFloat = (jclass)env->NewGlobalRef(classFloat); if (classFloat == NULL) return JNI_FALSE; classDouble = env->FindClass("java/lang/Double"); if (classDouble == NULL) return JNI_FALSE; classDouble = (jclass)env->NewGlobalRef(classDouble); if (classDouble == NULL) return JNI_FALSE; classBoolean = env->FindClass("java/lang/Boolean"); if (classBoolean == NULL) return JNI_FALSE; classBoolean = (jclass)env->NewGlobalRef(classBoolean); if (classBoolean == NULL) return JNI_FALSE; classCharacter = env->FindClass("java/lang/Character"); if (classCharacter == NULL) return JNI_FALSE; classCharacter = (jclass)env->NewGlobalRef(classCharacter); if (classCharacter == NULL) return JNI_FALSE; Byte_init_ID = env->GetMethodID(classByte, "", "(B)V"); if (Byte_init_ID == NULL) return JNI_FALSE; Byte_value_ID = env->GetFieldID(classByte, "value", "B"); if (Byte_value_ID == NULL) return JNI_FALSE; Short_init_ID = env->GetMethodID(classShort, "", "(S)V"); if (Short_init_ID == NULL) return JNI_FALSE; Short_value_ID = env->GetFieldID(classShort, "value", "S"); if (Short_value_ID == NULL) return JNI_FALSE; Integer_init_ID = env->GetMethodID(classInteger, "", "(I)V"); if (Integer_init_ID == NULL) return JNI_FALSE; Integer_value_ID = env->GetFieldID(classInteger, "value", "I"); if (Integer_value_ID == NULL) return JNI_FALSE; Long_init_ID = env->GetMethodID(classLong, "", "(J)V"); if (Long_init_ID == NULL) return JNI_FALSE; Long_value_ID = env->GetFieldID(classLong, "value", "J"); if (Long_value_ID == NULL) return JNI_FALSE; Float_init_ID = env->GetMethodID(classFloat, "", "(F)V"); if (Float_init_ID == NULL) return JNI_FALSE; Float_value_ID = env->GetFieldID(classFloat, "value", "F"); if (Float_value_ID == NULL) return JNI_FALSE; Double_init_ID = env->GetMethodID(classDouble, "", "(D)V"); if (Double_init_ID == NULL) return JNI_FALSE; Double_value_ID = env->GetFieldID(classDouble, "value", "D"); if (Double_value_ID == NULL) return JNI_FALSE; Boolean_init_ID = env->GetMethodID(classBoolean, "", "(Z)V"); if (Boolean_init_ID == NULL) return JNI_FALSE; Boolean_value_ID = env->GetFieldID(classBoolean, "value", "Z"); if (Boolean_value_ID == NULL) return JNI_FALSE; Character_init_ID = env->GetMethodID(classCharacter, "", "(C)V"); if (Integer_init_ID == NULL) return JNI_FALSE; Character_value_ID = env->GetFieldID(classCharacter, "value", "C"); if (Character_value_ID == NULL) return JNI_FALSE; // Get InterfaceInfoManager #ifdef DEBUG_frankm cerr << "Getting InterfaceInfoManager" << endl; #endif interfaceInfoManager = XPTI_GetInterfaceInfoManager(); if (!interfaceInfoManager) { // XXX: throw exception or something cerr << "Failed to find InterfaceInfoManager" << endl; return JNI_FALSE; } #ifdef DEBUG_frankm cerr << "InterfaceInfoManager found!" << endl; #endif cache_initialized = JNI_TRUE; return cache_initialized; } void xpjd_FlushJavaCaches(JNIEnv *env) { cache_initialized = JNI_FALSE; // For basic types env->DeleteGlobalRef(classString); classString = NULL; env->DeleteGlobalRef(classByte); classByte = NULL; env->DeleteGlobalRef(classShort); classShort = NULL; env->DeleteGlobalRef(classInteger); classInteger = NULL; env->DeleteGlobalRef(classLong); classLong = NULL; env->DeleteGlobalRef(classFloat); classFloat = NULL; env->DeleteGlobalRef(classDouble); classDouble = NULL; env->DeleteGlobalRef(classBoolean); classBoolean = NULL; env->DeleteGlobalRef(classCharacter); classCharacter = NULL; Byte_init_ID = NULL; Byte_value_ID = NULL; Short_init_ID = NULL; Short_value_ID = NULL; Integer_init_ID = NULL; Integer_value_ID = NULL; Long_init_ID = NULL; Long_value_ID = NULL; Float_init_ID = NULL; Float_value_ID = NULL; Double_init_ID = NULL; Double_value_ID = NULL; Boolean_init_ID = NULL; Boolean_value_ID = NULL; Character_init_ID = NULL; Character_value_ID = NULL; } jboolean xpjd_GetInterfaceInfo(JNIEnv *env, jobject iid, nsIInterfaceInfo **info) { nsID *nativeIID = ID_GetNative(env, iid); return xpjd_GetInterfaceInfoNative(*nativeIID, info); } jboolean xpjd_GetInterfaceInfoNative(REFNSIID iid, nsIInterfaceInfo **info) { nsresult res; // Get info *info = nsnull; res = interfaceInfoManager->GetInfoForIID(&iid, info); if (NS_FAILED(res)) { cerr << "Failed to find info for " << iid.ToString() << endl; return JNI_FALSE; } return JNI_TRUE; } void xpjd_InvokeMethod(JNIEnv *env, nsISupports *target, nsIInterfaceInfo *info, jint methodIndex, jobjectArray jargs) { nsresult res = NS_OK; nsXPTCVariant paramArray[32]; nsXPTCVariant *params = paramArray; int nparams = 0; const int capacity = sizeof(paramArray)/sizeof(paramArray[0]); // XXX: check info != NULL, target != null, jargs != null, env != null // XXX: check bounds of methodIndex nparams = xpjd_BuildParamsForOffset(info, methodIndex, capacity, ¶ms); if (nparams < 0) { // PENDING: throw an exception cerr << "Couldn't initialize parameter array" << endl; goto finally; } res = JArrayToVariant(env, nparams, params, jargs); if (NS_FAILED(res)) { // PENDING: throw an exception cerr << "Array and parameter list mismatch" << endl; goto finally; } //xpjp_SafeAddRef(target); res = XPTC_InvokeByIndex(target, methodIndex, nparams, params); //xpjp_SafeRelease(target); if (NS_FAILED(res)) { // PENDING: throw an exception cerr << "Invocation failed, status: " << res << endl; goto finally; } res = VariantToJArray(env, jargs, nparams, params); if (NS_FAILED(res)) { // PENDING: throw an exception cerr << "Array and parameter list mismatch" << endl; goto finally; } finally: if (params != paramArray) { nsAllocator::Free(params); } }