]> git.saurik.com Git - apple/javascriptcore.git/blob - bindings/jni/jni_jsobject.cpp
JavaScriptCore-466.1.tar.gz
[apple/javascriptcore.git] / bindings / jni / jni_jsobject.cpp
1 /*
2 * Copyright (C) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26 #include "config.h"
27
28 #if ENABLE(JAVA_BINDINGS)
29
30 #include "identifier.h"
31 #include "internal.h"
32 #include "interpreter.h"
33 #include "jni_jsobject.h"
34 #include "jni_runtime.h"
35 #include "jni_utility.h"
36 #include "JSGlobalObject.h"
37 #include "list.h"
38 #include "runtime_object.h"
39 #include "runtime_root.h"
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <wtf/Assertions.h>
42
43 using namespace KJS::Bindings;
44 using namespace KJS;
45
46 #ifdef NDEBUG
47 #define JS_LOG(formatAndArgs...) ((void)0)
48 #else
49 #define JS_LOG(formatAndArgs...) { \
50 fprintf (stderr, "%s(%p,%p): ", __PRETTY_FUNCTION__, RootObject::runLoop(), CFRunLoopGetCurrent()); \
51 fprintf(stderr, formatAndArgs); \
52 }
53 #endif
54
55 #define UndefinedHandle 1
56
57 static bool isJavaScriptThread()
58 {
59 return (RootObject::runLoop() == CFRunLoopGetCurrent());
60 }
61
62 jvalue JavaJSObject::invoke (JSObjectCallContext *context)
63 {
64 jvalue result;
65
66 bzero ((void *)&result, sizeof(jvalue));
67
68 if (!isJavaScriptThread()) {
69 // Send the call context to the thread that is allowed to
70 // call JavaScript.
71 RootObject::dispatchToJavaScriptThread(context);
72 result = context->result;
73 }
74 else {
75 jlong nativeHandle = context->nativeHandle;
76 if (nativeHandle == UndefinedHandle || nativeHandle == 0) {
77 return result;
78 }
79
80 if (context->type == CreateNative) {
81 result.j = JavaJSObject::createNative(nativeHandle);
82 }
83 else {
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);
87 return result;
88 }
89
90 switch (context->type){
91 case Call: {
92 result.l = JavaJSObject(nativeHandle).call(context->string, context->args);
93 break;
94 }
95
96 case Eval: {
97 result.l = JavaJSObject(nativeHandle).eval(context->string);
98 break;
99 }
100
101 case GetMember: {
102 result.l = JavaJSObject(nativeHandle).getMember(context->string);
103 break;
104 }
105
106 case SetMember: {
107 JavaJSObject(nativeHandle).setMember(context->string, context->value);
108 break;
109 }
110
111 case RemoveMember: {
112 JavaJSObject(nativeHandle).removeMember(context->string);
113 break;
114 }
115
116 case GetSlot: {
117 result.l = JavaJSObject(nativeHandle).getSlot(context->index);
118 break;
119 }
120
121 case SetSlot: {
122 JavaJSObject(nativeHandle).setSlot(context->index, context->value);
123 break;
124 }
125
126 case ToString: {
127 result.l = (jobject) JavaJSObject(nativeHandle).toString();
128 break;
129 }
130
131 case Finalize: {
132 JavaJSObject(nativeHandle).finalize();
133 break;
134 }
135
136 default: {
137 fprintf (stderr, "%s: invalid JavaScript call\n", __PRETTY_FUNCTION__);
138 }
139 }
140 }
141 context->result = result;
142 }
143
144 return result;
145 }
146
147
148 JavaJSObject::JavaJSObject(jlong nativeJSObject)
149 {
150 _imp = jlong_to_impptr(nativeJSObject);
151
152 ASSERT(_imp);
153 _rootObject = findProtectingRootObject(_imp);
154 ASSERT(_rootObject);
155 }
156
157 RootObject* JavaJSObject::rootObject() const
158 {
159 return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
160 }
161
162 jobject JavaJSObject::call(jstring methodName, jobjectArray args) const
163 {
164 JS_LOG ("methodName = %s\n", JavaString(methodName).UTF8String());
165
166 RootObject* rootObject = this->rootObject();
167 if (!rootObject)
168 return 0;
169
170 // Lookup the function object.
171 ExecState* exec = rootObject->globalObject()->globalExec();
172 JSLock lock;
173
174 Identifier identifier(JavaString(methodName).ustring());
175 JSValue *func = _imp->get (exec, identifier);
176 if (func->isUndefinedOrNull())
177 return 0;
178
179 // Call the function object.
180 JSObject *funcImp = static_cast<JSObject*>(func);
181 JSObject *thisObj = const_cast<JSObject*>(_imp);
182 List argList;
183 getListFromJArray(args, argList);
184 rootObject->globalObject()->startTimeoutCheck();
185 JSValue *result = funcImp->call(exec, thisObj, argList);
186 rootObject->globalObject()->stopTimeoutCheck();
187
188 return convertValueToJObject(result);
189 }
190
191 jobject JavaJSObject::eval(jstring script) const
192 {
193 JS_LOG ("script = %s\n", JavaString(script).UTF8String());
194
195 JSObject *thisObj = const_cast<JSObject*>(_imp);
196 JSValue *result;
197
198 JSLock lock;
199
200 RootObject* rootObject = this->rootObject();
201 if (!rootObject)
202 return 0;
203
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();
208
209 if (type == Normal) {
210 result = completion.value();
211 if (!result)
212 result = jsUndefined();
213 } else
214 result = jsUndefined();
215
216 return convertValueToJObject (result);
217 }
218
219 jobject JavaJSObject::getMember(jstring memberName) const
220 {
221 JS_LOG ("(%p) memberName = %s\n", _imp, JavaString(memberName).UTF8String());
222
223 RootObject* rootObject = this->rootObject();
224 if (!rootObject)
225 return 0;
226
227 ExecState* exec = rootObject->globalObject()->globalExec();
228
229 JSLock lock;
230 JSValue *result = _imp->get (exec, Identifier (JavaString(memberName).ustring()));
231
232 return convertValueToJObject(result);
233 }
234
235 void JavaJSObject::setMember(jstring memberName, jobject value) const
236 {
237 JS_LOG ("memberName = %s, value = %p\n", JavaString(memberName).UTF8String(), value);
238
239 RootObject* rootObject = this->rootObject();
240 if (!rootObject)
241 return;
242
243 ExecState* exec = rootObject->globalObject()->globalExec();
244 JSLock lock;
245 _imp->put(exec, Identifier (JavaString(memberName).ustring()), convertJObjectToValue(value));
246 }
247
248
249 void JavaJSObject::removeMember(jstring memberName) const
250 {
251 JS_LOG ("memberName = %s\n", JavaString(memberName).UTF8String());
252
253 RootObject* rootObject = this->rootObject();
254 if (!rootObject)
255 return;
256
257 ExecState* exec = rootObject->globalObject()->globalExec();
258 JSLock lock;
259 _imp->deleteProperty(exec, Identifier (JavaString(memberName).ustring()));
260 }
261
262
263 jobject JavaJSObject::getSlot(jint index) const
264 {
265 #ifdef __LP64__
266 JS_LOG ("index = %d\n", index);
267 #else
268 JS_LOG ("index = %ld\n", index);
269 #endif
270
271 RootObject* rootObject = this->rootObject();
272 if (!rootObject)
273 return 0;
274
275 ExecState* exec = rootObject->globalObject()->globalExec();
276
277 JSLock lock;
278 JSValue *result = _imp->get (exec, (unsigned)index);
279
280 return convertValueToJObject(result);
281 }
282
283
284 void JavaJSObject::setSlot(jint index, jobject value) const
285 {
286 #ifdef __LP64__
287 JS_LOG ("index = %d, value = %p\n", index, value);
288 #else
289 JS_LOG ("index = %ld, value = %p\n", index, value);
290 #endif
291
292 RootObject* rootObject = this->rootObject();
293 if (!rootObject)
294 return;
295
296 ExecState* exec = rootObject->globalObject()->globalExec();
297 JSLock lock;
298 _imp->put(exec, (unsigned)index, convertJObjectToValue(value));
299 }
300
301
302 jstring JavaJSObject::toString() const
303 {
304 JS_LOG ("\n");
305
306 RootObject* rootObject = this->rootObject();
307 if (!rootObject)
308 return 0;
309
310 JSLock lock;
311 JSObject *thisObj = const_cast<JSObject*>(_imp);
312 ExecState* exec = rootObject->globalObject()->globalExec();
313
314 return (jstring)convertValueToJValue (exec, thisObj, object_type, "java.lang.String").l;
315 }
316
317 void JavaJSObject::finalize() const
318 {
319 if (RootObject* rootObject = this->rootObject())
320 rootObject->gcUnprotect(_imp);
321 }
322
323 // We're either creating a 'Root' object (via a call to JavaJSObject.getWindow()), or
324 // another JavaJSObject.
325 jlong JavaJSObject::createNative(jlong nativeHandle)
326 {
327 JS_LOG ("nativeHandle = %d\n", (int)nativeHandle);
328
329 if (nativeHandle == UndefinedHandle)
330 return nativeHandle;
331
332 if (findProtectingRootObject(jlong_to_impptr(nativeHandle)))
333 return nativeHandle;
334
335 CreateRootObjectFunction createRootObject = RootObject::createRootObject();
336 if (!createRootObject)
337 return ptr_to_jlong(0);
338
339 RefPtr<RootObject> rootObject = createRootObject(jlong_to_ptr(nativeHandle));
340
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().
344 if (rootObject) {
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);
350 }
351
352 return nativeHandle;
353 }
354
355 jobject JavaJSObject::convertValueToJObject (JSValue *value) const
356 {
357 JSLock lock;
358
359 RootObject* rootObject = this->rootObject();
360 if (!rootObject)
361 return 0;
362
363 ExecState* exec = rootObject->globalObject()->globalExec();
364 JNIEnv *env = getJNIEnv();
365 jobject result = 0;
366
367 // See section 22.7 of 'JavaScript: The Definitive Guide, 4th Edition',
368 // figure 22-5.
369 // number -> java.lang.Double
370 // string -> java.lang.String
371 // boolean -> java.lang.Boolean
372 // Java instance -> Java instance
373 // Everything else -> JavaJSObject
374
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));
381 }
382 }
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());
387 }
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));
393 }
394 }
395 else {
396 // Create a JavaJSObject.
397 jlong nativeHandle;
398
399 if (type == ObjectType){
400 JSObject *imp = static_cast<JSObject*>(value);
401
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)
410 return 0;
411
412 return runtimeInstance->javaInstance();
413 }
414 else {
415 nativeHandle = ptr_to_jlong(imp);
416 rootObject->gcProtect(imp);
417 }
418 }
419 // All other types will result in an undefined object.
420 else {
421 nativeHandle = UndefinedHandle;
422 }
423
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;
427
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");
433 }
434
435 jmethodID constructorID = env->GetMethodID (JSObjectClass, "<init>", "(J)V");
436 if (constructorID != NULL) {
437 result = env->NewObject (JSObjectClass, constructorID, nativeHandle);
438 }
439 }
440
441 return result;
442 }
443
444 JSValue *JavaJSObject::convertJObjectToValue (jobject theObject) const
445 {
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',
450 // figure 22-4.
451 jobject classOfInstance = callJNIObjectMethod(theObject, "getClass", "()Ljava/lang/Class;");
452 jstring className = (jstring)callJNIObjectMethod(classOfInstance, "getName", "()Ljava/lang/String;");
453
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();
464 }
465 jlong nativeHandle = env->GetLongField(theObject, fieldID);
466 if (nativeHandle == UndefinedHandle) {
467 return jsUndefined();
468 }
469 JSObject *imp = static_cast<JSObject*>(jlong_to_impptr(nativeHandle));
470 return imp;
471 }
472
473 JSLock lock;
474 JavaInstance* javaInstance = new JavaInstance(theObject, _rootObject);
475 return KJS::Bindings::Instance::createRuntimeObject(javaInstance);
476 }
477
478 void JavaJSObject::getListFromJArray(jobjectArray jArray, List& list) const
479 {
480 JNIEnv *env = getJNIEnv();
481 int i, numObjects = jArray ? env->GetArrayLength (jArray) : 0;
482
483 for (i = 0; i < numObjects; i++) {
484 jobject anObject = env->GetObjectArrayElement ((jobjectArray)jArray, i);
485 if (anObject) {
486 list.append(convertJObjectToValue(anObject));
487 env->DeleteLocalRef (anObject);
488 }
489 else {
490 env->ExceptionDescribe();
491 env->ExceptionClear();
492 }
493 }
494 }
495
496 extern "C" {
497
498 jlong KJS_JSCreateNativeJSObject (JNIEnv*, jclass, jstring, jlong nativeHandle, jboolean)
499 {
500 JSObjectCallContext context;
501 context.type = CreateNative;
502 context.nativeHandle = nativeHandle;
503 return JavaJSObject::invoke (&context).j;
504 }
505
506 void KJS_JSObject_JSFinalize (JNIEnv*, jclass, jlong nativeHandle)
507 {
508 JSObjectCallContext context;
509 context.type = Finalize;
510 context.nativeHandle = nativeHandle;
511 JavaJSObject::invoke (&context);
512 }
513
514 jobject KJS_JSObject_JSObjectCall (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring methodName, jobjectArray args, jboolean)
515 {
516 JSObjectCallContext context;
517 context.type = Call;
518 context.nativeHandle = nativeHandle;
519 context.string = methodName;
520 context.args = args;
521 return JavaJSObject::invoke (&context).l;
522 }
523
524 jobject KJS_JSObject_JSObjectEval (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jscript, jboolean)
525 {
526 JSObjectCallContext context;
527 context.type = Eval;
528 context.nativeHandle = nativeHandle;
529 context.string = jscript;
530 return JavaJSObject::invoke (&context).l;
531 }
532
533 jobject KJS_JSObject_JSObjectGetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean)
534 {
535 JSObjectCallContext context;
536 context.type = GetMember;
537 context.nativeHandle = nativeHandle;
538 context.string = jname;
539 return JavaJSObject::invoke (&context).l;
540 }
541
542 void KJS_JSObject_JSObjectSetMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jobject value, jboolean)
543 {
544 JSObjectCallContext context;
545 context.type = SetMember;
546 context.nativeHandle = nativeHandle;
547 context.string = jname;
548 context.value = value;
549 JavaJSObject::invoke (&context);
550 }
551
552 void KJS_JSObject_JSObjectRemoveMember (JNIEnv*, jclass, jlong nativeHandle, jstring, jstring jname, jboolean)
553 {
554 JSObjectCallContext context;
555 context.type = RemoveMember;
556 context.nativeHandle = nativeHandle;
557 context.string = jname;
558 JavaJSObject::invoke (&context);
559 }
560
561 jobject KJS_JSObject_JSObjectGetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jboolean)
562 {
563 JSObjectCallContext context;
564 context.type = GetSlot;
565 context.nativeHandle = nativeHandle;
566 context.index = jindex;
567 return JavaJSObject::invoke (&context).l;
568 }
569
570 void KJS_JSObject_JSObjectSetSlot (JNIEnv*, jclass, jlong nativeHandle, jstring, jint jindex, jobject value, jboolean)
571 {
572 JSObjectCallContext context;
573 context.type = SetSlot;
574 context.nativeHandle = nativeHandle;
575 context.index = jindex;
576 context.value = value;
577 JavaJSObject::invoke (&context);
578 }
579
580 jstring KJS_JSObject_JSObjectToString (JNIEnv*, jclass, jlong nativeHandle)
581 {
582 JSObjectCallContext context;
583 context.type = ToString;
584 context.nativeHandle = nativeHandle;
585 return (jstring)JavaJSObject::invoke (&context).l;
586 }
587
588 }
589
590 #endif // ENABLE(JAVA_BINDINGS)