1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is Mozilla Communicator client code, released
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.
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.
38 * This Original Code has been modified by IBM Corporation. Modifications made
39 * by IBM described herein are Copyright (c) International Business Machines
41 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
43 * Date Modified by Description of modification
44 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
46 * ***** END LICENSE BLOCK ***** */
49 * This file is part of the Java-vendor-neutral implementation of LiveConnect
51 * It contains the native code implementation of JS's JavaObject class.
53 * An instance of JavaObject is the JavaScript reflection of a Java object.
61 #include "jsj_private.h" /* LiveConnect internals */
62 #include "jsj_hash.h" /* Hash table with Java object as key */
69 * This is a hash table that maps from Java objects to JS objects.
70 * It is used to ensure that the same JS object is obtained when a Java
71 * object is reflected more than once, so that JS object equality tests
72 * work in the expected manner, i.e. the "==" and "===" operators.
74 * The table entry keys are Java objects (of type jobject) and the entry values
75 * are JSObject pointers. Because the jobject type is an opaque handle and
76 * not necessarily a pointer, the hashing and key comparison functions must
77 * invoke the appropriate JVM functions.
79 * When the corresponding JS object instance is finalized, the entry is
80 * removed from the table, and a Java GC root for the Java object is removed.
82 static JSJHashTable
*java_obj_reflections
= NULL
;
85 static PRMonitor
*java_obj_reflections_monitor
= NULL
;
86 static int java_obj_reflections_mutation_count
= 0;
89 static JSBool installed_GC_callback
= JS_FALSE
;
90 static JSGCCallback old_GC_callback
= NULL
;
91 static JavaObjectWrapper
* deferred_wrappers
= NULL
;
93 static JSBool
jsj_GC_callback(JSContext
*cx
, JSGCStatus status
)
95 if (status
== JSGC_END
&& deferred_wrappers
) {
97 JSJavaThreadState
*jsj_env
= jsj_EnterJava(cx
, &jEnv
);
99 JavaObjectWrapper
* java_wrapper
= deferred_wrappers
;
100 while (java_wrapper
) {
101 deferred_wrappers
= java_wrapper
->u
.next
;
102 if (java_wrapper
->java_obj
)
103 (*jEnv
)->DeleteGlobalRef(jEnv
, java_wrapper
->java_obj
);
104 jsj_ReleaseJavaClassDescriptor(cx
, jEnv
, java_wrapper
->class_descriptor
);
105 JS_free(cx
, java_wrapper
);
106 java_wrapper
= deferred_wrappers
;
108 jsj_ExitJava(jsj_env
);
111 /* always chain to old GC callback if non-null. */
112 return old_GC_callback
? old_GC_callback(cx
, status
) : JS_TRUE
;
116 jsj_InitJavaObjReflectionsTable(void)
118 JS_ASSERT(!java_obj_reflections
);
120 java_obj_reflections
=
121 JSJ_NewHashTable(512, jsj_HashJavaObject
, jsj_JavaObjectComparator
,
123 if (!java_obj_reflections
)
126 #ifdef JSJ_THREADSAFE
127 java_obj_reflections_monitor
= (struct PRMonitor
*) PR_NewMonitor();
128 if (!java_obj_reflections_monitor
) {
129 JSJ_HashTableDestroy(java_obj_reflections
);
138 jsj_WrapJavaObject(JSContext
*cx
,
143 JSJHashNumber hash_code
;
145 JSObject
*js_wrapper_obj
;
146 JavaObjectWrapper
*java_wrapper
;
147 JavaClassDescriptor
*class_descriptor
;
148 JSJHashEntry
*he
, **hep
;
150 #ifdef JSJ_THREADSAFE
154 js_wrapper_obj
= NULL
;
156 hash_code
= jsj_HashJavaObject((void*)java_obj
, (void*)jEnv
);
158 #ifdef JSJ_THREADSAFE
159 PR_EnterMonitor(java_obj_reflections_monitor
);
162 if (!installed_GC_callback
) {
164 * Hook into GC callback mechanism, so we can defer deleting global
165 * references until it's safe.
167 old_GC_callback
= JS_SetGCCallback(cx
, jsj_GC_callback
);
168 installed_GC_callback
= JS_TRUE
;
171 hep
= JSJ_HashTableRawLookup(java_obj_reflections
,
172 hash_code
, java_obj
, (void*)jEnv
);
175 #ifdef JSJ_THREADSAFE
176 /* Track mutations to hash table */
177 mutation_count
= java_obj_reflections_mutation_count
;
179 /* We must temporarily release this monitor so as to avoid
180 deadlocks with the JS GC. See Bugsplat #354852 */
181 PR_ExitMonitor(java_obj_reflections_monitor
);
185 js_wrapper_obj
= (JSObject
*)he
->value
;
187 return js_wrapper_obj
;
190 /* No existing reflection found. Construct a new one */
191 class_descriptor
= jsj_GetJavaClassDescriptor(cx
, jEnv
, java_class
);
192 if (!class_descriptor
)
194 if (class_descriptor
->type
== JAVA_SIGNATURE_ARRAY
) {
195 js_class
= &JavaArray_class
;
197 JS_ASSERT(IS_OBJECT_TYPE(class_descriptor
->type
));
198 js_class
= &JavaObject_class
;
201 /* Create new JS object to reflect Java object */
202 js_wrapper_obj
= JS_NewObject(cx
, js_class
, NULL
, NULL
);
206 /* Create private, native portion of JavaObject */
208 (JavaObjectWrapper
*)JS_malloc(cx
, sizeof(JavaObjectWrapper
));
210 jsj_ReleaseJavaClassDescriptor(cx
, jEnv
, class_descriptor
);
213 JS_SetPrivate(cx
, js_wrapper_obj
, java_wrapper
);
214 java_wrapper
->class_descriptor
= class_descriptor
;
215 java_wrapper
->java_obj
= NULL
;
217 #ifdef JSJ_THREADSAFE
218 PR_EnterMonitor(java_obj_reflections_monitor
);
220 /* We may need to do the hash table lookup again, since some other
221 thread may have updated it while the lock wasn't being held. */
222 if (mutation_count
!= java_obj_reflections_mutation_count
) {
223 hep
= JSJ_HashTableRawLookup(java_obj_reflections
,
224 hash_code
, java_obj
, (void*)jEnv
);
227 js_wrapper_obj
= (JSObject
*)he
->value
;
228 if (js_wrapper_obj
) {
229 PR_ExitMonitor(java_obj_reflections_monitor
);
230 return js_wrapper_obj
;
235 java_obj_reflections_mutation_count
++;
239 java_obj
= (*jEnv
)->NewGlobalRef(jEnv
, java_obj
);
240 java_wrapper
->java_obj
= java_obj
;
244 /* cache the hash code for all time. */
245 java_wrapper
->u
.hash_code
= hash_code
;
247 /* Add the JavaObject to the hash table */
248 he
= JSJ_HashTableRawAdd(java_obj_reflections
, hep
, hash_code
,
249 java_obj
, js_wrapper_obj
, (void*)jEnv
);
250 #ifdef JSJ_THREADSAFE
251 PR_ExitMonitor(java_obj_reflections_monitor
);
255 (*jEnv
)->DeleteGlobalRef(jEnv
, java_obj
);
259 return js_wrapper_obj
;
262 /* No need to free js_wrapper_obj, as it will be finalized by GC. */
263 JS_ReportOutOfMemory(cx
);
268 remove_java_obj_reflection_from_hashtable(jobject java_obj
, JSJHashNumber hash_code
)
270 JSJHashEntry
*he
, **hep
;
272 #ifdef JSJ_THREADSAFE
273 PR_EnterMonitor(java_obj_reflections_monitor
);
276 hep
= JSJ_HashTableRawLookup(java_obj_reflections
, hash_code
,
282 JSJ_HashTableRawRemove(java_obj_reflections
, hep
, he
, NULL
);
284 #ifdef JSJ_THREADSAFE
285 java_obj_reflections_mutation_count
++;
287 PR_ExitMonitor(java_obj_reflections_monitor
);
292 JavaObject_finalize(JSContext
*cx
, JSObject
*obj
)
294 JavaObjectWrapper
*java_wrapper
;
297 JSJavaThreadState
*jsj_env
;
299 java_wrapper
= JS_GetPrivate(cx
, obj
);
302 java_obj
= java_wrapper
->java_obj
;
305 remove_java_obj_reflection_from_hashtable(java_obj
, java_wrapper
->u
.hash_code
);
307 /* defer releasing global refs until it is safe to do so. */
308 java_wrapper
->u
.next
= deferred_wrappers
;
309 deferred_wrappers
= java_wrapper
;
311 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
313 jsj_ReleaseJavaClassDescriptor(cx
, jEnv
, java_wrapper
->class_descriptor
);
314 JS_free(cx
, java_wrapper
);
315 jsj_ExitJava(jsj_env
);
317 java_wrapper
->u
.next
= deferred_wrappers
;
318 deferred_wrappers
= java_wrapper
;
323 /* Trivial helper for jsj_DiscardJavaObjReflections(), below */
325 enumerate_remove_java_obj(JSJHashEntry
*he
, JSIntn i
, void *arg
)
327 JSJavaThreadState
*jsj_env
= (JSJavaThreadState
*)arg
;
328 JNIEnv
*jEnv
= jsj_env
->jEnv
;
330 JavaObjectWrapper
*java_wrapper
;
331 JSObject
*java_wrapper_obj
;
333 java_wrapper_obj
= (JSObject
*)he
->value
;
335 /* Warning: NULL argument may cause assertion in JS engine, but it's actually OK */
336 java_wrapper
= JS_GetPrivate(jsj_env
->cx
, java_wrapper_obj
);
337 java_obj
= java_wrapper
->java_obj
;
338 (*jEnv
)->DeleteGlobalRef(jEnv
, java_obj
);
339 java_wrapper
->java_obj
= NULL
;
340 return HT_ENUMERATE_REMOVE
;
343 /* This shutdown routine discards all JNI references to Java objects
344 that have been reflected into JS, even if there are still references
347 jsj_DiscardJavaObjReflections(JNIEnv
*jEnv
)
349 JSJavaThreadState
*jsj_env
;
352 /* Get the per-thread state corresponding to the current Java thread */
353 jsj_env
= jsj_MapJavaThreadToJSJavaThreadState(jEnv
, &err_msg
);
357 jsj_LogError(err_msg
);
358 JS_smprintf_free(err_msg
);
366 if (java_obj_reflections
) {
367 JSJ_HashTableEnumerateEntries(java_obj_reflections
,
368 enumerate_remove_java_obj
,
370 JSJ_HashTableDestroy(java_obj_reflections
);
371 java_obj_reflections
= NULL
;
376 JavaObject_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
378 JavaObjectWrapper
*java_wrapper
;
379 JavaClassDescriptor
*class_descriptor
;
382 JSJavaThreadState
*jsj_env
;
385 java_wrapper
= JS_GetPrivate(cx
, obj
);
387 if (type
== JSTYPE_OBJECT
) {
388 *vp
= OBJECT_TO_JSVAL(obj
);
392 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
393 JSJMSG_BAD_OP_JOBJECT
);
397 java_obj
= java_wrapper
->java_obj
;
398 class_descriptor
= java_wrapper
->class_descriptor
;
402 *vp
= OBJECT_TO_JSVAL(obj
);
405 case JSTYPE_FUNCTION
:
406 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
407 JSJMSG_CONVERT_TO_FUNC
);
412 /* Get the Java per-thread environment pointer for this JSContext */
413 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
417 /* Either extract a C-string from the java.lang.String object
418 or call the Java toString() method */
419 result
= jsj_ConvertJavaObjectToJSString(cx
, jEnv
, class_descriptor
, java_obj
, vp
);
420 jsj_ExitJava(jsj_env
);
424 /* Get the Java per-thread environment pointer for this JSContext */
425 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
429 /* Call Java doubleValue() method, if applicable */
430 result
= jsj_ConvertJavaObjectToJSNumber(cx
, jEnv
, class_descriptor
, java_obj
, vp
);
431 jsj_ExitJava(jsj_env
);
435 /* Get the Java per-thread environment pointer for this JSContext */
436 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
440 /* Call booleanValue() method, if applicable */
441 result
= jsj_ConvertJavaObjectToJSBoolean(cx
, jEnv
, class_descriptor
, java_obj
, vp
);
442 jsj_ExitJava(jsj_env
);
452 * Get a property from the prototype object of a native ECMA object, i.e.
453 * return <js_constructor_name>.prototype.<member_name>
454 * This is used to allow Java objects to inherit methods from Array.prototype
455 * and String.prototype.
458 inherit_props_from_JS_natives(JSContext
*cx
, const char *js_constructor_name
,
459 const char *member_name
, jsval
*vp
)
461 JSObject
*global_obj
, *constructor_obj
, *prototype_obj
;
462 jsval constructor_val
, prototype_val
;
464 global_obj
= JS_GetGlobalObject(cx
);
465 JS_ASSERT(global_obj
);
469 JS_GetProperty(cx
, global_obj
, js_constructor_name
, &constructor_val
);
470 JS_ASSERT(JSVAL_IS_OBJECT(constructor_val
));
471 constructor_obj
= JSVAL_TO_OBJECT(constructor_val
);
473 JS_GetProperty(cx
, constructor_obj
, "prototype", &prototype_val
);
474 JS_ASSERT(JSVAL_IS_OBJECT(prototype_val
));
475 prototype_obj
= JSVAL_TO_OBJECT(prototype_val
);
477 return JS_GetProperty(cx
, prototype_obj
, member_name
, vp
) && *vp
!= JSVAL_VOID
;
480 struct JSJPropertyInfo
{
481 JSBool wantProp
; /* input param tells whether prop is returned */
482 const char* name
; /* output param, name of property (XXX ASCII) */
483 uintN attributes
; /* output param, attributes of property */
484 JSProperty
*prop
; /* output param, if wantProp, held pointer that
485 must be released via OBJ_DROP_PROPERTY */
487 typedef struct JSJPropertyInfo JSJPropertyInfo
;
490 lookup_member_by_id(JSContext
*cx
, JNIEnv
*jEnv
, JSObject
*obj
,
491 JavaObjectWrapper
**java_wrapperp
, jsid id
,
492 JavaMemberDescriptor
**member_descriptorp
,
493 jsval
*vp
, JSObject
**proto_chainp
,
494 JSJPropertyInfo
*prop_infop
)
497 JavaObjectWrapper
*java_wrapper
;
498 JavaMemberDescriptor
*member_descriptor
;
499 const char *member_name
;
500 JavaClassDescriptor
*class_descriptor
;
501 JSObject
*proto_chain
;
502 JSBool found_in_proto
;
504 found_in_proto
= JS_FALSE
;
505 member_descriptor
= NULL
;
506 java_wrapper
= JS_GetPrivate(cx
, obj
);
508 /* Handle accesses to prototype object */
510 if (JS_IdToValue(cx
, id
, &idval
) && JSVAL_IS_STRING(idval
) &&
511 (member_name
= JS_GetStringBytes(JSVAL_TO_STRING(idval
))) != NULL
) {
512 if (!strcmp(member_name
, "constructor"))
515 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
, JSJMSG_BAD_OP_JOBJECT
);
519 class_descriptor
= java_wrapper
->class_descriptor
;
520 JS_ASSERT(IS_REFERENCE_TYPE(class_descriptor
->type
));
522 member_descriptor
= jsj_LookupJavaMemberDescriptorById(cx
, jEnv
, class_descriptor
, id
);
523 if (member_descriptor
)
526 /* Instances can reference static methods and fields */
527 member_descriptor
= jsj_LookupJavaStaticMemberDescriptorById(cx
, jEnv
, class_descriptor
, id
);
528 if (member_descriptor
)
531 /* Ensure that the property we're searching for is string-valued. */
532 JS_IdToValue(cx
, id
, &idval
);
533 if (!JSVAL_IS_STRING(idval
)) {
534 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
, JSJMSG_BAD_JOBJECT_EXPR
);
537 member_name
= JS_GetStringBytes(JSVAL_TO_STRING(idval
));
540 * A little LC3 feature magic:
541 * + Instances of java.lang.String "inherit" the standard ECMA string methods
542 * of String.prototype. All of the ECMA string methods convert the Java
543 * string to a JS string before performing the string operation. For example,
544 * s = new java.lang.String("foobar");
546 * + Similarly, instances of Java arrays "inherit" the standard ECMA array
547 * methods of Array.prototype. (Not all of these methods work properly
548 * on JavaArray objects, however, since the 'length' property is read-only.)
551 if ((class_descriptor
->type
== JAVA_SIGNATURE_JAVA_LANG_STRING
) &&
552 inherit_props_from_JS_natives(cx
, "String", member_name
, vp
))
554 if ((class_descriptor
->type
== JAVA_SIGNATURE_ARRAY
) &&
555 inherit_props_from_JS_natives(cx
, "Array", member_name
, vp
))
559 /* Check for access to magic prototype chain property */
560 if (!strcmp(member_name
, "__proto__")) {
561 proto_chain
= JS_GetPrototype(cx
, obj
);
563 *vp
= OBJECT_TO_JSVAL(proto_chain
);
568 * See if the property looks like the explicit resolution of an
569 * overloaded method, e.g. "max(double,double)", first as an instance method,
570 * then as a static method. If we find such a method, it will be cached
571 * so future accesses won't run this code.
573 member_descriptor
= jsj_ResolveExplicitMethod(cx
, jEnv
, class_descriptor
, id
, JS_FALSE
);
574 if (member_descriptor
)
576 member_descriptor
= jsj_ResolveExplicitMethod(cx
, jEnv
, class_descriptor
, id
, JS_TRUE
);
577 if (member_descriptor
)
580 /* Is the property defined in the prototype chain? */
581 if (proto_chainp
&& prop_infop
) {
582 /* If so, follow __proto__ link to search prototype chain */
583 proto_chain
= JS_GetPrototype(cx
, obj
);
585 /* Use OBJ_LOOKUP_PROPERTY to determine if and where the property
586 actually exists in the prototype chain. */
588 if (!OBJ_LOOKUP_PROPERTY(cx
, proto_chain
, id
, proto_chainp
,
589 &prop_infop
->prop
)) {
592 if (prop_infop
->prop
) {
593 if (!OBJ_GET_ATTRIBUTES(cx
, *proto_chainp
, id
, prop_infop
->prop
,
594 &prop_infop
->attributes
)) {
595 OBJ_DROP_PROPERTY(cx
, *proto_chainp
, prop_infop
->prop
);
598 if (!prop_infop
->wantProp
) {
599 OBJ_DROP_PROPERTY(cx
, *proto_chainp
, prop_infop
->prop
);
600 prop_infop
->prop
= NULL
;
602 prop_infop
->name
= member_name
;
603 found_in_proto
= JS_TRUE
;
609 /* Report lack of Java member with the given property name */
610 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
, JSJMSG_NO_INSTANCE_NAME
,
611 class_descriptor
->name
, member_name
);
615 /* Success. Handle the multiple return values */
617 *java_wrapperp
= java_wrapper
;
618 if (member_descriptorp
)
619 *member_descriptorp
= member_descriptor
;
620 if (proto_chainp
&& !found_in_proto
)
621 *proto_chainp
= NULL
;
625 JS_EXPORT_API(JSBool
)
626 JavaObject_getPropertyById(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
629 JavaMemberDescriptor
*member_descriptor
;
630 JavaObjectWrapper
*java_wrapper
;
633 jsval field_val
, method_val
;
635 JSJavaThreadState
*jsj_env
;
636 JSObject
*proto_chain
;
637 JSJPropertyInfo prop_info
;
639 /* printf("In JavaObject_getProperty\n"); */
641 /* Get the Java per-thread environment pointer for this JSContext */
642 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
648 prop_info
.wantProp
= JS_FALSE
;
649 if (!lookup_member_by_id(cx
, jEnv
, obj
, &java_wrapper
, id
, &member_descriptor
, vp
,
650 &proto_chain
, &prop_info
)) {
651 jsj_ExitJava(jsj_env
);
655 /* Handle access to special, non-Java properties of JavaObjects, e.g. the
656 "constructor" property of the prototype object */
657 if (!member_descriptor
) {
658 jsj_ExitJava(jsj_env
);
660 return JS_GetProperty(cx
, proto_chain
, prop_info
.name
, vp
);
664 java_obj
= java_wrapper
->java_obj
;
665 field_val
= method_val
= JSVAL_VOID
;
667 if (jaApplet
&& (*jEnv
)->IsInstanceOf(jEnv
, java_obj
, jaApplet
)) {
668 jsj_JSIsCallingApplet
= JS_TRUE
;
671 /* If a field member, get the value of the field */
672 if (member_descriptor
->field
) {
673 success
= jsj_GetJavaFieldValue(cx
, jEnv
, member_descriptor
->field
, java_obj
, &field_val
);
675 jsj_ExitJava(jsj_env
);
680 /* If a method member, build a wrapper around the Java method */
681 if (member_descriptor
->methods
) {
682 /* Create a function object with this JavaObject as its parent, so that
683 JSFUN_BOUND_METHOD binds it as the default 'this' for the function. */
684 funobj
= JS_CloneFunctionObject(cx
, member_descriptor
->invoke_func_obj
, obj
);
686 jsj_ExitJava(jsj_env
);
689 method_val
= OBJECT_TO_JSVAL(funobj
);
693 /* Always create a JavaMember object, even though it's inefficient */
694 obj
= jsj_CreateJavaMember(cx
, method_val
, field_val
);
696 jsj_ExitJava(jsj_env
);
699 *vp
= OBJECT_TO_JSVAL(obj
);
700 #else /* !TEST_JAVAMEMBER */
702 if (member_descriptor
->field
) {
703 if (!member_descriptor
->methods
) {
704 /* Return value of Java field */
707 /* Handle special case of access to a property that could refer
708 to either a Java field or a method that share the same name.
709 In Java, such ambiguity is not possible because the compiler
710 can statically determine which is being accessed. */
711 obj
= jsj_CreateJavaMember(cx
, method_val
, field_val
);
713 jsj_ExitJava(jsj_env
);
716 *vp
= OBJECT_TO_JSVAL(obj
);
720 /* Return wrapper around Java method */
724 #endif /* !TEST_JAVAMEMBER */
726 jsj_ExitJava(jsj_env
);
731 JavaObject_setPropertyById(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
734 const char *member_name
;
735 JavaObjectWrapper
*java_wrapper
;
736 JavaClassDescriptor
*class_descriptor
;
737 JavaMemberDescriptor
*member_descriptor
;
740 JSJavaThreadState
*jsj_env
;
741 JSObject
*proto_chain
;
742 JSJPropertyInfo prop_info
;
745 /* printf("In JavaObject_setProperty\n"); */
747 /* Get the Java per-thread environment pointer for this JSContext */
748 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
752 prop_info
.wantProp
= JS_FALSE
;
753 if (!lookup_member_by_id(cx
, jEnv
, obj
, &java_wrapper
, id
, &member_descriptor
, NULL
,
754 &proto_chain
, &prop_info
)) {
755 jsj_ExitJava(jsj_env
);
759 /* Could be assignment to magic JS __proto__ property rather than a Java field */
760 if (!member_descriptor
) {
761 if (proto_chain
&& (prop_info
.attributes
& JSPROP_SHARED
)) {
762 JS_SetProperty(cx
, proto_chain
, prop_info
.name
, vp
);
764 JS_IdToValue(cx
, id
, &idval
);
765 if (!JSVAL_IS_STRING(idval
))
767 member_name
= JS_GetStringBytes(JSVAL_TO_STRING(idval
));
768 if (strcmp(member_name
, "__proto__"))
770 if (!JSVAL_IS_OBJECT(*vp
)) {
771 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
772 JSJMSG_BAD_PROTO_ASSIGNMENT
);
773 jsj_ExitJava(jsj_env
);
776 JS_SetPrototype(cx
, obj
, JSVAL_TO_OBJECT(*vp
));
778 jsj_ExitJava(jsj_env
);
782 /* Check for the case where there is a method with the given name, but no field
784 if (!member_descriptor
->field
)
787 /* Silently fail if field value is final (immutable), as required by ECMA spec */
788 if (member_descriptor
->field
->modifiers
& ACC_FINAL
) {
789 jsj_ExitJava(jsj_env
);
793 java_obj
= java_wrapper
->java_obj
;
795 if (jaApplet
&& (*jEnv
)->IsInstanceOf(jEnv
, java_obj
, jaApplet
)) {
796 jsj_JSIsCallingApplet
= JS_TRUE
;
799 result
= jsj_SetJavaFieldValue(cx
, jEnv
, member_descriptor
->field
, java_obj
, *vp
);
800 jsj_ExitJava(jsj_env
);
804 JS_IdToValue(cx
, id
, &idval
);
805 member_name
= JS_GetStringBytes(JSVAL_TO_STRING(idval
));
806 class_descriptor
= java_wrapper
->class_descriptor
;
807 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
808 JSJMSG_NO_NAME_IN_CLASS
,
809 member_name
, class_descriptor
->name
);
810 jsj_ExitJava(jsj_env
);
815 JavaObject_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
816 JSObject
**objp
, JSProperty
**propp
)
819 JSErrorReporter old_reporter
;
821 JSObject
*proto_chain
;
822 JSJPropertyInfo prop_info
;
823 JSJavaThreadState
*jsj_env
;
825 /* printf("In JavaObject_lookupProperty()\n"); */
827 /* Get the Java per-thread environment pointer for this JSContext */
828 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
832 old_reporter
= JS_SetErrorReporter(cx
, NULL
);
833 prop_info
.wantProp
= JS_TRUE
;
834 if (lookup_member_by_id(cx
, jEnv
, obj
, NULL
, id
, NULL
, &dummy_val
,
835 &proto_chain
, &prop_info
)) {
836 /* signify that the property is in the prototype chain or the object itself. */
839 *propp
= prop_info
.prop
;
842 *propp
= (JSProperty
*)1;
849 JS_SetErrorReporter(cx
, old_reporter
);
850 jsj_ExitJava(jsj_env
);
855 JavaObject_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval value
,
856 JSPropertyOp getter
, JSPropertyOp setter
,
857 uintN attrs
, JSProperty
**propp
)
859 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
860 JSJMSG_JOBJECT_PROP_DEFINE
);
865 JavaObject_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
,
866 JSProperty
*prop
, uintN
*attrsp
)
868 /* We don't maintain JS property attributes for Java class members */
869 *attrsp
= JSPROP_PERMANENT
|JSPROP_ENUMERATE
;
874 JavaObject_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
,
875 JSProperty
*prop
, uintN
*attrsp
)
877 /* We don't maintain JS property attributes for Java class members */
878 if (*attrsp
!= (JSPROP_PERMANENT
|JSPROP_ENUMERATE
)) {
883 /* Silently ignore all setAttribute attempts */
888 JavaObject_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
890 JSVersion version
= JS_GetVersion(cx
);
894 if (!JSVERSION_IS_ECMA(version
)) {
895 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
896 JSJMSG_JOBJECT_PROP_DELETE
);
899 /* Attempts to delete permanent properties are silently ignored
906 JavaObject_defaultValue(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
908 /* printf("In JavaObject_defaultValue()\n"); */
909 return JavaObject_convert(cx
, obj
, type
, vp
);
913 JavaObject_newEnumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
914 jsval
*statep
, jsid
*idp
)
916 JavaObjectWrapper
*java_wrapper
;
917 JavaMemberDescriptor
*member_descriptor
;
918 JavaClassDescriptor
*class_descriptor
;
920 JSJavaThreadState
*jsj_env
;
922 java_wrapper
= JS_GetPrivate(cx
, obj
);
923 /* Check for prototype object */
925 *statep
= JSVAL_NULL
;
927 *idp
= INT_TO_JSVAL(0);
931 class_descriptor
= java_wrapper
->class_descriptor
;
934 case JSENUMERATE_INIT
:
936 /* Get the Java per-thread environment pointer for this JSContext */
937 jsj_env
= jsj_EnterJava(cx
, &jEnv
);
941 member_descriptor
= jsj_GetClassInstanceMembers(cx
, jEnv
, class_descriptor
);
942 *statep
= PRIVATE_TO_JSVAL(member_descriptor
);
944 *idp
= INT_TO_JSVAL(class_descriptor
->num_instance_members
);
945 jsj_ExitJava(jsj_env
);
948 case JSENUMERATE_NEXT
:
949 member_descriptor
= JSVAL_TO_PRIVATE(*statep
);
950 if (member_descriptor
) {
952 /* Don't enumerate explicit-signature methods, i.e. enumerate toValue,
953 but not toValue(int), toValue(double), etc. */
954 while (member_descriptor
->methods
&& member_descriptor
->methods
->is_alias
) {
955 member_descriptor
= member_descriptor
->next
;
956 if (!member_descriptor
) {
957 *statep
= JSVAL_NULL
;
962 *idp
= member_descriptor
->id
;
963 *statep
= PRIVATE_TO_JSVAL(member_descriptor
->next
);
967 /* Fall through ... */
969 case JSENUMERATE_DESTROY
:
970 *statep
= JSVAL_NULL
;
980 JavaObject_checkAccess(JSContext
*cx
, JSObject
*obj
, jsid id
,
981 JSAccessMode mode
, jsval
*vp
, uintN
*attrsp
)
985 JS_ReportErrorNumber(cx
, jsj_GetErrorMessage
, NULL
,
986 JSJMSG_JOBJECT_PROP_WATCH
);
994 #define JSJ_SLOT_COUNT (JSSLOT_PRIVATE+1)
997 jsj_wrapper_newObjectMap(JSContext
*cx
, jsrefcount nrefs
, JSObjectOps
*ops
,
998 JSClass
*clasp
, JSObject
*obj
)
1002 map
= (JSObjectMap
*) JS_malloc(cx
, sizeof(JSObjectMap
));
1006 map
->freeslot
= JSJ_SLOT_COUNT
;
1012 jsj_wrapper_destroyObjectMap(JSContext
*cx
, JSObjectMap
*map
)
1018 jsj_wrapper_getRequiredSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
)
1020 JS_ASSERT(slot
< JSJ_SLOT_COUNT
);
1021 JS_ASSERT(obj
->map
->freeslot
== JSJ_SLOT_COUNT
);
1022 return STOBJ_GET_SLOT(obj
, slot
);
1026 jsj_wrapper_setRequiredSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, jsval v
)
1028 JS_ASSERT(slot
< JSJ_SLOT_COUNT
);
1029 JS_ASSERT(obj
->map
->freeslot
== JSJ_SLOT_COUNT
);
1030 STOBJ_SET_SLOT(obj
, slot
, v
);
1034 JSObjectOps JavaObject_ops
= {
1035 /* Mandatory non-null function pointer members. */
1036 jsj_wrapper_newObjectMap
, /* newObjectMap */
1037 jsj_wrapper_destroyObjectMap
, /* destroyObjectMap */
1038 JavaObject_lookupProperty
,
1039 JavaObject_defineProperty
,
1040 JavaObject_getPropertyById
, /* getProperty */
1041 JavaObject_setPropertyById
, /* setProperty */
1042 JavaObject_getAttributes
,
1043 JavaObject_setAttributes
,
1044 JavaObject_deleteProperty
,
1045 JavaObject_defaultValue
,
1046 JavaObject_newEnumerate
,
1047 JavaObject_checkAccess
,
1049 /* Optionally non-null members start here. */
1050 NULL
, /* thisObject */
1051 NULL
, /* dropProperty */
1053 NULL
, /* construct */
1054 NULL
, /* xdrObject */
1055 NULL
, /* hasInstance */
1056 NULL
, /* setProto */
1057 NULL
, /* setParent */
1060 jsj_wrapper_getRequiredSlot
, /* getRequiredSlot */
1061 jsj_wrapper_setRequiredSlot
/* setRequiredSlot */
1064 static JSObjectOps
*
1065 JavaObject_getObjectOps(JSContext
*cx
, JSClass
*clazz
)
1067 return &JavaObject_ops
;
1070 JSClass JavaObject_class
= {
1071 "JavaObject", JSCLASS_HAS_PRIVATE
,
1072 NULL
, NULL
, NULL
, NULL
,
1073 NULL
, NULL
, JavaObject_convert
, JavaObject_finalize
,
1075 /* Optionally non-null members start here. */
1076 JavaObject_getObjectOps
,
1077 NULL
, /* checkAccess */
1079 NULL
, /* construct */
1080 NULL
, /* xdrObject */
1081 NULL
, /* hasInstance */
1086 extern JS_IMPORT_DATA(JSObjectOps
) js_ObjectOps
;
1089 jsj_init_JavaObject(JSContext
*cx
, JSObject
*global_obj
)
1091 return JS_InitClass(cx
, global_obj
,
1092 0, &JavaObject_class
, 0, 0,