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.
27 #include "identifier.h"
29 #include "interpreter.h"
31 #include "jni_jsobject.h"
32 #include "jni_runtime.h"
33 #include "jni_utility.h"
34 #include "runtime_object.h"
35 #include "runtime_root.h"
37 #include <CoreFoundation/CoreFoundation.h>
40 using namespace KJS::Bindings;
44 #define JS_LOG(formatAndArgs...) ((void)0)
46 #define JS_LOG(formatAndArgs...) { \
47 fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
48 fprintf(stderr, formatAndArgs); \
52 #define UndefinedHandle 1
54 static bool isJavaScriptThread()
56 return (RootObject::runLoop() == CFRunLoopGetCurrent());
59 jvalue JavaJSObject::invoke (JSObjectCallContext *context)
63 bzero ((void *)&result, sizeof(jvalue));
65 if (!isJavaScriptThread()) {
66 // Send the call context to the thread that is allowed to
68 RootObject::dispatchToJavaScriptThread(context);
69 result = context->result;
72 jlong nativeHandle = context->nativeHandle;
73 if (nativeHandle == UndefinedHandle || nativeHandle == 0) {
77 if (context->type == CreateNative) {
78 result.j = JavaJSObject::createNative(nativeHandle);
81 JSObject *imp = jlong_to_impptr(nativeHandle);
82 if (!rootForImp(imp)) {
83 fprintf (stderr, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__, __LINE__, context->type);
87 switch (context->type){
89 result.l = JavaJSObject(nativeHandle).call(context->string, context->args);
94 result.l = JavaJSObject(nativeHandle).eval(context->string);
99 result.l = JavaJSObject(nativeHandle).getMember(context->string);
104 JavaJSObject(nativeHandle).setMember(context->string, context->value);
109 JavaJSObject(nativeHandle).removeMember(context->string);
114 result.l = JavaJSObject(nativeHandle).getSlot(context->index);
119 JavaJSObject(nativeHandle).setSlot(context->index, context->value);
124 result.l = (jobject) JavaJSObject(nativeHandle).toString();
129 JSObject *imp = jlong_to_impptr(nativeHandle);
130 if (findReferenceDictionary(imp) == 0) {
131 // We may have received a finalize method call from the VM
132 // AFTER removing our last reference to the Java instance.
133 JS_LOG ("finalize called on instance we have already removed.\n");
136 JavaJSObject(nativeHandle).finalize();
142 fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__);
146 context->result = result;
153 JavaJSObject::JavaJSObject(jlong nativeJSObject)
155 _imp = jlong_to_impptr(nativeJSObject);
157 // If we are unable to cast the nativeJSObject to an JSObject something is
161 _root = rootForImp(_imp);
163 // If we can't find the root for the object something is terribly wrong.
168 jobject JavaJSObject::call(jstring methodName, jobjectArray args) const
170 JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String());
172 // Lookup the function object.
173 ExecState *exec = _root->interpreter()->globalExec();
176 Identifier identifier(JavaString(methodName).ustring());
177 JSValue *func = _imp->get (exec, identifier);
178 if (func->isUndefinedOrNull()) {
179 // Maybe throw an exception here?
183 // Call the function object.
184 JSObject *funcImp = static_cast<JSObject*>(func);
185 JSObject *thisObj = const_cast<JSObject*>(_imp);
186 List argList = listFromJArray(args);
187 JSValue *result = funcImp->call(exec, thisObj, argList);
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 Completion completion = _root->interpreter()->evaluate(UString(), 0, JavaString(script).ustring(),thisObj);
202 ComplType type = completion.complType();
204 if (type == Normal) {
205 result = completion.value();
207 result = jsUndefined();
209 result = jsUndefined();
211 return convertValueToJObject (result);
214 jobject JavaJSObject::getMember(jstring memberName) const
216 JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String());
218 ExecState *exec = _root->interpreter()->globalExec();
221 JSValue *result = _imp->get (exec, Identifier (JavaString(memberName).ustring()));
223 return convertValueToJObject(result);
226 void JavaJSObject::setMember(jstring memberName, jobject value) const
228 JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value);
229 ExecState *exec = _root->interpreter()->globalExec();
231 _imp->put(exec, Identifier (JavaString(memberName).ustring()), convertJObjectToValue(value));
235 void JavaJSObject::removeMember(jstring memberName) const
237 JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String());
239 ExecState *exec = _root->interpreter()->globalExec();
241 _imp->deleteProperty(exec, Identifier (JavaString(memberName).ustring()));
245 jobject JavaJSObject::getSlot(jint index) const
248 JS_LOG ("index = %d\n", index);
250 JS_LOG ("index = %ld\n", index);
253 ExecState *exec = _root->interpreter()->globalExec();
256 JSValue *result = _imp->get (exec, (unsigned)index);
258 return convertValueToJObject(result);
262 void JavaJSObject::setSlot(jint index, jobject value) const
265 JS_LOG ("index = %d, value = %p\n", index, value);
267 JS_LOG ("index = %ld, value = %p\n", index, value);
270 ExecState *exec = _root->interpreter()->globalExec();
272 _imp->put(exec, (unsigned)index, convertJObjectToValue(value));
276 jstring JavaJSObject::toString() const
281 JSObject *thisObj = const_cast<JSObject*>(_imp);
282 ExecState *exec = _root->interpreter()->globalExec();
284 return (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l;
287 void JavaJSObject::finalize() const
291 removeNativeReference (_imp);
294 // We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or
295 // another JavaJSObject.
296 jlong JavaJSObject::createNative(jlong nativeHandle)
298 JS_LOG ("nativeHandle = %d\n", (int)nativeHandle);
300 if (nativeHandle == UndefinedHandle)
302 else if (rootForImp(jlong_to_impptr(nativeHandle))){
306 FindRootObjectForNativeHandleFunctionPtr aFunc = RootObject::findRootObjectForNativeHandleFunction();
308 Bindings::RootObject *root = aFunc(jlong_to_ptr(nativeHandle));
309 // If root is !NULL We must have been called via netscape.javascript.JavaJSObject.getWindow(),
310 // otherwise we are being called after creating a JavaJSObject in
311 // JavaJSObject::convertValueToJObject().
313 addNativeReference (root, root->rootObjectImp());
314 return ptr_to_jlong(root->rootObjectImp());
321 return ptr_to_jlong(0);
324 jobject JavaJSObject::convertValueToJObject (JSValue *value) const
326 ExecState *exec = _root->interpreter()->globalExec();
327 JNIEnv *env = getJNIEnv();
330 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
332 // number -> java.lang.Double
333 // string -> java.lang.String
334 // boolean -> java.lang.Boolean
335 // Java instance -> Java instance
336 // Everything else -> JavaJSObject
338 JSType type = value->type();
339 if (type == NumberType) {
340 jclass JSObjectClass = env->FindClass ("java/lang/Double");
341 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(D)V");
342 if (constructorID != NULL) {
343 result = env->NewObject (JSObjectClass, constructorID, (jdouble)value->toNumber(exec));
346 else if (type == StringType) {
347 UString stringValue = value->toString(exec);
348 JNIEnv *env = getJNIEnv();
349 result = env->NewString ((const jchar *)stringValue.data(), stringValue.size());
351 else if (type == BooleanType) {
352 jclass JSObjectClass = env->FindClass ("java/lang/Boolean");
353 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(Z)V");
354 if (constructorID != NULL) {
355 result = env->NewObject (JSObjectClass, constructorID, (jboolean)value->toBoolean(exec));
359 // Create a JavaJSObject.
362 if (type == ObjectType){
363 JSObject *imp = static_cast<JSObject*>(value);
365 // We either have a wrapper around a Java instance or a JavaScript
366 // object. If we have a wrapper around a Java instance, return that
367 // instance, otherwise create a new Java JavaJSObject with the JSObject*
368 // as it's nativeHandle.
369 if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) {
370 RuntimeObjectImp *runtimeImp = static_cast<RuntimeObjectImp*>(value);
371 Bindings::JavaInstance *runtimeInstance = static_cast<Bindings::JavaInstance *>(runtimeImp->getInternalInstance());
372 return runtimeInstance->javaInstance();
375 nativeHandle = ptr_to_jlong(imp);
377 // Bump our 'meta' reference count for the imp. We maintain the reference
378 // until either finalize is called or the applet shuts down.
379 addNativeReference (_root, imp);
382 // All other types will result in an undefined object.
384 nativeHandle = UndefinedHandle;
387 // Now create the Java JavaJSObject. Look for the JavaJSObject in it's new (Tiger)
388 // location and in the original Java 1.4.2 location.
389 jclass JSObjectClass;
391 JSObjectClass = env->FindClass ("sun/plugin/javascript/webkit/JSObject");
392 if (!JSObjectClass) {
393 env->ExceptionDescribe();
394 env->ExceptionClear();
395 JSObjectClass = env->FindClass ("apple/applet/JSObject");
398 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V");
399 if (constructorID != NULL) {
400 result = env->NewObject (JSObjectClass, constructorID, nativeHandle);
407 JSValue *JavaJSObject::convertJObjectToValue (jobject theObject) const
409 // Instances of netscape.javascript.JSObject get converted back to
410 // JavaScript objects. All other objects are wrapped. It's not
411 // possible to pass primitive types from the Java to JavaScript.
412 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
414 jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;");
415 jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;");
417 // Only the sun.plugin.javascript.webkit.JSObject has a member called nativeJSObject. This class is
418 // created above to wrap internal browser objects. The constructor of this class takes the native
419 // pointer and stores it in this object, so that it can be retrieved below.
420 if (strcmp(Bindings::JavaString(className).UTF8String(), "sun.plugin.javascript.webkit.JSObject") == 0) {
421 // Pull the nativeJSObject value from the Java instance. This is a
422 // pointer to the JSObject.
423 JNIEnv *env = getJNIEnv();
424 jfieldID fieldID = env->GetFieldID((jclass)classOfInstance, "nativeJSObject", "J");
425 if (fieldID == NULL) {
426 return jsUndefined();
428 jlong nativeHandle = env->GetLongField(theObject, fieldID);
429 if (nativeHandle == UndefinedHandle) {
430 return jsUndefined();
432 JSObject *imp = static_cast<JSObject*>(jlong_to_impptr(nativeHandle));
437 RuntimeObjectImp *newImp = new RuntimeObjectImp(new Bindings::JavaInstance (theObject, _root));
442 List JavaJSObject::listFromJArray(jobjectArray jArray) const
444 JNIEnv *env = getJNIEnv();
445 int i, numObjects = jArray ? env->GetArrayLength (jArray) : 0;
448 for (i = 0; i < numObjects; i++) {
449 jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i);
451 aList.append (convertJObjectToValue(anObject));
452 env->DeleteLocalRef (anObject);
455 env->ExceptionDescribe();
456 env->ExceptionClear();
464 jlong KJS_JSCreateNativeJSObject (JNIEnv*, jclass, jstring, jlong nativeHandle, jboolean)
466 JSObjectCallContext context;
467 context.type = CreateNative;
468 context.nativeHandle = nativeHandle;
469 return JavaJSObject::invoke (&context).j;
472 void KJS_JSObject_JSFinalize (JNIEnv*, jclass, jlong nativeHandle)
474 JSObjectCallContext context;
475 context.type = Finalize;
476 context.nativeHandle = nativeHandle;
477 JavaJSObject::invoke (&context);
480 jobject KJS_JSObject_JSObjectCall (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring methodName, jobjectArray args, jboolean)
482 JSObjectCallContext context;
484 context.nativeHandle = nativeHandle;
485 context.string = methodName;
487 return JavaJSObject::invoke (&context).l;
490 jobject KJS_JSObject_JSObjectEval (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jscript, jboolean)
492 JSObjectCallContext context;
494 context.nativeHandle = nativeHandle;
495 context.string = jscript;
496 return JavaJSObject::invoke (&context).l;
499 jobject KJS_JSObject_JSObjectGetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean)
501 JSObjectCallContext context;
502 context.type = GetMember;
503 context.nativeHandle = nativeHandle;
504 context.string = jname;
505 return JavaJSObject::invoke (&context).l;
508 void KJS_JSObject_JSObjectSetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jobject value, jboolean)
510 JSObjectCallContext context;
511 context.type = SetMember;
512 context.nativeHandle = nativeHandle;
513 context.string = jname;
514 context.value = value;
515 JavaJSObject::invoke (&context);
518 void KJS_JSObject_JSObjectRemoveMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean)
520 JSObjectCallContext context;
521 context.type = RemoveMember;
522 context.nativeHandle = nativeHandle;
523 context.string = jname;
524 JavaJSObject::invoke (&context);
527 jobject KJS_JSObject_JSObjectGetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jboolean)
529 JSObjectCallContext context;
530 context.type = GetSlot;
531 context.nativeHandle = nativeHandle;
532 context.index = jindex;
533 return JavaJSObject::invoke (&context).l;
536 void KJS_JSObject_JSObjectSetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jobject value, jboolean)
538 JSObjectCallContext context;
539 context.type = SetSlot;
540 context.nativeHandle = nativeHandle;
541 context.index = jindex;
542 context.value = value;
543 JavaJSObject::invoke (&context);
546 jstring KJS_JSObject_JSObjectToString (JNIEnv*, jclass, jlong nativeHandle)
548 JSObjectCallContext context;
549 context.type = ToString;
550 context.nativeHandle = nativeHandle;
551 return (jstring)JavaJSObject::invoke (&context).l;