Reviewed by Sam.
[WebKit-https.git] / WebCore / bridge / jni / jni_runtime.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 #include "config.h"
26 #include <internal.h>
27 #include <ustring.h>
28 #include <value.h>
29
30 #include <jni_utility.h>
31 #include <jni_runtime.h>
32
33 #include <runtime_array.h>
34 #include <runtime_object.h>
35 #include <runtime_root.h>
36
37 #ifdef NDEBUG
38 #define JS_LOG(formatAndArgs...) ((void)0)
39 #else
40 #define JS_LOG(formatAndArgs...) { \
41     fprintf (stderr, "%s:%d -- %s:  ", __FILE__, __LINE__, __FUNCTION__); \
42     fprintf(stderr, formatAndArgs); \
43 }
44 #endif
45
46 using namespace KJS;
47 using namespace KJS::Bindings;
48
49
50 JavaParameter::JavaParameter (JNIEnv *env, jstring type)
51 {
52     _type = JavaString (env, type);
53     _JNIType = JNITypeFromClassName (_type.UTF8String());
54 }
55
56 JavaField::JavaField (JNIEnv *env, jobject aField)
57 {
58     // Get field type
59     jobject fieldType = callJNIObjectMethod (aField, "getType", "()Ljava/lang/Class;");
60     jstring fieldTypeName = (jstring)callJNIObjectMethod (fieldType, "getName", "()Ljava/lang/String;");
61     _type = JavaString(env, fieldTypeName);
62     _JNIType = JNITypeFromClassName (_type.UTF8String());
63
64     // Get field name
65     jstring fieldName = (jstring)callJNIObjectMethod (aField, "getName", "()Ljava/lang/String;");
66     _name = JavaString(env, fieldName);
67
68     _field = new JObjectWrapper(aField);
69 }
70
71 JSValue* JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject)
72 {
73     if (type[0] != '[')
74         return jsUndefined();
75
76     return new RuntimeArray(exec, new JavaArray((jobject)anObject, type, rootObject));
77 }
78
79 jvalue JavaField::dispatchValueFromInstance(ExecState *exec, const JavaInstance *instance, const char *name, const char *sig, JNIType returnType) const
80 {
81     jobject jinstance = instance->javaInstance();
82     jobject fieldJInstance = _field->_instance;
83     JNIEnv *env = getJNIEnv();
84     jvalue result;
85
86     bzero (&result, sizeof(jvalue));
87     jclass cls = env->GetObjectClass(fieldJInstance);
88     if ( cls != NULL ) {
89         jmethodID mid = env->GetMethodID(cls, name, sig);
90         if ( mid != NULL )
91         {
92             RootObject* rootObject = instance->rootObject();
93             if (rootObject && rootObject->nativeHandle()) {
94                 JSValue *exceptionDescription = NULL;
95                 jvalue args[1];
96                 
97                 args[0].l = jinstance;
98                 dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription);
99                 if (exceptionDescription)
100                     throwError(exec, GeneralError, exceptionDescription->toString(exec));
101             }
102         }
103     }
104     return result;
105 }
106
107 JSValue *JavaField::valueFromInstance(ExecState *exec, const Instance *i) const 
108 {
109     const JavaInstance *instance = static_cast<const JavaInstance *>(i);
110
111     JSValue *jsresult = jsUndefined();
112     
113     switch (_JNIType) {
114         case array_type:
115         case object_type: {
116             jvalue result = dispatchValueFromInstance (exec, instance, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", object_type);
117             jobject anObject = result.l;
118
119             const char *arrayType = type();
120             if (arrayType[0] == '[') {
121                 jsresult = JavaArray::convertJObjectToArray(exec, anObject, arrayType, instance->rootObject());
122             }
123             else if (anObject != 0){
124                 jsresult = Instance::createRuntimeObject(Instance::JavaLanguage, anObject, instance->rootObject());
125             }
126         }
127         break;
128             
129         case boolean_type:
130             jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z);
131             break;
132             
133         case byte_type:
134         case char_type:
135         case short_type:
136         
137         case int_type: {
138             jint value;
139             jvalue result = dispatchValueFromInstance (exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type);
140             value = result.i;
141             jsresult = jsNumber((int)value);
142         }
143         break;
144
145         case long_type:
146         case float_type:
147         case double_type: {
148             jdouble value;
149             jvalue result = dispatchValueFromInstance (exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type);
150             value = result.i;
151             jsresult = jsNumber((double)value);
152         }
153         break;
154         default:
155         break;
156     }
157
158     JS_LOG ("getting %s = %s\n", name(), jsresult->toString(exec).ascii());
159     
160     return jsresult;
161 }
162
163 void JavaField::dispatchSetValueToInstance(ExecState *exec, const JavaInstance *instance, jvalue javaValue, const char *name, const char *sig) const
164 {
165     jobject jinstance = instance->javaInstance();
166     jobject fieldJInstance = _field->_instance;
167     JNIEnv *env = getJNIEnv();
168
169     jclass cls = env->GetObjectClass(fieldJInstance);
170     if ( cls != NULL ) {
171         jmethodID mid = env->GetMethodID(cls, name, sig);
172         if ( mid != NULL )
173         {
174             RootObject* rootObject = instance->rootObject();
175             if (rootObject && rootObject->nativeHandle()) {
176                 JSValue *exceptionDescription = NULL;
177                 jvalue args[2];
178                 jvalue result;
179                 
180                 args[0].l = jinstance;
181                 args[1] = javaValue;
182                 dispatchJNICall(rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription);
183                 if (exceptionDescription)
184                     throwError(exec, GeneralError, exceptionDescription->toString(exec));
185             }
186         }
187     }
188 }
189
190 void JavaField::setValueToInstance(ExecState *exec, const Instance *i, JSValue *aValue) const
191 {
192     const JavaInstance *instance = static_cast<const JavaInstance *>(i);
193     jvalue javaValue = convertValueToJValue (exec, aValue, _JNIType, type());
194
195     JS_LOG ("setting value %s to %s\n", name(), aValue->toString(exec).ascii());
196
197     switch (_JNIType) {
198         case array_type:
199         case object_type: {
200             dispatchSetValueToInstance (exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");
201         }
202         break;
203             
204         case boolean_type: {
205             dispatchSetValueToInstance (exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V");
206         }
207         break;
208             
209         case byte_type: {
210             dispatchSetValueToInstance (exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V");
211         }
212         break;
213
214         case char_type: {
215             dispatchSetValueToInstance (exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V");
216         }
217         break;
218
219         case short_type: {
220             dispatchSetValueToInstance (exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V");
221         }
222         break;
223
224         case int_type: {
225             dispatchSetValueToInstance (exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V");
226         }
227         break;
228
229         case long_type: {
230             dispatchSetValueToInstance (exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V");
231         }
232         break;
233
234         case float_type: {
235             dispatchSetValueToInstance (exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V");
236         }
237         break;
238
239         case double_type: {
240             dispatchSetValueToInstance (exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V");
241         }
242         break;
243         default:
244         break;
245     }
246 }
247
248 JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod)
249 {
250     // Get return type
251     jobject returnType = callJNIObjectMethod (aMethod, "getReturnType", "()Ljava/lang/Class;");
252     jstring returnTypeName = (jstring)callJNIObjectMethod (returnType, "getName", "()Ljava/lang/String;");
253     _returnType =JavaString (env, returnTypeName);
254     _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String());
255     env->DeleteLocalRef (returnType);
256     env->DeleteLocalRef (returnTypeName);
257
258     // Get method name
259     jstring methodName = (jstring)callJNIObjectMethod (aMethod, "getName", "()Ljava/lang/String;");
260     _name = JavaString (env, methodName);
261     env->DeleteLocalRef (methodName);
262
263     // Get parameters
264     jarray jparameters = (jarray)callJNIObjectMethod (aMethod, "getParameterTypes", "()[Ljava/lang/Class;");
265     _numParameters = env->GetArrayLength (jparameters);
266     _parameters = new JavaParameter[_numParameters];
267     
268     int i;
269     for (i = 0; i < _numParameters; i++) {
270         jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i);
271         jstring parameterName = (jstring)callJNIObjectMethod (aParameter, "getName", "()Ljava/lang/String;");
272         _parameters[i] = JavaParameter(env, parameterName);
273         env->DeleteLocalRef (aParameter);
274         env->DeleteLocalRef (parameterName);
275     }
276     env->DeleteLocalRef (jparameters);
277
278     // Created lazily.
279     _signature = 0;
280     _methodID = 0;
281     
282     jclass modifierClass = env->FindClass("java/lang/reflect/Modifier");
283     int modifiers = callJNIIntMethod (aMethod, "getModifiers", "()I");
284     _isStatic = (bool)callJNIStaticBooleanMethod (modifierClass, "isStatic", "(I)Z", modifiers);
285 }
286
287 JavaMethod::~JavaMethod() 
288 {
289     if (_signature)
290         free(_signature);
291     delete [] _parameters;
292 };
293
294 // JNI method signatures use '/' between components of a class name, but
295 // we get '.' between components from the reflection API.
296 static void appendClassName(UString& aString, const char* className)
297 {
298     ASSERT(JSLock::lockCount() > 0);
299     
300     char *result, *cp = strdup(className);
301     
302     result = cp;
303     while (*cp) {
304         if (*cp == '.')
305             *cp = '/';
306         cp++;
307     }
308         
309     aString.append(result);
310
311     free (result);
312 }
313
314 const char *JavaMethod::signature() const 
315 {
316     if (!_signature) {
317         JSLock lock;
318
319         UString signatureBuilder("(");
320         for (int i = 0; i < _numParameters; i++) {
321             JavaParameter* aParameter = parameterAt(i);
322             JNIType _JNIType = aParameter->getJNIType();
323             if (_JNIType == array_type)
324                 appendClassName(signatureBuilder, aParameter->type());
325             else {
326                 signatureBuilder.append(signatureFromPrimitiveType(_JNIType));
327                 if (_JNIType == object_type) {
328                     appendClassName(signatureBuilder, aParameter->type());
329                     signatureBuilder.append(";");
330                 }
331             }
332         }
333         signatureBuilder.append(")");
334         
335         const char *returnType = _returnType.UTF8String();
336         if (_JNIReturnType == array_type) {
337             appendClassName(signatureBuilder, returnType);
338         } else {
339             signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType));
340             if (_JNIReturnType == object_type) {
341                 appendClassName(signatureBuilder, returnType);
342                 signatureBuilder.append(";");
343             }
344         }
345         
346         _signature = strdup(signatureBuilder.ascii());
347     }
348     
349     return _signature;
350 }
351
352 JNIType JavaMethod::JNIReturnType() const
353 {
354     return _JNIReturnType;
355 }
356
357 jmethodID JavaMethod::methodID (jobject obj) const
358 {
359     if (_methodID == 0) {
360         _methodID = getMethodID (obj, name(), signature());
361     }
362     return _methodID;
363 }
364
365
366 JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject)
367     : Array(rootObject)
368 {
369     _array = new JObjectWrapper(array);
370     // Java array are fixed length, so we can cache length.
371     JNIEnv *env = getJNIEnv();
372     _length = env->GetArrayLength((jarray)_array->_instance);
373     _type = strdup(type);
374     _rootObject = rootObject;
375 }
376
377 JavaArray::~JavaArray () 
378 {
379     free ((void *)_type);
380 }
381
382 RootObject* JavaArray::rootObject() const 
383
384     return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
385 }
386
387 void JavaArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const
388 {
389     JNIEnv *env = getJNIEnv();
390     char *javaClassName = 0;
391     
392     JNIType arrayType = JNITypeFromPrimitiveType(_type[1]);
393     if (_type[1] == 'L'){
394         // The type of the array will be something like:
395         // "[Ljava.lang.string;".  This is guaranteed, so no need
396         // for extra sanity checks.
397         javaClassName = strdup(&_type[2]);
398         javaClassName[strchr(javaClassName, ';')-javaClassName] = 0;
399     }
400     jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName);
401     
402     switch (arrayType) {
403         case object_type: {
404             env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l);
405             break;
406         }
407             
408         case boolean_type: {
409             env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z);
410             break;
411         }
412             
413         case byte_type: {
414             env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b);
415             break;
416         }
417             
418         case char_type: {
419             env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c);
420             break;
421         }
422             
423         case short_type: {
424             env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s);
425             break;
426         }
427             
428         case int_type: {
429             env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i);
430             break;
431         }
432             
433         case long_type: {
434             env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j);
435         }
436             
437         case float_type: {
438             env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f);
439             break;
440         }
441             
442         case double_type: {
443             env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d);
444             break;
445         }
446         default:
447         break;
448     }
449     
450     if (javaClassName)
451         free ((void *)javaClassName);
452 }
453
454
455 JSValue *JavaArray::valueAt(ExecState *exec, unsigned int index) const
456 {
457     JNIEnv *env = getJNIEnv();
458     JNIType arrayType = JNITypeFromPrimitiveType(_type[1]);
459     switch (arrayType) {
460         case object_type: {
461             jobjectArray objectArray = (jobjectArray)javaArray();
462             jobject anObject;
463             anObject = env->GetObjectArrayElement(objectArray, index);
464
465             // No object?
466             if (!anObject) {
467                 return jsNull();
468             }
469             
470             // Nested array?
471             if (_type[1] == '[') {
472                 return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject());
473             }
474             // or array of other object type?
475             return Instance::createRuntimeObject(Instance::JavaLanguage, anObject, rootObject());
476         }
477             
478         case boolean_type: {
479             jbooleanArray booleanArray = (jbooleanArray)javaArray();
480             jboolean aBoolean;
481             env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean);
482             return jsBoolean(aBoolean);
483         }
484             
485         case byte_type: {
486             jbyteArray byteArray = (jbyteArray)javaArray();
487             jbyte aByte;
488             env->GetByteArrayRegion(byteArray, index, 1, &aByte);
489             return jsNumber(aByte);
490         }
491             
492         case char_type: {
493             jcharArray charArray = (jcharArray)javaArray();
494             jchar aChar;
495             env->GetCharArrayRegion(charArray, index, 1, &aChar);
496             return jsNumber(aChar);
497             break;
498         }
499             
500         case short_type: {
501             jshortArray shortArray = (jshortArray)javaArray();
502             jshort aShort;
503             env->GetShortArrayRegion(shortArray, index, 1, &aShort);
504             return jsNumber(aShort);
505         }
506             
507         case int_type: {
508             jintArray intArray = (jintArray)javaArray();
509             jint anInt;
510             env->GetIntArrayRegion(intArray, index, 1, &anInt);
511             return jsNumber(anInt);
512         }
513             
514         case long_type: {
515             jlongArray longArray = (jlongArray)javaArray();
516             jlong aLong;
517             env->GetLongArrayRegion(longArray, index, 1, &aLong);
518             return jsNumber(aLong);
519         }
520             
521         case float_type: {
522             jfloatArray floatArray = (jfloatArray)javaArray();
523             jfloat aFloat;
524             env->GetFloatArrayRegion(floatArray, index, 1, &aFloat);
525             return jsNumber(aFloat);
526         }
527             
528         case double_type: {
529             jdoubleArray doubleArray = (jdoubleArray)javaArray();
530             jdouble aDouble;
531             env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble);
532             return jsNumber(aDouble);
533         }
534         default:
535         break;
536     }
537     return jsUndefined();
538 }
539
540 unsigned int JavaArray::getLength() const
541 {
542     return _length;
543 }
544
545