Reviewed by Adam.
[WebKit-https.git] / JavaScriptCore / bindings / 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 JavaConstructor::JavaConstructor (JNIEnv *env, jobject aConstructor)
249 {
250     // Get parameters
251     jarray jparameters = (jarray)callJNIObjectMethod (aConstructor, "getParameterTypes", "()[Ljava/lang/Class;");
252     _numParameters = env->GetArrayLength (jparameters);
253     _parameters = new JavaParameter[_numParameters];
254     
255     int i;
256     for (i = 0; i < _numParameters; i++) {
257         jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i);
258         jstring parameterName = (jstring)callJNIObjectMethod (aParameter, "getName", "()Ljava/lang/String;");
259         _parameters[i] = JavaParameter(env, parameterName);
260         env->DeleteLocalRef (aParameter);
261         env->DeleteLocalRef (parameterName);
262     }
263 }
264
265 JavaMethod::JavaMethod (JNIEnv *env, jobject aMethod)
266 {
267     // Get return type
268     jobject returnType = callJNIObjectMethod (aMethod, "getReturnType", "()Ljava/lang/Class;");
269     jstring returnTypeName = (jstring)callJNIObjectMethod (returnType, "getName", "()Ljava/lang/String;");
270     _returnType =JavaString (env, returnTypeName);
271     _JNIReturnType = JNITypeFromClassName (_returnType.UTF8String());
272     env->DeleteLocalRef (returnType);
273     env->DeleteLocalRef (returnTypeName);
274
275     // Get method name
276     jstring methodName = (jstring)callJNIObjectMethod (aMethod, "getName", "()Ljava/lang/String;");
277     _name = JavaString (env, methodName);
278     env->DeleteLocalRef (methodName);
279
280     // Get parameters
281     jarray jparameters = (jarray)callJNIObjectMethod (aMethod, "getParameterTypes", "()[Ljava/lang/Class;");
282     _numParameters = env->GetArrayLength (jparameters);
283     _parameters = new JavaParameter[_numParameters];
284     
285     int i;
286     for (i = 0; i < _numParameters; i++) {
287         jobject aParameter = env->GetObjectArrayElement ((jobjectArray)jparameters, i);
288         jstring parameterName = (jstring)callJNIObjectMethod (aParameter, "getName", "()Ljava/lang/String;");
289         _parameters[i] = JavaParameter(env, parameterName);
290         env->DeleteLocalRef (aParameter);
291         env->DeleteLocalRef (parameterName);
292     }
293     env->DeleteLocalRef (jparameters);
294
295     // Created lazily.
296     _signature = 0;
297     _methodID = 0;
298     
299     jclass modifierClass = env->FindClass("java/lang/reflect/Modifier");
300     int modifiers = callJNIIntMethod (aMethod, "getModifiers", "()I");
301     _isStatic = (bool)callJNIStaticBooleanMethod (modifierClass, "isStatic", "(I)Z", modifiers);
302 }
303
304 JavaMethod::~JavaMethod() 
305 {
306     if (_signature)
307         free(_signature);
308     delete [] _parameters;
309 };
310
311 // JNI method signatures use '/' between components of a class name, but
312 // we get '.' between components from the reflection API.
313 static void appendClassName(UString& aString, const char* className)
314 {
315     ASSERT(JSLock::lockCount() > 0);
316     
317     char *result, *cp = strdup(className);
318     
319     result = cp;
320     while (*cp) {
321         if (*cp == '.')
322             *cp = '/';
323         cp++;
324     }
325         
326     aString.append(result);
327
328     free (result);
329 }
330
331 const char *JavaMethod::signature() const 
332 {
333     if (!_signature) {
334         JSLock lock;
335
336         UString signatureBuilder("(");
337         for (int i = 0; i < _numParameters; i++) {
338             JavaParameter *aParameter = static_cast<JavaParameter *>(parameterAt(i));
339             JNIType _JNIType = aParameter->getJNIType();
340             if (_JNIType == array_type)
341                 appendClassName(signatureBuilder, aParameter->type());
342             else {
343                 signatureBuilder.append(signatureFromPrimitiveType(_JNIType));
344                 if (_JNIType == object_type) {
345                     appendClassName(signatureBuilder, aParameter->type());
346                     signatureBuilder.append(";");
347                 }
348             }
349         }
350         signatureBuilder.append(")");
351         
352         const char *returnType = _returnType.UTF8String();
353         if (_JNIReturnType == array_type) {
354             appendClassName(signatureBuilder, returnType);
355         } else {
356             signatureBuilder.append(signatureFromPrimitiveType(_JNIReturnType));
357             if (_JNIReturnType == object_type) {
358                 appendClassName(signatureBuilder, returnType);
359                 signatureBuilder.append(";");
360             }
361         }
362         
363         _signature = strdup(signatureBuilder.ascii());
364     }
365     
366     return _signature;
367 }
368
369 JNIType JavaMethod::JNIReturnType() const
370 {
371     return _JNIReturnType;
372 }
373
374 jmethodID JavaMethod::methodID (jobject obj) const
375 {
376     if (_methodID == 0) {
377         _methodID = getMethodID (obj, name(), signature());
378     }
379     return _methodID;
380 }
381
382
383 JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject) 
384 {
385     _array = new JObjectWrapper(array);
386     // Java array are fixed length, so we can cache length.
387     JNIEnv *env = getJNIEnv();
388     _length = env->GetArrayLength((jarray)_array->_instance);
389     _type = strdup(type);
390     _rootObject = rootObject;
391 }
392
393 JavaArray::~JavaArray () 
394 {
395     free ((void *)_type);
396 }
397
398 RootObject* JavaArray::rootObject() const 
399
400     return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
401 }
402
403 void JavaArray::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const
404 {
405     JNIEnv *env = getJNIEnv();
406     char *javaClassName = 0;
407     
408     JNIType arrayType = JNITypeFromPrimitiveType(_type[1]);
409     if (_type[1] == 'L'){
410         // The type of the array will be something like:
411         // "[Ljava.lang.string;".  This is guaranteed, so no need
412         // for extra sanity checks.
413         javaClassName = strdup(&_type[2]);
414         javaClassName[strchr(javaClassName, ';')-javaClassName] = 0;
415     }
416     jvalue aJValue = convertValueToJValue (exec, aValue, arrayType, javaClassName);
417     
418     switch (arrayType) {
419         case object_type: {
420             env->SetObjectArrayElement((jobjectArray)javaArray(), index, aJValue.l);
421             break;
422         }
423             
424         case boolean_type: {
425             env->SetBooleanArrayRegion((jbooleanArray)javaArray(), index, 1, &aJValue.z);
426             break;
427         }
428             
429         case byte_type: {
430             env->SetByteArrayRegion((jbyteArray)javaArray(), index, 1, &aJValue.b);
431             break;
432         }
433             
434         case char_type: {
435             env->SetCharArrayRegion((jcharArray)javaArray(), index, 1, &aJValue.c);
436             break;
437         }
438             
439         case short_type: {
440             env->SetShortArrayRegion((jshortArray)javaArray(), index, 1, &aJValue.s);
441             break;
442         }
443             
444         case int_type: {
445             env->SetIntArrayRegion((jintArray)javaArray(), index, 1, &aJValue.i);
446             break;
447         }
448             
449         case long_type: {
450             env->SetLongArrayRegion((jlongArray)javaArray(), index, 1, &aJValue.j);
451         }
452             
453         case float_type: {
454             env->SetFloatArrayRegion((jfloatArray)javaArray(), index, 1, &aJValue.f);
455             break;
456         }
457             
458         case double_type: {
459             env->SetDoubleArrayRegion((jdoubleArray)javaArray(), index, 1, &aJValue.d);
460             break;
461         }
462         default:
463         break;
464     }
465     
466     if (javaClassName)
467         free ((void *)javaClassName);
468 }
469
470
471 JSValue *JavaArray::valueAt(ExecState *exec, unsigned int index) const
472 {
473     JNIEnv *env = getJNIEnv();
474     JNIType arrayType = JNITypeFromPrimitiveType(_type[1]);
475     switch (arrayType) {
476         case object_type: {
477             jobjectArray objectArray = (jobjectArray)javaArray();
478             jobject anObject;
479             anObject = env->GetObjectArrayElement(objectArray, index);
480
481             // No object?
482             if (!anObject) {
483                 return jsNull();
484             }
485             
486             // Nested array?
487             if (_type[1] == '[') {
488                 return JavaArray::convertJObjectToArray(exec, anObject, _type+1, rootObject());
489             }
490             // or array of other object type?
491             return Instance::createRuntimeObject(Instance::JavaLanguage, anObject, rootObject());
492         }
493             
494         case boolean_type: {
495             jbooleanArray booleanArray = (jbooleanArray)javaArray();
496             jboolean aBoolean;
497             env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean);
498             return jsBoolean(aBoolean);
499         }
500             
501         case byte_type: {
502             jbyteArray byteArray = (jbyteArray)javaArray();
503             jbyte aByte;
504             env->GetByteArrayRegion(byteArray, index, 1, &aByte);
505             return jsNumber(aByte);
506         }
507             
508         case char_type: {
509             jcharArray charArray = (jcharArray)javaArray();
510             jchar aChar;
511             env->GetCharArrayRegion(charArray, index, 1, &aChar);
512             return jsNumber(aChar);
513             break;
514         }
515             
516         case short_type: {
517             jshortArray shortArray = (jshortArray)javaArray();
518             jshort aShort;
519             env->GetShortArrayRegion(shortArray, index, 1, &aShort);
520             return jsNumber(aShort);
521         }
522             
523         case int_type: {
524             jintArray intArray = (jintArray)javaArray();
525             jint anInt;
526             env->GetIntArrayRegion(intArray, index, 1, &anInt);
527             return jsNumber(anInt);
528         }
529             
530         case long_type: {
531             jlongArray longArray = (jlongArray)javaArray();
532             jlong aLong;
533             env->GetLongArrayRegion(longArray, index, 1, &aLong);
534             return jsNumber(aLong);
535         }
536             
537         case float_type: {
538             jfloatArray floatArray = (jfloatArray)javaArray();
539             jfloat aFloat;
540             env->GetFloatArrayRegion(floatArray, index, 1, &aFloat);
541             return jsNumber(aFloat);
542         }
543             
544         case double_type: {
545             jdoubleArray doubleArray = (jdoubleArray)javaArray();
546             jdouble aDouble;
547             env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble);
548             return jsNumber(aDouble);
549         }
550         default:
551         break;
552     }
553     return jsUndefined();
554 }
555
556 unsigned int JavaArray::getLength() const
557 {
558     return _length;
559 }
560
561