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