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