mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-13 11:10:13 +01:00
1824 lines
63 KiB
C
1824 lines
63 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** 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 Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of 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.
|
|
*
|
|
* This Original Code has been modified by IBM Corporation. Modifications made
|
|
* by IBM described herein are Copyright (c) International Business Machines
|
|
* Corporation, 2000.
|
|
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
|
|
*
|
|
* Date Modified by Description of modification
|
|
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* 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 "jsclist.h" /* Circular linked lists */
|
|
|
|
/* A list of Java methods */
|
|
typedef JSCList MethodList;
|
|
|
|
/* An element in a list of Java methods */
|
|
typedef struct MethodListElement {
|
|
JSCList linkage;
|
|
JavaMethodSpec *method;
|
|
} MethodListElement;
|
|
|
|
/*
|
|
* This is the return value of functions which compare either two types or two
|
|
* method signatures to see if either is "preferred" when converting from
|
|
* specified JavaScript value(s).
|
|
*/
|
|
typedef enum JSJTypePreference {
|
|
JSJPREF_FIRST_ARG = 1, /* First argument preferred */
|
|
JSJPREF_SECOND_ARG = 2, /* Second argument preferred */
|
|
JSJPREF_AMBIGUOUS = 3 /* Neither preferred over the other */
|
|
} JSJTypePreference;
|
|
|
|
/*
|
|
* Classification of JS types with slightly different granularity than JSType.
|
|
* This is used to resolve among overloaded Java methods.
|
|
*/
|
|
typedef enum JSJType {
|
|
JSJTYPE_VOID, /* undefined */
|
|
JSJTYPE_BOOLEAN, /* boolean */
|
|
JSJTYPE_NUMBER, /* number */
|
|
JSJTYPE_STRING, /* string */
|
|
JSJTYPE_NULL, /* null */
|
|
JSJTYPE_JAVACLASS, /* JavaClass */
|
|
JSJTYPE_JAVAOBJECT, /* JavaObject */
|
|
JSJTYPE_JAVAARRAY, /* JavaArray */
|
|
JSJTYPE_JSARRAY, /* JS Array */
|
|
JSJTYPE_OBJECT, /* Any other JS Object, including functions */
|
|
JSJTYPE_LIMIT
|
|
} JSJType;
|
|
|
|
/*
|
|
* 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 = JS_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;
|
|
}
|
|
|
|
/*
|
|
* 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,
|
|
JSBool whitespace)
|
|
{
|
|
const char *first_arg_signature, *rest_arg_signatures, *sig, *separator;
|
|
JavaSignature **rest_args;
|
|
|
|
if (num_args == 0)
|
|
return strdup("");
|
|
|
|
/* 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, whitespace);
|
|
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. */
|
|
separator = whitespace ? " " : "";
|
|
sig = JS_smprintf("%s,%s%s", first_arg_signature, separator, 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 =
|
|
convert_java_method_arg_signatures_to_hr_string(cx, arg_signatures,
|
|
method_signature->num_args,
|
|
JS_TRUE);
|
|
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 */
|
|
sig_cstr = JS_smprintf("%s %s(%s)", return_val_sig_cstr, method_name, arg_sigs_cstr);
|
|
free((void*)arg_sigs_cstr);
|
|
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);
|
|
(*jEnv)->DeleteLocalRef(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_signature = jsj_GetJavaClassDescriptor(cx, jEnv, 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);
|
|
(*jEnv)->DeleteLocalRef(jEnv, return_val_class);
|
|
}
|
|
|
|
if (!return_val_signature)
|
|
goto error;
|
|
method_signature->return_val_signature = return_val_signature;
|
|
|
|
(*jEnv)->DeleteLocalRef(jEnv, arg_classes);
|
|
return method_signature;
|
|
|
|
error:
|
|
|
|
if (arg_classes)
|
|
(*jEnv)->DeleteLocalRef(jEnv, arg_classes);
|
|
jsj_PurgeJavaMethodSignature(cx, jEnv, method_signature);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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 = JS_smprintf("(%s)%s", arg_sigs_cstr, return_val_sig_cstr);
|
|
free((void*)arg_sigs_cstr);
|
|
} else {
|
|
sig_cstr = JS_smprintf("()%s", return_val_sig_cstr);
|
|
}
|
|
|
|
free((void*)return_val_sig_cstr);
|
|
|
|
if (!sig_cstr) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return NULL;
|
|
}
|
|
return sig_cstr;
|
|
}
|
|
|
|
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_AddNamedRoot(cx, &member_descriptor->invoke_func_obj,
|
|
"&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))
|
|
goto dont_reflect_method;
|
|
|
|
/* Abstract methods can't be invoked */
|
|
if (modifiers & ACC_ABSTRACT)
|
|
goto dont_reflect_method;
|
|
|
|
/* Reflect all instance methods or all static methods, but not both */
|
|
if (reflect_only_static_methods != ((modifiers & ACC_STATIC) != 0))
|
|
goto dont_reflect_method;
|
|
|
|
/* 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);
|
|
(*jEnv)->DeleteLocalRef(jEnv, method_name_jstr);
|
|
if (!ok) {
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_method);
|
|
(*jEnv)->DeleteLocalRef(jEnv, joMethodArray);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
dont_reflect_method:
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_method);
|
|
}
|
|
|
|
(*jEnv)->DeleteLocalRef(jEnv, joMethodArray);
|
|
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))
|
|
goto dont_reflect_constructor;
|
|
|
|
/* 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) {
|
|
(*jEnv)->DeleteLocalRef(jEnv, joConstructorArray);
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_constructor);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
dont_reflect_constructor:
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_constructor);
|
|
}
|
|
|
|
(*jEnv)->DeleteLocalRef(jEnv, joConstructorArray);
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Free up a JavaMethodSpec and all its resources.
|
|
*/
|
|
void
|
|
jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec)
|
|
{
|
|
if (!method_spec->is_alias) {
|
|
JS_FREE_IF(cx, (char*)method_spec->name);
|
|
jsj_PurgeJavaMethodSignature(cx, jEnv, &method_spec->signature);
|
|
}
|
|
JS_free(cx, method_spec);
|
|
}
|
|
|
|
static JavaMethodSpec *
|
|
copy_java_method_descriptor(JSContext *cx, JavaMethodSpec *method)
|
|
{
|
|
JavaMethodSpec *copy;
|
|
|
|
copy = (JavaMethodSpec*)JS_malloc(cx, sizeof(JavaMethodSpec));
|
|
if (!copy)
|
|
return NULL;
|
|
memcpy(copy, method, sizeof(JavaMethodSpec));
|
|
copy->next = NULL;
|
|
copy->is_alias = JS_TRUE;
|
|
return copy;
|
|
}
|
|
|
|
/*
|
|
* See if a reference to a JavaObject/JavaClass's property looks like the
|
|
* explicit resolution of an overloaded method, e.g.
|
|
* java.lang.String["max(double,double)"]. If so, add a new member
|
|
* (JavaMemberDescriptor) to the object that is an alias for the resolved
|
|
* method.
|
|
*/
|
|
JavaMemberDescriptor *
|
|
jsj_ResolveExplicitMethod(JSContext *cx, JNIEnv *jEnv,
|
|
JavaClassDescriptor *class_descriptor,
|
|
jsid method_name_id,
|
|
JSBool is_static)
|
|
{
|
|
JavaMethodSpec *method;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JavaMethodSignature *ms;
|
|
JSString *simple_name_jsstr;
|
|
JSFunction *fun;
|
|
JSBool is_constructor;
|
|
int left_paren;
|
|
const char *sig_cstr, *method_name;
|
|
char *arg_start;
|
|
jsid id;
|
|
jsval method_name_jsval;
|
|
|
|
/*
|
|
* Get the simple name of the explicit method, i.e. get "cos" from
|
|
* "cos(double)", and use it to create a new JS string.
|
|
*/
|
|
JS_IdToValue(cx, method_name_id, &method_name_jsval);
|
|
method_name = JS_GetStringBytes(JSVAL_TO_STRING(method_name_jsval));
|
|
arg_start = strchr(method_name, '('); /* Skip until '(' */
|
|
/* If no left-paren, then this is not a case of explicit method resolution */
|
|
if (!arg_start)
|
|
return NULL;
|
|
/* Left-paren must be first character for constructors */
|
|
is_constructor = (is_static && (arg_start == method_name));
|
|
|
|
left_paren = arg_start - method_name;
|
|
simple_name_jsstr = JS_NewStringCopyN(cx, method_name, left_paren);
|
|
if (!simple_name_jsstr)
|
|
return NULL;
|
|
|
|
/* Find all the methods in the same class with the same simple name */
|
|
JS_ValueToId(cx, STRING_TO_JSVAL(simple_name_jsstr), &id);
|
|
if (is_constructor)
|
|
member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
|
|
else if (is_static)
|
|
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
|
|
else
|
|
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
|
|
if (!member_descriptor) /* No member with matching simple name ? */
|
|
return NULL;
|
|
|
|
/*
|
|
* Do a UTF8 comparison of method signatures with all methods of the same name,
|
|
* so as to discover a method which exactly matches the specified argument types.
|
|
*/
|
|
if (!strlen(arg_start + 1))
|
|
return NULL;
|
|
arg_start = JS_strdup(cx, arg_start + 1);
|
|
if (!arg_start)
|
|
return NULL;
|
|
arg_start[strlen(arg_start) - 1] = '\0'; /* Get rid of ')' */
|
|
sig_cstr = NULL; /* Quiet gcc warning about uninitialized variable */
|
|
for (method = member_descriptor->methods; method; method = method->next) {
|
|
ms = &method->signature;
|
|
sig_cstr = convert_java_method_arg_signatures_to_hr_string(cx, ms->arg_signatures,
|
|
ms->num_args, JS_FALSE);
|
|
if (!sig_cstr)
|
|
return NULL;
|
|
|
|
if (!strcmp(sig_cstr, arg_start))
|
|
break;
|
|
JS_free(cx, (void*)sig_cstr);
|
|
}
|
|
JS_free(cx, arg_start);
|
|
if (!method)
|
|
return NULL;
|
|
JS_free(cx, (void*)sig_cstr);
|
|
|
|
/* Don't bother doing anything if the method isn't overloaded */
|
|
if (!member_descriptor->methods->next)
|
|
return member_descriptor;
|
|
|
|
/*
|
|
* To speed up performance for future accesses, create a new member descriptor
|
|
* with a name equal to the explicit method name, i.e. "cos(double)".
|
|
*/
|
|
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
|
|
if (!member_descriptor)
|
|
return NULL;
|
|
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
|
|
|
|
member_descriptor->id = method_name_id;
|
|
member_descriptor->name =
|
|
JS_strdup(cx, is_constructor ? "<init>" : JS_GetStringBytes(simple_name_jsstr));
|
|
if (!member_descriptor->name) {
|
|
JS_free(cx, member_descriptor);
|
|
return NULL;
|
|
}
|
|
|
|
member_descriptor->methods = copy_java_method_descriptor(cx, method);
|
|
if (!member_descriptor->methods) {
|
|
JS_free(cx, (void*)member_descriptor->name);
|
|
JS_free(cx, member_descriptor);
|
|
return NULL;
|
|
}
|
|
|
|
fun = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0,
|
|
JSFUN_BOUND_METHOD, NULL, method_name);
|
|
member_descriptor->invoke_func_obj = JS_GetFunctionObject(fun);
|
|
JS_AddNamedRoot(cx, &member_descriptor->invoke_func_obj,
|
|
"&member_descriptor->invoke_func_obj");
|
|
|
|
/* THREADSAFETY */
|
|
/* Add the new aliased member to the list of all members for the class */
|
|
if (is_static) {
|
|
member_descriptor->next = class_descriptor->static_members;
|
|
class_descriptor->static_members = member_descriptor;
|
|
} else {
|
|
member_descriptor->next = class_descriptor->instance_members;
|
|
class_descriptor->instance_members = member_descriptor;
|
|
}
|
|
|
|
return member_descriptor;
|
|
}
|
|
|
|
/*
|
|
* 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 = JS_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;
|
|
}
|
|
|
|
/*
|
|
* This is an error reporting routine used when a method of the correct name
|
|
* and class exists but either the number of arguments is incorrect or the
|
|
* arguments are of the wrong type.
|
|
*/
|
|
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 = JS_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 = JS_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 = JS_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 = JS_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);
|
|
}
|
|
|
|
/*
|
|
* This is an error reporting routine used when a method of the correct name
|
|
* and class exists but more than one Java method in a set of overloaded
|
|
* methods are compatible with the JavaScript argument types and none of them
|
|
* match any better than all the others.
|
|
*/
|
|
static void
|
|
report_ambiguous_method_match(JSContext *cx,
|
|
JavaMemberDescriptor *member_descriptor,
|
|
JavaClassDescriptor *class_descriptor,
|
|
MethodList *ambiguous_methods,
|
|
JSBool is_static_method,
|
|
uintN argc, jsval *argv)
|
|
{
|
|
const char *err, *js_arg_string, *tmp, *method_str, *method_name;
|
|
JSBool is_constructor;
|
|
JavaMethodSpec *method;
|
|
MethodListElement *method_list_element;
|
|
|
|
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 = JS_smprintf("The choice of Java constructor for class %s with "
|
|
"JavaScript argument types %s is ambiguous.\n",
|
|
class_descriptor->name,
|
|
js_arg_string);
|
|
method_name = class_descriptor->name;
|
|
} else {
|
|
err = JS_smprintf("The choice of %sJava method %s.%s matching "
|
|
"JavaScript argument types %s is ambiguous.\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 = JS_smprintf("%sCandidate methods are:\n", err);
|
|
if (!tmp)
|
|
goto out_of_memory;
|
|
err = tmp;
|
|
|
|
method_list_element = (MethodListElement*)JS_LIST_HEAD(ambiguous_methods);
|
|
while ((MethodList*)method_list_element != ambiguous_methods) {
|
|
method = method_list_element->method;
|
|
method_str =
|
|
jsj_ConvertJavaMethodSignatureToHRString(cx, method_name, &method->signature);
|
|
if (!method_str)
|
|
goto out_of_memory;
|
|
tmp = JS_smprintf("%s %s\n", err, method_str);
|
|
free((char*)method_str);
|
|
if (!tmp)
|
|
goto out_of_memory;
|
|
err = tmp;
|
|
|
|
method_list_element = (MethodListElement*)method_list_element->linkage.next;
|
|
}
|
|
|
|
JS_ReportError(cx, err);
|
|
return;
|
|
|
|
out_of_memory:
|
|
if (js_arg_string)
|
|
free((char*)js_arg_string);
|
|
if (err)
|
|
free((char*)err);
|
|
}
|
|
|
|
/*
|
|
* Compute classification of JS types with slightly different granularity
|
|
* than JSType. This is used to resolve among overloaded Java methods.
|
|
*/
|
|
static JSJType
|
|
compute_jsj_type(JSContext *cx, jsval v)
|
|
{
|
|
JSObject *js_obj;
|
|
|
|
if (JSVAL_IS_OBJECT(v)) {
|
|
if (JSVAL_IS_NULL(v))
|
|
return JSJTYPE_NULL;
|
|
js_obj = JSVAL_TO_OBJECT(v);
|
|
if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0))
|
|
return JSJTYPE_JAVAOBJECT;
|
|
if (JS_InstanceOf(cx, js_obj, &JavaArray_class, 0))
|
|
return JSJTYPE_JAVAARRAY;
|
|
if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0))
|
|
return JSJTYPE_JAVACLASS;
|
|
if (JS_IsArrayObject(cx, js_obj))
|
|
return JSJTYPE_JSARRAY;
|
|
return JSJTYPE_OBJECT;
|
|
} else if (JSVAL_IS_NUMBER(v)) {
|
|
return JSJTYPE_NUMBER;
|
|
} else if (JSVAL_IS_STRING(v)) {
|
|
return JSJTYPE_STRING;
|
|
} else if (JSVAL_IS_BOOLEAN(v)) {
|
|
return JSJTYPE_BOOLEAN;
|
|
} else if (JSVAL_IS_VOID(v)) {
|
|
return JSJTYPE_VOID;
|
|
}
|
|
JS_ASSERT(0); /* Unknown JS type ! */
|
|
return JSJTYPE_VOID;
|
|
}
|
|
|
|
/*
|
|
* Ranking table used to establish preferences among Java types when converting
|
|
* from JavaScript types. Each row represents a different JavaScript source
|
|
* type and each column represents a different target Java type. Lower values
|
|
* in the table indicate conversions that are ranked higher. The special
|
|
* value 99 indicates a disallowed JS->Java conversion. The special value of
|
|
* 0 indicates special handling is required to determine ranking.
|
|
*/
|
|
static int rank_table[JSJTYPE_LIMIT][JAVA_SIGNATURE_LIMIT] = {
|
|
/* boolean long
|
|
| char | float java.lang.Boolean
|
|
| | byte | | double | java.lang.Class
|
|
| | | short | | | array | | java.lang.Double
|
|
| | | | int | | | | object | | netscape.javascript.JSObject
|
|
| | | | | | | | | | | | | | java.lang.Object
|
|
| | | | | | | | | | | | | | | java.lang.String */
|
|
|
|
{99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 1, 1}, /* undefined */
|
|
{ 1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 99, 99, 99, 3, 4}, /* boolean */
|
|
{99, 7, 8, 6, 5, 4, 3, 1, 99, 99, 99, 99, 2, 99, 11, 9}, /* number */
|
|
{99, 3, 4, 4, 4, 4, 4, 4, 99, 99, 99, 99, 99, 99, 2, 1}, /* string */
|
|
{99, 99, 99, 99, 99, 99, 99, 99, 1, 1, 1, 1, 1, 1, 1, 1}, /* null */
|
|
{99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 1, 99, 2, 3, 4}, /* JavaClass */
|
|
{99, 7, 8, 6, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1}, /* JavaObject */
|
|
{99, 99, 99, 99, 99, 99, 99, 99, 0, 0, 99, 99, 99, 99, 0, 1}, /* JavaArray */
|
|
{99, 99, 99, 99, 99, 99, 99, 99, 2, 99, 99, 99, 99, 1, 3, 4}, /* JS Array */
|
|
{99, 9, 10, 8, 7, 6, 5, 4, 99, 99, 99, 99, 99, 1, 2, 3} /* other JS object */
|
|
};
|
|
|
|
/*
|
|
* Returns JS_TRUE if JavaScript arguments are compatible with the specified
|
|
* Java method signature.
|
|
*/
|
|
static JSBool
|
|
method_signature_matches_JS_args(JSContext *cx, JNIEnv *jEnv, uintN argc, jsval *argv,
|
|
JavaMethodSignature *method_signature)
|
|
{
|
|
uintN i;
|
|
JavaClassDescriptor *descriptor;
|
|
JavaObjectWrapper *java_wrapper;
|
|
jclass java_class;
|
|
jobject java_obj;
|
|
JSObject *js_obj;
|
|
JSJType js_type;
|
|
jsval js_val;
|
|
int rank;
|
|
|
|
if (argc != (uintN)method_signature->num_args)
|
|
return JS_FALSE;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
js_val = argv[i];
|
|
js_type = compute_jsj_type(cx, js_val);
|
|
descriptor = method_signature->arg_signatures[i];
|
|
rank = rank_table[js_type][(int)descriptor->type - 2];
|
|
|
|
/* Check for disallowed JS->Java conversion */
|
|
if (rank == 99)
|
|
return JS_FALSE;
|
|
|
|
/* Check for special handling required by conversion from JavaObject */
|
|
if (rank == 0) {
|
|
java_class = descriptor->java_class;
|
|
|
|
js_obj = JSVAL_TO_OBJECT(js_val);
|
|
java_wrapper = JS_GetPrivate(cx, js_obj);
|
|
java_obj = java_wrapper->java_obj;
|
|
|
|
if (!(*jEnv)->IsInstanceOf(jEnv, java_obj, java_class))
|
|
return JS_FALSE;
|
|
}
|
|
}
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
#ifdef HAS_OLD_STYLE_METHOD_RESOLUTION
|
|
|
|
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 (cost < lowest_cost) {
|
|
lowest_cost = cost;
|
|
best_method_match = method;
|
|
num_method_matches++;
|
|
}
|
|
}
|
|
|
|
if (!best_method_match)
|
|
report_method_match_failure(cx, member_descriptor, class_descriptor,
|
|
is_static_method, argc, argv);
|
|
|
|
if (lowest_cost != 0)
|
|
return NULL;
|
|
|
|
return best_method_match;
|
|
}
|
|
|
|
#else /* !OLD_STYLE_METHOD_RESOLUTION */
|
|
|
|
/*
|
|
* Determine the more natural (preferred) JavaScript->Java conversion
|
|
* given one JavaScript value and two Java types.
|
|
* See http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html
|
|
* for details.
|
|
*/
|
|
static JSJTypePreference
|
|
preferred_conversion(JSContext *cx, JNIEnv *jEnv, jsval js_val,
|
|
JavaClassDescriptor *descriptor1,
|
|
JavaClassDescriptor *descriptor2)
|
|
{
|
|
JSJType js_type;
|
|
int rank1, rank2;
|
|
jclass java_class1, java_class2;
|
|
JavaObjectWrapper *java_wrapper;
|
|
jobject java_obj;
|
|
JSObject *js_obj;
|
|
|
|
js_type = compute_jsj_type(cx, js_val);
|
|
rank1 = rank_table[js_type][(int)descriptor1->type - 2];
|
|
rank2 = rank_table[js_type][(int)descriptor2->type - 2];
|
|
|
|
/* Fast path for conversion from most JS types */
|
|
if (rank1 < rank2)
|
|
return JSJPREF_FIRST_ARG;
|
|
|
|
/*
|
|
* Special logic is required for matching the classes of wrapped
|
|
* Java objects.
|
|
*/
|
|
if (rank2 == 0) {
|
|
java_class1 = descriptor1->java_class;
|
|
java_class2 = descriptor2->java_class;
|
|
|
|
js_obj = JSVAL_TO_OBJECT(js_val);
|
|
java_wrapper = JS_GetPrivate(cx, js_obj);
|
|
java_obj = java_wrapper->java_obj;
|
|
|
|
/* Unwrapped JavaObject must be compatible with Java arg type */
|
|
if (!(*jEnv)->IsInstanceOf(jEnv, java_obj, java_class2))
|
|
return JSJPREF_FIRST_ARG;
|
|
|
|
/*
|
|
* For JavaObject arguments, any compatible reference type is preferable
|
|
* to any primitive Java type or to java.lang.String.
|
|
*/
|
|
if (rank1 != 0)
|
|
return JSJPREF_SECOND_ARG;
|
|
|
|
/*
|
|
* If argument of type descriptor1 is subclass of type descriptor 2, then
|
|
* descriptor1 is preferred and vice-versa.
|
|
*/
|
|
if ((*jEnv)->IsAssignableFrom(jEnv, java_class1, java_class2))
|
|
return JSJPREF_FIRST_ARG;
|
|
|
|
if ((*jEnv)->IsAssignableFrom(jEnv, java_class2, java_class1))
|
|
return JSJPREF_SECOND_ARG;
|
|
|
|
/* This can happen in unusual situations involving interface types. */
|
|
return JSJPREF_AMBIGUOUS;
|
|
}
|
|
|
|
if (rank1 > rank2)
|
|
return JSJPREF_SECOND_ARG;
|
|
|
|
return JSJPREF_AMBIGUOUS;
|
|
}
|
|
|
|
static JSJTypePreference
|
|
method_preferred(JSContext *cx, JNIEnv *jEnv, jsval *argv,
|
|
JavaMethodSignature *method_signature1,
|
|
JavaMethodSignature *method_signature2)
|
|
{
|
|
int arg_index, argc, preference;
|
|
jsval val;
|
|
JavaSignature* *arg_signatures1;
|
|
JavaSignature* *arg_signatures2;
|
|
JavaSignature *arg_type1, *arg_type2;
|
|
|
|
arg_signatures1 = method_signature1->arg_signatures;
|
|
arg_signatures2 = method_signature2->arg_signatures;
|
|
argc = method_signature1->num_args;
|
|
JS_ASSERT(argc == method_signature2->num_args);
|
|
|
|
preference = 0;
|
|
for (arg_index = 0; arg_index < argc; arg_index++) {
|
|
val = argv[arg_index];
|
|
arg_type1 = *arg_signatures1++;
|
|
arg_type2 = *arg_signatures2++;
|
|
|
|
if (arg_type1 == arg_type2)
|
|
continue;
|
|
|
|
preference |= preferred_conversion(cx, jEnv, val, arg_type1, arg_type2);
|
|
|
|
if ((JSJTypePreference)preference == JSJPREF_AMBIGUOUS)
|
|
return JSJPREF_AMBIGUOUS;
|
|
}
|
|
return (JSJTypePreference)preference;
|
|
}
|
|
|
|
/*
|
|
* This routine applies heuristics to guess the intended Java method given the
|
|
* runtime JavaScript argument types and the type signatures of the candidate
|
|
* methods. Informally, the method with Java parameter types that most closely
|
|
* match the JavaScript types is chosen. A more precise specification is
|
|
* provided in the lc3_method_resolution.html file. The code uses a very
|
|
* brute-force approach.
|
|
*/
|
|
static JavaMethodSpec *
|
|
resolve_overloaded_method(JSContext *cx, JNIEnv *jEnv,
|
|
JavaMemberDescriptor *member_descriptor,
|
|
JavaClassDescriptor *class_descriptor,
|
|
JSBool is_static_method,
|
|
uintN argc, jsval *argv)
|
|
{
|
|
JSJTypePreference preference;
|
|
JavaMethodSpec *method, *best_method_match;
|
|
MethodList ambiguous_methods;
|
|
MethodListElement *method_list_element, *next_element;
|
|
|
|
/*
|
|
* Determine the first Java method among the overloaded methods of the same name
|
|
* that matches all the JS arguments.
|
|
*/
|
|
for (method = member_descriptor->methods; method; method = method->next) {
|
|
if (method_signature_matches_JS_args(cx, jEnv, argc, argv, &method->signature))
|
|
break;
|
|
}
|
|
|
|
/* Report an error if no method matched the JS arguments */
|
|
if (!method) {
|
|
report_method_match_failure(cx, member_descriptor, class_descriptor,
|
|
is_static_method, argc, argv);
|
|
return NULL;
|
|
}
|
|
|
|
/* Shortcut a common case */
|
|
if (!method->next)
|
|
return method;
|
|
|
|
/*
|
|
* Form a list of all methods that are neither more or less preferred than the
|
|
* best matching method discovered so far.
|
|
*/
|
|
JS_INIT_CLIST(&ambiguous_methods);
|
|
|
|
best_method_match = method;
|
|
|
|
/* See if there are any Java methods that are a better fit for the JS args */
|
|
for (method = method->next; method; method = method->next) {
|
|
if (method->signature.num_args != (int)argc)
|
|
continue;
|
|
preference = method_preferred(cx, jEnv, argv, &best_method_match->signature,
|
|
&method->signature);
|
|
if (preference == JSJPREF_SECOND_ARG) {
|
|
best_method_match = method;
|
|
} else if (preference == JSJPREF_AMBIGUOUS) {
|
|
/* Add this method to the list of ambiguous methods */
|
|
method_list_element =
|
|
(MethodListElement*)JS_malloc(cx, sizeof(MethodListElement));
|
|
if (!method_list_element)
|
|
goto error;
|
|
method_list_element->method = method;
|
|
JS_APPEND_LINK(&method_list_element->linkage, &ambiguous_methods);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Ensure that best_method_match is preferred to all methods on the
|
|
* ambiguous_methods list.
|
|
*/
|
|
|
|
for (method_list_element = (MethodListElement*)JS_LIST_HEAD(&ambiguous_methods);
|
|
(MethodList*)method_list_element != &ambiguous_methods;
|
|
method_list_element = next_element) {
|
|
next_element = (MethodListElement*)method_list_element->linkage.next;
|
|
method = method_list_element->method;
|
|
preference = method_preferred(cx, jEnv, argv, &best_method_match->signature,
|
|
&method->signature);
|
|
if (preference != JSJPREF_FIRST_ARG)
|
|
continue;
|
|
JS_REMOVE_LINK(&method_list_element->linkage);
|
|
JS_free(cx, method_list_element);
|
|
}
|
|
|
|
/*
|
|
* The chosen method must be maximally preferred, i.e. there can be no other
|
|
* method that is just as preferred.
|
|
*/
|
|
if (!JS_CLIST_IS_EMPTY(&ambiguous_methods)) {
|
|
/* Add the best_method_match to the list of ambiguous methods */
|
|
method_list_element =
|
|
(MethodListElement*)JS_malloc(cx, sizeof(MethodListElement));
|
|
if (!method_list_element)
|
|
goto error;
|
|
method_list_element->method = best_method_match;
|
|
JS_APPEND_LINK(&method_list_element->linkage, &ambiguous_methods);
|
|
|
|
/* Report the problem */
|
|
report_ambiguous_method_match(cx, member_descriptor, class_descriptor,
|
|
&ambiguous_methods, is_static_method, argc, argv);
|
|
goto error;
|
|
}
|
|
|
|
return best_method_match;
|
|
|
|
error:
|
|
/* Delete the storage for the ambiguous_method list */
|
|
while (!JS_CLIST_IS_EMPTY(&ambiguous_methods)) {
|
|
method_list_element = (MethodListElement*)JS_LIST_HEAD(&ambiguous_methods);
|
|
JS_REMOVE_LINK(&method_list_element->linkage);
|
|
JS_free(cx, method_list_element);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* !HAS_OLD_STYLE_METHOD_RESOLUTION */
|
|
|
|
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;
|
|
JS_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_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;
|
|
JNIEnv *jEnv;
|
|
JSBool *localv, error_occurred, success;
|
|
|
|
success = error_occurred = JS_FALSE;
|
|
return_val_signature = NULL; /* Quiet gcc uninitialized variable warning */
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
|
|
method call and that new thread wants to perform a GC. */
|
|
#ifdef JSJ_THREADSAFE
|
|
JS_EndRequest(cx);
|
|
#endif
|
|
|
|
#define CALL_JAVA_METHOD(type, member) \
|
|
JS_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; \
|
|
} \
|
|
JS_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_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;
|
|
|
|
case JAVA_SIGNATURE_UNKNOWN:
|
|
JS_ASSERT(0);
|
|
error_occurred = JS_TRUE;
|
|
goto out;
|
|
|
|
/* Non-primitive (reference) type */
|
|
default:
|
|
JS_ASSERT(IS_REFERENCE_TYPE(return_val_signature->type));
|
|
CALL_JAVA_METHOD(Object, l);
|
|
break;
|
|
}
|
|
|
|
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);
|
|
|
|
#ifdef JSJ_THREADSAFE
|
|
JS_BeginRequest(cx);
|
|
#endif
|
|
|
|
if (!error_occurred) {
|
|
success = jsj_ConvertJavaValueToJSValue(cx, jEnv, return_val_signature, &java_value, vp);
|
|
if (IS_REFERENCE_TYPE(return_val_signature->type))
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_value.l);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
JNIEnv *jEnv;
|
|
JSBool *localv;
|
|
JSBool success, error_occurred;
|
|
java_object = NULL; /* Stifle gcc uninitialized variable warning */
|
|
|
|
success = 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;
|
|
}
|
|
}
|
|
|
|
/* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
|
|
method call and that new thread wants to perform a GC. */
|
|
#ifdef JSJ_THREADSAFE
|
|
JS_EndRequest(cx);
|
|
#endif
|
|
|
|
/* Call the constructor */
|
|
java_object = (*jEnv)->NewObjectA(jEnv, java_class, methodID, jargv);
|
|
|
|
#ifdef JSJ_THREADSAFE
|
|
JS_BeginRequest(cx);
|
|
#endif
|
|
|
|
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)
|
|
success = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_object, vp);
|
|
(*jEnv)->DeleteLocalRef(jEnv, java_object);
|
|
return success;
|
|
}
|
|
|
|
static JSBool
|
|
invoke_overloaded_java_constructor(JSContext *cx,
|
|
JSJavaThreadState *jsj_env,
|
|
JavaMemberDescriptor *member,
|
|
JavaClassDescriptor *class_descriptor,
|
|
uintN argc, jsval *argv,
|
|
jsval *vp)
|
|
{
|
|
jclass java_class;
|
|
JavaMethodSpec *method;
|
|
JNIEnv *jEnv;
|
|
|
|
jEnv = jsj_env->jEnv;
|
|
|
|
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);
|
|
}
|
|
|
|
static JSBool
|
|
java_constructor_wrapper(JSContext *cx, JSJavaThreadState *jsj_env,
|
|
JavaMemberDescriptor *member_descriptor,
|
|
JavaClassDescriptor *class_descriptor,
|
|
uintN argc, jsval *argv, jsval *vp)
|
|
{
|
|
jint modifiers;
|
|
JNIEnv *jEnv;
|
|
|
|
jEnv = jsj_env->jEnv;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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,
|
|
class_descriptor, argc, argv, vp);
|
|
}
|
|
|
|
JS_EXPORT_API(JSBool)
|
|
jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *vp)
|
|
{
|
|
JavaClassDescriptor *class_descriptor;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JSJavaThreadState *jsj_env;
|
|
JNIEnv *jEnv;
|
|
JSBool result;
|
|
|
|
obj = JSVAL_TO_OBJECT(argv[-2]);
|
|
class_descriptor = JS_GetPrivate(cx, obj);
|
|
JS_ASSERT(class_descriptor);
|
|
if (!class_descriptor)
|
|
return JS_FALSE;
|
|
|
|
/* XXX, workaround for bug 200016, all classes in sun.plugin package should not
|
|
be accessible in liveconnect.
|
|
Ideally, this checking should be done in JPI side, but it's not going to happen
|
|
until Sun JRE 1.5.1 */
|
|
if (strstr(class_descriptor->name, "sun.plugin.") == class_descriptor->name)
|
|
return JS_FALSE;
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_env = jsj_EnterJava(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
|
|
result = java_constructor_wrapper(cx, jsj_env, member_descriptor,
|
|
class_descriptor, argc, argv, vp);
|
|
jsj_ExitJava(jsj_env);
|
|
return result;
|
|
}
|
|
|
|
|
|
static JSBool
|
|
static_method_wrapper(JSContext *cx, JSJavaThreadState *jsj_env,
|
|
JavaClassDescriptor *class_descriptor,
|
|
jsid id,
|
|
uintN argc, jsval *argv, jsval *vp)
|
|
{
|
|
JNIEnv *jEnv;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
|
|
jEnv = jsj_env->jEnv;
|
|
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
|
|
|
|
/* Is it a static method that is not a constructor ? */
|
|
if (member_descriptor && strcmp(member_descriptor->name, "<init>")) {
|
|
return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
|
|
class_descriptor->java_class,
|
|
class_descriptor, argc, argv, vp);
|
|
}
|
|
|
|
JS_ASSERT(member_descriptor);
|
|
if (!member_descriptor)
|
|
return JS_FALSE;
|
|
|
|
/* Must be an explicitly resolved overloaded constructor */
|
|
return java_constructor_wrapper(cx, jsj_env, member_descriptor,
|
|
class_descriptor, argc, argv, vp);
|
|
}
|
|
|
|
JS_EXTERN_API(JSBool)
|
|
jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *vp)
|
|
{
|
|
JSFunction *function;
|
|
JavaClassDescriptor *class_descriptor;
|
|
jsid id;
|
|
jsval idval;
|
|
JNIEnv *jEnv;
|
|
JSJavaThreadState *jsj_env;
|
|
JSBool result;
|
|
|
|
class_descriptor = JS_GetPrivate(cx, obj);
|
|
if (!class_descriptor)
|
|
return JS_FALSE;
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_env = jsj_EnterJava(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
JS_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);
|
|
|
|
result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
|
|
jsj_ExitJava(jsj_env);
|
|
return result;
|
|
}
|
|
|
|
JS_EXPORT_API(JSBool)
|
|
jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
|
|
uintN argc, jsval *argv, jsval *vp)
|
|
{
|
|
JSFunction *function;
|
|
JavaMemberDescriptor *member_descriptor;
|
|
JavaObjectWrapper *java_wrapper;
|
|
JavaClassDescriptor *class_descriptor;
|
|
jsid id;
|
|
jsval idval;
|
|
JSJavaThreadState *jsj_env;
|
|
JNIEnv *jEnv;
|
|
jobject java_obj;
|
|
JSBool result;
|
|
|
|
java_wrapper = JS_GetPrivate(cx, obj);
|
|
if (!java_wrapper)
|
|
return JS_FALSE;
|
|
java_obj = java_wrapper->java_obj;
|
|
|
|
JS_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;
|
|
|
|
/* Get the Java per-thread environment pointer for this JSContext */
|
|
jsj_env = jsj_EnterJava(cx, &jEnv);
|
|
if (!jEnv)
|
|
return JS_FALSE;
|
|
|
|
if (jaApplet && (*jEnv)->IsInstanceOf(jEnv, java_obj, jaApplet)) {
|
|
jsj_JSIsCallingApplet = JS_TRUE;
|
|
}
|
|
|
|
/* Try to find an instance method with the given name first */
|
|
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
|
|
if (member_descriptor)
|
|
result = invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
|
|
JS_FALSE, java_obj,
|
|
class_descriptor, argc, argv, vp);
|
|
|
|
/* If no instance method was found, try for a static method or constructor */
|
|
else
|
|
result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
|
|
jsj_ExitJava(jsj_env);
|
|
return result;
|
|
}
|
|
|