dmose%mozilla.org ce50f7d151 updated license boilerplate to xPL 1.1, a=chofmann@netscape.com,r=endico@mozilla.org
git-svn-id: svn://10.0.0.236/trunk@52900 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-06 02:47:15 +00:00

820 lines
23 KiB
C++

/*
* 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 <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#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 = &current->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 = &current->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<n>: T_I<n> 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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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,
&params);
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);
}
}