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