gecko-dev/js/ref/liveconnect/jsj_method.c

1152 строки
38 KiB
C

/* -*- Mode: C; tab-width: 8 -*-
* Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code used to reflect Java methods as properties of
* JavaObject objects and the code to invoke those methods.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* LiveConnect external API */
#include "jscntxt.h" /* for error reporting */
/*
* A helper function for jsj_ConvertJavaMethodSignatureToString():
* Compute JNI-style (string) signatures for an array of JavaSignature objects
* and concatenate the results into a single string.
*
* If an error is encountered, NULL is returned and the error reporter is called.
*/
static const char *
convert_java_method_arg_signatures_to_string(JSContext *cx,
JavaSignature **arg_signatures,
int num_args)
{
const char *first_arg_signature, *rest_arg_signatures, *sig;
JavaSignature **rest_args;
/* Convert the first method argument in the array to a string */
first_arg_signature = jsj_ConvertJavaSignatureToString(cx, arg_signatures[0]);
if (!first_arg_signature)
return NULL;
/* If this is the last method argument in the array, we're done. */
if (num_args == 1)
return first_arg_signature;
/* Convert the remaining method arguments to a string */
rest_args = &arg_signatures[1];
rest_arg_signatures =
convert_java_method_arg_signatures_to_string(cx, rest_args, num_args - 1);
if (!rest_arg_signatures) {
free((void*)first_arg_signature);
return NULL;
}
/* Concatenate the signature string of this argument with the signature
strings of all the remaining arguments. */
sig = PR_smprintf("%s%s", first_arg_signature, rest_arg_signatures);
free((void*)first_arg_signature);
free((void*)rest_arg_signatures);
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
/*
* Compute a JNI-style (string) signature for the given method, e.g. the method
* "String MyFunc(int, byte)" is translated to "(IB)Ljava/lang/String;".
*
* If an error is encountered, NULL is returned and the error reporter is called.
*/
const char *
jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
JavaMethodSignature *method_signature)
{
JavaSignature **arg_signatures, *return_val_signature;
const char *arg_sigs_cstr;
const char *return_val_sig_cstr;
const char *sig_cstr;
arg_signatures = method_signature->arg_signatures;
return_val_signature = method_signature->return_val_signature;
/* Convert the method argument signatures to a C-string */
arg_sigs_cstr = NULL;
if (arg_signatures) {
arg_sigs_cstr =
convert_java_method_arg_signatures_to_string(cx, arg_signatures,
method_signature->num_args);
if (!arg_sigs_cstr)
return NULL;
}
/* Convert the method return value signature to a C-string */
return_val_sig_cstr = jsj_ConvertJavaSignatureToString(cx, return_val_signature);
if (!return_val_sig_cstr) {
free((void*)arg_sigs_cstr);
return NULL;
}
/* Compose method arg signatures string and return val signature string */
if (arg_sigs_cstr) {
sig_cstr = PR_smprintf("(%s)%s", arg_sigs_cstr, return_val_sig_cstr);
free((void*)arg_sigs_cstr);
} else {
sig_cstr = PR_smprintf("()%s", return_val_sig_cstr);
}
free((void*)return_val_sig_cstr);
if (!sig_cstr) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig_cstr;
}
/*
* A helper function for jsj_ConvertJavaMethodSignatureToHRString():
* Compute human-readable string signatures for an array of JavaSignatures
* and concatenate the results into a single string.
*
* If an error is encountered, NULL is returned and the error reporter is called.
*/
static const char *
convert_java_method_arg_signatures_to_hr_string(JSContext *cx,
JavaSignature **arg_signatures,
int num_args)
{
const char *first_arg_signature, *rest_arg_signatures, *sig;
JavaSignature **rest_args;
/* Convert the first method argument in the array to a string */
first_arg_signature = jsj_ConvertJavaSignatureToHRString(cx, arg_signatures[0]);
if (!first_arg_signature)
return NULL;
/* If this is the last method argument in the array, we're done. */
if (num_args == 1)
return first_arg_signature;
/* Convert the remaining method arguments to a string */
rest_args = &arg_signatures[1];
rest_arg_signatures =
convert_java_method_arg_signatures_to_hr_string(cx, rest_args, num_args - 1);
if (!rest_arg_signatures) {
free((void*)first_arg_signature);
return NULL;
}
/* Concatenate the signature string of this argument with the signature
strings of all the remaining arguments. */
sig = PR_smprintf("%s, %s", first_arg_signature, rest_arg_signatures);
free((void*)first_arg_signature);
free((void*)rest_arg_signatures);
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
/*
* Compute a string signature for the given method using the same type names
* that appear in Java source files, e.g. "int[][]", rather than the JNI-style
* type strings that are provided by the functions above. An example return
* value might be "String MyFunc(int, byte, char[][], java.lang.String)".
*
* If an error is encountered, NULL is returned and the error reporter is called.
*/
const char *
jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
const char *method_name,
JavaMethodSignature *method_signature)
{
JavaSignature **arg_signatures, *return_val_signature;
const char *arg_sigs_cstr;
const char *return_val_sig_cstr;
const char *sig_cstr;
arg_signatures = method_signature->arg_signatures;
return_val_signature = method_signature->return_val_signature;
/* Convert the method argument signatures to a C-string */
arg_sigs_cstr = NULL;
if (arg_signatures) {
arg_sigs_cstr =
convert_java_method_arg_signatures_to_hr_string(cx, arg_signatures,
method_signature->num_args);
if (!arg_sigs_cstr)
return NULL;
}
/* Convert the method return value signature to a C-string */
return_val_sig_cstr = jsj_ConvertJavaSignatureToHRString(cx, return_val_signature);
if (!return_val_sig_cstr) {
free((void*)arg_sigs_cstr);
return NULL;
}
/* Compose method arg signatures string and return val signature string */
if (arg_sigs_cstr) {
sig_cstr = PR_smprintf("%s %s(%s)", return_val_sig_cstr, method_name, arg_sigs_cstr);
free((void*)arg_sigs_cstr);
} else {
sig_cstr = PR_smprintf("%s %s()", return_val_sig_cstr, method_name);
}
free((void*)return_val_sig_cstr);
if (!sig_cstr) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig_cstr;
}
/*
* Destroy the sub-structure of a JavaMethodSignature object without free'ing
* the method signature itself.
*/
void
jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *method_signature)
{
int i, num_args;
JavaSignature **arg_signatures;
if (!method_signature) /* Paranoia */
return;
/* Free the method argument signatures */
num_args = method_signature->num_args;
arg_signatures = method_signature->arg_signatures;
for (i = 0; i < num_args; i++)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, arg_signatures[i]);
if (arg_signatures)
JS_free(cx, arg_signatures);
/* Free the return type signature */
if (method_signature->return_val_signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, method_signature->return_val_signature);
}
/*
* Fill in the members of a JavaMethodSignature object using the method
* argument, which can be either an instance of either java.lang.reflect.Method
* or java.lang.reflect.Constructor.
*
* With normal completion, return the original method_signature argument.
* If an error occurs, return NULL and call the error reporter.
*/
JavaMethodSignature *
jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv,
jobject method,
JavaMethodSignature *method_signature)
{
int i;
jboolean is_constructor;
jclass return_val_class;
jsize num_args;
JavaSignature *return_val_signature;
jarray arg_classes;
jmethodID getParameterTypes;
memset(method_signature, 0, sizeof (JavaMethodSignature));
is_constructor = (*jEnv)->IsInstanceOf(jEnv, method, jlrConstructor);
/* Get a Java array that lists the class of each of the method's arguments */
if (is_constructor)
getParameterTypes = jlrConstructor_getParameterTypes;
else
getParameterTypes = jlrMethod_getParameterTypes;
arg_classes = (*jEnv)->CallObjectMethod(jEnv, method, getParameterTypes);
if (!arg_classes) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine argument signature of method");
goto error;
}
/* Compute the number of method arguments */
num_args = jsj_GetJavaArrayLength(cx, jEnv, arg_classes);
if (num_args < 0)
goto error;
method_signature->num_args = num_args;
/* Build a JavaSignature array corresponding to the method's arguments */
if (num_args) {
JavaSignature **arg_signatures;
/* Allocate an array of JavaSignatures, one for each method argument */
size_t arg_signatures_size = num_args * sizeof(JavaSignature *);
arg_signatures = (JavaSignature **)JS_malloc(cx, arg_signatures_size);
if (!arg_signatures)
goto error;
memset(arg_signatures, 0, arg_signatures_size);
method_signature->arg_signatures = arg_signatures;
/* Build an array of JavaSignature objects, each of which corresponds
to the type of an individual method argument. */
for (i = 0; i < num_args; i++) {
JavaSignature *a;
jclass arg_class = (*jEnv)->GetObjectArrayElement(jEnv, arg_classes, i);
a = arg_signatures[i] = jsj_GetJavaClassDescriptor(cx, jEnv, arg_class);
if (!a) {
jsj_UnexpectedJavaError(cx, jEnv, "Could not determine Java class "
"signature using java.lang.reflect");
goto error;
}
}
}
/* Get the Java class of the method's return value */
if (is_constructor) {
/* Constructors always have a "void" return type */
return_val_class = jlVoid_TYPE;
} else {
return_val_class =
(*jEnv)->CallObjectMethod(jEnv, method, jlrMethod_getReturnType);
if (!return_val_class) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine return type of method "
"using java.lang.reflect.Method.getReturnType()");
goto error;
}
}
/* Build a JavaSignature object corresponding to the method's return value */
return_val_signature = jsj_GetJavaClassDescriptor(cx, jEnv, return_val_class);
if (!return_val_signature)
goto error;
method_signature->return_val_signature = return_val_signature;
return method_signature;
error:
jsj_PurgeJavaMethodSignature(cx, jEnv, method_signature);
return NULL;
}
static JSBool
add_java_method_to_class_descriptor(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring method_name_jstr,
jobject java_method,
JSBool is_static_method,
JSBool is_constructor)
{
jmethodID methodID;
JSFunction *fun;
jclass java_class = class_descriptor->java_class;
JavaMemberDescriptor *member_descriptor = NULL;
const char *sig_cstr = NULL;
const char *method_name = NULL;
JavaMethodSignature *signature = NULL;
JavaMethodSpec **specp, *method_spec = NULL;
if (is_constructor) {
member_descriptor = jsj_GetJavaClassConstructors(cx, class_descriptor);
} else {
if (is_static_method) {
member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
} else {
member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
fun = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0,
JSFUN_BOUND_METHOD, NULL, member_descriptor->name);
member_descriptor->invoke_func_obj = JS_GetFunctionObject(fun);
JS_AddRoot(cx, &member_descriptor->invoke_func_obj);
}
}
if (!member_descriptor)
return JS_FALSE;
method_spec = (JavaMethodSpec*)JS_malloc(cx, sizeof(JavaMethodSpec));
if (!method_spec)
goto error;
memset(method_spec, 0, sizeof(JavaMethodSpec));
signature = jsj_InitJavaMethodSignature(cx, jEnv, java_method, &method_spec->signature);
if (!signature)
goto error;
method_name = JS_strdup(cx, member_descriptor->name);
if (!method_name)
goto error;
method_spec->name = method_name;
sig_cstr = jsj_ConvertJavaMethodSignatureToString(cx, signature);
if (!sig_cstr)
goto error;
if (is_static_method)
methodID = (*jEnv)->GetStaticMethodID(jEnv, java_class, method_name, sig_cstr);
else
methodID = (*jEnv)->GetMethodID(jEnv, java_class, method_name, sig_cstr);
method_spec->methodID = methodID;
if (!methodID) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java method ID for %s.%s() (sig=%s)",
class_descriptor->name, method_name, sig_cstr);
goto error;
}
JS_free(cx, (char*)sig_cstr);
/* Add method to end of list of overloaded methods for this class member */
specp = &member_descriptor->methods;
while (*specp) {
specp = &(*specp)->next;
}
*specp = method_spec;
return JS_TRUE;
error:
if (method_spec)
JS_FREE_IF(cx, (char*)method_spec->name);
JS_FREE_IF(cx, (char*)sig_cstr);
if (signature)
jsj_PurgeJavaMethodSignature(cx, jEnv, signature);
JS_FREE_IF(cx, method_spec);
return JS_FALSE;
}
JSBool
jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_methods)
{
jarray joMethodArray, joConstructorArray;
jsize num_methods, num_constructors;
int i;
jclass java_class;
JSBool ok, reflect_only_instance_methods;
reflect_only_instance_methods = !reflect_only_static_methods;
/* Get a java array of java.lang.reflect.Method objects, by calling
java.lang.Class.getMethods(). */
java_class = class_descriptor->java_class;
joMethodArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getMethods);
if (!joMethodArray) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine Java object's methods "
"using java.lang.Class.getMethods()");
return JS_FALSE;
}
/* Iterate over the class methods */
num_methods = (*jEnv)->GetArrayLength(jEnv, joMethodArray);
for (i = 0; i < num_methods; i++) {
jstring method_name_jstr;
/* Get the i'th reflected method */
jobject java_method = (*jEnv)->GetObjectArrayElement(jEnv, joMethodArray, i);
/* Get the method modifiers, eg static, public, private, etc. */
jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_method, jlrMethod_getModifiers);
/* Don't allow access to private or protected Java methods. */
if (!(modifiers & ACC_PUBLIC))
continue;
/* Abstract methods can't be invoked */
if (modifiers & ACC_ABSTRACT)
continue;
/* Reflect all instance methods or all static methods, but not both */
if (reflect_only_static_methods != ((modifiers & ACC_STATIC) != 0))
continue;
/* Add a JavaMethodSpec object to the JavaClassDescriptor */
method_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_method, jlrMethod_getName);
ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, method_name_jstr, java_method,
reflect_only_static_methods, JS_FALSE);
if (!ok)
return JS_FALSE;
}
if (reflect_only_instance_methods)
return JS_TRUE;
joConstructorArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getConstructors);
if (!joConstructorArray) {
jsj_UnexpectedJavaError(cx, jEnv, "internal error: "
"Can't determine Java class's constructors "
"using java.lang.Class.getConstructors()");
return JS_FALSE;
}
/* Iterate over the class constructors */
num_constructors = (*jEnv)->GetArrayLength(jEnv, joConstructorArray);
for (i = 0; i < num_constructors; i++) {
/* Get the i'th reflected constructor */
jobject java_constructor =
(*jEnv)->GetObjectArrayElement(jEnv, joConstructorArray, i);
/* Get the method modifiers, eg public, private, etc. */
jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_constructor,
jlrConstructor_getModifiers);
/* Don't allow access to private or protected Java methods. */
if (!(modifiers & ACC_PUBLIC))
continue;
/* Add a JavaMethodSpec object to the JavaClassDescriptor */
ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, NULL,
java_constructor,
JS_FALSE, JS_TRUE);
if (!ok)
return JS_FALSE;
}
return JS_TRUE;
}
/*
* Free up a JavaMethodSpec and all its resources.
*/
void
jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec)
{
JS_FREE_IF(cx, (char*)method_spec->name);
jsj_PurgeJavaMethodSignature(cx, jEnv, &method_spec->signature);
JS_free(cx, method_spec);
}
static JSBool
method_signature_matches_JS_args(JSContext *cx, JNIEnv *jEnv, uintN argc, jsval *argv,
JavaMethodSignature *method_signature, int *cost)
{
uintN i;
JavaSignature *arg_signature;
JSBool dummy_bool;
if (argc != (uintN)method_signature->num_args)
return JS_FALSE;
for (i = 0; i < argc; i++) {
arg_signature = method_signature->arg_signatures[i];
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, argv[i], arg_signature, cost,
NULL, &dummy_bool))
return JS_FALSE;
}
return JS_TRUE;
}
/*
* Return the JavaScript types that a JavaScript method was invoked with
* as a string, e.g. a JS method invoked like foo(3, 'hey', 'you', true)
* would cause a return value of "(number, string, string, boolean)".
* The returned string must be free'ed by the caller.
* Returns NULL and reports an error if out-of-memory.
*/
static const char *
get_js_arg_types_as_string(JSContext *cx, uintN argc, jsval *argv)
{
uintN i;
const char *arg_type, *arg_string, *tmp;
if (argc == 0)
return strdup("()");
arg_string = strdup("(");
if (!arg_string)
goto out_of_memory;
for (i = 0; i < argc; i++) {
arg_type = JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i]));
tmp = PR_smprintf("%s%s%s%s", arg_string, i ? ", " : "", arg_type,
(i == argc-1) ? ")" : "");
free((char*)arg_string);
if (!tmp)
goto out_of_memory;
arg_string = tmp;
}
return arg_string;
out_of_memory:
JS_ReportOutOfMemory(cx);
return NULL;
}
static void
report_method_match_failure(JSContext *cx,
JavaMemberDescriptor *member_descriptor,
JavaClassDescriptor *class_descriptor,
JSBool is_static_method,
uintN argc, jsval *argv)
{
const char *err, *js_arg_string, *tmp, *method_str, *method_name;
JSBool is_constructor;
JavaMethodSpec *method;
err = NULL;
is_constructor = (!strcmp(member_descriptor->name, "<init>"));
js_arg_string = get_js_arg_types_as_string(cx, argc, argv);
if (!js_arg_string)
goto out_of_memory;
if (is_constructor) {
err = PR_smprintf("There is no Java constructor for class %s that matches "
"JavaScript argument types %s.\n", class_descriptor->name,
js_arg_string);
method_name = class_descriptor->name;
} else {
err = PR_smprintf("There is no %sJava method %s.%s that matches "
"JavaScript argument types %s.\n",
is_static_method ? "static ": "",
class_descriptor->name, member_descriptor->name, js_arg_string);
method_name = member_descriptor->name;
}
if (!err)
goto out_of_memory;
tmp = PR_smprintf("%sCandidate methods with the same name are:\n", err);
if (!tmp)
goto out_of_memory;
err = tmp;
method = member_descriptor->methods;
while (method) {
method_str =
jsj_ConvertJavaMethodSignatureToHRString(cx, method_name, &method->signature);
if (!method_str)
goto out_of_memory;
tmp = PR_smprintf("%s %s\n", err, method_str);
free((char*)method_str);
if (!tmp)
goto out_of_memory;
err = tmp;
method = method->next;
}
JS_ReportError(cx, err);
return;
out_of_memory:
if (js_arg_string)
free((char*)js_arg_string);
if (err)
free((char*)err);
}
static JavaMethodSpec *
resolve_overloaded_method(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor,
JavaClassDescriptor *class_descriptor,
JSBool is_static_method,
uintN argc, jsval *argv)
{
int cost, lowest_cost, num_method_matches;
JavaMethodSpec *best_method_match, *method;
num_method_matches = 0;
lowest_cost = 10000;
best_method_match = NULL;
for (method = member_descriptor->methods; method; method = method->next) {
cost = 0;
if (!method_signature_matches_JS_args(cx, jEnv, argc, argv, &method->signature, &cost))
continue;
#if 1
if (cost < lowest_cost) {
lowest_cost = cost;
best_method_match = method;
num_method_matches++;
}
#else
best_method_match = method;
break;
#endif
}
if (!best_method_match)
report_method_match_failure(cx, member_descriptor, class_descriptor,
is_static_method, argc, argv);
/*
#ifdef LIVECONNECT_IMPROVEMENTS
if (num_method_matches > 1)
return NULL;
#else
if (lowest_cost != 0)
return NULL;
#endif
*/
return best_method_match;
}
static jvalue *
convert_JS_method_args_to_java_argv(JSContext *cx, JNIEnv *jEnv, jsval *argv,
JavaMethodSpec *method, JSBool **localvp)
{
jvalue *jargv;
JSBool ok, *localv;
uintN i, argc;
JavaSignature **arg_signatures;
JavaMethodSignature *signature;
signature = &method->signature;
argc = signature->num_args;
PR_ASSERT(argc != 0);
arg_signatures = signature->arg_signatures;
jargv = (jvalue *)JS_malloc(cx, sizeof(jvalue) * argc);
if (!jargv)
return NULL;
/*
* Allocate an array that contains a flag for each argument, indicating whether
* or not the conversion from a JS value to a Java value resulted in a new
* JNI local reference.
*/
localv = (JSBool *)JS_malloc(cx, sizeof(JSBool) * argc);
*localvp = localv;
if (!localv) {
JS_free(cx, jargv);
return NULL;
}
for (i = 0; i < argc; i++) {
int dummy_cost;
ok = jsj_ConvertJSValueToJavaValue(cx, jEnv, argv[i], arg_signatures[i],
&dummy_cost, &jargv[i], &localv[i]);
if (!ok) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_CONVERT_JS_VALUE);
JS_free(cx, jargv);
JS_free(cx, localv);
*localvp = NULL;
return NULL;
}
}
return jargv;
}
static JSBool
invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
jobject java_class_or_instance,
JavaClassDescriptor *class_descriptor,
JavaMethodSpec *method,
JSBool is_static_method,
jsval *argv, jsval *vp)
{
jvalue java_value;
jvalue *jargv;
uintN argc, i;
jobject java_object;
jclass java_class;
jmethodID methodID;
JavaMethodSignature *signature;
JavaSignature *return_val_signature;
JSContext *old_cx;
JNIEnv *jEnv;
JSBool *localv, error_occurred;
error_occurred = JS_FALSE;
methodID = method->methodID;
signature = &method->signature;
argc = signature->num_args;
jEnv = jsj_env->jEnv;
if (is_static_method) {
java_object = NULL;
java_class = java_class_or_instance;
} else {
java_object = java_class_or_instance;
java_class = NULL;
}
old_cx = JSJ_SetDefaultJSContextForJavaThread(cx, jsj_env);
#ifdef DEBUG
if (old_cx && (old_cx != cx)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_MULTIPLE_JTHREADS);
}
#endif
jargv = NULL;
localv = NULL;
if (argc) {
jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method, &localv);
if (!jargv) {
error_occurred = JS_TRUE;
goto out;
}
}
#define CALL_JAVA_METHOD(type, member) \
PR_BEGIN_MACRO \
if (is_static_method) { \
java_value.member = (*jEnv)->CallStatic##type##MethodA(jEnv, java_class, methodID, jargv);\
} else { \
java_value.member = (*jEnv)->Call##type##MethodA(jEnv, java_object, methodID, jargv);\
} \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()", \
class_descriptor->name, method->name); \
error_occurred = JS_TRUE; \
goto out; \
} \
PR_END_MACRO
return_val_signature = signature->return_val_signature;
switch(return_val_signature->type) {
case JAVA_SIGNATURE_BYTE:
CALL_JAVA_METHOD(Byte, b);
break;
case JAVA_SIGNATURE_CHAR:
CALL_JAVA_METHOD(Char, c);
break;
case JAVA_SIGNATURE_FLOAT:
CALL_JAVA_METHOD(Float, f);
break;
case JAVA_SIGNATURE_DOUBLE:
CALL_JAVA_METHOD(Double, d);
break;
case JAVA_SIGNATURE_INT:
CALL_JAVA_METHOD(Int, i);
break;
case JAVA_SIGNATURE_LONG:
CALL_JAVA_METHOD(Long, j);
break;
case JAVA_SIGNATURE_SHORT:
CALL_JAVA_METHOD(Short, s);
break;
case JAVA_SIGNATURE_BOOLEAN:
CALL_JAVA_METHOD(Boolean, z);
break;
case JAVA_SIGNATURE_ARRAY:
case JAVA_SIGNATURE_CLASS:
CALL_JAVA_METHOD(Object, l);
break;
case JAVA_SIGNATURE_VOID:
if (is_static_method)
(*jEnv)->CallStaticVoidMethodA(jEnv, java_class, methodID, jargv);
else
(*jEnv)->CallVoidMethodA(jEnv, java_object, methodID, jargv);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()",
class_descriptor->name, method->name);
error_occurred = JS_TRUE;
goto out;
}
break;
default:
PR_ASSERT(0);
return JS_FALSE;
}
out:
JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
if (localv) {
for (i = 0; i < argc; i++) {
if (localv[i])
(*jEnv)->DeleteLocalRef(jEnv, jargv[i].l);
}
JS_free(cx, localv);
}
if (jargv)
JS_free(cx, jargv);
if (error_occurred)
return JS_FALSE;
else
return jsj_ConvertJavaValueToJSValue(cx, jEnv, return_val_signature, &java_value, vp);
}
static JSBool
invoke_overloaded_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
JavaMemberDescriptor *member,
JSBool is_static_method,
jobject java_class_or_instance,
JavaClassDescriptor *class_descriptor,
uintN argc, jsval *argv,
jsval *vp)
{
JavaMethodSpec *method;
JNIEnv *jEnv;
jEnv = jsj_env->jEnv;
method = resolve_overloaded_method(cx, jEnv, member, class_descriptor,
is_static_method, argc, argv);
if (!method)
return JS_FALSE;
return invoke_java_method(cx, jsj_env, java_class_or_instance, class_descriptor,
method, is_static_method, argv, vp);
}
PR_CALLBACK JSBool
jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp)
{
JSFunction *function;
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
jsid id;
jsval idval;
JNIEnv *jEnv;
JSJavaThreadState *jsj_env;
jobject java_class;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
java_class = class_descriptor->java_class;
PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
JS_ValueToId(cx, idval, &id);
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
PR_ASSERT(0);
return JS_FALSE;
}
return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
java_class, class_descriptor, argc, argv, vp);
}
PR_CALLBACK JSBool
jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp)
{
JSFunction *function;
JavaMemberDescriptor *member_descriptor;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
jsint id;
jsval idval;
JSJavaThreadState *jsj_env;
JNIEnv *jEnv;
jobject java_obj, java_class;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
return JS_FALSE;
java_obj = java_wrapper->java_obj;
PR_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
JS_ValueToId(cx, idval, &id);
class_descriptor = java_wrapper->class_descriptor;
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
/* If no instance method was found, try for a static method */
if (!member_descriptor) {
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
PR_ASSERT(0);
return JS_FALSE;
}
java_class = class_descriptor->java_class;
return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
java_class, class_descriptor, argc, argv, vp);
}
return invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
JS_FALSE, java_obj,
class_descriptor, argc, argv, vp);
}
static JSBool
invoke_java_constructor(JSContext *cx,
JSJavaThreadState *jsj_env,
jclass java_class,
JavaMethodSpec *method,
jsval *argv, jsval *vp)
{
jvalue *jargv;
uintN argc, i;
jobject java_object;
jmethodID methodID;
JavaMethodSignature *signature;
JSContext *old_cx;
JNIEnv *jEnv;
JSBool *localv;
JSBool error_occurred = JS_FALSE;
methodID = method->methodID;
signature = &method->signature;
argc = signature->num_args;
jEnv = jsj_env->jEnv;
jargv = NULL;
localv = NULL;
if (argc) {
jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method, &localv);
if (!jargv) {
error_occurred = JS_TRUE;
goto out;
}
}
old_cx = JSJ_SetDefaultJSContextForJavaThread(cx, jsj_env);
#ifdef DEBUG
if (old_cx && (old_cx != cx)) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_MULTIPLE_JTHREADS);
}
#endif
/* Call the constructor */
java_object = (*jEnv)->NewObjectA(jEnv, java_class, methodID, jargv);
JSJ_SetDefaultJSContextForJavaThread(old_cx, jsj_env);
if (!java_object) {
jsj_ReportJavaError(cx, jEnv, "Error while constructing instance of %s",
jsj_GetJavaClassName(cx, jEnv, java_class));
error_occurred = JS_TRUE;
goto out;
}
out:
if (localv) {
for (i = 0; i < argc; i++) {
if (localv[i])
(*jEnv)->DeleteLocalRef(jEnv, jargv[i].l);
}
JS_free(cx, localv);
}
if (jargv)
JS_free(cx, jargv);
if (error_occurred)
return JS_FALSE;
else
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_object, vp);
}
static JSBool
invoke_overloaded_java_constructor(JSContext *cx,
JSJavaThreadState *jsj_env,
JavaMemberDescriptor *member,
JSObject *obj, uintN argc, jsval *argv,
jsval *vp)
{
jclass java_class;
JavaMethodSpec *method;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
jEnv = jsj_env->jEnv;
class_descriptor = JS_GetPrivate(cx, obj);
PR_ASSERT(class_descriptor);
if (!class_descriptor)
return JS_FALSE;
method = resolve_overloaded_method(cx, jEnv, member, class_descriptor, JS_TRUE,
argc, argv);
if (!method)
return JS_FALSE;
java_class = class_descriptor->java_class;
return invoke_java_constructor(cx, jsj_env, java_class, method, argv, vp);
}
PR_CALLBACK JSBool
jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp)
{
jint modifiers;
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JSJavaThreadState *jsj_env;
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_env = jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
obj = JSVAL_TO_OBJECT(argv[-2]);
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
/* Get class/interface flags and check them */
modifiers = class_descriptor->modifiers;
if (modifiers & ACC_ABSTRACT) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_ABSTRACT_JCLASS, class_descriptor->name);
return JS_FALSE;
}
if (modifiers & ACC_INTERFACE) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_IS_INTERFACE, class_descriptor->name);
return JS_FALSE;
}
if ( !(modifiers & ACC_PUBLIC) ) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NOT_PUBLIC, class_descriptor->name);
return JS_FALSE;
}
member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
if (!member_descriptor) {
JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
JSJMSG_NO_CONSTRUCTORS, class_descriptor->name);
return JS_FALSE;
}
return invoke_overloaded_java_constructor(cx, jsj_env, member_descriptor, obj, argc, argv, vp);
}