2 * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #if ENABLE(JAVA_BINDINGS)
30 #include "identifier.h"
32 #include "interpreter.h"
33 #include "jni_jsobject.h"
34 #include "jni_runtime.h"
35 #include "jni_utility.h"
36 #include "JSGlobalObject.h"
38 #include "runtime_object.h"
39 #include "runtime_root.h"
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <wtf/Assertions.h>
43 using namespace KJS::Bindings
;
47 #define JS_LOG(formatAndArgs...) ((void)0)
49 #define JS_LOG(formatAndArgs...) { \
50 fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
51 fprintf(stderr, formatAndArgs); \
55 #define UndefinedHandle 1
57 static bool isJavaScriptThread()
59 return (RootObject::runLoop() == CFRunLoopGetCurrent());
62 jvalue
JavaJSObject::invoke (JSObjectCallContext
*context
)
66 bzero ((void *)&result
, sizeof(jvalue
));
68 if (!isJavaScriptThread()) {
69 // Send the call context to the thread that is allowed to
71 RootObject::dispatchToJavaScriptThread(context
);
72 result
= context
->result
;
75 jlong nativeHandle
= context
->nativeHandle
;
76 if (nativeHandle
== UndefinedHandle
|| nativeHandle
== 0) {
80 if (context
->type
== CreateNative
) {
81 result
.j
= JavaJSObject::createNative(nativeHandle
);
84 JSObject
*imp
= jlong_to_impptr(nativeHandle
);
85 if (!findProtectingRootObject(imp
)) {
86 fprintf (stderr
, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__
, __LINE__
, context
->type
);
90 switch (context
->type
){
92 result
.l
= JavaJSObject(nativeHandle
).call(context
->string
, context
->args
);
97 result
.l
= JavaJSObject(nativeHandle
).eval(context
->string
);
102 result
.l
= JavaJSObject(nativeHandle
).getMember(context
->string
);
107 JavaJSObject(nativeHandle
).setMember(context
->string
, context
->value
);
112 JavaJSObject(nativeHandle
).removeMember(context
->string
);
117 result
.l
= JavaJSObject(nativeHandle
).getSlot(context
->index
);
122 JavaJSObject(nativeHandle
).setSlot(context
->index
, context
->value
);
127 result
.l
= (jobject
) JavaJSObject(nativeHandle
).toString();
132 JavaJSObject(nativeHandle
).finalize();
137 fprintf (stderr
, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__
);
141 context
->result
= result
;
148 JavaJSObject::JavaJSObject(jlong nativeJSObject
)
150 _imp
= jlong_to_impptr(nativeJSObject
);
153 _rootObject
= findProtectingRootObject(_imp
);
157 RootObject
* JavaJSObject::rootObject() const
159 return _rootObject
&& _rootObject
->isValid() ? _rootObject
.get() : 0;
162 jobject
JavaJSObject::call(jstring methodName
, jobjectArray args
) const
164 JS_LOG ("methodName = %s\n", JavaString(methodName
).UTF8String());
166 RootObject
* rootObject
= this->rootObject();
170 // Lookup the function object.
171 ExecState
* exec
= rootObject
->globalObject()->globalExec();
174 Identifier
identifier(JavaString(methodName
).ustring());
175 JSValue
*func
= _imp
->get (exec
, identifier
);
176 if (func
->isUndefinedOrNull())
179 // Call the function object.
180 JSObject
*funcImp
= static_cast<JSObject
*>(func
);
181 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
183 getListFromJArray(args
, argList
);
184 rootObject
->globalObject()->startTimeoutCheck();
185 JSValue
*result
= funcImp
->call(exec
, thisObj
, argList
);
186 rootObject
->globalObject()->stopTimeoutCheck();
188 return convertValueToJObject(result
);
191 jobject
JavaJSObject::eval(jstring script
) const
193 JS_LOG ("script = %s\n", JavaString(script
).UTF8String());
195 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
200 RootObject
* rootObject
= this->rootObject();
204 rootObject
->globalObject()->startTimeoutCheck();
205 Completion completion
= Interpreter::evaluate(rootObject
->globalObject()->globalExec(), UString(), 0, JavaString(script
).ustring(),thisObj
);
206 rootObject
->globalObject()->stopTimeoutCheck();
207 ComplType type
= completion
.complType();
209 if (type
== Normal
) {
210 result
= completion
.value();
212 result
= jsUndefined();
214 result
= jsUndefined();
216 return convertValueToJObject (result
);
219 jobject
JavaJSObject::getMember(jstring memberName
) const
221 JS_LOG ("(%p) memberName = %s\n", _imp
, JavaString(memberName
).UTF8String());
223 RootObject
* rootObject
= this->rootObject();
227 ExecState
* exec
= rootObject
->globalObject()->globalExec();
230 JSValue
*result
= _imp
->get (exec
, Identifier (JavaString(memberName
).ustring()));
232 return convertValueToJObject(result
);
235 void JavaJSObject::setMember(jstring memberName
, jobject value
) const
237 JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName
).UTF8String(), value
);
239 RootObject
* rootObject
= this->rootObject();
243 ExecState
* exec
= rootObject
->globalObject()->globalExec();
245 _imp
->put(exec
, Identifier (JavaString(memberName
).ustring()), convertJObjectToValue(value
));
249 void JavaJSObject::removeMember(jstring memberName
) const
251 JS_LOG ("memberName = %s\n", JavaString(memberName
).UTF8String());
253 RootObject
* rootObject
= this->rootObject();
257 ExecState
* exec
= rootObject
->globalObject()->globalExec();
259 _imp
->deleteProperty(exec
, Identifier (JavaString(memberName
).ustring()));
263 jobject
JavaJSObject::getSlot(jint index
) const
266 JS_LOG ("index = %d\n", index
);
268 JS_LOG ("index = %ld\n", index
);
271 RootObject
* rootObject
= this->rootObject();
275 ExecState
* exec
= rootObject
->globalObject()->globalExec();
278 JSValue
*result
= _imp
->get (exec
, (unsigned)index
);
280 return convertValueToJObject(result
);
284 void JavaJSObject::setSlot(jint index
, jobject value
) const
287 JS_LOG ("index = %d, value = %p\n", index
, value
);
289 JS_LOG ("index = %ld, value = %p\n", index
, value
);
292 RootObject
* rootObject
= this->rootObject();
296 ExecState
* exec
= rootObject
->globalObject()->globalExec();
298 _imp
->put(exec
, (unsigned)index
, convertJObjectToValue(value
));
302 jstring
JavaJSObject::toString() const
306 RootObject
* rootObject
= this->rootObject();
311 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
312 ExecState
* exec
= rootObject
->globalObject()->globalExec();
314 return (jstring
)convertValueToJValue (exec
, thisObj
, object_type
, "java.lang.String").l
;
317 void JavaJSObject::finalize() const
319 if (RootObject
* rootObject
= this->rootObject())
320 rootObject
->gcUnprotect(_imp
);
323 // We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or
324 // another JavaJSObject.
325 jlong
JavaJSObject::createNative(jlong nativeHandle
)
327 JS_LOG ("nativeHandle = %d\n", (int)nativeHandle
);
329 if (nativeHandle
== UndefinedHandle
)
332 if (findProtectingRootObject(jlong_to_impptr(nativeHandle
)))
335 CreateRootObjectFunction createRootObject
= RootObject::createRootObject();
336 if (!createRootObject
)
337 return ptr_to_jlong(0);
339 RefPtr
<RootObject
> rootObject
= createRootObject(jlong_to_ptr(nativeHandle
));
341 // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(),
342 // otherwise we are being called after creating a JavaJSObject in
343 // JavaJSObject::convertValueToJObject().
345 JSObject
* globalObject
= rootObject
->globalObject();
346 // We call gcProtect here to get the object into the root object's "protect set" which
347 // is used to test if a native handle is valid as well as getting the root object given the handle.
348 rootObject
->gcProtect(globalObject
);
349 return ptr_to_jlong(globalObject
);
355 jobject
JavaJSObject::convertValueToJObject (JSValue
*value
) const
359 RootObject
* rootObject
= this->rootObject();
363 ExecState
* exec
= rootObject
->globalObject()->globalExec();
364 JNIEnv
*env
= getJNIEnv();
367 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
369 // number -> java.lang.Double
370 // string -> java.lang.String
371 // boolean -> java.lang.Boolean
372 // Java instance -> Java instance
373 // Everything else -> JavaJSObject
375 JSType type
= value
->type();
376 if (type
== NumberType
) {
377 jclass JSObjectClass
= env
->FindClass ("java/lang/Double");
378 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(D)V");
379 if (constructorID
!= NULL
) {
380 result
= env
->NewObject (JSObjectClass
, constructorID
, (jdouble
)value
->toNumber(exec
));
383 else if (type
== StringType
) {
384 UString stringValue
= value
->toString(exec
);
385 JNIEnv
*env
= getJNIEnv();
386 result
= env
->NewString ((const jchar
*)stringValue
.data(), stringValue
.size());
388 else if (type
== BooleanType
) {
389 jclass JSObjectClass
= env
->FindClass ("java/lang/Boolean");
390 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(Z)V");
391 if (constructorID
!= NULL
) {
392 result
= env
->NewObject (JSObjectClass
, constructorID
, (jboolean
)value
->toBoolean(exec
));
396 // Create a JavaJSObject.
399 if (type
== ObjectType
){
400 JSObject
*imp
= static_cast<JSObject
*>(value
);
402 // We either have a wrapper around a Java instance or a JavaScript
403 // object. If we have a wrapper around a Java instance, return that
404 // instance, otherwise create a new Java JavaJSObject with the JSObject*
405 // as it's nativeHandle.
406 if (imp
->classInfo() && strcmp(imp
->classInfo()->className
, "RuntimeObject") == 0) {
407 RuntimeObjectImp
*runtimeImp
= static_cast<RuntimeObjectImp
*>(value
);
408 JavaInstance
*runtimeInstance
= static_cast<JavaInstance
*>(runtimeImp
->getInternalInstance());
409 if (!runtimeInstance
)
412 return runtimeInstance
->javaInstance();
415 nativeHandle
= ptr_to_jlong(imp
);
416 rootObject
->gcProtect(imp
);
419 // All other types will result in an undefined object.
421 nativeHandle
= UndefinedHandle
;
424 // Now create the Java JavaJSObject. Look for the JavaJSObject in it's new (Tiger)
425 // location and in the original Java 1.4.2 location.
426 jclass JSObjectClass
;
428 JSObjectClass
= env
->FindClass ("sun/plugin/javascript/webkit/JSObject");
429 if (!JSObjectClass
) {
430 env
->ExceptionDescribe();
431 env
->ExceptionClear();
432 JSObjectClass
= env
->FindClass ("apple/applet/JSObject");
435 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(J)V");
436 if (constructorID
!= NULL
) {
437 result
= env
->NewObject (JSObjectClass
, constructorID
, nativeHandle
);
444 JSValue
*JavaJSObject::convertJObjectToValue (jobject theObject
) const
446 // Instances of netscape.javascript.JSObject get converted back to
447 // JavaScript objects. All other objects are wrapped. It's not
448 // possible to pass primitive types from the Java to JavaScript.
449 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
451 jobject classOfInstance
= callJNIObjectMethod(theObject
, "getClass", "()Ljava/lang/Class;");
452 jstring className
= (jstring
)callJNIObjectMethod(classOfInstance
, "getName", "()Ljava/lang/String;");
454 // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is
455 // created above to wrap internal browser objects. The constructor of this class takes the native
456 // pointer and stores it in this object, so that it can be retrieved below.
457 if (strcmp(JavaString(className
).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) {
458 // Pull the nativeJSObject value from the Java instance. This is a
459 // pointer to the JSObject.
460 JNIEnv
*env
= getJNIEnv();
461 jfieldID fieldID
= env
->GetFieldID((jclass
)classOfInstance
, "nativeJSObject", "J");
462 if (fieldID
== NULL
) {
463 return jsUndefined();
465 jlong nativeHandle
= env
->GetLongField(theObject
, fieldID
);
466 if (nativeHandle
== UndefinedHandle
) {
467 return jsUndefined();
469 JSObject
*imp
= static_cast<JSObject
*>(jlong_to_impptr(nativeHandle
));
474 JavaInstance
* javaInstance
= new JavaInstance(theObject
, _rootObject
);
475 return KJS::Bindings::Instance::createRuntimeObject(javaInstance
);
478 void JavaJSObject::getListFromJArray(jobjectArray jArray
, List
& list
) const
480 JNIEnv
*env
= getJNIEnv();
481 int i
, numObjects
= jArray
? env
->GetArrayLength (jArray
) : 0;
483 for (i
= 0; i
< numObjects
; i
++) {
484 jobject anObject
= env
->GetObjectArrayElement ((jobjectArray
)jArray
, i
);
486 list
.append(convertJObjectToValue(anObject
));
487 env
->DeleteLocalRef (anObject
);
490 env
->ExceptionDescribe();
491 env
->ExceptionClear();
498 jlong
KJS_JSCreateNativeJSObject (JNIEnv
*, jclass
, jstring
, jlong nativeHandle
, jboolean
)
500 JSObjectCallContext context
;
501 context
.type
= CreateNative
;
502 context
.nativeHandle
= nativeHandle
;
503 return JavaJSObject::invoke (&context
).j
;
506 void KJS_JSObject_JSFinalize (JNIEnv
*, jclass
, jlong nativeHandle
)
508 JSObjectCallContext context
;
509 context
.type
= Finalize
;
510 context
.nativeHandle
= nativeHandle
;
511 JavaJSObject::invoke (&context
);
514 jobject
KJS_JSObject_JSObjectCall (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring methodName
, jobjectArray args
, jboolean
)
516 JSObjectCallContext context
;
518 context
.nativeHandle
= nativeHandle
;
519 context
.string
= methodName
;
521 return JavaJSObject::invoke (&context
).l
;
524 jobject
KJS_JSObject_JSObjectEval (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jscript
, jboolean
)
526 JSObjectCallContext context
;
528 context
.nativeHandle
= nativeHandle
;
529 context
.string
= jscript
;
530 return JavaJSObject::invoke (&context
).l
;
533 jobject
KJS_JSObject_JSObjectGetMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jboolean
)
535 JSObjectCallContext context
;
536 context
.type
= GetMember
;
537 context
.nativeHandle
= nativeHandle
;
538 context
.string
= jname
;
539 return JavaJSObject::invoke (&context
).l
;
542 void KJS_JSObject_JSObjectSetMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jobject value
, jboolean
)
544 JSObjectCallContext context
;
545 context
.type
= SetMember
;
546 context
.nativeHandle
= nativeHandle
;
547 context
.string
= jname
;
548 context
.value
= value
;
549 JavaJSObject::invoke (&context
);
552 void KJS_JSObject_JSObjectRemoveMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jboolean
)
554 JSObjectCallContext context
;
555 context
.type
= RemoveMember
;
556 context
.nativeHandle
= nativeHandle
;
557 context
.string
= jname
;
558 JavaJSObject::invoke (&context
);
561 jobject
KJS_JSObject_JSObjectGetSlot (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jint jindex
, jboolean
)
563 JSObjectCallContext context
;
564 context
.type
= GetSlot
;
565 context
.nativeHandle
= nativeHandle
;
566 context
.index
= jindex
;
567 return JavaJSObject::invoke (&context
).l
;
570 void KJS_JSObject_JSObjectSetSlot (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jint jindex
, jobject value
, jboolean
)
572 JSObjectCallContext context
;
573 context
.type
= SetSlot
;
574 context
.nativeHandle
= nativeHandle
;
575 context
.index
= jindex
;
576 context
.value
= value
;
577 JavaJSObject::invoke (&context
);
580 jstring
KJS_JSObject_JSObjectToString (JNIEnv
*, jclass
, jlong nativeHandle
)
582 JSObjectCallContext context
;
583 context
.type
= ToString
;
584 context
.nativeHandle
= nativeHandle
;
585 return (jstring
)JavaJSObject::invoke (&context
).l
;
590 #endif // ENABLE(JAVA_BINDINGS)