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