1/*
2 * Copyright 2010, The Android Open Source Project
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 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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
26#include "config.h"
27#include "JNIUtilityPrivate.h"
28
29#if ENABLE(JAVA_BRIDGE)
30
31#include "JavaInstanceJobjectV8.h"
32#include "JavaNPObjectV8.h"
33#if PLATFORM(ANDROID)
34#include "npruntime_impl.h"
35#endif // PLATFORM(ANDROID)
36#include "JavaValueV8.h"
37#include <wtf/text/CString.h>
38
39namespace JSC {
40
41namespace Bindings {
42
43JavaValue convertNPVariantToJavaValue(NPVariant value, const String& javaClass)
44{
45    CString javaClassName = javaClass.utf8();
46    JavaType javaType = javaTypeFromClassName(javaClassName.data());
47    JavaValue result;
48    result.m_type = javaType;
49    NPVariantType type = value.type;
50
51    switch (javaType) {
52    case JavaTypeArray:
53#if PLATFORM(ANDROID)
54        // If we're converting to an array, see if the NPVariant has a length
55        // property. If so, create a JNI array of the relevant length and to get
56        // the elements of the NPVariant. When we interpret the JavaValue later,
57        // we know that the array is represented as a JNI array.
58        //
59        // FIXME: This is a hack. We should not be using JNI here. We should
60        // represent the JavaValue without JNI.
61        {
62            JNIEnv* env = getJNIEnv();
63            jobject javaArray;
64            NPObject* object = NPVARIANT_IS_OBJECT(value) ? NPVARIANT_TO_OBJECT(value) : 0;
65            NPVariant npvLength;
66            bool success = _NPN_GetProperty(0, object, _NPN_GetStringIdentifier("length"), &npvLength);
67            if (!success) {
68                // No length property so we don't know how many elements to put into the array.
69                // Treat this as an error.
70                // JSC sends null for an array that is not an array of strings or basic types,
71                // do this also in the unknown length case.
72                break;
73            }
74
75            // Convert to null if the length property is not a number.
76            if (!NPVARIANT_IS_INT32(npvLength) && !NPVARIANT_IS_DOUBLE(npvLength))
77                break;
78
79            // Convert to null if the length property is out of bounds.
80            double doubleLength = NPVARIANT_IS_INT32(npvLength) ? NPVARIANT_TO_INT32(npvLength) : NPVARIANT_TO_DOUBLE(npvLength);
81            if (doubleLength < 0.0 || doubleLength > INT32_MAX)
82                break;
83
84            jsize length = static_cast<jsize>(doubleLength);
85
86            if (!strcmp(javaClassName.data(), "[Ljava.lang.String;")) {
87                // Match JSC behavior by only allowing Object arrays if they are Strings.
88                jclass stringClass = env->FindClass("java/lang/String");
89                javaArray = env->NewObjectArray(length, stringClass, 0);
90                for (jsize i = 0; i < length; i++) {
91                    NPVariant npvValue;
92                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
93                    if (NPVARIANT_IS_STRING(npvValue)) {
94                        NPString str = NPVARIANT_TO_STRING(npvValue);
95                        env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray), i, env->NewStringUTF(str.UTF8Characters));
96                    }
97                }
98
99                env->DeleteLocalRef(stringClass);
100            } else if (!strcmp(javaClassName.data(), "[B")) {
101                // array of bytes
102                javaArray = env->NewByteArray(length);
103                // Now iterate over each element and add to the array.
104                for (jsize i = 0; i < length; i++) {
105                    NPVariant npvValue;
106                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
107                    jbyte bVal = 0;
108                    if (NPVARIANT_IS_INT32(npvValue))
109                        bVal = static_cast<jbyte>(NPVARIANT_TO_INT32(npvValue));
110                    else if (NPVARIANT_IS_DOUBLE(npvValue))
111                        bVal = static_cast<jbyte>(NPVARIANT_TO_DOUBLE(npvValue));
112                    env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray), i, 1, &bVal);
113                }
114            } else if (!strcmp(javaClassName.data(), "[C")) {
115                // array of chars
116                javaArray = env->NewCharArray(length);
117                // Now iterate over each element and add to the array.
118                for (jsize i = 0; i < length; i++) {
119                    NPVariant npvValue;
120                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
121                    jchar cVal = 0;
122                    if (NPVARIANT_IS_INT32(npvValue))
123                        cVal = static_cast<jchar>(NPVARIANT_TO_INT32(npvValue));
124                    env->SetCharArrayRegion(static_cast<jcharArray>(javaArray), i, 1, &cVal);
125                }
126            } else if (!strcmp(javaClassName.data(), "[D")) {
127                // array of doubles
128                javaArray = env->NewDoubleArray(length);
129                // Now iterate over each element and add to the array.
130                for (jsize i = 0; i < length; i++) {
131                    NPVariant npvValue;
132                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
133                    if (NPVARIANT_IS_DOUBLE(npvValue)) {
134                        jdouble dVal = NPVARIANT_TO_DOUBLE(npvValue);
135                        env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray), i, 1, &dVal);
136                    }
137                }
138            } else if (!strcmp(javaClassName.data(), "[F")) {
139                // array of floats
140                javaArray = env->NewFloatArray(length);
141                // Now iterate over each element and add to the array.
142                for (jsize i = 0; i < length; i++) {
143                    NPVariant npvValue;
144                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
145                    if (NPVARIANT_IS_DOUBLE(npvValue)) {
146                        jfloat fVal = static_cast<jfloat>(NPVARIANT_TO_DOUBLE(npvValue));
147                        env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray), i, 1, &fVal);
148                    }
149                }
150            } else if (!strcmp(javaClassName.data(), "[I")) {
151                // array of ints
152                javaArray = env->NewIntArray(length);
153                // Now iterate over each element and add to the array.
154                for (jsize i = 0; i < length; i++) {
155                    NPVariant npvValue;
156                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
157                    jint iVal = 0;
158                    if (NPVARIANT_IS_INT32(npvValue))
159                        iVal = NPVARIANT_TO_INT32(npvValue);
160                    else if (NPVARIANT_IS_DOUBLE(npvValue))
161                        iVal = static_cast<jint>(NPVARIANT_TO_DOUBLE(npvValue));
162                    env->SetIntArrayRegion(static_cast<jintArray>(javaArray), i, 1, &iVal);
163                }
164            } else if (!strcmp(javaClassName.data(), "[J")) {
165                // array of longs
166                javaArray = env->NewLongArray(length);
167                // Now iterate over each element and add to the array.
168                for (jsize i = 0; i < length; i++) {
169                    NPVariant npvValue;
170                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
171                    jlong jVal = 0;
172                    if (NPVARIANT_IS_INT32(npvValue))
173                        jVal = static_cast<jlong>(NPVARIANT_TO_INT32(npvValue));
174                    else if (NPVARIANT_IS_DOUBLE(npvValue))
175                        jVal = static_cast<jlong>(NPVARIANT_TO_DOUBLE(npvValue));
176                    env->SetLongArrayRegion(static_cast<jlongArray>(javaArray), i, 1, &jVal);
177                }
178            } else if (!strcmp(javaClassName.data(), "[S")) {
179                // array of shorts
180                javaArray = env->NewShortArray(length);
181                // Now iterate over each element and add to the array.
182                for (jsize i = 0; i < length; i++) {
183                    NPVariant npvValue;
184                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
185                    jshort sVal = 0;
186                    if (NPVARIANT_IS_INT32(npvValue))
187                        sVal = static_cast<jshort>(NPVARIANT_TO_INT32(npvValue));
188                    else if (NPVARIANT_IS_DOUBLE(npvValue))
189                        sVal = static_cast<jshort>(NPVARIANT_TO_DOUBLE(npvValue));
190                    env->SetShortArrayRegion(static_cast<jshortArray>(javaArray), i, 1, &sVal);
191                }
192            } else if (!strcmp(javaClassName.data(), "[Z")) {
193                // array of booleans
194                javaArray = env->NewBooleanArray(length);
195                // Now iterate over each element and add to the array.
196                for (jsize i = 0; i < length; i++) {
197                    NPVariant npvValue;
198                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
199                    if (NPVARIANT_IS_BOOLEAN(npvValue)) {
200                        jboolean zVal = NPVARIANT_TO_BOOLEAN(npvValue);
201                        env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray), i, 1, &zVal);
202                    }
203                }
204            } else {
205                // JSC sends null for an array that is not an array of strings or basic types.
206                break;
207            }
208
209            result.m_objectValue = adoptRef(new JavaInstanceJobject(javaArray, false));
210            env->DeleteLocalRef(javaArray);
211        }
212        break;
213#endif // PLATFORM(ANDROID)
214
215    case JavaTypeObject:
216        {
217            // See if we have a Java instance.
218            if (type == NPVariantType_Object) {
219                NPObject* objectImp = NPVARIANT_TO_OBJECT(value);
220                result.m_objectValue = ExtractJavaInstance(objectImp);
221            }
222        }
223        break;
224
225    case JavaTypeString:
226        {
227#ifdef CONVERT_NULL_TO_EMPTY_STRING
228            if (type == NPVariantType_Null) {
229                result.m_type = JavaTypeString;
230                result.m_stringValue = String::fromUTF8("");
231            } else
232#else
233            if (type == NPVariantType_String)
234#endif
235            {
236                NPString src = NPVARIANT_TO_STRING(value);
237                result.m_type = JavaTypeString;
238                result.m_stringValue = String::fromUTF8(src.UTF8Characters);
239            }
240#if PLATFORM(ANDROID)
241            else if (type == NPVariantType_Int32) {
242                result.m_type = JavaTypeString;
243                result.m_stringValue = String::number(NPVARIANT_TO_INT32(value));
244            } else if (type == NPVariantType_Bool) {
245                result.m_type = JavaTypeString;
246                result.m_stringValue = NPVARIANT_TO_BOOLEAN(value) ? "true" : "false";
247            } else if (type == NPVariantType_Double) {
248                result.m_type = JavaTypeString;
249                result.m_stringValue = String::number(NPVARIANT_TO_DOUBLE(value));
250            } else if (!NPVARIANT_IS_NULL(value)) {
251                result.m_type = JavaTypeString;
252                result.m_stringValue = "undefined";
253            }
254#endif // PLATFORM(ANDROID)
255        }
256        break;
257
258    case JavaTypeBoolean:
259        {
260            if (type == NPVariantType_Bool)
261                result.m_booleanValue = NPVARIANT_TO_BOOLEAN(value);
262        }
263        break;
264
265    case JavaTypeByte:
266        {
267            if (type == NPVariantType_Int32)
268                result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_INT32(value));
269            else if (type == NPVariantType_Double)
270                result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_DOUBLE(value));
271        }
272        break;
273
274    case JavaTypeChar:
275        {
276            if (type == NPVariantType_Int32)
277                result.m_charValue = static_cast<unsigned short>(NPVARIANT_TO_INT32(value));
278        }
279        break;
280
281    case JavaTypeShort:
282        {
283            if (type == NPVariantType_Int32)
284                result.m_shortValue = static_cast<short>(NPVARIANT_TO_INT32(value));
285            else if (type == NPVariantType_Double)
286                result.m_shortValue = static_cast<short>(NPVARIANT_TO_DOUBLE(value));
287        }
288        break;
289
290    case JavaTypeInt:
291        {
292            if (type == NPVariantType_Int32)
293                result.m_intValue = static_cast<int>(NPVARIANT_TO_INT32(value));
294            else if (type == NPVariantType_Double)
295                result.m_intValue = static_cast<int>(NPVARIANT_TO_DOUBLE(value));
296        }
297        break;
298
299    case JavaTypeLong:
300        {
301            if (type == NPVariantType_Int32)
302                result.m_longValue = static_cast<long long>(NPVARIANT_TO_INT32(value));
303            else if (type == NPVariantType_Double)
304                result.m_longValue = static_cast<long long>(NPVARIANT_TO_DOUBLE(value));
305        }
306        break;
307
308    case JavaTypeFloat:
309        {
310            if (type == NPVariantType_Int32)
311                result.m_floatValue = static_cast<float>(NPVARIANT_TO_INT32(value));
312            else if (type == NPVariantType_Double)
313                result.m_floatValue = static_cast<float>(NPVARIANT_TO_DOUBLE(value));
314        }
315        break;
316
317    case JavaTypeDouble:
318        {
319            if (type == NPVariantType_Int32)
320                result.m_doubleValue = static_cast<double>(NPVARIANT_TO_INT32(value));
321            else if (type == NPVariantType_Double)
322                result.m_doubleValue = static_cast<double>(NPVARIANT_TO_DOUBLE(value));
323        }
324        break;
325    default:
326        break;
327    }
328    return result;
329}
330
331
332void convertJavaValueToNPVariant(JavaValue value, NPVariant* result)
333{
334    switch (value.m_type) {
335    case JavaTypeVoid:
336        {
337            VOID_TO_NPVARIANT(*result);
338        }
339        break;
340
341    case JavaTypeObject:
342        {
343            // If the JavaValue is a String object, it should have type JavaTypeString.
344            if (value.m_objectValue)
345                OBJECT_TO_NPVARIANT(JavaInstanceToNPObject(value.m_objectValue.get()), *result);
346            else
347                VOID_TO_NPVARIANT(*result);
348        }
349        break;
350
351    case JavaTypeString:
352        {
353#if PLATFORM(ANDROID)
354            // This entire file will likely be removed usptream soon.
355            if (value.m_stringValue.isNull()) {
356                VOID_TO_NPVARIANT(*result);
357                break;
358            }
359#endif
360            const char* utf8String = strdup(value.m_stringValue.utf8().data());
361            // The copied string is freed in NPN_ReleaseVariantValue (see npruntime.cpp)
362            STRINGZ_TO_NPVARIANT(utf8String, *result);
363        }
364        break;
365
366    case JavaTypeBoolean:
367        {
368            BOOLEAN_TO_NPVARIANT(value.m_booleanValue, *result);
369        }
370        break;
371
372    case JavaTypeByte:
373        {
374            INT32_TO_NPVARIANT(value.m_byteValue, *result);
375        }
376        break;
377
378    case JavaTypeChar:
379        {
380            INT32_TO_NPVARIANT(value.m_charValue, *result);
381        }
382        break;
383
384    case JavaTypeShort:
385        {
386            INT32_TO_NPVARIANT(value.m_shortValue, *result);
387        }
388        break;
389
390    case JavaTypeInt:
391        {
392            INT32_TO_NPVARIANT(value.m_intValue, *result);
393        }
394        break;
395
396        // TODO: Check if cast to double is needed.
397    case JavaTypeLong:
398        {
399            DOUBLE_TO_NPVARIANT(value.m_longValue, *result);
400        }
401        break;
402
403    case JavaTypeFloat:
404        {
405            DOUBLE_TO_NPVARIANT(value.m_floatValue, *result);
406        }
407        break;
408
409    case JavaTypeDouble:
410        {
411            DOUBLE_TO_NPVARIANT(value.m_doubleValue, *result);
412        }
413        break;
414
415    case JavaTypeInvalid:
416    default:
417        {
418            VOID_TO_NPVARIANT(*result);
419        }
420        break;
421    }
422}
423
424#if PLATFORM(ANDROID)
425JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type, bool requireAnnotation)
426#else
427JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type)
428#endif
429{
430    JavaValue result;
431    result.m_type = type;
432    switch (result.m_type) {
433    case JavaTypeVoid:
434        break;
435    case JavaTypeObject:
436#if PLATFORM(ANDROID)
437        result.m_objectValue = new JavaInstanceJobject(value.l, requireAnnotation);
438#else
439        result.m_objectValue = new JavaInstanceJobject(value.l);
440#endif
441        break;
442    case JavaTypeString:
443        {
444            jstring javaString = static_cast<jstring>(value.l);
445            if (!javaString) {
446                // result.m_stringValue is null by default
447                break;
448            }
449            const UChar* characters = getUCharactersFromJStringInEnv(getJNIEnv(), javaString);
450            // We take a copy to allow the Java String to be released.
451            result.m_stringValue = String(characters, getJNIEnv()->GetStringLength(javaString));
452            releaseUCharactersForJStringInEnv(getJNIEnv(), javaString, characters);
453        }
454        break;
455    case JavaTypeBoolean:
456        result.m_booleanValue = value.z == JNI_FALSE ? false : true;
457        break;
458    case JavaTypeByte:
459        result.m_byteValue = value.b;
460        break;
461    case JavaTypeChar:
462        result.m_charValue = value.c;
463        break;
464    case JavaTypeShort:
465        result.m_shortValue = value.s;
466        break;
467    case JavaTypeInt:
468        result.m_intValue = value.i;
469        break;
470    case JavaTypeLong:
471        result.m_longValue = value.j;
472        break;
473    case JavaTypeFloat:
474        result.m_floatValue = value.f;
475        break;
476    case JavaTypeDouble:
477        result.m_doubleValue = value.d;
478        break;
479    default:
480        ASSERT(false);
481    }
482    return result;
483}
484
485jvalue javaValueToJvalue(const JavaValue& value)
486{
487    jvalue result;
488    memset(&result, 0, sizeof(jvalue));
489    switch (value.m_type) {
490    case JavaTypeVoid:
491        break;
492#if PLATFORM(ANDROID)
493    case JavaTypeArray:
494#endif
495    case JavaTypeObject:
496        if (value.m_objectValue) {
497            // This method is used only by JavaInstanceJobject, so we know the
498            // derived type of the object.
499            result.l = static_cast<JavaInstanceJobject*>(value.m_objectValue.get())->javaInstance();
500        }
501        break;
502    case JavaTypeString:
503        // This creates a local reference to a new String object, which will
504        // be released when the call stack returns to Java. Note that this
505        // may cause leaks if invoked from a native message loop, as is the
506        // case in workers.
507        if (value.m_stringValue.isNull()) {
508            // result.l is null by default.
509            break;
510        }
511        result.l = getJNIEnv()->NewString(value.m_stringValue.characters(), value.m_stringValue.length());
512        break;
513    case JavaTypeBoolean:
514        result.z = value.m_booleanValue ? JNI_TRUE : JNI_FALSE;
515        break;
516    case JavaTypeByte:
517        result.b = value.m_byteValue;
518        break;
519    case JavaTypeChar:
520        result.c = value.m_charValue;
521        break;
522    case JavaTypeShort:
523        result.s = value.m_shortValue;
524        break;
525    case JavaTypeInt:
526        result.i = value.m_intValue;
527        break;
528    case JavaTypeLong:
529        result.j = value.m_longValue;
530        break;
531    case JavaTypeFloat:
532        result.f = value.m_floatValue;
533        break;
534    case JavaTypeDouble:
535        result.d = value.m_doubleValue;
536        break;
537    default:
538        ASSERT(false);
539    }
540    return result;
541}
542
543} // namespace Bindings
544
545} // namespace JSC
546
547#endif // ENABLE(JAVA_BRIDGE)
548