1/*
2 * Copyright (C) 2003, 2010 Apple, Inc.  All rights reserved.
3 * Copyright 2009, 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 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "JNIUtilityPrivate.h"
29
30#if ENABLE(JAVA_BRIDGE)
31
32#include "JavaArrayJSC.h"
33#include "JavaInstanceJSC.h"
34#include "JavaRuntimeObject.h"
35#include "jni_jsobject.h"
36#include "runtime_array.h"
37#include "runtime_object.h"
38#include "runtime_root.h"
39#include <runtime/JSArray.h>
40#include <runtime/JSLock.h>
41
42namespace JSC {
43
44namespace Bindings {
45
46static jobject convertArrayInstanceToJavaArray(ExecState* exec, JSArray* jsArray, const char* javaClassName)
47{
48    JNIEnv* env = getJNIEnv();
49    // As JS Arrays can contain a mixture of objects, assume we can convert to
50    // the requested Java Array type requested, unless the array type is some object array
51    // other than a string.
52    unsigned length = jsArray->length();
53    jobjectArray jarray = 0;
54
55    // Build the correct array type
56    switch (javaTypeFromPrimitiveType(javaClassName[1])) {
57    case JavaTypeObject:
58            {
59            // Only support string object types
60            if (!strcmp("[Ljava.lang.String;", javaClassName)) {
61                jarray = (jobjectArray)env->NewObjectArray(length,
62                    env->FindClass("java/lang/String"),
63                    env->NewStringUTF(""));
64                for (unsigned i = 0; i < length; i++) {
65                    JSValue item = jsArray->get(exec, i);
66                    UString stringValue = item.toString(exec);
67                    env->SetObjectArrayElement(jarray, i,
68                        env->functions->NewString(env, (const jchar *)stringValue.characters(), stringValue.length()));
69                }
70            }
71            break;
72        }
73
74    case JavaTypeBoolean:
75        {
76            jarray = (jobjectArray)env->NewBooleanArray(length);
77            for (unsigned i = 0; i < length; i++) {
78                JSValue item = jsArray->get(exec, i);
79                jboolean value = (jboolean)item.toNumber(exec);
80                env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value);
81            }
82            break;
83        }
84
85    case JavaTypeByte:
86        {
87            jarray = (jobjectArray)env->NewByteArray(length);
88            for (unsigned i = 0; i < length; i++) {
89                JSValue item = jsArray->get(exec, i);
90                jbyte value = (jbyte)item.toNumber(exec);
91                env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value);
92            }
93            break;
94        }
95
96    case JavaTypeChar:
97        {
98            jarray = (jobjectArray)env->NewCharArray(length);
99            for (unsigned i = 0; i < length; i++) {
100                JSValue item = jsArray->get(exec, i);
101                UString stringValue = item.toString(exec);
102                jchar value = 0;
103                if (stringValue.length() > 0)
104                    value = ((const jchar*)stringValue.characters())[0];
105                env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value);
106            }
107            break;
108        }
109
110    case JavaTypeShort:
111        {
112            jarray = (jobjectArray)env->NewShortArray(length);
113            for (unsigned i = 0; i < length; i++) {
114                JSValue item = jsArray->get(exec, i);
115                jshort value = (jshort)item.toNumber(exec);
116                env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value);
117            }
118            break;
119        }
120
121    case JavaTypeInt:
122        {
123            jarray = (jobjectArray)env->NewIntArray(length);
124            for (unsigned i = 0; i < length; i++) {
125                JSValue item = jsArray->get(exec, i);
126                jint value = (jint)item.toNumber(exec);
127                env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value);
128            }
129            break;
130        }
131
132    case JavaTypeLong:
133        {
134            jarray = (jobjectArray)env->NewLongArray(length);
135            for (unsigned i = 0; i < length; i++) {
136                JSValue item = jsArray->get(exec, i);
137                jlong value = (jlong)item.toNumber(exec);
138                env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value);
139            }
140            break;
141        }
142
143    case JavaTypeFloat:
144        {
145            jarray = (jobjectArray)env->NewFloatArray(length);
146            for (unsigned i = 0; i < length; i++) {
147                JSValue item = jsArray->get(exec, i);
148                jfloat value = (jfloat)item.toNumber(exec);
149                env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value);
150            }
151            break;
152        }
153
154    case JavaTypeDouble:
155        {
156            jarray = (jobjectArray)env->NewDoubleArray(length);
157            for (unsigned i = 0; i < length; i++) {
158                JSValue item = jsArray->get(exec, i);
159                jdouble value = (jdouble)item.toNumber(exec);
160                env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value);
161            }
162            break;
163        }
164
165    case JavaTypeArray: // don't handle embedded arrays
166    case JavaTypeVoid: // Don't expect arrays of void objects
167    case JavaTypeInvalid: // Array of unknown objects
168        break;
169    }
170
171    // if it was not one of the cases handled, then null is returned
172    return jarray;
173}
174
175jvalue convertValueToJValue(ExecState* exec, RootObject* rootObject, JSValue value, JavaType javaType, const char* javaClassName)
176{
177    JSLock lock(SilenceAssertionsOnly);
178
179    jvalue result;
180    memset(&result, 0, sizeof(jvalue));
181
182    switch (javaType) {
183    case JavaTypeArray:
184    case JavaTypeObject:
185        {
186            // FIXME: JavaJSObject::convertValueToJObject functionality is almost exactly the same,
187            // these functions should use common code.
188
189            if (value.isObject()) {
190                JSObject* object = asObject(value);
191                if (object->inherits(&JavaRuntimeObject::s_info)) {
192                    // Unwrap a Java instance.
193                    JavaRuntimeObject* runtimeObject = static_cast<JavaRuntimeObject*>(object);
194                    JavaInstance* instance = runtimeObject->getInternalJavaInstance();
195                    if (instance)
196                        result.l = instance->javaInstance();
197                } else if (object->classInfo() == &RuntimeArray::s_info) {
198                    // Input is a JavaScript Array that was originally created from a Java Array
199                    RuntimeArray* imp = static_cast<RuntimeArray*>(object);
200                    JavaArray* array = static_cast<JavaArray*>(imp->getConcreteArray());
201                    result.l = array->javaArray();
202                } else if (object->classInfo() == &JSArray::s_info) {
203                    // Input is a Javascript Array. We need to create it to a Java Array.
204                    result.l = convertArrayInstanceToJavaArray(exec, asArray(value), javaClassName);
205                } else if ((!result.l && (!strcmp(javaClassName, "java.lang.Object")))
206                           || (!strcmp(javaClassName, "netscape.javascript.JSObject"))) {
207                    // Wrap objects in JSObject instances.
208                    JNIEnv* env = getJNIEnv();
209                    jclass jsObjectClass = env->FindClass("sun/plugin/javascript/webkit/JSObject");
210                    jmethodID constructorID = env->GetMethodID(jsObjectClass, "<init>", "(J)V");
211                    if (constructorID) {
212                        jlong nativeHandle = ptr_to_jlong(object);
213                        rootObject->gcProtect(object);
214                        result.l = env->NewObject(jsObjectClass, constructorID, nativeHandle);
215                    }
216                }
217            }
218
219            // Create an appropriate Java object if target type is java.lang.Object.
220            if (!result.l && !strcmp(javaClassName, "java.lang.Object")) {
221                if (value.isString()) {
222                    UString stringValue = asString(value)->value(exec);
223                    JNIEnv* env = getJNIEnv();
224                    jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length());
225                    result.l = javaString;
226                } else if (value.isNumber()) {
227                    double doubleValue = value.uncheckedGetNumber();
228                    JNIEnv* env = getJNIEnv();
229                    jclass clazz = env->FindClass("java/lang/Double");
230                    jmethodID constructor = env->GetMethodID(clazz, "<init>", "(D)V");
231                    jobject javaDouble = env->functions->NewObject(env, clazz, constructor, doubleValue);
232                    result.l = javaDouble;
233                } else if (value.isBoolean()) {
234                    bool boolValue = value.getBoolean();
235                    JNIEnv* env = getJNIEnv();
236                    jclass clazz = env->FindClass("java/lang/Boolean");
237                    jmethodID constructor = env->GetMethodID(clazz, "<init>", "(Z)V");
238                    jobject javaBoolean = env->functions->NewObject(env, clazz, constructor, boolValue);
239                    result.l = javaBoolean;
240                } else if (value.isUndefined()) {
241                    UString stringValue = "undefined";
242                    JNIEnv* env = getJNIEnv();
243                    jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length());
244                    result.l = javaString;
245                }
246            }
247
248            // Convert value to a string if the target type is a java.lang.String, and we're not
249            // converting from a null.
250            if (!result.l && !strcmp(javaClassName, "java.lang.String")) {
251                if (!value.isNull()) {
252                    UString stringValue = value.toString(exec);
253                    JNIEnv* env = getJNIEnv();
254                    jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.characters(), stringValue.length());
255                    result.l = javaString;
256                }
257            }
258        }
259        break;
260
261    case JavaTypeBoolean:
262        {
263            result.z = (jboolean)value.toNumber(exec);
264        }
265        break;
266
267    case JavaTypeByte:
268        {
269            result.b = (jbyte)value.toNumber(exec);
270        }
271        break;
272
273    case JavaTypeChar:
274        {
275            result.c = (jchar)value.toNumber(exec);
276        }
277        break;
278
279    case JavaTypeShort:
280        {
281            result.s = (jshort)value.toNumber(exec);
282        }
283        break;
284
285    case JavaTypeInt:
286        {
287            result.i = (jint)value.toNumber(exec);
288        }
289        break;
290
291    case JavaTypeLong:
292        {
293            result.j = (jlong)value.toNumber(exec);
294        }
295        break;
296
297    case JavaTypeFloat:
298        {
299            result.f = (jfloat)value.toNumber(exec);
300        }
301        break;
302
303    case JavaTypeDouble:
304        {
305            result.d = (jdouble)value.toNumber(exec);
306        }
307        break;
308
309    case JavaTypeInvalid:
310    case JavaTypeVoid:
311        break;
312    }
313    return result;
314}
315
316} // end of namespace Bindings
317
318} // end of namespace JSC
319
320#endif // ENABLE(JAVA_BRIDGE)
321