JNIUtilityPrivate.cpp revision e3edcfcfc731bd5051947d8c0a4b2685e7cae84d
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            jsize length = 0;
76            if (NPVARIANT_IS_INT32(npvLength))
77                length = static_cast<jsize>(NPVARIANT_TO_INT32(npvLength));
78            else if (NPVARIANT_IS_DOUBLE(npvLength))
79                length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(npvLength));
80
81            if (!strcmp(javaClassName.data(), "[Ljava.lang.String;")) {
82                // Match JSC behavior by only allowing Object arrays if they are Strings.
83                jclass stringClass = env->FindClass("java/lang/String");
84                javaArray = env->NewObjectArray(length, stringClass, 0);
85                for (jsize i = 0; i < length; i++) {
86                    NPVariant npvValue;
87                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
88                    if(NPVARIANT_IS_STRING(npvValue)) {
89                        NPString str = NPVARIANT_TO_STRING(npvValue);
90                        env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray), i, env->NewStringUTF(str.UTF8Characters));
91                    }
92                }
93
94                env->DeleteLocalRef(stringClass);
95            } else if (!strcmp(javaClassName.data(), "[B")) {
96                // array of bytes
97                javaArray = env->NewByteArray(length);
98                // Now iterate over each element and add to the array.
99                for (jsize i = 0; i < length; i++) {
100                    NPVariant npvValue;
101                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
102                    jbyte bVal = 0;
103                    if (NPVARIANT_IS_INT32(npvValue)) {
104                        bVal = static_cast<jbyte>(NPVARIANT_TO_INT32(npvValue));
105                    } else if (NPVARIANT_IS_DOUBLE(npvValue)) {
106                        bVal = static_cast<jbyte>(NPVARIANT_TO_DOUBLE(npvValue));
107                    }
108                    env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray), i, 1, &bVal);
109                }
110            } else if (!strcmp(javaClassName.data(), "[C")) {
111                // array of chars
112                javaArray = env->NewCharArray(length);
113                // Now iterate over each element and add to the array.
114                for (jsize i = 0; i < length; i++) {
115                    NPVariant npvValue;
116                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
117                    jchar cVal = 0;
118                    if (NPVARIANT_IS_INT32(npvValue)) {
119                        cVal = static_cast<jchar>(NPVARIANT_TO_INT32(npvValue));
120                    } else if (NPVARIANT_IS_STRING(npvValue)) {
121                        NPString str = NPVARIANT_TO_STRING(npvValue);
122                        cVal = str.UTF8Characters[0];
123                    }
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                    }
163                    env->SetIntArrayRegion(static_cast<jintArray>(javaArray), i, 1, &iVal);
164                }
165            } else if (!strcmp(javaClassName.data(), "[J")) {
166                // array of longs
167                javaArray = env->NewLongArray(length);
168                // Now iterate over each element and add to the array.
169                for (jsize i = 0; i < length; i++) {
170                    NPVariant npvValue;
171                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
172                    jlong jVal = 0;
173                    if (NPVARIANT_IS_INT32(npvValue)) {
174                        jVal = static_cast<jlong>(NPVARIANT_TO_INT32(npvValue));
175                    } else if (NPVARIANT_IS_DOUBLE(npvValue)) {
176                        jVal = static_cast<jlong>(NPVARIANT_TO_DOUBLE(npvValue));
177                    }
178                    env->SetLongArrayRegion(static_cast<jlongArray>(javaArray), i, 1, &jVal);
179                }
180            } else if (!strcmp(javaClassName.data(), "[S")) {
181                // array of shorts
182                javaArray = env->NewShortArray(length);
183                // Now iterate over each element and add to the array.
184                for (jsize i = 0; i < length; i++) {
185                    NPVariant npvValue;
186                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
187                    jshort sVal = 0;
188                    if (NPVARIANT_IS_INT32(npvValue)) {
189                        sVal = static_cast<jshort>(NPVARIANT_TO_INT32(npvValue));
190                    } else if (NPVARIANT_IS_DOUBLE(npvValue)) {
191                        sVal = static_cast<jshort>(NPVARIANT_TO_DOUBLE(npvValue));
192                    }
193                    env->SetShortArrayRegion(static_cast<jshortArray>(javaArray), i, 1, &sVal);
194                }
195            } else if (!strcmp(javaClassName.data(), "[Z")) {
196                // array of booleans
197                javaArray = env->NewBooleanArray(length);
198                // Now iterate over each element and add to the array.
199                for (jsize i = 0; i < length; i++) {
200                    NPVariant npvValue;
201                    _NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
202                    if (NPVARIANT_IS_BOOLEAN(npvValue)) {
203                        jboolean zVal = NPVARIANT_TO_BOOLEAN(npvValue);
204                        env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray), i, 1, &zVal);
205                    }
206                }
207            } else {
208                // JSC sends null for an array that is not an array of strings or basic types.
209                break;
210            }
211
212            result.m_objectValue = adoptRef(new JavaInstanceJobject(javaArray));
213            env->DeleteLocalRef(javaArray);
214        }
215        break;
216#endif // PLATFORM(ANDROID)
217
218    case JavaTypeObject:
219        {
220            // See if we have a Java instance.
221            if (type == NPVariantType_Object) {
222                NPObject* objectImp = NPVARIANT_TO_OBJECT(value);
223                result.m_objectValue = ExtractJavaInstance(objectImp);
224            }
225        }
226        break;
227
228    case JavaTypeString:
229        {
230#ifdef CONVERT_NULL_TO_EMPTY_STRING
231            if (type == NPVariantType_Null) {
232                result.m_type = JavaTypeString;
233                result.m_stringValue = String::fromUTF8("");
234            } else
235#else
236            if (type == NPVariantType_String)
237#endif
238            {
239                NPString src = NPVARIANT_TO_STRING(value);
240                result.m_type = JavaTypeString;
241                result.m_stringValue = String::fromUTF8(src.UTF8Characters);
242            }
243#if PLATFORM(ANDROID)
244            else if (type == NPVariantType_Int32) {
245                result.m_type = JavaTypeString;
246                result.m_stringValue = String::number(NPVARIANT_TO_INT32(value));
247            } else if (type == NPVariantType_Bool) {
248                result.m_type = JavaTypeString;
249                result.m_stringValue = NPVARIANT_TO_BOOLEAN(value) ? "true" : "false";
250            } else if (type == NPVariantType_Double) {
251                result.m_type = JavaTypeString;
252                result.m_stringValue = String::number(NPVARIANT_TO_DOUBLE(value));
253            } else if (!NPVARIANT_IS_NULL(value)) {
254                result.m_type = JavaTypeString;
255                result.m_stringValue = "undefined";
256            }
257#endif // PLATFORM(ANDROID)
258        }
259        break;
260
261    case JavaTypeBoolean:
262        {
263            if (type == NPVariantType_Bool)
264                result.m_booleanValue = NPVARIANT_TO_BOOLEAN(value);
265        }
266        break;
267
268    case JavaTypeByte:
269        {
270            if (type == NPVariantType_Int32)
271                result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_INT32(value));
272            else if (type == NPVariantType_Double)
273                result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_DOUBLE(value));
274        }
275        break;
276
277    case JavaTypeChar:
278        {
279            if (type == NPVariantType_Int32)
280                result.m_charValue = static_cast<unsigned short>(NPVARIANT_TO_INT32(value));
281        }
282        break;
283
284    case JavaTypeShort:
285        {
286            if (type == NPVariantType_Int32)
287                result.m_shortValue = static_cast<short>(NPVARIANT_TO_INT32(value));
288            else if (type == NPVariantType_Double)
289                result.m_shortValue = static_cast<short>(NPVARIANT_TO_DOUBLE(value));
290        }
291        break;
292
293    case JavaTypeInt:
294        {
295            if (type == NPVariantType_Int32)
296                result.m_intValue = static_cast<int>(NPVARIANT_TO_INT32(value));
297            else if (type == NPVariantType_Double)
298                result.m_intValue = static_cast<int>(NPVARIANT_TO_DOUBLE(value));
299        }
300        break;
301
302    case JavaTypeLong:
303        {
304            if (type == NPVariantType_Int32)
305                result.m_longValue = static_cast<long long>(NPVARIANT_TO_INT32(value));
306            else if (type == NPVariantType_Double)
307                result.m_longValue = static_cast<long long>(NPVARIANT_TO_DOUBLE(value));
308        }
309        break;
310
311    case JavaTypeFloat:
312        {
313            if (type == NPVariantType_Int32)
314                result.m_floatValue = static_cast<float>(NPVARIANT_TO_INT32(value));
315            else if (type == NPVariantType_Double)
316                result.m_floatValue = static_cast<float>(NPVARIANT_TO_DOUBLE(value));
317        }
318        break;
319
320    case JavaTypeDouble:
321        {
322            if (type == NPVariantType_Int32)
323                result.m_doubleValue = static_cast<double>(NPVARIANT_TO_INT32(value));
324            else if (type == NPVariantType_Double)
325                result.m_doubleValue = static_cast<double>(NPVARIANT_TO_DOUBLE(value));
326        }
327        break;
328    default:
329        break;
330    }
331    return result;
332}
333
334
335void convertJavaValueToNPVariant(JavaValue value, NPVariant* result)
336{
337    switch (value.m_type) {
338    case JavaTypeVoid:
339        {
340            VOID_TO_NPVARIANT(*result);
341        }
342        break;
343
344    case JavaTypeObject:
345        {
346            // If the JavaValue is a String object, it should have type JavaTypeString.
347            if (value.m_objectValue)
348                OBJECT_TO_NPVARIANT(JavaInstanceToNPObject(value.m_objectValue.get()), *result);
349            else
350                VOID_TO_NPVARIANT(*result);
351        }
352        break;
353
354    case JavaTypeString:
355        {
356#if PLATFORM(ANDROID)
357            // This entire file will likely be removed usptream soon.
358            if (value.m_stringValue.isNull()) {
359                VOID_TO_NPVARIANT(*result);
360                break;
361            }
362#endif
363            const char* utf8String = strdup(value.m_stringValue.utf8().data());
364            // The copied string is freed in NPN_ReleaseVariantValue (see npruntime.cpp)
365            STRINGZ_TO_NPVARIANT(utf8String, *result);
366        }
367        break;
368
369    case JavaTypeBoolean:
370        {
371            BOOLEAN_TO_NPVARIANT(value.m_booleanValue, *result);
372        }
373        break;
374
375    case JavaTypeByte:
376        {
377            INT32_TO_NPVARIANT(value.m_byteValue, *result);
378        }
379        break;
380
381    case JavaTypeChar:
382        {
383            INT32_TO_NPVARIANT(value.m_charValue, *result);
384        }
385        break;
386
387    case JavaTypeShort:
388        {
389            INT32_TO_NPVARIANT(value.m_shortValue, *result);
390        }
391        break;
392
393    case JavaTypeInt:
394        {
395            INT32_TO_NPVARIANT(value.m_intValue, *result);
396        }
397        break;
398
399        // TODO: Check if cast to double is needed.
400    case JavaTypeLong:
401        {
402            DOUBLE_TO_NPVARIANT(value.m_longValue, *result);
403        }
404        break;
405
406    case JavaTypeFloat:
407        {
408            DOUBLE_TO_NPVARIANT(value.m_floatValue, *result);
409        }
410        break;
411
412    case JavaTypeDouble:
413        {
414            DOUBLE_TO_NPVARIANT(value.m_doubleValue, *result);
415        }
416        break;
417
418    case JavaTypeInvalid:
419    default:
420        {
421            VOID_TO_NPVARIANT(*result);
422        }
423        break;
424    }
425}
426
427JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type)
428{
429    JavaValue result;
430    result.m_type = type;
431    switch (result.m_type) {
432    case JavaTypeVoid:
433        break;
434    case JavaTypeObject:
435        result.m_objectValue = new JavaInstanceJobject(value.l);
436        break;
437    case JavaTypeString:
438        {
439            jstring javaString = static_cast<jstring>(value.l);
440            if (!javaString) {
441                // result.m_stringValue is null by default
442                break;
443            }
444            const UChar* characters = getUCharactersFromJStringInEnv(getJNIEnv(), javaString);
445            // We take a copy to allow the Java String to be released.
446            result.m_stringValue = String(characters, getJNIEnv()->GetStringLength(javaString));
447            releaseUCharactersForJStringInEnv(getJNIEnv(), javaString, characters);
448        }
449        break;
450    case JavaTypeBoolean:
451        result.m_booleanValue = value.z == JNI_FALSE ? false : true;
452        break;
453    case JavaTypeByte:
454        result.m_byteValue = value.b;
455        break;
456    case JavaTypeChar:
457        result.m_charValue = value.c;
458        break;
459    case JavaTypeShort:
460        result.m_shortValue = value.s;
461        break;
462    case JavaTypeInt:
463        result.m_intValue = value.i;
464        break;
465    case JavaTypeLong:
466        result.m_longValue = value.j;
467        break;
468    case JavaTypeFloat:
469        result.m_floatValue = value.f;
470        break;
471    case JavaTypeDouble:
472        result.m_doubleValue = value.d;
473        break;
474    default:
475        ASSERT(false);
476    }
477    return result;
478}
479
480jvalue javaValueToJvalue(const JavaValue& value)
481{
482    jvalue result;
483    memset(&result, 0, sizeof(jvalue));
484    switch (value.m_type) {
485    case JavaTypeVoid:
486        break;
487#if PLATFORM(ANDROID)
488    case JavaTypeArray:
489#endif
490    case JavaTypeObject:
491        if (value.m_objectValue) {
492            // This method is used only by JavaInstanceJobject, so we know the
493            // derived type of the object.
494            result.l = static_cast<JavaInstanceJobject*>(value.m_objectValue.get())->javaInstance();
495        }
496        break;
497    case JavaTypeString:
498        // This creates a local reference to a new String object, which will
499        // be released when the call stack returns to Java. Note that this
500        // may cause leaks if invoked from a native message loop, as is the
501        // case in workers.
502        if (value.m_stringValue.isNull()) {
503            // result.l is null by default.
504            break;
505        }
506        result.l = getJNIEnv()->NewString(value.m_stringValue.characters(), value.m_stringValue.length());
507        break;
508    case JavaTypeBoolean:
509        result.z = value.m_booleanValue ? JNI_TRUE : JNI_FALSE;
510        break;
511    case JavaTypeByte:
512        result.b = value.m_byteValue;
513        break;
514    case JavaTypeChar:
515        result.c = value.m_charValue;
516        break;
517    case JavaTypeShort:
518        result.s = value.m_shortValue;
519        break;
520    case JavaTypeInt:
521        result.i = value.m_intValue;
522        break;
523    case JavaTypeLong:
524        result.j = value.m_longValue;
525        break;
526    case JavaTypeFloat:
527        result.f = value.m_floatValue;
528        break;
529    case JavaTypeDouble:
530        result.d = value.m_doubleValue;
531        break;
532    default:
533        ASSERT(false);
534    }
535    return result;
536}
537
538} // namespace Bindings
539
540} // namespace JSC
541
542#endif // ENABLE(JAVA_BRIDGE)
543