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.
25 #include <CoreFoundation/CoreFoundation.h>
29 #include <identifier.h>
31 #include <interpreter.h>
33 #include <jni_jsobject.h>
34 #include <jni_runtime.h>
35 #include <jni_utility.h>
36 #include <runtime_object.h>
37 #include <runtime_root.h>
39 using namespace KJS::Bindings;
43 #define JS_LOG(formatAndArgs...) ((void)0)
45 #define JS_LOG(formatAndArgs...) { \
46 fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
47 fprintf(stderr, formatAndArgs); \
51 #define UndefinedHandle 1
53 static bool isJavaScriptThread()
55 return (RootObject::runLoop() == CFRunLoopGetCurrent());
58 jvalue JSObject::invoke (JSObjectCallContext *context)
62 bzero ((void *)&result, sizeof(jvalue));
64 if (!isJavaScriptThread()) {
65 // Send the call context to the thread that is allowed to
67 RootObject::dispatchToJavaScriptThread(context);
68 result = context->result;
71 jlong nativeHandle = context->nativeHandle;
72 if (nativeHandle == UndefinedHandle || nativeHandle == 0) {
76 if (context->type == CreateNative) {
77 result.j = JSObject::createNative(nativeHandle);
80 KJS::ObjectImp *imp = jlong_to_impptr(nativeHandle);
81 if (!rootForImp(imp)) {
82 fprintf (stderr, "%s:%d: Attempt to access JavaScript from destroyed applet, type %d.\n", __FILE__, __LINE__, context->type);
86 switch (context->type){
88 result.l = JSObject(nativeHandle).call(context->string, context->args);
93 result.l = JSObject(nativeHandle).eval(context->string);
98 result.l = JSObject(nativeHandle).getMember(context->string);
103 JSObject(nativeHandle).setMember(context->string, context->value);
108 JSObject(nativeHandle).removeMember(context->string);
113 result.l = JSObject(nativeHandle).getSlot(context->index);
118 JSObject(nativeHandle).setSlot(context->index, context->value);
123 result.l = (jobject) JSObject(nativeHandle).toString();
128 ObjectImp *imp = jlong_to_impptr(nativeHandle);
129 if (findReferenceDictionary(imp) == 0) {
130 // We may have received a finalize method call from the VM
131 // AFTER removing our last reference to the Java instance.
132 JS_LOG ("finalize called on instance we have already removed.\n");
135 JSObject(nativeHandle).finalize();
141 fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__);
145 context->result = result;
152 JSObject::JSObject(jlong nativeJSObject)
154 _imp = jlong_to_impptr(nativeJSObject);
156 // If we are unable to cast the nativeJSObject to an ObjectImp something is
160 _root = rootForImp(_imp);
162 // If we can't find the root for the object something is terribly wrong.
167 jobject JSObject::call(jstring methodName, jobjectArray args) const
169 JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String());
171 // Lookup the function object.
172 ExecState *exec = _root->interpreter()->globalExec();
175 Identifier identifier(JavaString(methodName).ustring());
176 Value func = _imp->get (exec, identifier);
177 Interpreter::unlock();
178 if (func.isNull() || func.type() == UndefinedType) {
179 // Maybe throw an exception here?
183 // Call the function object.
184 ObjectImp *funcImp = static_cast<ObjectImp*>(func.imp());
185 Object thisObj = Object(const_cast<ObjectImp*>(_imp));
186 List argList = listFromJArray(args);
188 Value result = funcImp->call (exec, thisObj, argList);
189 Interpreter::unlock();
191 // Convert and return the result of the function call.
192 return convertValueToJObject (result);
195 jobject JSObject::eval(jstring script) const
197 JS_LOG ("script = %s\n", JavaString(script).UTF8String());
199 Object thisObj = Object(const_cast<ObjectImp*>(_imp));
204 Completion completion = _root->interpreter()->evaluate(UString(), 0, JavaString(script).ustring(),thisObj);
205 ComplType type = completion.complType();
207 if (type == Normal) {
208 result = completion.value();
209 if (result.isNull()) {
210 result = Undefined();
214 result = Undefined();
216 Interpreter::unlock();
218 return convertValueToJObject (result);
221 jobject JSObject::getMember(jstring memberName) const
223 JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String());
225 ExecState *exec = _root->interpreter()->globalExec();
228 Value result = _imp->get (exec, Identifier (JavaString(memberName).ustring()));
229 Interpreter::unlock();
231 return convertValueToJObject (result);
234 void JSObject::setMember(jstring memberName, jobject value) const
236 JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value);
237 ExecState *exec = _root->interpreter()->globalExec();
239 _imp->put (exec, Identifier (JavaString(memberName).ustring()), convertJObjectToValue(value));
240 Interpreter::unlock();
244 void JSObject::removeMember(jstring memberName) const
246 JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String());
248 ExecState *exec = _root->interpreter()->globalExec();
250 _imp->deleteProperty (exec, Identifier (JavaString(memberName).ustring()));
251 Interpreter::unlock();
255 jobject JSObject::getSlot(jint index) const
257 JS_LOG ("index = %d\n", index);
259 ExecState *exec = _root->interpreter()->globalExec();
261 Value result = _imp->get (exec, (unsigned)index);
262 Interpreter::unlock();
264 return convertValueToJObject (result);
268 void JSObject::setSlot(jint index, jobject value) const
270 JS_LOG ("index = %d, value = %p\n", index, value);
272 ExecState *exec = _root->interpreter()->globalExec();
274 _imp->put (exec, (unsigned)index, convertJObjectToValue(value));
275 Interpreter::unlock();
279 jstring JSObject::toString() const
284 Object thisObj = Object(const_cast<ObjectImp*>(_imp));
285 ExecState *exec = _root->interpreter()->globalExec();
287 jstring result = (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l;
289 Interpreter::unlock();
294 void JSObject::finalize() const
298 removeNativeReference (_imp);
301 // We're either creating a 'Root' object (via a call to JSObject.getWindow()), or
303 jlong JSObject::createNative(jlong nativeHandle)
305 JS_LOG ("nativeHandle = %d\n", (int)nativeHandle);
307 if (nativeHandle == UndefinedHandle)
309 else if (rootForImp(jlong_to_impptr(nativeHandle))){
313 FindRootObjectForNativeHandleFunctionPtr aFunc = RootObject::findRootObjectForNativeHandleFunction();
315 Bindings::RootObject *root = aFunc(jlong_to_ptr(nativeHandle));
316 // If root is !NULL We must have been called via netscape.javascript.JSObject.getWindow(),
317 // otherwise we are being called after creating a JSObject in
318 // JSObject::convertValueToJObject().
320 addNativeReference (root, root->rootObjectImp());
321 return ptr_to_jlong(root->rootObjectImp());
328 return ptr_to_jlong(0);
331 jobject JSObject::convertValueToJObject (KJS::Value value) const
333 ExecState *exec = _root->interpreter()->globalExec();
334 JNIEnv *env = getJNIEnv();
337 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
339 // number -> java.lang.Double
340 // string -> java.lang.String
341 // boolean -> java.lang.Boolean
342 // Java instance -> Java instance
343 // Everything else -> JSObject
345 KJS::Type type = value.type();
346 if (type == KJS::NumberType) {
347 jclass JSObjectClass = env->FindClass ("java/lang/Double");
348 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(D)V");
349 if (constructorID != NULL) {
350 result = env->NewObject (JSObjectClass, constructorID, (jdouble)value.toNumber(exec));
353 else if (type == KJS::StringType) {
354 KJS::UString stringValue = value.toString(exec);
355 JNIEnv *env = getJNIEnv();
356 result = env->NewString ((const jchar *)stringValue.data(), stringValue.size());
358 else if (type == KJS::BooleanType) {
359 jclass JSObjectClass = env->FindClass ("java/lang/Boolean");
360 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(Z)V");
361 if (constructorID != NULL) {
362 result = env->NewObject (JSObjectClass, constructorID, (jboolean)value.toBoolean(exec));
366 // Create a JSObject.
369 if (type == KJS::ObjectType){
370 KJS::ObjectImp *imp = static_cast<KJS::ObjectImp*>(value.imp());
372 // We either have a wrapper around a Java instance or a JavaScript
373 // object. If we have a wrapper around a Java instance, return that
374 // instance, otherwise create a new Java JSObject with the ObjectImp*
375 // as it's nativeHandle.
376 if (imp->classInfo() && strcmp(imp->classInfo()->className, "RuntimeObject") == 0) {
377 KJS::RuntimeObjectImp *runtimeImp = static_cast<KJS::RuntimeObjectImp*>(value.imp());
378 Bindings::JavaInstance *runtimeInstance = static_cast<Bindings::JavaInstance *>(runtimeImp->getInternalInstance());
379 return runtimeInstance->javaInstance();
382 nativeHandle = ptr_to_jlong(imp);
384 // Bump our 'meta' reference count for the imp. We maintain the reference
385 // until either finalize is called or the applet shuts down.
386 addNativeReference (_root, imp);
389 // All other types will result in an undefined object.
391 nativeHandle = UndefinedHandle;
394 // Now create the Java JSObject. Look for the JSObject in it's new (Tiger)
395 // location and in the original Java 1.4.2 location.
396 jclass JSObjectClass;
398 JSObjectClass = env->FindClass ("sun/plugin/javascript/webkit/JSObject");
399 if (!JSObjectClass) {
400 env->ExceptionDescribe();
401 env->ExceptionClear();
402 JSObjectClass = env->FindClass ("apple/applet/JSObject");
405 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V");
406 if (constructorID != NULL) {
407 result = env->NewObject (JSObjectClass, constructorID, nativeHandle);
414 KJS::Value JSObject::convertJObjectToValue (jobject theObject) const
416 // Instances of netscape.javascript.JSObject get converted back to
417 // JavaScript objects. All other objects are wrapped. It's not
418 // possible to pass primitive types from the Java to JavaScript.
419 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
421 jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;");
422 jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;");
424 if (strcmp(Bindings::JavaString(className).UTF8String(), "netscape.javascript.JSObject") == 0) {
425 // Pull the nativeJSObject value from the Java instance. This is a
426 // pointer to the ObjectImp.
427 JNIEnv *env = getJNIEnv();
428 jfieldID fieldID = env->GetFieldID((jclass)classOfInstance, "nativeJSObject", "long");
429 if (fieldID == NULL) {
430 return KJS::Undefined();
432 jlong nativeHandle = env->GetLongField(theObject, fieldID);
433 if (nativeHandle == UndefinedHandle) {
434 return KJS::Undefined();
436 KJS::ObjectImp *imp = static_cast<KJS::ObjectImp*>(jlong_to_impptr(nativeHandle));
437 return KJS::Object(const_cast<KJS::ObjectImp*>(imp));
441 KJS::RuntimeObjectImp *newImp = new KJS::RuntimeObjectImp(new Bindings::JavaInstance (theObject, _root));
442 Interpreter::unlock();
444 return KJS::Object(newImp);
447 KJS::List JSObject::listFromJArray(jobjectArray jArray) const
449 JNIEnv *env = getJNIEnv();
450 long i, numObjects = jArray ? env->GetArrayLength (jArray) : 0;
453 for (i = 0; i < numObjects; i++) {
454 jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i);
456 aList.append (convertJObjectToValue(anObject));
457 env->DeleteLocalRef (anObject);
460 env->ExceptionDescribe();
461 env->ExceptionClear();
469 jlong KJS_JSCreateNativeJSObject (JNIEnv *env, jclass clazz, jstring jurl, jlong nativeHandle, jboolean ctx)
471 JSObjectCallContext context;
472 context.type = CreateNative;
473 context.nativeHandle = nativeHandle;
474 return JSObject::invoke (&context).j;
477 void KJS_JSObject_JSFinalize (JNIEnv *env, jclass jsClass, jlong nativeHandle)
479 JSObjectCallContext context;
480 context.type = Finalize;
481 context.nativeHandle = nativeHandle;
482 JSObject::invoke (&context);
485 jobject KJS_JSObject_JSObjectCall (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jstring methodName, jobjectArray args, jboolean ctx)
487 JSObjectCallContext context;
489 context.nativeHandle = nativeHandle;
490 context.string = methodName;
492 return JSObject::invoke (&context).l;
495 jobject KJS_JSObject_JSObjectEval (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jstring jscript, jboolean ctx)
497 JSObjectCallContext context;
499 context.nativeHandle = nativeHandle;
500 context.string = jscript;
501 return JSObject::invoke (&context).l;
504 jobject KJS_JSObject_JSObjectGetMember (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jstring jname, jboolean ctx)
506 JSObjectCallContext context;
507 context.type = GetMember;
508 context.nativeHandle = nativeHandle;
509 context.string = jname;
510 return JSObject::invoke (&context).l;
513 void KJS_JSObject_JSObjectSetMember (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jstring jname, jobject value, jboolean ctx)
515 JSObjectCallContext context;
516 context.type = SetMember;
517 context.nativeHandle = nativeHandle;
518 context.string = jname;
519 context.value = value;
520 JSObject::invoke (&context);
523 void KJS_JSObject_JSObjectRemoveMember (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jstring jname, jboolean ctx)
525 JSObjectCallContext context;
526 context.type = RemoveMember;
527 context.nativeHandle = nativeHandle;
528 context.string = jname;
529 JSObject::invoke (&context);
532 jobject KJS_JSObject_JSObjectGetSlot (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jint jindex, jboolean ctx)
534 JSObjectCallContext context;
535 context.type = GetSlot;
536 context.nativeHandle = nativeHandle;
537 context.index = jindex;
538 return JSObject::invoke (&context).l;
541 void KJS_JSObject_JSObjectSetSlot (JNIEnv *env, jclass jsClass, jlong nativeHandle, jstring jurl, jint jindex, jobject value, jboolean ctx)
543 JSObjectCallContext context;
544 context.type = SetSlot;
545 context.nativeHandle = nativeHandle;
546 context.index = jindex;
547 context.value = value;
548 JSObject::invoke (&context);
551 jstring KJS_JSObject_JSObjectToString (JNIEnv *env, jclass clazz, jlong nativeHandle)
553 JSObjectCallContext context;
554 context.type = ToString;
555 context.nativeHandle = nativeHandle;
556 return (jstring)JSObject::invoke (&context).l;