blob: c6e0a6c11064330b4b98d15cecaf3121cd0b50f1 [file] [log] [blame]
/*
* Copyright 2010, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "JNIUtilityPrivate.h"
#if ENABLE(JAVA_BRIDGE)
#include "JavaInstanceJobjectV8.h"
#include "JavaNPObjectV8.h"
#if PLATFORM(ANDROID)
#include "npruntime_impl.h"
#endif // PLATFORM(ANDROID)
#include "JavaValueV8.h"
#include <wtf/text/CString.h>
namespace JSC {
namespace Bindings {
JavaValue convertNPVariantToJavaValue(NPVariant value, const String& javaClass)
{
CString javaClassName = javaClass.utf8();
JavaType javaType = javaTypeFromClassName(javaClassName.data());
JavaValue result;
result.m_type = javaType;
NPVariantType type = value.type;
switch (javaType) {
case JavaTypeArray:
#if PLATFORM(ANDROID)
// If we're converting to an array, see if the NPVariant has a length
// property. If so, create a JNI array of the relevant length and to get
// the elements of the NPVariant. When we interpret the JavaValue later,
// we know that the array is represented as a JNI array.
//
// FIXME: This is a hack. We should not be using JNI here. We should
// represent the JavaValue without JNI.
{
JNIEnv* env = getJNIEnv();
jobject javaArray;
NPObject* object = NPVARIANT_IS_OBJECT(value) ? NPVARIANT_TO_OBJECT(value) : 0;
NPVariant npvLength;
bool success = _NPN_GetProperty(0, object, _NPN_GetStringIdentifier("length"), &npvLength);
if (!success) {
// No length property so we don't know how many elements to put into the array.
// Treat this as an error.
// JSC sends null for an array that is not an array of strings or basic types,
// do this also in the unknown length case.
break;
}
// Convert to null if the length property is not a number.
if (!NPVARIANT_IS_INT32(npvLength) && !NPVARIANT_IS_DOUBLE(npvLength))
break;
// Convert to null if the length property is out of bounds.
double doubleLength = NPVARIANT_IS_INT32(npvLength) ? NPVARIANT_TO_INT32(npvLength) : NPVARIANT_TO_DOUBLE(npvLength);
if (doubleLength < 0.0 || doubleLength > INT32_MAX)
break;
jsize length = static_cast<jsize>(doubleLength);
if (!strcmp(javaClassName.data(), "[Ljava.lang.String;")) {
// Match JSC behavior by only allowing Object arrays if they are Strings.
jclass stringClass = env->FindClass("java/lang/String");
javaArray = env->NewObjectArray(length, stringClass, 0);
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
if (NPVARIANT_IS_STRING(npvValue)) {
NPString str = NPVARIANT_TO_STRING(npvValue);
env->SetObjectArrayElement(static_cast<jobjectArray>(javaArray), i, env->NewStringUTF(str.UTF8Characters));
}
}
env->DeleteLocalRef(stringClass);
} else if (!strcmp(javaClassName.data(), "[B")) {
// array of bytes
javaArray = env->NewByteArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
jbyte bVal = 0;
if (NPVARIANT_IS_INT32(npvValue))
bVal = static_cast<jbyte>(NPVARIANT_TO_INT32(npvValue));
else if (NPVARIANT_IS_DOUBLE(npvValue))
bVal = static_cast<jbyte>(NPVARIANT_TO_DOUBLE(npvValue));
env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray), i, 1, &bVal);
}
} else if (!strcmp(javaClassName.data(), "[C")) {
// array of chars
javaArray = env->NewCharArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
jchar cVal = 0;
if (NPVARIANT_IS_INT32(npvValue))
cVal = static_cast<jchar>(NPVARIANT_TO_INT32(npvValue));
env->SetCharArrayRegion(static_cast<jcharArray>(javaArray), i, 1, &cVal);
}
} else if (!strcmp(javaClassName.data(), "[D")) {
// array of doubles
javaArray = env->NewDoubleArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
if (NPVARIANT_IS_DOUBLE(npvValue)) {
jdouble dVal = NPVARIANT_TO_DOUBLE(npvValue);
env->SetDoubleArrayRegion(static_cast<jdoubleArray>(javaArray), i, 1, &dVal);
}
}
} else if (!strcmp(javaClassName.data(), "[F")) {
// array of floats
javaArray = env->NewFloatArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
if (NPVARIANT_IS_DOUBLE(npvValue)) {
jfloat fVal = static_cast<jfloat>(NPVARIANT_TO_DOUBLE(npvValue));
env->SetFloatArrayRegion(static_cast<jfloatArray>(javaArray), i, 1, &fVal);
}
}
} else if (!strcmp(javaClassName.data(), "[I")) {
// array of ints
javaArray = env->NewIntArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
jint iVal = 0;
if (NPVARIANT_IS_INT32(npvValue))
iVal = NPVARIANT_TO_INT32(npvValue);
else if (NPVARIANT_IS_DOUBLE(npvValue))
iVal = static_cast<jint>(NPVARIANT_TO_DOUBLE(npvValue));
env->SetIntArrayRegion(static_cast<jintArray>(javaArray), i, 1, &iVal);
}
} else if (!strcmp(javaClassName.data(), "[J")) {
// array of longs
javaArray = env->NewLongArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
jlong jVal = 0;
if (NPVARIANT_IS_INT32(npvValue))
jVal = static_cast<jlong>(NPVARIANT_TO_INT32(npvValue));
else if (NPVARIANT_IS_DOUBLE(npvValue))
jVal = static_cast<jlong>(NPVARIANT_TO_DOUBLE(npvValue));
env->SetLongArrayRegion(static_cast<jlongArray>(javaArray), i, 1, &jVal);
}
} else if (!strcmp(javaClassName.data(), "[S")) {
// array of shorts
javaArray = env->NewShortArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
jshort sVal = 0;
if (NPVARIANT_IS_INT32(npvValue))
sVal = static_cast<jshort>(NPVARIANT_TO_INT32(npvValue));
else if (NPVARIANT_IS_DOUBLE(npvValue))
sVal = static_cast<jshort>(NPVARIANT_TO_DOUBLE(npvValue));
env->SetShortArrayRegion(static_cast<jshortArray>(javaArray), i, 1, &sVal);
}
} else if (!strcmp(javaClassName.data(), "[Z")) {
// array of booleans
javaArray = env->NewBooleanArray(length);
// Now iterate over each element and add to the array.
for (jsize i = 0; i < length; i++) {
NPVariant npvValue;
_NPN_GetProperty(0, object, _NPN_GetIntIdentifier(i), &npvValue);
if (NPVARIANT_IS_BOOLEAN(npvValue)) {
jboolean zVal = NPVARIANT_TO_BOOLEAN(npvValue);
env->SetBooleanArrayRegion(static_cast<jbooleanArray>(javaArray), i, 1, &zVal);
}
}
} else {
// JSC sends null for an array that is not an array of strings or basic types.
break;
}
result.m_objectValue = adoptRef(new JavaInstanceJobject(javaArray, false));
env->DeleteLocalRef(javaArray);
}
break;
#endif // PLATFORM(ANDROID)
case JavaTypeObject:
{
// See if we have a Java instance.
if (type == NPVariantType_Object) {
NPObject* objectImp = NPVARIANT_TO_OBJECT(value);
result.m_objectValue = ExtractJavaInstance(objectImp);
}
}
break;
case JavaTypeString:
{
#ifdef CONVERT_NULL_TO_EMPTY_STRING
if (type == NPVariantType_Null) {
result.m_type = JavaTypeString;
result.m_stringValue = String::fromUTF8("");
} else
#else
if (type == NPVariantType_String)
#endif
{
NPString src = NPVARIANT_TO_STRING(value);
result.m_type = JavaTypeString;
result.m_stringValue = String::fromUTF8(src.UTF8Characters);
}
#if PLATFORM(ANDROID)
else if (type == NPVariantType_Int32) {
result.m_type = JavaTypeString;
result.m_stringValue = String::number(NPVARIANT_TO_INT32(value));
} else if (type == NPVariantType_Bool) {
result.m_type = JavaTypeString;
result.m_stringValue = NPVARIANT_TO_BOOLEAN(value) ? "true" : "false";
} else if (type == NPVariantType_Double) {
result.m_type = JavaTypeString;
result.m_stringValue = String::number(NPVARIANT_TO_DOUBLE(value));
} else if (!NPVARIANT_IS_NULL(value)) {
result.m_type = JavaTypeString;
result.m_stringValue = "undefined";
}
#endif // PLATFORM(ANDROID)
}
break;
case JavaTypeBoolean:
{
if (type == NPVariantType_Bool)
result.m_booleanValue = NPVARIANT_TO_BOOLEAN(value);
}
break;
case JavaTypeByte:
{
if (type == NPVariantType_Int32)
result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_byteValue = static_cast<signed char>(NPVARIANT_TO_DOUBLE(value));
}
break;
case JavaTypeChar:
{
if (type == NPVariantType_Int32)
result.m_charValue = static_cast<unsigned short>(NPVARIANT_TO_INT32(value));
}
break;
case JavaTypeShort:
{
if (type == NPVariantType_Int32)
result.m_shortValue = static_cast<short>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_shortValue = static_cast<short>(NPVARIANT_TO_DOUBLE(value));
}
break;
case JavaTypeInt:
{
if (type == NPVariantType_Int32)
result.m_intValue = static_cast<int>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_intValue = static_cast<int>(NPVARIANT_TO_DOUBLE(value));
}
break;
case JavaTypeLong:
{
if (type == NPVariantType_Int32)
result.m_longValue = static_cast<long long>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_longValue = static_cast<long long>(NPVARIANT_TO_DOUBLE(value));
}
break;
case JavaTypeFloat:
{
if (type == NPVariantType_Int32)
result.m_floatValue = static_cast<float>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_floatValue = static_cast<float>(NPVARIANT_TO_DOUBLE(value));
}
break;
case JavaTypeDouble:
{
if (type == NPVariantType_Int32)
result.m_doubleValue = static_cast<double>(NPVARIANT_TO_INT32(value));
else if (type == NPVariantType_Double)
result.m_doubleValue = static_cast<double>(NPVARIANT_TO_DOUBLE(value));
}
break;
default:
break;
}
return result;
}
void convertJavaValueToNPVariant(JavaValue value, NPVariant* result)
{
switch (value.m_type) {
case JavaTypeVoid:
{
VOID_TO_NPVARIANT(*result);
}
break;
case JavaTypeObject:
{
// If the JavaValue is a String object, it should have type JavaTypeString.
if (value.m_objectValue)
OBJECT_TO_NPVARIANT(JavaInstanceToNPObject(value.m_objectValue.get()), *result);
else
VOID_TO_NPVARIANT(*result);
}
break;
case JavaTypeString:
{
#if PLATFORM(ANDROID)
// This entire file will likely be removed usptream soon.
if (value.m_stringValue.isNull()) {
VOID_TO_NPVARIANT(*result);
break;
}
#endif
const char* utf8String = strdup(value.m_stringValue.utf8().data());
// The copied string is freed in NPN_ReleaseVariantValue (see npruntime.cpp)
STRINGZ_TO_NPVARIANT(utf8String, *result);
}
break;
case JavaTypeBoolean:
{
BOOLEAN_TO_NPVARIANT(value.m_booleanValue, *result);
}
break;
case JavaTypeByte:
{
INT32_TO_NPVARIANT(value.m_byteValue, *result);
}
break;
case JavaTypeChar:
{
INT32_TO_NPVARIANT(value.m_charValue, *result);
}
break;
case JavaTypeShort:
{
INT32_TO_NPVARIANT(value.m_shortValue, *result);
}
break;
case JavaTypeInt:
{
INT32_TO_NPVARIANT(value.m_intValue, *result);
}
break;
// TODO: Check if cast to double is needed.
case JavaTypeLong:
{
DOUBLE_TO_NPVARIANT(value.m_longValue, *result);
}
break;
case JavaTypeFloat:
{
DOUBLE_TO_NPVARIANT(value.m_floatValue, *result);
}
break;
case JavaTypeDouble:
{
DOUBLE_TO_NPVARIANT(value.m_doubleValue, *result);
}
break;
case JavaTypeInvalid:
default:
{
VOID_TO_NPVARIANT(*result);
}
break;
}
}
#if PLATFORM(ANDROID)
JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type, bool requireAnnotation)
#else
JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type)
#endif
{
JavaValue result;
result.m_type = type;
switch (result.m_type) {
case JavaTypeVoid:
break;
case JavaTypeObject:
#if PLATFORM(ANDROID)
result.m_objectValue = new JavaInstanceJobject(value.l, requireAnnotation);
#else
result.m_objectValue = new JavaInstanceJobject(value.l);
#endif
break;
case JavaTypeString:
{
jstring javaString = static_cast<jstring>(value.l);
if (!javaString) {
// result.m_stringValue is null by default
break;
}
const UChar* characters = getUCharactersFromJStringInEnv(getJNIEnv(), javaString);
// We take a copy to allow the Java String to be released.
result.m_stringValue = String(characters, getJNIEnv()->GetStringLength(javaString));
releaseUCharactersForJStringInEnv(getJNIEnv(), javaString, characters);
}
break;
case JavaTypeBoolean:
result.m_booleanValue = value.z == JNI_FALSE ? false : true;
break;
case JavaTypeByte:
result.m_byteValue = value.b;
break;
case JavaTypeChar:
result.m_charValue = value.c;
break;
case JavaTypeShort:
result.m_shortValue = value.s;
break;
case JavaTypeInt:
result.m_intValue = value.i;
break;
case JavaTypeLong:
result.m_longValue = value.j;
break;
case JavaTypeFloat:
result.m_floatValue = value.f;
break;
case JavaTypeDouble:
result.m_doubleValue = value.d;
break;
default:
ASSERT(false);
}
return result;
}
jvalue javaValueToJvalue(const JavaValue& value)
{
jvalue result;
memset(&result, 0, sizeof(jvalue));
switch (value.m_type) {
case JavaTypeVoid:
break;
#if PLATFORM(ANDROID)
case JavaTypeArray:
#endif
case JavaTypeObject:
if (value.m_objectValue) {
// This method is used only by JavaInstanceJobject, so we know the
// derived type of the object.
result.l = static_cast<JavaInstanceJobject*>(value.m_objectValue.get())->javaInstance();
}
break;
case JavaTypeString:
// This creates a local reference to a new String object, which will
// be released when the call stack returns to Java. Note that this
// may cause leaks if invoked from a native message loop, as is the
// case in workers.
if (value.m_stringValue.isNull()) {
// result.l is null by default.
break;
}
result.l = getJNIEnv()->NewString(value.m_stringValue.characters(), value.m_stringValue.length());
break;
case JavaTypeBoolean:
result.z = value.m_booleanValue ? JNI_TRUE : JNI_FALSE;
break;
case JavaTypeByte:
result.b = value.m_byteValue;
break;
case JavaTypeChar:
result.c = value.m_charValue;
break;
case JavaTypeShort:
result.s = value.m_shortValue;
break;
case JavaTypeInt:
result.i = value.m_intValue;
break;
case JavaTypeLong:
result.j = value.m_longValue;
break;
case JavaTypeFloat:
result.f = value.m_floatValue;
break;
case JavaTypeDouble:
result.d = value.m_doubleValue;
break;
default:
ASSERT(false);
}
return result;
}
} // namespace Bindings
} // namespace JSC
#endif // ENABLE(JAVA_BRIDGE)