1/*
2 * Copyright (C) 2003, 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
3 * Copyright 2010, The Android Open Source Project
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "JNIBridgeJSC.h"
29
30#if ENABLE(MAC_JAVA_BRIDGE)
31
32#include "JNIUtilityPrivate.h"
33#include "runtime_array.h"
34#include "runtime_object.h"
35#include <runtime/Error.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
46using namespace JSC;
47using namespace JSC::Bindings;
48
49
50JavaField::JavaField(JNIEnv* env, jobject aField)
51{
52    // Get field type name
53    jstring fieldTypeName = 0;
54    if (jobject fieldType = callJNIMethod<jobject>(aField, "getType", "()Ljava/lang/Class;"))
55        fieldTypeName = static_cast<jstring>(callJNIMethod<jobject>(fieldType, "getName", "()Ljava/lang/String;"));
56    if (!fieldTypeName)
57        fieldTypeName = env->NewStringUTF("<Unknown>");
58    m_type = JavaString(env, fieldTypeName);
59
60    m_JNIType = JNITypeFromClassName(m_type.UTF8String());
61
62    // Get field name
63    jstring fieldName = static_cast<jstring>(callJNIMethod<jobject>(aField, "getName", "()Ljava/lang/String;"));
64    if (!fieldName)
65        fieldName = env->NewStringUTF("<Unknown>");
66    m_name = JavaString(env, fieldName);
67
68    m_field = new JObjectWrapper(aField);
69}
70
71JSValue JavaArray::convertJObjectToArray(ExecState* exec, jobject anObject, const char* type, PassRefPtr<RootObject> rootObject)
72{
73    if (type[0] != '[')
74        return jsUndefined();
75
76    return new (exec) RuntimeArray(exec, new JavaArray(anObject, type, rootObject));
77}
78
79jvalue 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 = m_field->m_instance;
83    JNIEnv* env = getJNIEnv();
84    jvalue result;
85
86    memset(&result, 0, sizeof(jvalue));
87    jclass cls = env->GetObjectClass(fieldJInstance);
88    if (cls) {
89        jmethodID mid = env->GetMethodID(cls, name, sig);
90        if (mid) {
91            RootObject* rootObject = instance->rootObject();
92            if (rootObject && rootObject->nativeHandle()) {
93                JSValue exceptionDescription;
94                jvalue args[1];
95
96                args[0].l = jinstance;
97                dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, returnType, mid, args, result, 0, exceptionDescription);
98                if (exceptionDescription)
99                    throwError(exec, GeneralError, exceptionDescription.toString(exec));
100            }
101        }
102    }
103    return result;
104}
105
106JSValue JavaField::valueFromInstance(ExecState* exec, const Instance* i) const
107{
108    const JavaInstance* instance = static_cast<const JavaInstance*>(i);
109
110    JSValue jsresult = jsUndefined();
111
112    switch (m_JNIType) {
113    case array_type:
114    case object_type:
115        {
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            else if (anObject)
123                jsresult = JavaInstance::create(anObject, instance->rootObject())->createRuntimeObject(exec);
124        }
125        break;
126
127    case boolean_type:
128        jsresult = jsBoolean(dispatchValueFromInstance(exec, instance, "getBoolean", "(Ljava/lang/Object;)Z", boolean_type).z);
129        break;
130
131    case byte_type:
132    case char_type:
133    case short_type:
134
135    case int_type:
136        {
137            jint value;
138            jvalue result = dispatchValueFromInstance(exec, instance, "getInt", "(Ljava/lang/Object;)I", int_type);
139            value = result.i;
140            jsresult = jsNumber(exec, static_cast<int>(value));
141        }
142        break;
143
144    case long_type:
145    case float_type:
146    case double_type:
147        {
148            jdouble value;
149            jvalue result = dispatchValueFromInstance(exec, instance, "getDouble", "(Ljava/lang/Object;)D", double_type);
150            value = result.i;
151            jsresult = jsNumber(exec, static_cast<double>(value));
152        }
153        break;
154    default:
155        break;
156    }
157
158    JS_LOG("getting %s = %s\n", UString(name()).UTF8String().c_str(), jsresult.toString(exec).ascii());
159
160    return jsresult;
161}
162
163void 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 = m_field->m_instance;
167    JNIEnv* env = getJNIEnv();
168
169    jclass cls = env->GetObjectClass(fieldJInstance);
170    if (cls) {
171        jmethodID mid = env->GetMethodID(cls, name, sig);
172        if (mid) {
173            RootObject* rootObject = instance->rootObject();
174            if (rootObject && rootObject->nativeHandle()) {
175                JSValue exceptionDescription;
176                jvalue args[2];
177                jvalue result;
178
179                args[0].l = jinstance;
180                args[1] = javaValue;
181                dispatchJNICall(exec, rootObject->nativeHandle(), fieldJInstance, false, void_type, mid, args, result, 0, exceptionDescription);
182                if (exceptionDescription)
183                    throwError(exec, GeneralError, exceptionDescription.toString(exec));
184            }
185        }
186    }
187}
188
189void JavaField::setValueToInstance(ExecState* exec, const Instance* i, JSValue aValue) const
190{
191    const JavaInstance* instance = static_cast<const JavaInstance*>(i);
192    jvalue javaValue = convertValueToJValue(exec, aValue, m_JNIType, type());
193
194    JS_LOG("setting value %s to %s\n", UString(name()).UTF8String().c_str(), aValue.toString(exec).ascii());
195
196    switch (m_JNIType) {
197    case array_type:
198    case object_type:
199        {
200            dispatchSetValueToInstance(exec, instance, javaValue, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V");
201        }
202        break;
203
204    case boolean_type:
205        {
206            dispatchSetValueToInstance(exec, instance, javaValue, "setBoolean", "(Ljava/lang/Object;Z)V");
207        }
208        break;
209
210    case byte_type:
211        {
212            dispatchSetValueToInstance(exec, instance, javaValue, "setByte", "(Ljava/lang/Object;B)V");
213        }
214        break;
215
216    case char_type:
217        {
218            dispatchSetValueToInstance(exec, instance, javaValue, "setChar", "(Ljava/lang/Object;C)V");
219        }
220        break;
221
222    case short_type:
223        {
224            dispatchSetValueToInstance(exec, instance, javaValue, "setShort", "(Ljava/lang/Object;S)V");
225        }
226        break;
227
228    case int_type:
229        {
230            dispatchSetValueToInstance(exec, instance, javaValue, "setInt", "(Ljava/lang/Object;I)V");
231        }
232        break;
233
234    case long_type:
235        {
236            dispatchSetValueToInstance(exec, instance, javaValue, "setLong", "(Ljava/lang/Object;J)V");
237        }
238        break;
239
240    case float_type:
241        {
242            dispatchSetValueToInstance(exec, instance, javaValue, "setFloat", "(Ljava/lang/Object;F)V");
243        }
244        break;
245
246    case double_type:
247        {
248            dispatchSetValueToInstance(exec, instance, javaValue, "setDouble", "(Ljava/lang/Object;D)V");
249        }
250        break;
251    default:
252        break;
253    }
254}
255
256JavaArray::JavaArray(jobject array, const char* type, PassRefPtr<RootObject> rootObject)
257    : Array(rootObject)
258{
259    m_array = new JObjectWrapper(array);
260    // Java array are fixed length, so we can cache length.
261    JNIEnv* env = getJNIEnv();
262    m_length = env->GetArrayLength(static_cast<jarray>(m_array->m_instance));
263    m_type = strdup(type);
264    m_rootObject = rootObject;
265}
266
267JavaArray::~JavaArray()
268{
269    free(const_cast<char*>(m_type));
270}
271
272RootObject* JavaArray::rootObject() const
273{
274    return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0;
275}
276
277void JavaArray::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const
278{
279    JNIEnv* env = getJNIEnv();
280    char* javaClassName = 0;
281
282    JNIType arrayType = JNITypeFromPrimitiveType(m_type[1]);
283    if (m_type[1] == 'L') {
284        // The type of the array will be something like:
285        // "[Ljava.lang.string;".  This is guaranteed, so no need
286        // for extra sanity checks.
287        javaClassName = strdup(&m_type[2]);
288        javaClassName[strchr(javaClassName, ';')-javaClassName] = 0;
289    }
290    jvalue aJValue = convertValueToJValue(exec, aValue, arrayType, javaClassName);
291
292    switch (arrayType) {
293    case object_type:
294        {
295            env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray()), index, aJValue.l);
296            break;
297        }
298
299    case boolean_type:
300        {
301            env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray()), index, 1, &aJValue.z);
302            break;
303        }
304
305    case byte_type:
306        {
307            env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray()), index, 1, &aJValue.b);
308            break;
309        }
310
311    case char_type:
312        {
313            env->SetCharArrayRegion(static_cast<jcharArray>(javaArray()), index, 1, &aJValue.c);
314            break;
315        }
316
317    case short_type:
318        {
319            env->SetShortArrayRegion(static_cast<jshortArray>(javaArray()), index, 1, &aJValue.s);
320            break;
321        }
322
323    case int_type:
324        {
325            env->SetIntArrayRegion(static_cast<jintArray>(javaArray()), index, 1, &aJValue.i);
326            break;
327        }
328
329    case long_type:
330        {
331            env->SetLongArrayRegion(static_cast<jlongArray>(javaArray()), index, 1, &aJValue.j);
332        }
333
334    case float_type:
335        {
336            env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray()), index, 1, &aJValue.f);
337            break;
338        }
339
340    case double_type:
341        {
342            env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray()), index, 1, &aJValue.d);
343            break;
344        }
345    default:
346        break;
347    }
348
349    if (javaClassName)
350        free(const_cast<char*>(javaClassName));
351}
352
353
354JSValue JavaArray::valueAt(ExecState* exec, unsigned index) const
355{
356    JNIEnv* env = getJNIEnv();
357    JNIType arrayType = JNITypeFromPrimitiveType(m_type[1]);
358    switch (arrayType) {
359    case object_type:
360        {
361            jobjectArray objectArray = static_cast<jobjectArray>(javaArray());
362            jobject anObject;
363            anObject = env->GetObjectArrayElement(objectArray, index);
364
365            // No object?
366            if (!anObject)
367                return jsNull();
368
369            // Nested array?
370            if (m_type[1] == '[')
371                return JavaArray::convertJObjectToArray(exec, anObject, m_type + 1, rootObject());
372            // or array of other object type?
373            return JavaInstance::create(anObject, rootObject())->createRuntimeObject(exec);
374        }
375
376    case boolean_type:
377        {
378            jbooleanArray booleanArray = static_cast<jbooleanArray>(javaArray());
379            jboolean aBoolean;
380            env->GetBooleanArrayRegion(booleanArray, index, 1, &aBoolean);
381            return jsBoolean(aBoolean);
382        }
383
384    case byte_type:
385        {
386            jbyteArray byteArray = static_cast<jbyteArray>(javaArray());
387            jbyte aByte;
388            env->GetByteArrayRegion(byteArray, index, 1, &aByte);
389            return jsNumber(exec, aByte);
390        }
391
392    case char_type:
393        {
394            jcharArray charArray = static_cast<jcharArray>(javaArray());
395            jchar aChar;
396            env->GetCharArrayRegion(charArray, index, 1, &aChar);
397            return jsNumber(exec, aChar);
398            break;
399        }
400
401    case short_type:
402        {
403            jshortArray shortArray = static_cast<jshortArray>(javaArray());
404            jshort aShort;
405            env->GetShortArrayRegion(shortArray, index, 1, &aShort);
406            return jsNumber(exec, aShort);
407        }
408
409    case int_type:
410        {
411            jintArray intArray = static_cast<jintArray>(javaArray());
412            jint anInt;
413            env->GetIntArrayRegion(intArray, index, 1, &anInt);
414            return jsNumber(exec, anInt);
415        }
416
417    case long_type:
418        {
419            jlongArray longArray = static_cast<jlongArray>(javaArray());
420            jlong aLong;
421            env->GetLongArrayRegion(longArray, index, 1, &aLong);
422            return jsNumber(exec, aLong);
423        }
424
425    case float_type:
426        {
427            jfloatArray floatArray = static_cast<jfloatArray>(javaArray());
428            jfloat aFloat;
429            env->GetFloatArrayRegion(floatArray, index, 1, &aFloat);
430            return jsNumber(exec, aFloat);
431        }
432
433    case double_type:
434        {
435            jdoubleArray doubleArray = static_cast<jdoubleArray>(javaArray());
436            jdouble aDouble;
437            env->GetDoubleArrayRegion(doubleArray, index, 1, &aDouble);
438            return jsNumber(exec, aDouble);
439        }
440    default:
441        break;
442    }
443    return jsUndefined();
444}
445
446unsigned int JavaArray::getLength() const
447{
448    return m_length;
449}
450
451#endif // ENABLE(MAC_JAVA_BRIDGE)
452