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