1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * This Original Code has been modified by IBM Corporation. Modifications made
39 * by IBM described herein are Copyright (c) International Business Machines
40 * Corporation, 2000.
41 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
42 *
43 * Date Modified by Description of modification
44 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
45 *
46 * ***** END LICENSE BLOCK ***** */
47
48 /*
49 * This file is part of the Java-vendor-neutral implementation of LiveConnect
50 *
51 * It contains the code used to reflect Java methods as properties of
52 * JavaObject objects and the code to invoke those methods.
53 *
54 */
55
56 #include <stdlib.h>
57 #include <string.h>
58
59 #include "jsj_private.h" /* LiveConnect internals */
60 #include "jsjava.h" /* LiveConnect external API */
61 #include "jsclist.h" /* Circular linked lists */
62
63 /* A list of Java methods */
64 typedef JSCList MethodList;
65
66 /* An element in a list of Java methods */
67 typedef struct MethodListElement {
68 JSCList linkage;
69 JavaMethodSpec *method;
70 } MethodListElement;
71
72 /*
73 * This is the return value of functions which compare either two types or two
74 * method signatures to see if either is "preferred" when converting from
75 * specified JavaScript value(s).
76 */
77 typedef enum JSJTypePreference {
78 JSJPREF_FIRST_ARG = 1, /* First argument preferred */
79 JSJPREF_SECOND_ARG = 2, /* Second argument preferred */
80 JSJPREF_AMBIGUOUS = 3 /* Neither preferred over the other */
81 } JSJTypePreference;
82
83 /*
84 * Classification of JS types with slightly different granularity than JSType.
85 * This is used to resolve among overloaded Java methods.
86 */
87 typedef enum JSJType {
88 JSJTYPE_VOID, /* undefined */
89 JSJTYPE_BOOLEAN, /* boolean */
90 JSJTYPE_NUMBER, /* number */
91 JSJTYPE_STRING, /* string */
92 JSJTYPE_NULL, /* null */
93 JSJTYPE_JAVACLASS, /* JavaClass */
94 JSJTYPE_JAVAOBJECT, /* JavaObject */
95 JSJTYPE_JAVAARRAY, /* JavaArray */
96 JSJTYPE_JSARRAY, /* JS Array */
97 JSJTYPE_OBJECT, /* Any other JS Object, including functions */
98 JSJTYPE_LIMIT
99 } JSJType;
100
101 /*
102 * A helper function for jsj_ConvertJavaMethodSignatureToString():
103 * Compute JNI-style (string) signatures for an array of JavaSignature objects
104 * and concatenate the results into a single string.
105 *
106 * If an error is encountered, NULL is returned and the error reporter is called.
107 */
108 static const char *
convert_java_method_arg_signatures_to_string(JSContext * cx,JavaSignature ** arg_signatures,int num_args)109 convert_java_method_arg_signatures_to_string(JSContext *cx,
110 JavaSignature **arg_signatures,
111 int num_args)
112 {
113 const char *first_arg_signature, *rest_arg_signatures, *sig;
114 JavaSignature **rest_args;
115
116 /* Convert the first method argument in the array to a string */
117 first_arg_signature = jsj_ConvertJavaSignatureToString(cx, arg_signatures[0]);
118 if (!first_arg_signature)
119 return NULL;
120
121 /* If this is the last method argument in the array, we're done. */
122 if (num_args == 1)
123 return first_arg_signature;
124
125 /* Convert the remaining method arguments to a string */
126 rest_args = &arg_signatures[1];
127 rest_arg_signatures =
128 convert_java_method_arg_signatures_to_string(cx, rest_args, num_args - 1);
129 if (!rest_arg_signatures) {
130 free((void*)first_arg_signature);
131 return NULL;
132 }
133
134 /* Concatenate the signature string of this argument with the signature
135 strings of all the remaining arguments. */
136 sig = JS_smprintf("%s%s", first_arg_signature, rest_arg_signatures);
137 free((void*)first_arg_signature);
138 free((void*)rest_arg_signatures);
139 if (!sig) {
140 JS_ReportOutOfMemory(cx);
141 return NULL;
142 }
143
144 return sig;
145 }
146
147 /*
148 * A helper function for jsj_ConvertJavaMethodSignatureToHRString():
149 * Compute human-readable string signatures for an array of JavaSignatures
150 * and concatenate the results into a single string.
151 *
152 * If an error is encountered, NULL is returned and the error reporter is called.
153 */
154 static const char *
convert_java_method_arg_signatures_to_hr_string(JSContext * cx,JavaSignature ** arg_signatures,int num_args,JSBool whitespace)155 convert_java_method_arg_signatures_to_hr_string(JSContext *cx,
156 JavaSignature **arg_signatures,
157 int num_args,
158 JSBool whitespace)
159 {
160 const char *first_arg_signature, *rest_arg_signatures, *sig, *separator;
161 JavaSignature **rest_args;
162
163 if (num_args == 0)
164 return strdup("");
165
166 /* Convert the first method argument in the array to a string */
167 first_arg_signature = jsj_ConvertJavaSignatureToHRString(cx, arg_signatures[0]);
168 if (!first_arg_signature)
169 return NULL;
170
171 /* If this is the last method argument in the array, we're done. */
172 if (num_args == 1)
173 return first_arg_signature;
174
175 /* Convert the remaining method arguments to a string */
176 rest_args = &arg_signatures[1];
177 rest_arg_signatures =
178 convert_java_method_arg_signatures_to_hr_string(cx, rest_args, num_args - 1, whitespace);
179 if (!rest_arg_signatures) {
180 free((void*)first_arg_signature);
181 return NULL;
182 }
183
184 /* Concatenate the signature string of this argument with the signature
185 strings of all the remaining arguments. */
186 separator = whitespace ? " " : "";
187 sig = JS_smprintf("%s,%s%s", first_arg_signature, separator, rest_arg_signatures);
188 free((void*)first_arg_signature);
189 free((void*)rest_arg_signatures);
190 if (!sig) {
191 JS_ReportOutOfMemory(cx);
192 return NULL;
193 }
194
195 return sig;
196 }
197 /*
198 * Compute a string signature for the given method using the same type names
199 * that appear in Java source files, e.g. "int[][]", rather than the JNI-style
200 * type strings that are provided by the functions above. An example return
201 * value might be "String MyFunc(int, byte, char[][], java.lang.String)".
202 *
203 * If an error is encountered, NULL is returned and the error reporter is called.
204 */
205 const char *
jsj_ConvertJavaMethodSignatureToHRString(JSContext * cx,const char * method_name,JavaMethodSignature * method_signature)206 jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
207 const char *method_name,
208 JavaMethodSignature *method_signature)
209 {
210 JavaSignature **arg_signatures, *return_val_signature;
211 const char *arg_sigs_cstr;
212 const char *return_val_sig_cstr;
213 const char *sig_cstr;
214
215 arg_signatures = method_signature->arg_signatures;
216 return_val_signature = method_signature->return_val_signature;
217
218 /* Convert the method argument signatures to a C-string */
219 arg_sigs_cstr =
220 convert_java_method_arg_signatures_to_hr_string(cx, arg_signatures,
221 method_signature->num_args,
222 JS_TRUE);
223 if (!arg_sigs_cstr)
224 return NULL;
225
226 /* Convert the method return value signature to a C-string */
227 return_val_sig_cstr = jsj_ConvertJavaSignatureToHRString(cx, return_val_signature);
228 if (!return_val_sig_cstr) {
229 free((void*)arg_sigs_cstr);
230 return NULL;
231 }
232
233 /* Compose method arg signatures string and return val signature string */
234 sig_cstr = JS_smprintf("%s %s(%s)", return_val_sig_cstr, method_name, arg_sigs_cstr);
235 free((void*)arg_sigs_cstr);
236 free((void*)return_val_sig_cstr);
237
238 if (!sig_cstr) {
239 JS_ReportOutOfMemory(cx);
240 return NULL;
241 }
242 return sig_cstr;
243 }
244 /*
245 * Destroy the sub-structure of a JavaMethodSignature object without free'ing
246 * the method signature itself.
247 */
248 void
jsj_PurgeJavaMethodSignature(JSContext * cx,JNIEnv * jEnv,JavaMethodSignature * method_signature)249 jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *method_signature)
250 {
251 int i, num_args;
252 JavaSignature **arg_signatures;
253
254 if (!method_signature) /* Paranoia */
255 return;
256
257 /* Free the method argument signatures */
258 num_args = method_signature->num_args;
259 arg_signatures = method_signature->arg_signatures;
260 for (i = 0; i < num_args; i++)
261 jsj_ReleaseJavaClassDescriptor(cx, jEnv, arg_signatures[i]);
262 if (arg_signatures)
263 JS_free(cx, arg_signatures);
264
265 /* Free the return type signature */
266 if (method_signature->return_val_signature)
267 jsj_ReleaseJavaClassDescriptor(cx, jEnv, method_signature->return_val_signature);
268 }
269
270 /*
271 * Fill in the members of a JavaMethodSignature object using the method
272 * argument, which can be either an instance of either java.lang.reflect.Method
273 * or java.lang.reflect.Constructor.
274 *
275 * With normal completion, return the original method_signature argument.
276 * If an error occurs, return NULL and call the error reporter.
277 */
278 JavaMethodSignature *
jsj_InitJavaMethodSignature(JSContext * cx,JNIEnv * jEnv,jobject method,JavaMethodSignature * method_signature)279 jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv,
280 jobject method,
281 JavaMethodSignature *method_signature)
282 {
283 int i;
284 jboolean is_constructor;
285 jclass return_val_class;
286 jsize num_args;
287 JavaSignature *return_val_signature;
288 jarray arg_classes;
289 jmethodID getParameterTypes;
290
291 memset(method_signature, 0, sizeof (JavaMethodSignature));
292
293 is_constructor = (*jEnv)->IsInstanceOf(jEnv, method, jlrConstructor);
294
295 /* Get a Java array that lists the class of each of the method's arguments */
296 if (is_constructor)
297 getParameterTypes = jlrConstructor_getParameterTypes;
298 else
299 getParameterTypes = jlrMethod_getParameterTypes;
300 arg_classes = (*jEnv)->CallObjectMethod(jEnv, method, getParameterTypes);
301 if (!arg_classes) {
302 jsj_UnexpectedJavaError(cx, jEnv,
303 "Can't determine argument signature of method");
304 goto error;
305 }
306
307 /* Compute the number of method arguments */
308 num_args = jsj_GetJavaArrayLength(cx, jEnv, arg_classes);
309 if (num_args < 0)
310 goto error;
311 method_signature->num_args = num_args;
312
313 /* Build a JavaSignature array corresponding to the method's arguments */
314 if (num_args) {
315 JavaSignature **arg_signatures;
316
317 /* Allocate an array of JavaSignatures, one for each method argument */
318 size_t arg_signatures_size = num_args * sizeof(JavaSignature *);
319 arg_signatures = (JavaSignature **)JS_malloc(cx, arg_signatures_size);
320 if (!arg_signatures)
321 goto error;
322 memset(arg_signatures, 0, arg_signatures_size);
323 method_signature->arg_signatures = arg_signatures;
324
325 /* Build an array of JavaSignature objects, each of which corresponds
326 to the type of an individual method argument. */
327 for (i = 0; i < num_args; i++) {
328 JavaSignature *a;
329 jclass arg_class = (*jEnv)->GetObjectArrayElement(jEnv, arg_classes, i);
330
331 a = arg_signatures[i] = jsj_GetJavaClassDescriptor(cx, jEnv, arg_class);
332 (*jEnv)->DeleteLocalRef(jEnv, arg_class);
333 if (!a) {
334 jsj_UnexpectedJavaError(cx, jEnv, "Could not determine Java class "
335 "signature using java.lang.reflect");
336 goto error;
337 }
338 }
339 }
340
341 /* Get the Java class of the method's return value */
342 if (is_constructor) {
343 /* Constructors always have a "void" return type */
344 return_val_signature = jsj_GetJavaClassDescriptor(cx, jEnv, jlVoid_TYPE);
345 } else {
346 return_val_class =
347 (*jEnv)->CallObjectMethod(jEnv, method, jlrMethod_getReturnType);
348 if (!return_val_class) {
349 jsj_UnexpectedJavaError(cx, jEnv,
350 "Can't determine return type of method "
351 "using java.lang.reflect.Method.getReturnType()");
352 goto error;
353 }
354
355 /* Build a JavaSignature object corresponding to the method's return value */
356 return_val_signature = jsj_GetJavaClassDescriptor(cx, jEnv, return_val_class);
357 (*jEnv)->DeleteLocalRef(jEnv, return_val_class);
358 }
359
360 if (!return_val_signature)
361 goto error;
362 method_signature->return_val_signature = return_val_signature;
363
364 (*jEnv)->DeleteLocalRef(jEnv, arg_classes);
365 return method_signature;
366
367 error:
368
369 if (arg_classes)
370 (*jEnv)->DeleteLocalRef(jEnv, arg_classes);
371 jsj_PurgeJavaMethodSignature(cx, jEnv, method_signature);
372 return NULL;
373 }
374
375 /*
376 * Compute a JNI-style (string) signature for the given method, e.g. the method
377 * "String MyFunc(int, byte)" is translated to "(IB)Ljava/lang/String;".
378 *
379 * If an error is encountered, NULL is returned and the error reporter is called.
380 */
381 const char *
jsj_ConvertJavaMethodSignatureToString(JSContext * cx,JavaMethodSignature * method_signature)382 jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
383 JavaMethodSignature *method_signature)
384 {
385 JavaSignature **arg_signatures, *return_val_signature;
386 const char *arg_sigs_cstr;
387 const char *return_val_sig_cstr;
388 const char *sig_cstr;
389
390 arg_signatures = method_signature->arg_signatures;
391 return_val_signature = method_signature->return_val_signature;
392
393 /* Convert the method argument signatures to a C-string */
394 arg_sigs_cstr = NULL;
395 if (arg_signatures) {
396 arg_sigs_cstr =
397 convert_java_method_arg_signatures_to_string(cx, arg_signatures,
398 method_signature->num_args);
399 if (!arg_sigs_cstr)
400 return NULL;
401 }
402
403 /* Convert the method return value signature to a C-string */
404 return_val_sig_cstr = jsj_ConvertJavaSignatureToString(cx, return_val_signature);
405 if (!return_val_sig_cstr) {
406 free((void*)arg_sigs_cstr);
407 return NULL;
408 }
409
410 /* Compose method arg signatures string and return val signature string */
411 if (arg_sigs_cstr) {
412 sig_cstr = JS_smprintf("(%s)%s", arg_sigs_cstr, return_val_sig_cstr);
413 free((void*)arg_sigs_cstr);
414 } else {
415 sig_cstr = JS_smprintf("()%s", return_val_sig_cstr);
416 }
417
418 free((void*)return_val_sig_cstr);
419
420 if (!sig_cstr) {
421 JS_ReportOutOfMemory(cx);
422 return NULL;
423 }
424 return sig_cstr;
425 }
426
427 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)428 add_java_method_to_class_descriptor(JSContext *cx, JNIEnv *jEnv,
429 JavaClassDescriptor *class_descriptor,
430 jstring method_name_jstr,
431 jobject java_method,
432 JSBool is_static_method,
433 JSBool is_constructor)
434 {
435 jmethodID methodID;
436 JSFunction *fun;
437 jclass java_class = class_descriptor->java_class;
438
439 JavaMemberDescriptor *member_descriptor = NULL;
440 const char *sig_cstr = NULL;
441 const char *method_name = NULL;
442 JavaMethodSignature *signature = NULL;
443 JavaMethodSpec **specp, *method_spec = NULL;
444
445 if (is_constructor) {
446 member_descriptor = jsj_GetJavaClassConstructors(cx, class_descriptor);
447 } else {
448 if (is_static_method) {
449 member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
450 } else {
451 member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, method_name_jstr);
452 }
453 fun = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0,
454 JSFUN_BOUND_METHOD, NULL, member_descriptor->name);
455 member_descriptor->invoke_func_obj = JS_GetFunctionObject(fun);
456 JS_AddNamedRoot(cx, &member_descriptor->invoke_func_obj,
457 "&member_descriptor->invoke_func_obj");
458 }
459 if (!member_descriptor)
460 return JS_FALSE;
461
462 method_spec = (JavaMethodSpec*)JS_malloc(cx, sizeof(JavaMethodSpec));
463 if (!method_spec)
464 goto error;
465 memset(method_spec, 0, sizeof(JavaMethodSpec));
466
467 signature = jsj_InitJavaMethodSignature(cx, jEnv, java_method, &method_spec->signature);
468 if (!signature)
469 goto error;
470
471 method_name = JS_strdup(cx, member_descriptor->name);
472 if (!method_name)
473 goto error;
474 method_spec->name = method_name;
475
476 sig_cstr = jsj_ConvertJavaMethodSignatureToString(cx, signature);
477 if (!sig_cstr)
478 goto error;
479
480 if (is_static_method)
481 methodID = (*jEnv)->GetStaticMethodID(jEnv, java_class, method_name, sig_cstr);
482 else
483 methodID = (*jEnv)->GetMethodID(jEnv, java_class, method_name, sig_cstr);
484 method_spec->methodID = methodID;
485
486 if (!methodID) {
487 jsj_UnexpectedJavaError(cx, jEnv,
488 "Can't get Java method ID for %s.%s() (sig=%s)",
489 class_descriptor->name, method_name, sig_cstr);
490 goto error;
491 }
492
493 JS_free(cx, (char*)sig_cstr);
494
495 /* Add method to end of list of overloaded methods for this class member */
496 specp = &member_descriptor->methods;
497 while (*specp) {
498 specp = &(*specp)->next;
499 }
500 *specp = method_spec;
501
502 return JS_TRUE;
503
504 error:
505 if (method_spec)
506 JS_FREE_IF(cx, (char*)method_spec->name);
507 JS_FREE_IF(cx, (char*)sig_cstr);
508 if (signature)
509 jsj_PurgeJavaMethodSignature(cx, jEnv, signature);
510 JS_FREE_IF(cx, method_spec);
511 return JS_FALSE;
512 }
513
514 JSBool
jsj_ReflectJavaMethods(JSContext * cx,JNIEnv * jEnv,JavaClassDescriptor * class_descriptor,JSBool reflect_only_static_methods)515 jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
516 JavaClassDescriptor *class_descriptor,
517 JSBool reflect_only_static_methods)
518 {
519 jarray joMethodArray, joConstructorArray;
520 jsize num_methods, num_constructors;
521 int i;
522 jclass java_class;
523 JSBool ok, reflect_only_instance_methods;
524
525 reflect_only_instance_methods = !reflect_only_static_methods;
526
527 /* Get a java array of java.lang.reflect.Method objects, by calling
528 java.lang.Class.getMethods(). */
529 java_class = class_descriptor->java_class;
530 joMethodArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getMethods);
531 if (!joMethodArray) {
532 jsj_UnexpectedJavaError(cx, jEnv,
533 "Can't determine Java object's methods "
534 "using java.lang.Class.getMethods()");
535 return JS_FALSE;
536 }
537
538 /* Iterate over the class methods */
539 num_methods = (*jEnv)->GetArrayLength(jEnv, joMethodArray);
540 for (i = 0; i < num_methods; i++) {
541 jstring method_name_jstr;
542
543 /* Get the i'th reflected method */
544 jobject java_method = (*jEnv)->GetObjectArrayElement(jEnv, joMethodArray, i);
545
546 /* Get the method modifiers, eg static, public, private, etc. */
547 jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_method, jlrMethod_getModifiers);
548
549 /* Don't allow access to private or protected Java methods. */
550 if (!(modifiers & ACC_PUBLIC))
551 goto dont_reflect_method;
552
553 /* Abstract methods can't be invoked */
554 if (modifiers & ACC_ABSTRACT)
555 goto dont_reflect_method;
556
557 /* Reflect all instance methods or all static methods, but not both */
558 if (reflect_only_static_methods != ((modifiers & ACC_STATIC) != 0))
559 goto dont_reflect_method;
560
561 /* Add a JavaMethodSpec object to the JavaClassDescriptor */
562 method_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_method, jlrMethod_getName);
563 ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, method_name_jstr, java_method,
564 reflect_only_static_methods, JS_FALSE);
565 (*jEnv)->DeleteLocalRef(jEnv, method_name_jstr);
566 if (!ok) {
567 (*jEnv)->DeleteLocalRef(jEnv, java_method);
568 (*jEnv)->DeleteLocalRef(jEnv, joMethodArray);
569 return JS_FALSE;
570 }
571
572 dont_reflect_method:
573 (*jEnv)->DeleteLocalRef(jEnv, java_method);
574 }
575
576 (*jEnv)->DeleteLocalRef(jEnv, joMethodArray);
577 if (reflect_only_instance_methods)
578 return JS_TRUE;
579
580 joConstructorArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getConstructors);
581 if (!joConstructorArray) {
582 jsj_UnexpectedJavaError(cx, jEnv, "internal error: "
583 "Can't determine Java class's constructors "
584 "using java.lang.Class.getConstructors()");
585 return JS_FALSE;
586 }
587
588 /* Iterate over the class constructors */
589 num_constructors = (*jEnv)->GetArrayLength(jEnv, joConstructorArray);
590 for (i = 0; i < num_constructors; i++) {
591 /* Get the i'th reflected constructor */
592 jobject java_constructor =
593 (*jEnv)->GetObjectArrayElement(jEnv, joConstructorArray, i);
594
595 /* Get the method modifiers, eg public, private, etc. */
596 jint modifiers = (*jEnv)->CallIntMethod(jEnv, java_constructor,
597 jlrConstructor_getModifiers);
598
599 /* Don't allow access to private or protected Java methods. */
600 if (!(modifiers & ACC_PUBLIC))
601 goto dont_reflect_constructor;
602
603 /* Add a JavaMethodSpec object to the JavaClassDescriptor */
604 ok = add_java_method_to_class_descriptor(cx, jEnv, class_descriptor, NULL,
605 java_constructor,
606 JS_FALSE, JS_TRUE);
607 if (!ok) {
608 (*jEnv)->DeleteLocalRef(jEnv, joConstructorArray);
609 (*jEnv)->DeleteLocalRef(jEnv, java_constructor);
610 return JS_FALSE;
611 }
612
613 dont_reflect_constructor:
614 (*jEnv)->DeleteLocalRef(jEnv, java_constructor);
615 }
616
617 (*jEnv)->DeleteLocalRef(jEnv, joConstructorArray);
618 return JS_TRUE;
619 }
620
621 /*
622 * Free up a JavaMethodSpec and all its resources.
623 */
624 void
jsj_DestroyMethodSpec(JSContext * cx,JNIEnv * jEnv,JavaMethodSpec * method_spec)625 jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec)
626 {
627 if (!method_spec->is_alias) {
628 JS_FREE_IF(cx, (char*)method_spec->name);
629 jsj_PurgeJavaMethodSignature(cx, jEnv, &method_spec->signature);
630 }
631 JS_free(cx, method_spec);
632 }
633
634 static JavaMethodSpec *
copy_java_method_descriptor(JSContext * cx,JavaMethodSpec * method)635 copy_java_method_descriptor(JSContext *cx, JavaMethodSpec *method)
636 {
637 JavaMethodSpec *copy;
638
639 copy = (JavaMethodSpec*)JS_malloc(cx, sizeof(JavaMethodSpec));
640 if (!copy)
641 return NULL;
642 memcpy(copy, method, sizeof(JavaMethodSpec));
643 copy->next = NULL;
644 copy->is_alias = JS_TRUE;
645 return copy;
646 }
647
648 /*
649 * See if a reference to a JavaObject/JavaClass's property looks like the
650 * explicit resolution of an overloaded method, e.g.
651 * java.lang.String["max(double,double)"]. If so, add a new member
652 * (JavaMemberDescriptor) to the object that is an alias for the resolved
653 * method.
654 */
655 JavaMemberDescriptor *
jsj_ResolveExplicitMethod(JSContext * cx,JNIEnv * jEnv,JavaClassDescriptor * class_descriptor,jsid method_name_id,JSBool is_static)656 jsj_ResolveExplicitMethod(JSContext *cx, JNIEnv *jEnv,
657 JavaClassDescriptor *class_descriptor,
658 jsid method_name_id,
659 JSBool is_static)
660 {
661 JavaMethodSpec *method;
662 JavaMemberDescriptor *member_descriptor;
663 JavaMethodSignature *ms;
664 JSString *simple_name_jsstr;
665 JSFunction *fun;
666 JSBool is_constructor;
667 int left_paren;
668 const char *sig_cstr, *method_name;
669 char *arg_start;
670 jsid id;
671 jsval method_name_jsval;
672
673 /*
674 * Get the simple name of the explicit method, i.e. get "cos" from
675 * "cos(double)", and use it to create a new JS string.
676 */
677 JS_IdToValue(cx, method_name_id, &method_name_jsval);
678 method_name = JS_GetStringBytes(JSVAL_TO_STRING(method_name_jsval));
679 arg_start = strchr(method_name, '('); /* Skip until '(' */
680 /* If no left-paren, then this is not a case of explicit method resolution */
681 if (!arg_start)
682 return NULL;
683 /* Left-paren must be first character for constructors */
684 is_constructor = (is_static && (arg_start == method_name));
685
686 left_paren = arg_start - method_name;
687 simple_name_jsstr = JS_NewStringCopyN(cx, method_name, left_paren);
688 if (!simple_name_jsstr)
689 return NULL;
690
691 /* Find all the methods in the same class with the same simple name */
692 JS_ValueToId(cx, STRING_TO_JSVAL(simple_name_jsstr), &id);
693 if (is_constructor)
694 member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
695 else if (is_static)
696 member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
697 else
698 member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
699 if (!member_descriptor) /* No member with matching simple name ? */
700 return NULL;
701
702 /*
703 * Do a UTF8 comparison of method signatures with all methods of the same name,
704 * so as to discover a method which exactly matches the specified argument types.
705 */
706 if (!strlen(arg_start + 1))
707 return NULL;
708 arg_start = JS_strdup(cx, arg_start + 1);
709 if (!arg_start)
710 return NULL;
711 arg_start[strlen(arg_start) - 1] = '\0'; /* Get rid of ')' */
712 sig_cstr = NULL; /* Quiet gcc warning about uninitialized variable */
713 for (method = member_descriptor->methods; method; method = method->next) {
714 ms = &method->signature;
715 sig_cstr = convert_java_method_arg_signatures_to_hr_string(cx, ms->arg_signatures,
716 ms->num_args, JS_FALSE);
717 if (!sig_cstr)
718 return NULL;
719
720 if (!strcmp(sig_cstr, arg_start))
721 break;
722 JS_free(cx, (void*)sig_cstr);
723 }
724 JS_free(cx, arg_start);
725 if (!method)
726 return NULL;
727 JS_free(cx, (void*)sig_cstr);
728
729 /* Don't bother doing anything if the method isn't overloaded */
730 if (!member_descriptor->methods->next)
731 return member_descriptor;
732
733 /*
734 * To speed up performance for future accesses, create a new member descriptor
735 * with a name equal to the explicit method name, i.e. "cos(double)".
736 */
737 member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
738 if (!member_descriptor)
739 return NULL;
740 memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
741
742 member_descriptor->id = method_name_id;
743 member_descriptor->name =
744 JS_strdup(cx, is_constructor ? "<init>" : JS_GetStringBytes(simple_name_jsstr));
745 if (!member_descriptor->name) {
746 JS_free(cx, member_descriptor);
747 return NULL;
748 }
749
750 member_descriptor->methods = copy_java_method_descriptor(cx, method);
751 if (!member_descriptor->methods) {
752 JS_free(cx, (void*)member_descriptor->name);
753 JS_free(cx, member_descriptor);
754 return NULL;
755 }
756
757 fun = JS_NewFunction(cx, jsj_JavaInstanceMethodWrapper, 0,
758 JSFUN_BOUND_METHOD, NULL, method_name);
759 member_descriptor->invoke_func_obj = JS_GetFunctionObject(fun);
760 JS_AddNamedRoot(cx, &member_descriptor->invoke_func_obj,
761 "&member_descriptor->invoke_func_obj");
762
763 /* THREADSAFETY */
764 /* Add the new aliased member to the list of all members for the class */
765 if (is_static) {
766 member_descriptor->next = class_descriptor->static_members;
767 class_descriptor->static_members = member_descriptor;
768 } else {
769 member_descriptor->next = class_descriptor->instance_members;
770 class_descriptor->instance_members = member_descriptor;
771 }
772
773 return member_descriptor;
774 }
775
776 /*
777 * Return the JavaScript types that a JavaScript method was invoked with
778 * as a string, e.g. a JS method invoked like foo(3, 'hey', 'you', true)
779 * would cause a return value of "(number, string, string, boolean)".
780 * The returned string must be free'ed by the caller.
781 * Returns NULL and reports an error if out-of-memory.
782 */
783 static const char *
get_js_arg_types_as_string(JSContext * cx,uintN argc,jsval * argv)784 get_js_arg_types_as_string(JSContext *cx, uintN argc, jsval *argv)
785 {
786 uintN i;
787 const char *arg_type, *arg_string, *tmp;
788
789 if (argc == 0)
790 return strdup("()");
791
792 arg_string = strdup("(");
793 if (!arg_string)
794 goto out_of_memory;
795 for (i = 0; i < argc; i++) {
796 arg_type = JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i]));
797 tmp = JS_smprintf("%s%s%s%s", arg_string, i ? ", " : "", arg_type,
798 (i == argc-1) ? ")" : "");
799 free((char*)arg_string);
800 if (!tmp)
801 goto out_of_memory;
802 arg_string = tmp;
803 }
804
805 return arg_string;
806
807 out_of_memory:
808 JS_ReportOutOfMemory(cx);
809 return NULL;
810 }
811
812 /*
813 * This is an error reporting routine used when a method of the correct name
814 * and class exists but either the number of arguments is incorrect or the
815 * arguments are of the wrong type.
816 */
817 static void
report_method_match_failure(JSContext * cx,JavaMemberDescriptor * member_descriptor,JavaClassDescriptor * class_descriptor,JSBool is_static_method,uintN argc,jsval * argv)818 report_method_match_failure(JSContext *cx,
819 JavaMemberDescriptor *member_descriptor,
820 JavaClassDescriptor *class_descriptor,
821 JSBool is_static_method,
822 uintN argc, jsval *argv)
823 {
824 const char *err, *js_arg_string, *tmp, *method_str, *method_name;
825 JSBool is_constructor;
826 JavaMethodSpec *method;
827
828 err = NULL;
829 is_constructor = (!strcmp(member_descriptor->name, "<init>"));
830
831 js_arg_string = get_js_arg_types_as_string(cx, argc, argv);
832 if (!js_arg_string)
833 goto out_of_memory;
834
835 if (is_constructor) {
836 err = JS_smprintf("There is no Java constructor for class %s that matches "
837 "JavaScript argument types %s.\n", class_descriptor->name,
838 js_arg_string);
839 method_name = class_descriptor->name;
840 } else {
841 err = JS_smprintf("There is no %sJava method %s.%s that matches "
842 "JavaScript argument types %s.\n",
843 is_static_method ? "static ": "",
844 class_descriptor->name, member_descriptor->name, js_arg_string);
845 method_name = member_descriptor->name;
846 }
847 if (!err)
848 goto out_of_memory;
849
850 tmp = JS_smprintf("%sCandidate methods with the same name are:\n", err);
851 if (!tmp)
852 goto out_of_memory;
853 err = tmp;
854
855 method = member_descriptor->methods;
856 while (method) {
857 method_str =
858 jsj_ConvertJavaMethodSignatureToHRString(cx, method_name, &method->signature);
859 if (!method_str)
860 goto out_of_memory;
861 tmp = JS_smprintf("%s %s\n", err, method_str);
862 free((char*)method_str);
863 if (!tmp)
864 goto out_of_memory;
865 err = tmp;
866
867 method = method->next;
868 }
869
870 JS_ReportError(cx, err);
871 return;
872
873 out_of_memory:
874 if (js_arg_string)
875 free((char*)js_arg_string);
876 if (err)
877 free((char*)err);
878 }
879
880 /*
881 * This is an error reporting routine used when a method of the correct name
882 * and class exists but more than one Java method in a set of overloaded
883 * methods are compatible with the JavaScript argument types and none of them
884 * match any better than all the others.
885 */
886 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)887 report_ambiguous_method_match(JSContext *cx,
888 JavaMemberDescriptor *member_descriptor,
889 JavaClassDescriptor *class_descriptor,
890 MethodList *ambiguous_methods,
891 JSBool is_static_method,
892 uintN argc, jsval *argv)
893 {
894 const char *err, *js_arg_string, *tmp, *method_str, *method_name;
895 JSBool is_constructor;
896 JavaMethodSpec *method;
897 MethodListElement *method_list_element;
898
899 err = NULL;
900 is_constructor = (!strcmp(member_descriptor->name, "<init>"));
901
902 js_arg_string = get_js_arg_types_as_string(cx, argc, argv);
903 if (!js_arg_string)
904 goto out_of_memory;
905
906 if (is_constructor) {
907 err = JS_smprintf("The choice of Java constructor for class %s with "
908 "JavaScript argument types %s is ambiguous.\n",
909 class_descriptor->name,
910 js_arg_string);
911 method_name = class_descriptor->name;
912 } else {
913 err = JS_smprintf("The choice of %sJava method %s.%s matching "
914 "JavaScript argument types %s is ambiguous.\n",
915 is_static_method ? "static ": "",
916 class_descriptor->name, member_descriptor->name,
917 js_arg_string);
918 method_name = member_descriptor->name;
919 }
920 if (!err)
921 goto out_of_memory;
922
923 tmp = JS_smprintf("%sCandidate methods are:\n", err);
924 if (!tmp)
925 goto out_of_memory;
926 err = tmp;
927
928 method_list_element = (MethodListElement*)JS_LIST_HEAD(ambiguous_methods);
929 while ((MethodList*)method_list_element != ambiguous_methods) {
930 method = method_list_element->method;
931 method_str =
932 jsj_ConvertJavaMethodSignatureToHRString(cx, method_name, &method->signature);
933 if (!method_str)
934 goto out_of_memory;
935 tmp = JS_smprintf("%s %s\n", err, method_str);
936 free((char*)method_str);
937 if (!tmp)
938 goto out_of_memory;
939 err = tmp;
940
941 method_list_element = (MethodListElement*)method_list_element->linkage.next;
942 }
943
944 JS_ReportError(cx, err);
945 return;
946
947 out_of_memory:
948 if (js_arg_string)
949 free((char*)js_arg_string);
950 if (err)
951 free((char*)err);
952 }
953
954 /*
955 * Compute classification of JS types with slightly different granularity
956 * than JSType. This is used to resolve among overloaded Java methods.
957 */
958 static JSJType
compute_jsj_type(JSContext * cx,jsval v)959 compute_jsj_type(JSContext *cx, jsval v)
960 {
961 JSObject *js_obj;
962
963 if (JSVAL_IS_OBJECT(v)) {
964 if (JSVAL_IS_NULL(v))
965 return JSJTYPE_NULL;
966 js_obj = JSVAL_TO_OBJECT(v);
967 if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0))
968 return JSJTYPE_JAVAOBJECT;
969 if (JS_InstanceOf(cx, js_obj, &JavaArray_class, 0))
970 return JSJTYPE_JAVAARRAY;
971 if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0))
972 return JSJTYPE_JAVACLASS;
973 if (JS_IsArrayObject(cx, js_obj))
974 return JSJTYPE_JSARRAY;
975 return JSJTYPE_OBJECT;
976 } else if (JSVAL_IS_NUMBER(v)) {
977 return JSJTYPE_NUMBER;
978 } else if (JSVAL_IS_STRING(v)) {
979 return JSJTYPE_STRING;
980 } else if (JSVAL_IS_BOOLEAN(v)) {
981 return JSJTYPE_BOOLEAN;
982 } else if (JSVAL_IS_VOID(v)) {
983 return JSJTYPE_VOID;
984 }
985 JS_ASSERT(0); /* Unknown JS type ! */
986 return JSJTYPE_VOID;
987 }
988
989 /*
990 * Ranking table used to establish preferences among Java types when converting
991 * from JavaScript types. Each row represents a different JavaScript source
992 * type and each column represents a different target Java type. Lower values
993 * in the table indicate conversions that are ranked higher. The special
994 * value 99 indicates a disallowed JS->Java conversion. The special value of
995 * 0 indicates special handling is required to determine ranking.
996 */
997 static int rank_table[JSJTYPE_LIMIT][JAVA_SIGNATURE_LIMIT] = {
998 /* boolean long
999 | char | float java.lang.Boolean
1000 | | byte | | double | java.lang.Class
1001 | | | short | | | array | | java.lang.Double
1002 | | | | int | | | | object | | netscape.javascript.JSObject
1003 | | | | | | | | | | | | | | java.lang.Object
1004 | | | | | | | | | | | | | | | java.lang.String */
1005
1006 {99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 1, 1}, /* undefined */
1007 { 1, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 99, 99, 99, 3, 4}, /* boolean */
1008 {99, 7, 8, 6, 5, 4, 3, 1, 99, 99, 99, 99, 2, 99, 11, 9}, /* number */
1009 {99, 3, 4, 4, 4, 4, 4, 4, 99, 99, 99, 99, 99, 99, 2, 1}, /* string */
1010 {99, 99, 99, 99, 99, 99, 99, 99, 1, 1, 1, 1, 1, 1, 1, 1}, /* null */
1011 {99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 1, 99, 2, 3, 4}, /* JavaClass */
1012 {99, 7, 8, 6, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1}, /* JavaObject */
1013 {99, 99, 99, 99, 99, 99, 99, 99, 0, 0, 99, 99, 99, 99, 0, 1}, /* JavaArray */
1014 {99, 99, 99, 99, 99, 99, 99, 99, 2, 99, 99, 99, 99, 1, 3, 4}, /* JS Array */
1015 {99, 9, 10, 8, 7, 6, 5, 4, 99, 99, 99, 99, 99, 1, 2, 3} /* other JS object */
1016 };
1017
1018 /*
1019 * Returns JS_TRUE if JavaScript arguments are compatible with the specified
1020 * Java method signature.
1021 */
1022 static JSBool
method_signature_matches_JS_args(JSContext * cx,JNIEnv * jEnv,uintN argc,jsval * argv,JavaMethodSignature * method_signature)1023 method_signature_matches_JS_args(JSContext *cx, JNIEnv *jEnv, uintN argc, jsval *argv,
1024 JavaMethodSignature *method_signature)
1025 {
1026 uintN i;
1027 JavaClassDescriptor *descriptor;
1028 JavaObjectWrapper *java_wrapper;
1029 jclass java_class;
1030 jobject java_obj;
1031 JSObject *js_obj;
1032 JSJType js_type;
1033 jsval js_val;
1034 int rank;
1035
1036 if (argc != (uintN)method_signature->num_args)
1037 return JS_FALSE;
1038
1039 for (i = 0; i < argc; i++) {
1040 js_val = argv[i];
1041 js_type = compute_jsj_type(cx, js_val);
1042 descriptor = method_signature->arg_signatures[i];
1043 rank = rank_table[js_type][(int)descriptor->type - 2];
1044
1045 /* Check for disallowed JS->Java conversion */
1046 if (rank == 99)
1047 return JS_FALSE;
1048
1049 /* Check for special handling required by conversion from JavaObject */
1050 if (rank == 0) {
1051 java_class = descriptor->java_class;
1052
1053 js_obj = JSVAL_TO_OBJECT(js_val);
1054 java_wrapper = JS_GetPrivate(cx, js_obj);
1055 java_obj = java_wrapper->java_obj;
1056
1057 if (!(*jEnv)->IsInstanceOf(jEnv, java_obj, java_class))
1058 return JS_FALSE;
1059 }
1060 }
1061
1062 return JS_TRUE;
1063 }
1064
1065 #ifdef HAS_OLD_STYLE_METHOD_RESOLUTION
1066
1067 static JavaMethodSpec *
resolve_overloaded_method(JSContext * cx,JNIEnv * jEnv,JavaMemberDescriptor * member_descriptor,JavaClassDescriptor * class_descriptor,JSBool is_static_method,uintN argc,jsval * argv)1068 resolve_overloaded_method(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor,
1069 JavaClassDescriptor *class_descriptor,
1070 JSBool is_static_method,
1071 uintN argc, jsval *argv)
1072 {
1073 int cost, lowest_cost, num_method_matches;
1074 JavaMethodSpec *best_method_match, *method;
1075
1076 num_method_matches = 0;
1077 lowest_cost = 10000;
1078 best_method_match = NULL;
1079
1080 for (method = member_descriptor->methods; method; method = method->next) {
1081 cost = 0;
1082 if (!method_signature_matches_JS_args(cx, jEnv, argc, argv, &method->signature, &cost))
1083 continue;
1084
1085 if (cost < lowest_cost) {
1086 lowest_cost = cost;
1087 best_method_match = method;
1088 num_method_matches++;
1089 }
1090 }
1091
1092 if (!best_method_match)
1093 report_method_match_failure(cx, member_descriptor, class_descriptor,
1094 is_static_method, argc, argv);
1095
1096 if (lowest_cost != 0)
1097 return NULL;
1098
1099 return best_method_match;
1100 }
1101
1102 #else /* !OLD_STYLE_METHOD_RESOLUTION */
1103
1104 /*
1105 * Determine the more natural (preferred) JavaScript->Java conversion
1106 * given one JavaScript value and two Java types.
1107 * See http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html
1108 * for details.
1109 */
1110 static JSJTypePreference
preferred_conversion(JSContext * cx,JNIEnv * jEnv,jsval js_val,JavaClassDescriptor * descriptor1,JavaClassDescriptor * descriptor2)1111 preferred_conversion(JSContext *cx, JNIEnv *jEnv, jsval js_val,
1112 JavaClassDescriptor *descriptor1,
1113 JavaClassDescriptor *descriptor2)
1114 {
1115 JSJType js_type;
1116 int rank1, rank2;
1117 jclass java_class1, java_class2;
1118 JavaObjectWrapper *java_wrapper;
1119 jobject java_obj;
1120 JSObject *js_obj;
1121
1122 js_type = compute_jsj_type(cx, js_val);
1123 rank1 = rank_table[js_type][(int)descriptor1->type - 2];
1124 rank2 = rank_table[js_type][(int)descriptor2->type - 2];
1125
1126 /* Fast path for conversion from most JS types */
1127 if (rank1 < rank2)
1128 return JSJPREF_FIRST_ARG;
1129
1130 /*
1131 * Special logic is required for matching the classes of wrapped
1132 * Java objects.
1133 */
1134 if (rank2 == 0) {
1135 java_class1 = descriptor1->java_class;
1136 java_class2 = descriptor2->java_class;
1137
1138 js_obj = JSVAL_TO_OBJECT(js_val);
1139 java_wrapper = JS_GetPrivate(cx, js_obj);
1140 java_obj = java_wrapper->java_obj;
1141
1142 /* Unwrapped JavaObject must be compatible with Java arg type */
1143 if (!(*jEnv)->IsInstanceOf(jEnv, java_obj, java_class2))
1144 return JSJPREF_FIRST_ARG;
1145
1146 /*
1147 * For JavaObject arguments, any compatible reference type is preferable
1148 * to any primitive Java type or to java.lang.String.
1149 */
1150 if (rank1 != 0)
1151 return JSJPREF_SECOND_ARG;
1152
1153 /*
1154 * If argument of type descriptor1 is subclass of type descriptor 2, then
1155 * descriptor1 is preferred and vice-versa.
1156 */
1157 if ((*jEnv)->IsAssignableFrom(jEnv, java_class1, java_class2))
1158 return JSJPREF_FIRST_ARG;
1159
1160 if ((*jEnv)->IsAssignableFrom(jEnv, java_class2, java_class1))
1161 return JSJPREF_SECOND_ARG;
1162
1163 /* This can happen in unusual situations involving interface types. */
1164 return JSJPREF_AMBIGUOUS;
1165 }
1166
1167 if (rank1 > rank2)
1168 return JSJPREF_SECOND_ARG;
1169
1170 return JSJPREF_AMBIGUOUS;
1171 }
1172
1173 static JSJTypePreference
method_preferred(JSContext * cx,JNIEnv * jEnv,jsval * argv,JavaMethodSignature * method_signature1,JavaMethodSignature * method_signature2)1174 method_preferred(JSContext *cx, JNIEnv *jEnv, jsval *argv,
1175 JavaMethodSignature *method_signature1,
1176 JavaMethodSignature *method_signature2)
1177 {
1178 int arg_index, argc, preference;
1179 jsval val;
1180 JavaSignature* *arg_signatures1;
1181 JavaSignature* *arg_signatures2;
1182 JavaSignature *arg_type1, *arg_type2;
1183
1184 arg_signatures1 = method_signature1->arg_signatures;
1185 arg_signatures2 = method_signature2->arg_signatures;
1186 argc = method_signature1->num_args;
1187 JS_ASSERT(argc == method_signature2->num_args);
1188
1189 preference = 0;
1190 for (arg_index = 0; arg_index < argc; arg_index++) {
1191 val = argv[arg_index];
1192 arg_type1 = *arg_signatures1++;
1193 arg_type2 = *arg_signatures2++;
1194
1195 if (arg_type1 == arg_type2)
1196 continue;
1197
1198 preference |= preferred_conversion(cx, jEnv, val, arg_type1, arg_type2);
1199
1200 if ((JSJTypePreference)preference == JSJPREF_AMBIGUOUS)
1201 return JSJPREF_AMBIGUOUS;
1202 }
1203 return (JSJTypePreference)preference;
1204 }
1205
1206 /*
1207 * This routine applies heuristics to guess the intended Java method given the
1208 * runtime JavaScript argument types and the type signatures of the candidate
1209 * methods. Informally, the method with Java parameter types that most closely
1210 * match the JavaScript types is chosen. A more precise specification is
1211 * provided in the lc3_method_resolution.html file. The code uses a very
1212 * brute-force approach.
1213 */
1214 static JavaMethodSpec *
resolve_overloaded_method(JSContext * cx,JNIEnv * jEnv,JavaMemberDescriptor * member_descriptor,JavaClassDescriptor * class_descriptor,JSBool is_static_method,uintN argc,jsval * argv)1215 resolve_overloaded_method(JSContext *cx, JNIEnv *jEnv,
1216 JavaMemberDescriptor *member_descriptor,
1217 JavaClassDescriptor *class_descriptor,
1218 JSBool is_static_method,
1219 uintN argc, jsval *argv)
1220 {
1221 JSJTypePreference preference;
1222 JavaMethodSpec *method, *best_method_match;
1223 MethodList ambiguous_methods;
1224 MethodListElement *method_list_element, *next_element;
1225
1226 /*
1227 * Determine the first Java method among the overloaded methods of the same name
1228 * that matches all the JS arguments.
1229 */
1230 for (method = member_descriptor->methods; method; method = method->next) {
1231 if (method_signature_matches_JS_args(cx, jEnv, argc, argv, &method->signature))
1232 break;
1233 }
1234
1235 /* Report an error if no method matched the JS arguments */
1236 if (!method) {
1237 report_method_match_failure(cx, member_descriptor, class_descriptor,
1238 is_static_method, argc, argv);
1239 return NULL;
1240 }
1241
1242 /* Shortcut a common case */
1243 if (!method->next)
1244 return method;
1245
1246 /*
1247 * Form a list of all methods that are neither more or less preferred than the
1248 * best matching method discovered so far.
1249 */
1250 JS_INIT_CLIST(&ambiguous_methods);
1251
1252 best_method_match = method;
1253
1254 /* See if there are any Java methods that are a better fit for the JS args */
1255 for (method = method->next; method; method = method->next) {
1256 if (method->signature.num_args != (int)argc)
1257 continue;
1258 preference = method_preferred(cx, jEnv, argv, &best_method_match->signature,
1259 &method->signature);
1260 if (preference == JSJPREF_SECOND_ARG) {
1261 best_method_match = method;
1262 } else if (preference == JSJPREF_AMBIGUOUS) {
1263 /* Add this method to the list of ambiguous methods */
1264 method_list_element =
1265 (MethodListElement*)JS_malloc(cx, sizeof(MethodListElement));
1266 if (!method_list_element)
1267 goto error;
1268 method_list_element->method = method;
1269 JS_APPEND_LINK(&method_list_element->linkage, &ambiguous_methods);
1270 }
1271 }
1272
1273 /*
1274 * Ensure that best_method_match is preferred to all methods on the
1275 * ambiguous_methods list.
1276 */
1277
1278 for (method_list_element = (MethodListElement*)JS_LIST_HEAD(&ambiguous_methods);
1279 (MethodList*)method_list_element != &ambiguous_methods;
1280 method_list_element = next_element) {
1281 next_element = (MethodListElement*)method_list_element->linkage.next;
1282 method = method_list_element->method;
1283 preference = method_preferred(cx, jEnv, argv, &best_method_match->signature,
1284 &method->signature);
1285 if (preference != JSJPREF_FIRST_ARG)
1286 continue;
1287 JS_REMOVE_LINK(&method_list_element->linkage);
1288 JS_free(cx, method_list_element);
1289 }
1290
1291 /*
1292 * The chosen method must be maximally preferred, i.e. there can be no other
1293 * method that is just as preferred.
1294 */
1295 if (!JS_CLIST_IS_EMPTY(&ambiguous_methods)) {
1296 /* Add the best_method_match to the list of ambiguous methods */
1297 method_list_element =
1298 (MethodListElement*)JS_malloc(cx, sizeof(MethodListElement));
1299 if (!method_list_element)
1300 goto error;
1301 method_list_element->method = best_method_match;
1302 JS_APPEND_LINK(&method_list_element->linkage, &ambiguous_methods);
1303
1304 /* Report the problem */
1305 report_ambiguous_method_match(cx, member_descriptor, class_descriptor,
1306 &ambiguous_methods, is_static_method, argc, argv);
1307 goto error;
1308 }
1309
1310 return best_method_match;
1311
1312 error:
1313 /* Delete the storage for the ambiguous_method list */
1314 while (!JS_CLIST_IS_EMPTY(&ambiguous_methods)) {
1315 method_list_element = (MethodListElement*)JS_LIST_HEAD(&ambiguous_methods);
1316 JS_REMOVE_LINK(&method_list_element->linkage);
1317 JS_free(cx, method_list_element);
1318 }
1319
1320 return NULL;
1321 }
1322
1323 #endif /* !HAS_OLD_STYLE_METHOD_RESOLUTION */
1324
1325 static jvalue *
convert_JS_method_args_to_java_argv(JSContext * cx,JNIEnv * jEnv,jsval * argv,JavaMethodSpec * method,JSBool ** localvp)1326 convert_JS_method_args_to_java_argv(JSContext *cx, JNIEnv *jEnv, jsval *argv,
1327 JavaMethodSpec *method, JSBool **localvp)
1328 {
1329 jvalue *jargv;
1330 JSBool ok, *localv;
1331 uintN i, argc;
1332 JavaSignature **arg_signatures;
1333 JavaMethodSignature *signature;
1334
1335
1336 signature = &method->signature;
1337 argc = signature->num_args;
1338 JS_ASSERT(argc != 0);
1339 arg_signatures = signature->arg_signatures;
1340
1341 jargv = (jvalue *)JS_malloc(cx, sizeof(jvalue) * argc);
1342
1343 if (!jargv)
1344 return NULL;
1345
1346 /*
1347 * Allocate an array that contains a flag for each argument, indicating whether
1348 * or not the conversion from a JS value to a Java value resulted in a new
1349 * JNI local reference.
1350 */
1351 localv = (JSBool *)JS_malloc(cx, sizeof(JSBool) * argc);
1352 *localvp = localv;
1353 if (!localv) {
1354 JS_free(cx, jargv);
1355 return NULL;
1356 }
1357
1358 for (i = 0; i < argc; i++) {
1359 int dummy_cost;
1360
1361 ok = jsj_ConvertJSValueToJavaValue(cx, jEnv, argv[i], arg_signatures[i],
1362 &dummy_cost, &jargv[i], &localv[i]);
1363 if (!ok) {
1364 JS_free(cx, jargv);
1365 JS_free(cx, localv);
1366 *localvp = NULL;
1367 return NULL;
1368 }
1369 }
1370
1371 return jargv;
1372 }
1373
1374 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)1375 invoke_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
1376 jobject java_class_or_instance,
1377 JavaClassDescriptor *class_descriptor,
1378 JavaMethodSpec *method,
1379 JSBool is_static_method,
1380 jsval *argv, jsval *vp)
1381 {
1382 jvalue java_value;
1383 jvalue *jargv;
1384 uintN argc, i;
1385 jobject java_object;
1386 jclass java_class;
1387 jmethodID methodID;
1388 JavaMethodSignature *signature;
1389 JavaSignature *return_val_signature;
1390 JNIEnv *jEnv;
1391 JSBool *localv, error_occurred, success;
1392
1393 success = error_occurred = JS_FALSE;
1394 return_val_signature = NULL; /* Quiet gcc uninitialized variable warning */
1395
1396 methodID = method->methodID;
1397 signature = &method->signature;
1398 argc = signature->num_args;
1399
1400 jEnv = jsj_env->jEnv;
1401
1402 if (is_static_method) {
1403 java_object = NULL;
1404 java_class = java_class_or_instance;
1405 } else {
1406 java_object = java_class_or_instance;
1407 java_class = NULL;
1408 }
1409
1410 jargv = NULL;
1411 localv = NULL;
1412 if (argc) {
1413 jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method, &localv);
1414 if (!jargv) {
1415 error_occurred = JS_TRUE;
1416 goto out;
1417 }
1418 }
1419
1420 /* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
1421 method call and that new thread wants to perform a GC. */
1422 #ifdef JSJ_THREADSAFE
1423 JS_EndRequest(cx);
1424 #endif
1425
1426 #define CALL_JAVA_METHOD(type, member) \
1427 JS_BEGIN_MACRO \
1428 if (is_static_method) { \
1429 java_value.member = (*jEnv)->CallStatic##type##MethodA(jEnv, java_class, methodID, jargv);\
1430 } else { \
1431 java_value.member = (*jEnv)->Call##type##MethodA(jEnv, java_object, methodID, jargv);\
1432 } \
1433 if ((*jEnv)->ExceptionOccurred(jEnv)) { \
1434 jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()", \
1435 class_descriptor->name, method->name); \
1436 error_occurred = JS_TRUE; \
1437 goto out; \
1438 } \
1439 JS_END_MACRO
1440
1441 return_val_signature = signature->return_val_signature;
1442 switch(return_val_signature->type) {
1443 case JAVA_SIGNATURE_BYTE:
1444 CALL_JAVA_METHOD(Byte, b);
1445 break;
1446
1447 case JAVA_SIGNATURE_CHAR:
1448 CALL_JAVA_METHOD(Char, c);
1449 break;
1450
1451 case JAVA_SIGNATURE_FLOAT:
1452 CALL_JAVA_METHOD(Float, f);
1453 break;
1454
1455 case JAVA_SIGNATURE_DOUBLE:
1456 CALL_JAVA_METHOD(Double, d);
1457 break;
1458
1459 case JAVA_SIGNATURE_INT:
1460 CALL_JAVA_METHOD(Int, i);
1461 break;
1462
1463 case JAVA_SIGNATURE_LONG:
1464 CALL_JAVA_METHOD(Long, j);
1465 break;
1466
1467 case JAVA_SIGNATURE_SHORT:
1468 CALL_JAVA_METHOD(Short, s);
1469 break;
1470
1471 case JAVA_SIGNATURE_BOOLEAN:
1472 CALL_JAVA_METHOD(Boolean, z);
1473 break;
1474
1475 case JAVA_SIGNATURE_VOID:
1476 if (is_static_method)
1477 (*jEnv)->CallStaticVoidMethodA(jEnv, java_class, methodID, jargv);
1478 else
1479 (*jEnv)->CallVoidMethodA(jEnv, java_object, methodID, jargv);
1480 if ((*jEnv)->ExceptionOccurred(jEnv)) {
1481 jsj_ReportJavaError(cx, jEnv, "Error calling method %s.%s()",
1482 class_descriptor->name, method->name);
1483 error_occurred = JS_TRUE;
1484 goto out;
1485 }
1486 break;
1487
1488 case JAVA_SIGNATURE_UNKNOWN:
1489 JS_ASSERT(0);
1490 error_occurred = JS_TRUE;
1491 goto out;
1492
1493 /* Non-primitive (reference) type */
1494 default:
1495 JS_ASSERT(IS_REFERENCE_TYPE(return_val_signature->type));
1496 CALL_JAVA_METHOD(Object, l);
1497 break;
1498 }
1499
1500 out:
1501
1502 if (localv) {
1503 for (i = 0; i < argc; i++) {
1504 if (localv[i])
1505 (*jEnv)->DeleteLocalRef(jEnv, jargv[i].l);
1506 }
1507 JS_free(cx, localv);
1508 }
1509 if (jargv)
1510 JS_free(cx, jargv);
1511
1512 #ifdef JSJ_THREADSAFE
1513 JS_BeginRequest(cx);
1514 #endif
1515
1516 if (!error_occurred) {
1517 success = jsj_ConvertJavaValueToJSValue(cx, jEnv, return_val_signature, &java_value, vp);
1518 if (IS_REFERENCE_TYPE(return_val_signature->type))
1519 (*jEnv)->DeleteLocalRef(jEnv, java_value.l);
1520 }
1521 return success;
1522 }
1523
1524 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)1525 invoke_overloaded_java_method(JSContext *cx, JSJavaThreadState *jsj_env,
1526 JavaMemberDescriptor *member,
1527 JSBool is_static_method,
1528 jobject java_class_or_instance,
1529 JavaClassDescriptor *class_descriptor,
1530 uintN argc, jsval *argv,
1531 jsval *vp)
1532 {
1533 JavaMethodSpec *method;
1534 JNIEnv *jEnv;
1535
1536 jEnv = jsj_env->jEnv;
1537
1538 method = resolve_overloaded_method(cx, jEnv, member, class_descriptor,
1539 is_static_method, argc, argv);
1540 if (!method)
1541 return JS_FALSE;
1542
1543 return invoke_java_method(cx, jsj_env, java_class_or_instance, class_descriptor,
1544 method, is_static_method, argv, vp);
1545 }
1546
1547 static JSBool
invoke_java_constructor(JSContext * cx,JSJavaThreadState * jsj_env,jclass java_class,JavaMethodSpec * method,jsval * argv,jsval * vp)1548 invoke_java_constructor(JSContext *cx,
1549 JSJavaThreadState *jsj_env,
1550 jclass java_class,
1551 JavaMethodSpec *method,
1552 jsval *argv, jsval *vp)
1553 {
1554 jvalue *jargv;
1555 uintN argc, i;
1556 jobject java_object;
1557 jmethodID methodID;
1558 JavaMethodSignature *signature;
1559 JNIEnv *jEnv;
1560 JSBool *localv;
1561 JSBool success, error_occurred;
1562 java_object = NULL; /* Stifle gcc uninitialized variable warning */
1563
1564 success = error_occurred = JS_FALSE;
1565
1566 methodID = method->methodID;
1567 signature = &method->signature;
1568 argc = signature->num_args;
1569
1570 jEnv = jsj_env->jEnv;
1571
1572 jargv = NULL;
1573 localv = NULL;
1574 if (argc) {
1575 jargv = convert_JS_method_args_to_java_argv(cx, jEnv, argv, method, &localv);
1576 if (!jargv) {
1577 error_occurred = JS_TRUE;
1578 goto out;
1579 }
1580 }
1581
1582 /* Prevent deadlocking if we re-enter JS on another thread as a result of a Java
1583 method call and that new thread wants to perform a GC. */
1584 #ifdef JSJ_THREADSAFE
1585 JS_EndRequest(cx);
1586 #endif
1587
1588 /* Call the constructor */
1589 java_object = (*jEnv)->NewObjectA(jEnv, java_class, methodID, jargv);
1590
1591 #ifdef JSJ_THREADSAFE
1592 JS_BeginRequest(cx);
1593 #endif
1594
1595 if (!java_object) {
1596 jsj_ReportJavaError(cx, jEnv, "Error while constructing instance of %s",
1597 jsj_GetJavaClassName(cx, jEnv, java_class));
1598 error_occurred = JS_TRUE;
1599 goto out;
1600 }
1601
1602 out:
1603 if (localv) {
1604 for (i = 0; i < argc; i++) {
1605 if (localv[i])
1606 (*jEnv)->DeleteLocalRef(jEnv, jargv[i].l);
1607 }
1608 JS_free(cx, localv);
1609 }
1610 if (jargv)
1611 JS_free(cx, jargv);
1612
1613 if (!error_occurred)
1614 success = jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_object, vp);
1615 (*jEnv)->DeleteLocalRef(jEnv, java_object);
1616 return success;
1617 }
1618
1619 static JSBool
invoke_overloaded_java_constructor(JSContext * cx,JSJavaThreadState * jsj_env,JavaMemberDescriptor * member,JavaClassDescriptor * class_descriptor,uintN argc,jsval * argv,jsval * vp)1620 invoke_overloaded_java_constructor(JSContext *cx,
1621 JSJavaThreadState *jsj_env,
1622 JavaMemberDescriptor *member,
1623 JavaClassDescriptor *class_descriptor,
1624 uintN argc, jsval *argv,
1625 jsval *vp)
1626 {
1627 jclass java_class;
1628 JavaMethodSpec *method;
1629 JNIEnv *jEnv;
1630
1631 jEnv = jsj_env->jEnv;
1632
1633 method = resolve_overloaded_method(cx, jEnv, member, class_descriptor, JS_TRUE,
1634 argc, argv);
1635 if (!method)
1636 return JS_FALSE;
1637
1638 java_class = class_descriptor->java_class;
1639 return invoke_java_constructor(cx, jsj_env, java_class, method, argv, vp);
1640 }
1641
1642 static JSBool
java_constructor_wrapper(JSContext * cx,JSJavaThreadState * jsj_env,JavaMemberDescriptor * member_descriptor,JavaClassDescriptor * class_descriptor,uintN argc,jsval * argv,jsval * vp)1643 java_constructor_wrapper(JSContext *cx, JSJavaThreadState *jsj_env,
1644 JavaMemberDescriptor *member_descriptor,
1645 JavaClassDescriptor *class_descriptor,
1646 uintN argc, jsval *argv, jsval *vp)
1647 {
1648 jint modifiers;
1649 JNIEnv *jEnv;
1650
1651 jEnv = jsj_env->jEnv;
1652
1653 /* Get class/interface flags and check them */
1654 modifiers = class_descriptor->modifiers;
1655 if (modifiers & ACC_ABSTRACT) {
1656 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
1657 JSJMSG_ABSTRACT_JCLASS, class_descriptor->name);
1658 return JS_FALSE;
1659 }
1660 if (modifiers & ACC_INTERFACE) {
1661 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
1662 JSJMSG_IS_INTERFACE, class_descriptor->name);
1663 return JS_FALSE;
1664 }
1665 if ( !(modifiers & ACC_PUBLIC) ) {
1666 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
1667 JSJMSG_NOT_PUBLIC, class_descriptor->name);
1668 return JS_FALSE;
1669 }
1670
1671 if (!member_descriptor) {
1672 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
1673 JSJMSG_NO_CONSTRUCTORS, class_descriptor->name);
1674 return JS_FALSE;
1675 }
1676
1677 return invoke_overloaded_java_constructor(cx, jsj_env, member_descriptor,
1678 class_descriptor, argc, argv, vp);
1679 }
1680
1681 JS_EXPORT_API(JSBool)
jsj_JavaConstructorWrapper(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * vp)1682 jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
1683 uintN argc, jsval *argv, jsval *vp)
1684 {
1685 JavaClassDescriptor *class_descriptor;
1686 JavaMemberDescriptor *member_descriptor;
1687 JSJavaThreadState *jsj_env;
1688 JNIEnv *jEnv;
1689 JSBool result;
1690
1691 obj = JSVAL_TO_OBJECT(argv[-2]);
1692 class_descriptor = JS_GetPrivate(cx, obj);
1693 JS_ASSERT(class_descriptor);
1694 if (!class_descriptor)
1695 return JS_FALSE;
1696
1697 /* XXX, workaround for bug 200016, all classes in sun.plugin package should not
1698 be accessible in liveconnect.
1699 Ideally, this checking should be done in JPI side, but it's not going to happen
1700 until Sun JRE 1.5.1 */
1701 if (strstr(class_descriptor->name, "sun.plugin.") == class_descriptor->name)
1702 return JS_FALSE;
1703
1704 /* Get the Java per-thread environment pointer for this JSContext */
1705 jsj_env = jsj_EnterJava(cx, &jEnv);
1706 if (!jEnv)
1707 return JS_FALSE;
1708 member_descriptor = jsj_LookupJavaClassConstructors(cx, jEnv, class_descriptor);
1709 result = java_constructor_wrapper(cx, jsj_env, member_descriptor,
1710 class_descriptor, argc, argv, vp);
1711 jsj_ExitJava(jsj_env);
1712 return result;
1713 }
1714
1715
1716 static JSBool
static_method_wrapper(JSContext * cx,JSJavaThreadState * jsj_env,JavaClassDescriptor * class_descriptor,jsid id,uintN argc,jsval * argv,jsval * vp)1717 static_method_wrapper(JSContext *cx, JSJavaThreadState *jsj_env,
1718 JavaClassDescriptor *class_descriptor,
1719 jsid id,
1720 uintN argc, jsval *argv, jsval *vp)
1721 {
1722 JNIEnv *jEnv;
1723 JavaMemberDescriptor *member_descriptor;
1724
1725 jEnv = jsj_env->jEnv;
1726 member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
1727
1728 /* Is it a static method that is not a constructor ? */
1729 if (member_descriptor && strcmp(member_descriptor->name, "<init>")) {
1730 return invoke_overloaded_java_method(cx, jsj_env, member_descriptor, JS_TRUE,
1731 class_descriptor->java_class,
1732 class_descriptor, argc, argv, vp);
1733 }
1734
1735 JS_ASSERT(member_descriptor);
1736 if (!member_descriptor)
1737 return JS_FALSE;
1738
1739 /* Must be an explicitly resolved overloaded constructor */
1740 return java_constructor_wrapper(cx, jsj_env, member_descriptor,
1741 class_descriptor, argc, argv, vp);
1742 }
1743
1744 JS_EXTERN_API(JSBool)
jsj_JavaStaticMethodWrapper(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * vp)1745 jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
1746 uintN argc, jsval *argv, jsval *vp)
1747 {
1748 JSFunction *function;
1749 JavaClassDescriptor *class_descriptor;
1750 jsid id;
1751 jsval idval;
1752 JNIEnv *jEnv;
1753 JSJavaThreadState *jsj_env;
1754 JSBool result;
1755
1756 class_descriptor = JS_GetPrivate(cx, obj);
1757 if (!class_descriptor)
1758 return JS_FALSE;
1759
1760 /* Get the Java per-thread environment pointer for this JSContext */
1761 jsj_env = jsj_EnterJava(cx, &jEnv);
1762 if (!jEnv)
1763 return JS_FALSE;
1764
1765 JS_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
1766 function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
1767 idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
1768 JS_ValueToId(cx, idval, &id);
1769
1770 result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
1771 jsj_ExitJava(jsj_env);
1772 return result;
1773 }
1774
1775 JS_EXPORT_API(JSBool)
jsj_JavaInstanceMethodWrapper(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * vp)1776 jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
1777 uintN argc, jsval *argv, jsval *vp)
1778 {
1779 JSFunction *function;
1780 JavaMemberDescriptor *member_descriptor;
1781 JavaObjectWrapper *java_wrapper;
1782 JavaClassDescriptor *class_descriptor;
1783 jsid id;
1784 jsval idval;
1785 JSJavaThreadState *jsj_env;
1786 JNIEnv *jEnv;
1787 jobject java_obj;
1788 JSBool result;
1789
1790 java_wrapper = JS_GetPrivate(cx, obj);
1791 if (!java_wrapper)
1792 return JS_FALSE;
1793 java_obj = java_wrapper->java_obj;
1794
1795 JS_ASSERT(JS_TypeOfValue(cx, argv[-2]) == JSTYPE_FUNCTION);
1796 function = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2]));
1797 idval = STRING_TO_JSVAL(JS_InternString(cx, JS_GetFunctionName(function)));
1798 JS_ValueToId(cx, idval, &id);
1799 class_descriptor = java_wrapper->class_descriptor;
1800
1801 /* Get the Java per-thread environment pointer for this JSContext */
1802 jsj_env = jsj_EnterJava(cx, &jEnv);
1803 if (!jEnv)
1804 return JS_FALSE;
1805
1806 if (jaApplet && (*jEnv)->IsInstanceOf(jEnv, java_obj, jaApplet)) {
1807 jsj_JSIsCallingApplet = JS_TRUE;
1808 }
1809
1810 /* Try to find an instance method with the given name first */
1811 member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
1812 if (member_descriptor)
1813 result = invoke_overloaded_java_method(cx, jsj_env, member_descriptor,
1814 JS_FALSE, java_obj,
1815 class_descriptor, argc, argv, vp);
1816
1817 /* If no instance method was found, try for a static method or constructor */
1818 else
1819 result = static_method_wrapper(cx, jsj_env, class_descriptor, id, argc, argv, vp);
1820 jsj_ExitJava(jsj_env);
1821 return result;
1822 }
1823
1824