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