Reviewed by Jon.
[WebKit-https.git] / WebCore / bridge / jni / jni_utility.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 #include "jni_utility.h"
28
29 #include "jni_runtime.h"
30 #include "runtime_array.h"
31 #include "runtime_object.h"
32 #include <kjs/array_instance.h>
33 #include <dlfcn.h>
34
35 namespace KJS {
36
37 namespace Bindings {
38
39 static jint KJS_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
40 {
41     static void* javaVMFramework = 0;
42     if (!javaVMFramework)
43         javaVMFramework = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY);
44     if (!javaVMFramework)
45         return JNI_ERR;
46
47     static jint(*functionPointer)(JavaVM**, jsize, jsize *) = 0;
48     if (!functionPointer)
49         functionPointer = (jint(*)(JavaVM**, jsize, jsize *))dlsym(javaVMFramework, "JNI_GetCreatedJavaVMs");
50     if (!functionPointer)
51         return JNI_ERR;
52     return functionPointer(vmBuf, bufLen, nVMs);
53 }
54
55 static JavaVM *jvm = 0;
56
57 // Provide the ability for an outside component to specify the JavaVM to use
58 // If the jvm value is set, the getJavaVM function below will just return. 
59 // In getJNIEnv(), if AttachCurrentThread is called to a VM that is already
60 // attached, the result is a no-op.
61 void setJavaVM(JavaVM *javaVM)
62 {
63     jvm = javaVM;
64 }
65
66 JavaVM *getJavaVM()
67 {
68     if (jvm)
69         return jvm;
70
71     JavaVM *jvmArray[1];
72     jsize bufLen = 1;
73     jsize nJVMs = 0;
74     jint jniError = 0;
75
76     // Assumes JVM is already running ..., one per process
77     jniError = KJS_GetCreatedJavaVMs(jvmArray, bufLen, &nJVMs);
78     if ( jniError == JNI_OK && nJVMs > 0 ) {
79         jvm = jvmArray[0];
80     }
81     else 
82         fprintf(stderr, "%s: JNI_GetCreatedJavaVMs failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError);
83         
84     return jvm;
85 }
86
87 JNIEnv* getJNIEnv()
88 {
89     union {
90         JNIEnv* env;
91         void* dummy;
92     } u;
93     jint jniError = 0;
94
95     jniError = (getJavaVM())->AttachCurrentThread(&u.dummy, NULL);
96     if (jniError == JNI_OK)
97         return u.env;
98     else
99         fprintf(stderr, "%s: AttachCurrentThread failed, returned %ld\n", __PRETTY_FUNCTION__, (long)jniError);
100     return NULL;
101 }
102
103 jmethodID getMethodID (jobject obj, const char *name, const char *sig)
104 {
105     JNIEnv *env = getJNIEnv();
106     jmethodID mid = 0;
107         
108     if ( env != NULL) {
109     jclass cls = env->GetObjectClass(obj);
110     if ( cls != NULL ) {
111             mid = env->GetMethodID(cls, name, sig);
112             if (!mid) {
113                 env->ExceptionClear();
114                 mid = env->GetStaticMethodID(cls, name, sig);
115                 if (!mid) {
116                     env->ExceptionClear();
117                 }
118             }
119         }
120         env->DeleteLocalRef(cls);
121     }
122     return mid;
123 }
124
125 const char *getCharactersFromJString (jstring aJString)
126 {
127     return getCharactersFromJStringInEnv (getJNIEnv(), aJString);
128 }
129
130 void releaseCharactersForJString (jstring aJString, const char *s)
131 {
132     releaseCharactersForJStringInEnv (getJNIEnv(), aJString, s);
133 }
134
135 const char *getCharactersFromJStringInEnv (JNIEnv *env, jstring aJString)
136 {
137     jboolean isCopy;
138     const char *s = env->GetStringUTFChars((jstring)aJString, &isCopy);
139     if (!s) {
140         env->ExceptionDescribe();
141         env->ExceptionClear();
142                 fprintf (stderr, "\n");
143     }
144     return s;
145 }
146
147 void releaseCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const char *s)
148 {
149     env->ReleaseStringUTFChars (aJString, s);
150 }
151
152 const jchar *getUCharactersFromJStringInEnv (JNIEnv *env, jstring aJString)
153 {
154     jboolean isCopy;
155     const jchar *s = env->GetStringChars((jstring)aJString, &isCopy);
156     if (!s) {
157         env->ExceptionDescribe();
158         env->ExceptionClear();
159                 fprintf (stderr, "\n");
160     }
161     return s;
162 }
163
164 void releaseUCharactersForJStringInEnv (JNIEnv *env, jstring aJString, const jchar *s)
165 {
166     env->ReleaseStringChars (aJString, s);
167 }
168
169 JNIType JNITypeFromClassName(const char *name)
170 {
171     JNIType type;
172     
173     if (strcmp("byte",name) == 0)
174         type = byte_type;
175     else if (strcmp("short",name) == 0)
176         type = short_type;
177     else if (strcmp("int",name) == 0)
178         type = int_type;
179     else if (strcmp("long",name) == 0)
180         type = long_type;
181     else if (strcmp("float",name) == 0)
182         type = float_type;
183     else if (strcmp("double",name) == 0)
184         type = double_type;
185     else if (strcmp("char",name) == 0)
186         type = char_type;
187     else if (strcmp("boolean",name) == 0)
188         type = boolean_type;
189     else if (strcmp("void",name) == 0)
190         type = void_type;
191     else if ('[' == name[0]) 
192         type = array_type;
193     else
194         type = object_type;
195         
196     return type;
197 }
198
199 const char *signatureFromPrimitiveType(JNIType type)
200 {
201     switch (type){
202         case void_type: 
203             return "V";
204         
205         case array_type:
206             return "[";
207         
208         case object_type:
209             return "L";
210         
211         case boolean_type:
212             return "Z";
213         
214         case byte_type:
215             return "B";
216             
217         case char_type:
218             return "C";
219         
220         case short_type:
221             return "S";
222         
223         case int_type:
224             return "I";
225         
226         case long_type:
227             return "J";
228         
229         case float_type:
230             return "F";
231         
232         case double_type:
233             return "D";
234
235         case invalid_type:
236         default:
237         break;
238     }
239     return "";
240 }
241
242 JNIType JNITypeFromPrimitiveType(char type)
243 {
244     switch (type){
245         case 'V': 
246             return void_type;
247         
248         case 'L':
249             return object_type;
250             
251         case '[':
252             return array_type;
253         
254         case 'Z':
255             return boolean_type;
256         
257         case 'B':
258             return byte_type;
259             
260         case 'C':
261             return char_type;
262         
263         case 'S':
264             return short_type;
265         
266         case 'I':
267             return int_type;
268         
269         case 'J':
270             return long_type;
271         
272         case 'F':
273             return float_type;
274         
275         case 'D':
276             return double_type;
277
278         default:
279         break;
280     }
281     return invalid_type;
282 }
283
284 jvalue getJNIField( jobject obj, JNIType type, const char *name, const char *signature)
285 {
286     JavaVM *jvm = getJavaVM();
287     JNIEnv *env = getJNIEnv();
288     jvalue result;
289
290     bzero (&result, sizeof(jvalue));
291     if ( obj != NULL && jvm != NULL && env != NULL) {
292         jclass cls = env->GetObjectClass(obj);
293         if ( cls != NULL ) {
294             jfieldID field = env->GetFieldID(cls, name, signature);
295             if ( field != NULL ) {
296                 switch (type) {
297                 case array_type:
298                 case object_type:
299                     result.l = env->functions->GetObjectField(env, obj, field);
300                     break;
301                 case boolean_type:
302                     result.z = env->functions->GetBooleanField(env, obj, field);
303                     break;
304                 case byte_type:
305                     result.b = env->functions->GetByteField(env, obj, field);
306                     break;
307                 case char_type:
308                     result.c = env->functions->GetCharField(env, obj, field);
309                     break;
310                 case short_type:
311                     result.s = env->functions->GetShortField(env, obj, field);
312                     break;
313                 case int_type:
314                     result.i = env->functions->GetIntField(env, obj, field);
315                     break;
316                 case long_type:
317                     result.j = env->functions->GetLongField(env, obj, field);
318                     break;
319                 case float_type:
320                     result.f = env->functions->GetFloatField(env, obj, field);
321                     break;
322                 case double_type:
323                     result.d = env->functions->GetDoubleField(env, obj, field);
324                     break;
325                 default:
326                     fprintf(stderr, "%s: invalid field type (%d)\n", __PRETTY_FUNCTION__, (int)type);
327                 }
328             }
329             else
330             {
331                 fprintf(stderr, "%s: Could not find field: %s\n", __PRETTY_FUNCTION__, name);
332                 env->ExceptionDescribe();
333                 env->ExceptionClear();
334                 fprintf (stderr, "\n");
335             }
336
337             env->DeleteLocalRef(cls);
338         }
339         else {
340             fprintf(stderr, "%s: Could not find class for object\n", __PRETTY_FUNCTION__);
341         }
342     }
343
344     return result;
345 }
346
347 static jobject convertArrayInstanceToJavaArray(ExecState *exec, JSValue *value, const char *javaClassName) {
348
349     ASSERT(JSLock::lockCount() > 0);
350     
351     JNIEnv *env = getJNIEnv();
352     // As JS Arrays can contain a mixture of objects, assume we can convert to
353     // the requested Java Array type requested, unless the array type is some object array
354     // other than a string.
355     ArrayInstance *jsArray = static_cast<ArrayInstance *>(value);
356     unsigned length = jsArray->getLength();
357     jobjectArray jarray = 0;
358     
359     // Build the correct array type
360     switch (JNITypeFromPrimitiveType(javaClassName[1])) { 
361         case object_type: {
362         // Only support string object types
363         if (0 == strcmp("[Ljava.lang.String;", javaClassName)) {
364             jarray = (jobjectArray)env->NewObjectArray(length,
365                 env->FindClass("java/lang/String"),
366                 env->NewStringUTF(""));
367             for(unsigned i = 0; i < length; i++) {
368                 JSValue* item = jsArray->getItem(i);
369                 UString stringValue = item->toString(exec);
370                 env->SetObjectArrayElement(jarray,i,
371                     env->functions->NewString(env, (const jchar *)stringValue.data(), stringValue.size()));
372                 }
373             }
374             break;
375         }
376         
377         case boolean_type: {
378             jarray = (jobjectArray)env->NewBooleanArray(length);
379             for(unsigned i = 0; i < length; i++) {
380                 JSValue* item = jsArray->getItem(i);
381                 jboolean value = (jboolean)item->toNumber(exec);
382                 env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value);
383             }
384             break;
385         }
386         
387         case byte_type: {
388             jarray = (jobjectArray)env->NewByteArray(length);
389             for(unsigned i = 0; i < length; i++) {
390                 JSValue* item = jsArray->getItem(i);
391                 jbyte value = (jbyte)item->toNumber(exec);
392                 env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value);
393             }
394             break;
395         }
396
397         case char_type: {
398             jarray = (jobjectArray)env->NewCharArray(length);
399             for(unsigned i = 0; i < length; i++) {
400                 JSValue* item = jsArray->getItem(i);
401                 UString stringValue = item->toString(exec);
402                 jchar value = 0;
403                 if (stringValue.size() > 0)
404                     value = ((const jchar*)stringValue.data())[0];
405                 env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value);
406             }
407             break;
408         }
409
410         case short_type: {
411             jarray = (jobjectArray)env->NewShortArray(length);
412             for(unsigned i = 0; i < length; i++) {
413                 JSValue* item = jsArray->getItem(i);
414                 jshort value = (jshort)item->toNumber(exec);
415                 env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value);
416             }
417             break;
418         }
419
420         case int_type: {
421             jarray = (jobjectArray)env->NewIntArray(length);
422             for(unsigned i = 0; i < length; i++) {
423                 JSValue* item = jsArray->getItem(i);
424                 jint value = (jint)item->toNumber(exec);
425                 env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value);
426             }
427             break;
428         }
429
430         case long_type: {
431             jarray = (jobjectArray)env->NewLongArray(length);
432             for(unsigned i = 0; i < length; i++) {
433                 JSValue* item = jsArray->getItem(i);
434                 jlong value = (jlong)item->toNumber(exec);
435                 env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value);
436             }
437             break;
438         }
439
440         case float_type: {
441             jarray = (jobjectArray)env->NewFloatArray(length);
442             for(unsigned i = 0; i < length; i++) {
443                 JSValue* item = jsArray->getItem(i);
444                 jfloat value = (jfloat)item->toNumber(exec);
445                 env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value);
446             }
447             break;
448         }
449     
450         case double_type: {
451             jarray = (jobjectArray)env->NewDoubleArray(length);
452             for(unsigned i = 0; i < length; i++) {
453                 JSValue* item = jsArray->getItem(i);
454                 jdouble value = (jdouble)item->toNumber(exec);
455                 env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value);
456             }
457             break;
458         }
459         
460         case array_type: // don't handle embedded arrays
461         case void_type: // Don't expect arrays of void objects
462         case invalid_type: // Array of unknown objects
463             break;
464     }
465     
466     // if it was not one of the cases handled, then null is returned
467     return jarray;
468 }
469
470
471 jvalue convertValueToJValue (ExecState *exec, JSValue *value, JNIType _JNIType, const char *javaClassName)
472 {
473     JSLock lock;
474     
475     jvalue result;
476    
477     switch (_JNIType){
478         case array_type:
479         case object_type: {
480             result.l = (jobject)0;
481             
482             // First see if we have a Java instance.
483             if (value->isObject()){
484                 JSObject *objectImp = static_cast<JSObject*>(value);
485                 if (objectImp->classInfo() == &RuntimeObjectImp::info) {
486                     RuntimeObjectImp *imp = static_cast<RuntimeObjectImp *>(value);
487                     JavaInstance *instance = static_cast<JavaInstance*>(imp->getInternalInstance());
488                     if (instance)
489                         result.l = instance->javaInstance();
490                 }
491                 else if (objectImp->classInfo() == &RuntimeArray::info) {
492                 // Input is a JavaScript Array that was originally created from a Java Array
493                     RuntimeArray *imp = static_cast<RuntimeArray *>(value);
494                     JavaArray *array = static_cast<JavaArray*>(imp->getConcreteArray());
495                     result.l = array->javaArray();
496                 } 
497                 else if (objectImp->classInfo() == &ArrayInstance::info) {
498                     // Input is a Javascript Array. We need to create it to a Java Array.
499                     result.l = convertArrayInstanceToJavaArray(exec, value, javaClassName);
500                 }
501             }
502             
503             // Now convert value to a string if the target type is a java.lang.string, and we're not
504             // converting from a Null.
505             if (result.l == 0 && strcmp(javaClassName, "java.lang.String") == 0) {
506 #ifdef CONVERT_NULL_TO_EMPTY_STRING
507                 if (value->isNull()) {
508                     JNIEnv *env = getJNIEnv();
509                     jchar buf[2];
510                     jobject javaString = env->functions->NewString (env, buf, 0);
511                     result.l = javaString;
512                 }
513                 else 
514 #else
515                 if (!value->isNull())
516 #endif
517                 {
518                     UString stringValue = value->toString(exec);
519                     JNIEnv *env = getJNIEnv();
520                     jobject javaString = env->functions->NewString (env, (const jchar *)stringValue.data(), stringValue.size());
521                     result.l = javaString;
522                 }
523             } else if (result.l == 0) 
524                 bzero (&result, sizeof(jvalue)); // Handle it the same as a void case
525         }
526         break;
527         
528         case boolean_type: {
529             result.z = (jboolean)value->toNumber(exec);
530         }
531         break;
532             
533         case byte_type: {
534             result.b = (jbyte)value->toNumber(exec);
535         }
536         break;
537         
538         case char_type: {
539             result.c = (jchar)value->toNumber(exec);
540         }
541         break;
542
543         case short_type: {
544             result.s = (jshort)value->toNumber(exec);
545         }
546         break;
547
548         case int_type: {
549             result.i = (jint)value->toNumber(exec);
550         }
551         break;
552
553         case long_type: {
554             result.j = (jlong)value->toNumber(exec);
555         }
556         break;
557
558         case float_type: {
559             result.f = (jfloat)value->toNumber(exec);
560         }
561         break;
562
563         case double_type: {
564             result.d = (jdouble)value->toNumber(exec);
565         }
566         break;
567             
568         break;
569
570         case invalid_type:
571         default:
572         case void_type: {
573             bzero (&result, sizeof(jvalue));
574         }
575         break;
576     }
577     return result;
578 }
579
580 }  // end of namespace Bindings
581
582 } // end of namespace KJS