Implement the V8 JNI bindings more completely.

Change-Id: I3b3d2bff84ee4a1039424cf1881e5d3d428127f9
diff --git a/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp b/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
index 1bdae53..46f0b2b 100644
--- a/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
+++ b/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
@@ -28,6 +28,7 @@
 
 #include "JavaInstanceV8.h"
 #include "JavaNPObjectV8.h"
+#include "npruntime_impl.h"
 
 namespace JSC {
 
@@ -40,9 +41,168 @@
 
     switch (jniType) {
     case array_type:
+        {
+            JNIEnv* env = getJNIEnv();
+            jobject javaArray;
+            NPObject* object = NPVARIANT_TO_OBJECT(value);
+            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.
+#ifdef EMULATE_JSC_BINDINGS
+                // JSC sends null for an array that is not an array of strings or basic types,
+                // do this also in the unknown length case.
+                memset(&result, 0, sizeof(jvalue));
+#else
+                // Sending NULL as JSC does seems dangerous. (Imagine the java method that asks
+                // for the length of the array it was passed). Here we send a 0 length array.
+                jclass objectArrayClass = env->FindClass("[Ljava/lang/Object;");
+                javaArray = env->NewObjectArray((jsize)0, objectArrayClass, 0);
+                env->DeleteLocalRef(objectArrayClass);
+#endif
+                break;
+            }
+
+            jsize length = static_cast<jsize>(NPVARIANT_TO_INT32(npvLength));
+
+            if (!strcmp(javaClassName, "[Ljava.lang.String;")) {
+                // Match JSC behavior by only allowing Object arrays if they are Strings.
+                jclass stringArrayClass = env->FindClass("[Ljava/lang/String;");
+                javaArray = env->NewObjectArray(length, stringArrayClass, 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(stringArrayClass);
+            } else if (!strcmp(javaClassName, "[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);
+                    if (NPVARIANT_IS_INT32(npvValue)) {
+                        jbyte bVal = static_cast<jbyte>(NPVARIANT_TO_INT32(npvValue));
+                        env->SetByteArrayRegion(static_cast<jbyteArray>(javaArray), i, 1, &bVal);
+                    }
+                }
+             } else if (!strcmp(javaClassName, "[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));
+                    } else if (NPVARIANT_IS_STRING(npvValue)) {
+                        NPString str = NPVARIANT_TO_STRING(npvValue);
+                        cVal = str.UTF8Characters[0];
+                    }
+                    env->SetCharArrayRegion(static_cast<jcharArray>(javaArray), i, 1, &cVal);
+                }
+             } else if (!strcmp(javaClassName, "[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, "[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, "[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);
+                    if (NPVARIANT_IS_INT32(npvValue)) {
+                        jint iVal = NPVARIANT_TO_INT32(npvValue);
+                        env->SetIntArrayRegion(static_cast<jintArray>(javaArray), i, 1, &iVal);
+                    }
+                }
+             } else if (!strcmp(javaClassName, "[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);
+                    if (NPVARIANT_IS_INT32(npvValue)) {
+                        jlong jVal = static_cast<jlong>(NPVARIANT_TO_INT32(npvValue));
+                        env->SetLongArrayRegion(static_cast<jlongArray>(javaArray), i, 1, &jVal);
+                    }
+                }
+             } else if (!strcmp(javaClassName, "[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);
+                    if (NPVARIANT_IS_INT32(npvValue)) {
+                        jshort sVal = static_cast<jshort>(NPVARIANT_TO_INT32(npvValue));
+                        env->SetShortArrayRegion(static_cast<jshortArray>(javaArray), i, 1, &sVal);
+                    }
+                }
+             } else if (!strcmp(javaClassName, "[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 {
+#ifdef EMULATE_JSC_BINDINGS
+                // JSC sends null for an array that is not an array of strings or basic types.
+                memset(&result, 0, sizeof(jvalue));
+                break;
+#else
+                // Sending NULL as JSC does seems dangerous. (Imagine the java method that asks
+                // for the length of the array it was passed). Here we send a 0 length array.
+                jclass objectArrayClass = env->FindClass("[Ljava/lang/Object;");
+                javaArray = env->NewObjectArray(0, objectArrayClass, 0);
+                env->DeleteLocalRef(objectArrayClass);
+#endif
+            }
+
+            result.l = javaArray;
+        }
+        break;
+
     case object_type:
         {
+            JNIEnv* env = getJNIEnv();
             result.l = static_cast<jobject>(0);
+            jobject javaString;
 
             // First see if we have a Java instance.
             if (type == NPVariantType_Object) {
@@ -56,7 +216,6 @@
             if (!result.l && !strcmp(javaClassName, "java.lang.String")) {
 #ifdef CONVERT_NULL_TO_EMPTY_STRING
                 if (type == NPVariantType_Null) {
-                    JNIEnv* env = getJNIEnv();
                     jchar buf[2];
                     jobject javaString = env->functions->NewString(env, buf, 0);
                     result.l = javaString;
@@ -66,10 +225,38 @@
 #endif
                 {
                     NPString src = NPVARIANT_TO_STRING(value);
-                    JNIEnv* env = getJNIEnv();
-                    jobject javaString = env->NewStringUTF(src.UTF8Characters);
+                    javaString = env->NewStringUTF(src.UTF8Characters);
                     result.l = javaString;
+                } else if (type == NPVariantType_Int32) {
+                    jint src = NPVARIANT_TO_INT32(value);
+                    jclass integerClass = env->FindClass("java/lang/Integer");
+                    jmethodID toString = env->GetStaticMethodID(integerClass, "toString", "(I)Ljava/lang/String;");
+                    javaString = env->CallStaticObjectMethod(integerClass, toString, src);
+                    result.l = javaString;
+                    env->DeleteLocalRef(integerClass);
+                } else if (type == NPVariantType_Bool) {
+                    jboolean src = NPVARIANT_TO_BOOLEAN(value);
+                    jclass booleanClass = env->FindClass("java/lang/Boolean");
+                    jmethodID toString = env->GetStaticMethodID(booleanClass, "toString", "(Z)Ljava/lang/String;");
+                    javaString = env->CallStaticObjectMethod(booleanClass, toString, src);
+                    result.l = javaString;
+                    env->DeleteLocalRef(booleanClass);
+                } else if (type == NPVariantType_Double) {
+                    jdouble src = NPVARIANT_TO_DOUBLE(value);
+                    jclass doubleClass = env->FindClass("java/lang/Double");
+                    jmethodID toString = env->GetStaticMethodID(doubleClass, "toString", "(D)Ljava/lang/String;");
+                    javaString = env->CallStaticObjectMethod(doubleClass, toString, src);
+                    result.l = javaString;
+                    env->DeleteLocalRef(doubleClass);
                 }
+#ifdef EMULATE_JSC_BINDINGS
+                // For the undefined value, JSC sends the String "undefined". Feels to me like we
+                // should send null in this case.
+                else if (!NPVARIANT_IS_NULL(value)) {
+                  javaString = env->NewStringUTF("undefined");
+                  result.l = javaString;
+                }
+#endif
             } else if (!result.l)
                 memset(&result, 0, sizeof(jvalue)); // Handle it the same as a void case
         }
@@ -97,6 +284,15 @@
         {
             if (type == NPVariantType_Int32)
                 result.c = static_cast<char>(NPVARIANT_TO_INT32(value));
+#ifndef EMULATE_JSC_BINDINGS
+            // There is no char type in JavaScript - just strings 1 character
+            // long. So just converting it to an int above doesn't work. Again,
+            // we emulate the behavior for now for maximum compatability.
+            else if (type == NPVariantType_String) {
+                NPString str = NPVARIANT_TO_STRING(value);
+                result.c = str.UTF8Characters[0];
+            }
+#endif
             else
                 memset(&result, 0, sizeof(jvalue));
         }
@@ -153,8 +349,6 @@
         }
         break;
 
-        break;
-
     case invalid_type:
     default:
     case void_type:
@@ -206,7 +400,17 @@
 
     case char_type:
         {
-            INT32_TO_NPVARIANT(value.c, *result);
+#ifndef EMULATE_JSC_BINDINGS
+            // There is no char type in JavaScript - just strings 1 character
+            // long. So just converting it to an int above doesn't work. Again,
+            // we emulate the behavior for now for maximum compatability.
+            if (!strcmp(javaTypeName, "char")) {
+                const char c = value.c;
+                const char* v = strndup(&c, 1);
+                STRINGZ_TO_NPVARIANT(v, *result);
+            } else
+#endif
+                INT32_TO_NPVARIANT(value.c, *result);
         }
         break;
 
diff --git a/WebCore/bridge/jni/v8/JNIUtilityPrivate.h b/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
index 951c1e8..da7a24c 100644
--- a/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
+++ b/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
@@ -29,6 +29,12 @@
 #include "JNIUtility.h"
 #include "npruntime.h"
 
+// FIXME: While fully implementing the bindings I noticed some differences between what
+// I wrote and seemed intuitive and what JSC does. Need to verify if my intuition is wrong
+// or there are bugs in the JSC bindings. For now, this macro makes the V8 bindings do the
+// same as the JSC bindings.
+#define EMULATE_JSC_BINDINGS 1
+
 namespace JSC {
 
 namespace Bindings {
diff --git a/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp b/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
index 54cb8d6..88e28d4 100644
--- a/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
+++ b/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
@@ -155,11 +155,19 @@
     if (!field)
         return false;
 
+#ifdef EMULATE_JSC_BINDINGS
+    // JSC does not seem to support returning object properties so we emulate that
+    // behaviour here.
+    jvalue value;
+#else
+    // FIXME: Note here that field->type() refers to the Java class name and NOT the
+    // JNI signature i.e. "int" as opposed to "I". This means that the field lookup
+    // will fail.
     jvalue value = getJNIField(instance->javaInstance(),
                                field->getJNIType(),
                                field->name().UTF8String(),
                                field->type());
-
+#endif
     convertJValueToNPVariant(value, field->getJNIType(), field->type(), result);
 
     return true;