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 "SourceCode.h"
31 #include "identifier.h"
33 #include "interpreter.h"
34 #include "jni_jsobject.h"
35 #include "jni_runtime.h"
36 #include "jni_utility.h"
37 #include "JSGlobalObject.h"
39 #include "runtime_object.h"
40 #include "runtime_root.h"
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <wtf/Assertions.h>
44 using namespace KJS::Bindings
;
48 #define JS_LOG(formatAndArgs...) ((void)0)
50 #define JS_LOG(formatAndArgs...) { \
51 fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
52 fprintf(stderr, formatAndArgs); \
56 #define UndefinedHandle 1
58 static bool isJavaScriptThread()
60 return (RootObject::runLoop() == CFRunLoopGetCurrent());
63 jvalue
JavaJSObject::invoke (JSObjectCallContext
*context
)
67 bzero ((void *)&result
, sizeof(jvalue
));
69 if (!isJavaScriptThread()) {
70 // Send the call context to the thread that is allowed to
72 RootObject::dispatchToJavaScriptThread(context
);
73 result
= context
->result
;
76 jlong nativeHandle
= context
->nativeHandle
;
77 if (nativeHandle
== UndefinedHandle
|| nativeHandle
== 0) {
81 if (context
->type
== CreateNative
) {
82 result
.j
= JavaJSObject::createNative(nativeHandle
);
85 JSObject
*imp
= jlong_to_impptr(nativeHandle
);
86 if (!findProtectingRootObject(imp
)) {
87 fprintf (stderr
, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__
, __LINE__
, context
->type
);
91 switch (context
->type
){
93 result
.l
= JavaJSObject(nativeHandle
).call(context
->string
, context
->args
);
98 result
.l
= JavaJSObject(nativeHandle
).eval(context
->string
);
103 result
.l
= JavaJSObject(nativeHandle
).getMember(context
->string
);
108 JavaJSObject(nativeHandle
).setMember(context
->string
, context
->value
);
113 JavaJSObject(nativeHandle
).removeMember(context
->string
);
118 result
.l
= JavaJSObject(nativeHandle
).getSlot(context
->index
);
123 JavaJSObject(nativeHandle
).setSlot(context
->index
, context
->value
);
128 result
.l
= (jobject
) JavaJSObject(nativeHandle
).toString();
133 JavaJSObject(nativeHandle
).finalize();
138 fprintf (stderr
, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__
);
142 context
->result
= result
;
149 JavaJSObject::JavaJSObject(jlong nativeJSObject
)
151 _imp
= jlong_to_impptr(nativeJSObject
);
154 _rootObject
= findProtectingRootObject(_imp
);
158 RootObject
* JavaJSObject::rootObject() const
160 return _rootObject
&& _rootObject
->isValid() ? _rootObject
.get() : 0;
163 jobject
JavaJSObject::call(jstring methodName
, jobjectArray args
) const
165 JS_LOG ("methodName = %s\n", JavaString(methodName
).UTF8String());
167 RootObject
* rootObject
= this->rootObject();
171 // Lookup the function object.
172 ExecState
* exec
= rootObject
->globalObject()->globalExec();
175 Identifier
identifier(JavaString(methodName
).ustring());
176 JSValue
*func
= _imp
->get (exec
, identifier
);
177 if (func
->isUndefinedOrNull())
180 // Call the function object.
181 JSObject
*funcImp
= static_cast<JSObject
*>(func
);
182 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
184 getListFromJArray(args
, argList
);
185 rootObject
->globalObject()->startTimeoutCheck();
186 JSValue
*result
= funcImp
->call(exec
, thisObj
, argList
);
187 rootObject
->globalObject()->stopTimeoutCheck();
189 return convertValueToJObject(result
);
192 jobject
JavaJSObject::eval(jstring script
) const
194 JS_LOG ("script = %s\n", JavaString(script
).UTF8String());
196 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
201 RootObject
* rootObject
= this->rootObject();
205 rootObject
->globalObject()->startTimeoutCheck();
207 SourceCode source
= makeSource(JavaString(script
).ustring(), UString());
208 Completion completion
= Interpreter::evaluate(rootObject
->globalObject()->globalExec(), source
, thisObj
);
210 rootObject
->globalObject()->stopTimeoutCheck();
211 ComplType type
= completion
.complType();
213 if (type
== Normal
) {
214 result
= completion
.value();
216 result
= jsUndefined();
218 result
= jsUndefined();
220 return convertValueToJObject (result
);
223 jobject
JavaJSObject::getMember(jstring memberName
) const
225 JS_LOG ("(%p) memberName = %s\n", _imp
, JavaString(memberName
).UTF8String());
227 RootObject
* rootObject
= this->rootObject();
231 ExecState
* exec
= rootObject
->globalObject()->globalExec();
234 JSValue
*result
= _imp
->get (exec
, Identifier (JavaString(memberName
).ustring()));
236 return convertValueToJObject(result
);
239 void JavaJSObject::setMember(jstring memberName
, jobject value
) const
241 JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName
).UTF8String(), value
);
243 RootObject
* rootObject
= this->rootObject();
247 ExecState
* exec
= rootObject
->globalObject()->globalExec();
249 _imp
->put(exec
, Identifier (JavaString(memberName
).ustring()), convertJObjectToValue(value
));
253 void JavaJSObject::removeMember(jstring memberName
) const
255 JS_LOG ("memberName = %s\n", JavaString(memberName
).UTF8String());
257 RootObject
* rootObject
= this->rootObject();
261 ExecState
* exec
= rootObject
->globalObject()->globalExec();
263 _imp
->deleteProperty(exec
, Identifier (JavaString(memberName
).ustring()));
267 jobject
JavaJSObject::getSlot(jint index
) const
270 JS_LOG ("index = %d\n", index
);
272 JS_LOG ("index = %ld\n", index
);
275 RootObject
* rootObject
= this->rootObject();
279 ExecState
* exec
= rootObject
->globalObject()->globalExec();
282 JSValue
*result
= _imp
->get (exec
, (unsigned)index
);
284 return convertValueToJObject(result
);
288 void JavaJSObject::setSlot(jint index
, jobject value
) const
291 JS_LOG ("index = %d, value = %p\n", index
, value
);
293 JS_LOG ("index = %ld, value = %p\n", index
, value
);
296 RootObject
* rootObject
= this->rootObject();
300 ExecState
* exec
= rootObject
->globalObject()->globalExec();
302 _imp
->put(exec
, (unsigned)index
, convertJObjectToValue(value
));
306 jstring
JavaJSObject::toString() const
310 RootObject
* rootObject
= this->rootObject();
315 JSObject
*thisObj
= const_cast<JSObject
*>(_imp
);
316 ExecState
* exec
= rootObject
->globalObject()->globalExec();
318 return (jstring
)convertValueToJValue (exec
, thisObj
, object_type
, "java.lang.String").l
;
321 void JavaJSObject::finalize() const
323 if (RootObject
* rootObject
= this->rootObject())
324 rootObject
->gcUnprotect(_imp
);
327 // We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or
328 // another JavaJSObject.
329 jlong
JavaJSObject::createNative(jlong nativeHandle
)
331 JS_LOG ("nativeHandle = %d\n", (int)nativeHandle
);
333 if (nativeHandle
== UndefinedHandle
)
336 if (findProtectingRootObject(jlong_to_impptr(nativeHandle
)))
339 CreateRootObjectFunction createRootObject
= RootObject::createRootObject();
340 if (!createRootObject
)
341 return ptr_to_jlong(0);
343 RefPtr
<RootObject
> rootObject
= createRootObject(jlong_to_ptr(nativeHandle
));
345 // If rootObject is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(),
346 // otherwise we are being called after creating a JavaJSObject in
347 // JavaJSObject::convertValueToJObject().
349 JSObject
* globalObject
= rootObject
->globalObject();
350 // We call gcProtect here to get the object into the root object's "protect set" which
351 // is used to test if a native handle is valid as well as getting the root object given the handle.
352 rootObject
->gcProtect(globalObject
);
353 return ptr_to_jlong(globalObject
);
359 jobject
JavaJSObject::convertValueToJObject (JSValue
*value
) const
363 RootObject
* rootObject
= this->rootObject();
367 ExecState
* exec
= rootObject
->globalObject()->globalExec();
368 JNIEnv
*env
= getJNIEnv();
371 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
373 // number -> java.lang.Double
374 // string -> java.lang.String
375 // boolean -> java.lang.Boolean
376 // Java instance -> Java instance
377 // Everything else -> JavaJSObject
379 JSType type
= value
->type();
380 if (type
== NumberType
) {
381 jclass JSObjectClass
= env
->FindClass ("java/lang/Double");
382 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(D)V");
383 if (constructorID
!= NULL
) {
384 result
= env
->NewObject (JSObjectClass
, constructorID
, (jdouble
)value
->toNumber(exec
));
387 else if (type
== StringType
) {
388 UString stringValue
= value
->toString(exec
);
389 JNIEnv
*env
= getJNIEnv();
390 result
= env
->NewString ((const jchar
*)stringValue
.data(), stringValue
.size());
392 else if (type
== BooleanType
) {
393 jclass JSObjectClass
= env
->FindClass ("java/lang/Boolean");
394 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(Z)V");
395 if (constructorID
!= NULL
) {
396 result
= env
->NewObject (JSObjectClass
, constructorID
, (jboolean
)value
->toBoolean(exec
));
400 // Create a JavaJSObject.
403 if (type
== ObjectType
){
404 JSObject
*imp
= static_cast<JSObject
*>(value
);
406 // We either have a wrapper around a Java instance or a JavaScript
407 // object. If we have a wrapper around a Java instance, return that
408 // instance, otherwise create a new Java JavaJSObject with the JSObject*
409 // as it's nativeHandle.
410 if (imp
->classInfo() && strcmp(imp
->classInfo()->className
, "RuntimeObject") == 0) {
411 RuntimeObjectImp
*runtimeImp
= static_cast<RuntimeObjectImp
*>(value
);
412 JavaInstance
*runtimeInstance
= static_cast<JavaInstance
*>(runtimeImp
->getInternalInstance());
413 if (!runtimeInstance
)
416 return runtimeInstance
->javaInstance();
419 nativeHandle
= ptr_to_jlong(imp
);
420 rootObject
->gcProtect(imp
);
423 // All other types will result in an undefined object.
425 nativeHandle
= UndefinedHandle
;
428 // Now create the Java JavaJSObject. Look for the JavaJSObject in it's new (Tiger)
429 // location and in the original Java 1.4.2 location.
430 jclass JSObjectClass
;
432 JSObjectClass
= env
->FindClass ("sun/plugin/javascript/webkit/JSObject");
433 if (!JSObjectClass
) {
434 env
->ExceptionDescribe();
435 env
->ExceptionClear();
436 JSObjectClass
= env
->FindClass ("apple/applet/JSObject");
439 jmethodID constructorID
= env
->GetMethodID (JSObjectClass
, "<init>", "(J)V");
440 if (constructorID
!= NULL
) {
441 result
= env
->NewObject (JSObjectClass
, constructorID
, nativeHandle
);
448 JSValue
*JavaJSObject::convertJObjectToValue (jobject theObject
) const
450 // Instances of netscape.javascript.JSObject get converted back to
451 // JavaScript objects. All other objects are wrapped. It's not
452 // possible to pass primitive types from the Java to JavaScript.
453 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
455 jobject classOfInstance
= callJNIObjectMethod(theObject
, "getClass", "()Ljava/lang/Class;");
456 jstring className
= (jstring
)callJNIObjectMethod(classOfInstance
, "getName", "()Ljava/lang/String;");
458 // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is
459 // created above to wrap internal browser objects. The constructor of this class takes the native
460 // pointer and stores it in this object, so that it can be retrieved below.
461 if (strcmp(JavaString(className
).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) {
462 // Pull the nativeJSObject value from the Java instance. This is a
463 // pointer to the JSObject.
464 JNIEnv
*env
= getJNIEnv();
465 jfieldID fieldID
= env
->GetFieldID((jclass
)classOfInstance
, "nativeJSObject", "J");
466 if (fieldID
== NULL
) {
467 return jsUndefined();
469 jlong nativeHandle
= env
->GetLongField(theObject
, fieldID
);
470 if (nativeHandle
== UndefinedHandle
) {
471 return jsUndefined();
473 JSObject
*imp
= static_cast<JSObject
*>(jlong_to_impptr(nativeHandle
));
478 JavaInstance
* javaInstance
= new JavaInstance(theObject
, _rootObject
);
479 return KJS::Bindings::Instance::createRuntimeObject(javaInstance
);
482 void JavaJSObject::getListFromJArray(jobjectArray jArray
, List
& list
) const
484 JNIEnv
*env
= getJNIEnv();
485 int i
, numObjects
= jArray
? env
->GetArrayLength (jArray
) : 0;
487 for (i
= 0; i
< numObjects
; i
++) {
488 jobject anObject
= env
->GetObjectArrayElement ((jobjectArray
)jArray
, i
);
490 list
.append(convertJObjectToValue(anObject
));
491 env
->DeleteLocalRef (anObject
);
494 env
->ExceptionDescribe();
495 env
->ExceptionClear();
502 jlong
KJS_JSCreateNativeJSObject (JNIEnv
*, jclass
, jstring
, jlong nativeHandle
, jboolean
)
504 JSObjectCallContext context
;
505 context
.type
= CreateNative
;
506 context
.nativeHandle
= nativeHandle
;
507 return JavaJSObject::invoke (&context
).j
;
510 void KJS_JSObject_JSFinalize (JNIEnv
*, jclass
, jlong nativeHandle
)
512 JSObjectCallContext context
;
513 context
.type
= Finalize
;
514 context
.nativeHandle
= nativeHandle
;
515 JavaJSObject::invoke (&context
);
518 jobject
KJS_JSObject_JSObjectCall (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring methodName
, jobjectArray args
, jboolean
)
520 JSObjectCallContext context
;
522 context
.nativeHandle
= nativeHandle
;
523 context
.string
= methodName
;
525 return JavaJSObject::invoke (&context
).l
;
528 jobject
KJS_JSObject_JSObjectEval (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jscript
, jboolean
)
530 JSObjectCallContext context
;
532 context
.nativeHandle
= nativeHandle
;
533 context
.string
= jscript
;
534 return JavaJSObject::invoke (&context
).l
;
537 jobject
KJS_JSObject_JSObjectGetMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jboolean
)
539 JSObjectCallContext context
;
540 context
.type
= GetMember
;
541 context
.nativeHandle
= nativeHandle
;
542 context
.string
= jname
;
543 return JavaJSObject::invoke (&context
).l
;
546 void KJS_JSObject_JSObjectSetMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jobject value
, jboolean
)
548 JSObjectCallContext context
;
549 context
.type
= SetMember
;
550 context
.nativeHandle
= nativeHandle
;
551 context
.string
= jname
;
552 context
.value
= value
;
553 JavaJSObject::invoke (&context
);
556 void KJS_JSObject_JSObjectRemoveMember (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jstring jname
, jboolean
)
558 JSObjectCallContext context
;
559 context
.type
= RemoveMember
;
560 context
.nativeHandle
= nativeHandle
;
561 context
.string
= jname
;
562 JavaJSObject::invoke (&context
);
565 jobject
KJS_JSObject_JSObjectGetSlot (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jint jindex
, jboolean
)
567 JSObjectCallContext context
;
568 context
.type
= GetSlot
;
569 context
.nativeHandle
= nativeHandle
;
570 context
.index
= jindex
;
571 return JavaJSObject::invoke (&context
).l
;
574 void KJS_JSObject_JSObjectSetSlot (JNIEnv
*, jclass
, jlong nativeHandle
, jstring
, jint jindex
, jobject value
, jboolean
)
576 JSObjectCallContext context
;
577 context
.type
= SetSlot
;
578 context
.nativeHandle
= nativeHandle
;
579 context
.index
= jindex
;
580 context
.value
= value
;
581 JavaJSObject::invoke (&context
);
584 jstring
KJS_JSObject_JSObjectToString (JNIEnv
*, jclass
, jlong nativeHandle
)
586 JSObjectCallContext context
;
587 context
.type
= ToString
;
588 context
.nativeHandle
= nativeHandle
;
589 return (jstring
)JavaJSObject::invoke (&context
).l
;
594 #endif // ENABLE(JAVA_BINDINGS)