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 
  42 
  43     #include "JSNode.h"
  44     #include "Node.h"
  45     #include "com_sun_webkit_dom_JSObject.h"
  46     #define JSOBJECT_CLASSNAME "com/sun/webkit/dom/JSObject"
  47 
  48 namespace JSC {
  49 
  50 namespace Bindings {
  51 
  52 static jobject convertArrayInstanceToJavaArray(ExecState* exec, JSArray* jsArray, const char* javaClassName)
  53 {
  54     JNIEnv* env = getJNIEnv();
  55     // As JS Arrays can contain a mixture of objects, assume we can convert to
  56     // the requested Java Array type requested, unless the array type is some object array
  57     // other than a string.
  58     unsigned length = jsArray->length();
  59     jobjectArray jarray = 0;
  60 
  61     // Build the correct array type
  62     switch (javaTypeFromPrimitiveType(javaClassName[1])) {
  63     case JavaTypeObject:
  64             {
  65             // Only support string object types
  66             if (!strcmp("[Ljava.lang.String;", javaClassName)) {
  67                 jarray = (jobjectArray)env->NewObjectArray(length,
  68                     env->FindClass("java/lang/String"),
  69                     env->NewStringUTF(""));
  70                 for (unsigned i = 0; i < length; i++) {
  71                     JSValue item = jsArray->get(exec, i);
  72                     String stringValue = item.toString(exec)->value(exec);
  73                     env->SetObjectArrayElement(jarray, i,
  74                         env->functions->NewString(env, (const jchar *)stringValue.deprecatedCharacters(), stringValue.length()));
  75                 }
  76             }
  77             break;
  78         }
  79 
  80     case JavaTypeBoolean:
  81         {
  82             jarray = (jobjectArray)env->NewBooleanArray(length);
  83             for (unsigned i = 0; i < length; i++) {
  84                 JSValue item = jsArray->get(exec, i);
  85                 jboolean value = (jboolean)item.toNumber(exec);
  86                 env->SetBooleanArrayRegion((jbooleanArray)jarray, (jsize)i, (jsize)1, &value);
  87             }
  88             break;
  89         }
  90 
  91     case JavaTypeByte:
  92         {
  93             jarray = (jobjectArray)env->NewByteArray(length);
  94             for (unsigned i = 0; i < length; i++) {
  95                 JSValue item = jsArray->get(exec, i);
  96                 jbyte value = (jbyte)item.toNumber(exec);
  97                 env->SetByteArrayRegion((jbyteArray)jarray, (jsize)i, (jsize)1, &value);
  98             }
  99             break;
 100         }
 101 
 102     case JavaTypeChar:
 103         {
 104             jarray = (jobjectArray)env->NewCharArray(length);
 105             for (unsigned i = 0; i < length; i++) {
 106                 JSValue item = jsArray->get(exec, i);
 107                 String stringValue = item.toString(exec)->value(exec);
 108                 jchar value = 0;
 109                 if (stringValue.length() > 0)
 110                     value = ((const jchar*)stringValue.deprecatedCharacters())[0];
 111                 env->SetCharArrayRegion((jcharArray)jarray, (jsize)i, (jsize)1, &value);
 112             }
 113             break;
 114         }
 115 
 116     case JavaTypeShort:
 117         {
 118             jarray = (jobjectArray)env->NewShortArray(length);
 119             for (unsigned i = 0; i < length; i++) {
 120                 JSValue item = jsArray->get(exec, i);
 121                 jshort value = (jshort)item.toNumber(exec);
 122                 env->SetShortArrayRegion((jshortArray)jarray, (jsize)i, (jsize)1, &value);
 123             }
 124             break;
 125         }
 126 
 127     case JavaTypeInt:
 128         {
 129             jarray = (jobjectArray)env->NewIntArray(length);
 130             for (unsigned i = 0; i < length; i++) {
 131                 JSValue item = jsArray->get(exec, i);
 132                 jint value = (jint)item.toNumber(exec);
 133                 env->SetIntArrayRegion((jintArray)jarray, (jsize)i, (jsize)1, &value);
 134             }
 135             break;
 136         }
 137 
 138     case JavaTypeLong:
 139         {
 140             jarray = (jobjectArray)env->NewLongArray(length);
 141             for (unsigned i = 0; i < length; i++) {
 142                 JSValue item = jsArray->get(exec, i);
 143                 jlong value = (jlong)item.toNumber(exec);
 144                 env->SetLongArrayRegion((jlongArray)jarray, (jsize)i, (jsize)1, &value);
 145             }
 146             break;
 147         }
 148 
 149     case JavaTypeFloat:
 150         {
 151             jarray = (jobjectArray)env->NewFloatArray(length);
 152             for (unsigned i = 0; i < length; i++) {
 153                 JSValue item = jsArray->get(exec, i);
 154                 jfloat value = (jfloat)item.toNumber(exec);
 155                 env->SetFloatArrayRegion((jfloatArray)jarray, (jsize)i, (jsize)1, &value);
 156             }
 157             break;
 158         }
 159 
 160     case JavaTypeDouble:
 161         {
 162             jarray = (jobjectArray)env->NewDoubleArray(length);
 163             for (unsigned i = 0; i < length; i++) {
 164                 JSValue item = jsArray->get(exec, i);
 165                 jdouble value = (jdouble)item.toNumber(exec);
 166                 env->SetDoubleArrayRegion((jdoubleArray)jarray, (jsize)i, (jsize)1, &value);
 167             }
 168             break;
 169         }
 170 
 171     case JavaTypeArray: // don't handle embedded arrays
 172     case JavaTypeVoid: // Don't expect arrays of void objects
 173     case JavaTypeInvalid: // Array of unknown objects
 174         break;
 175     }
 176 
 177     // if it was not one of the cases handled, then null is returned
 178     return jarray;
 179 }
 180 
 181 
 182 jobject convertUndefinedToJObject() {
 183     static JGObject jgoUndefined;
 184     if (!jgoUndefined) {
 185         JNIEnv* env = getJNIEnv();
 186         jclass clazz = env->FindClass(JSOBJECT_CLASSNAME);
 187         jgoUndefined = JLObject(env->GetStaticObjectField(
 188             clazz,
 189             env->GetStaticFieldID(clazz, "UNDEFINED", "Ljava/lang/String;")));
 190     }
 191     return jgoUndefined;
 192 }
 193 
 194 
 195 jvalue convertValueToJValue(ExecState* exec, RootObject* rootObject, JSValue value, JavaType javaType, const char* javaClassName)
 196 {
 197     JSLockHolder lock(exec);
 198 
 199     jvalue result;
 200     memset(&result, 0, sizeof(jvalue));
 201 
 202     switch (javaType) {
 203     case JavaTypeArray:
 204     case JavaTypeObject:
 205         {
 206             // FIXME: JavaJSObject::convertValueToJObject functionality is almost exactly the same,
 207             // these functions should use common code.
 208 
 209             if (value.isObject()) {
 210                 JSObject* object = asObject(value);
 211                 if (object->inherits(JavaRuntimeObject::info())) {
 212                     // Unwrap a Java instance.
 213                     JavaRuntimeObject* runtimeObject = static_cast<JavaRuntimeObject*>(object);
 214                     JavaInstance* instance = runtimeObject->getInternalJavaInstance();
 215                     if (instance) {
 216                         // Since instance->javaInstance() is WeakGlobalRef, creating a localref to safeguard javaInstance() from GC
 217                         JLObject jlinstance(instance->javaInstance(), true);
 218                         if (!jlinstance) {
 219                             LOG_ERROR("Could not get javaInstance for %p in JNIUtilityPrivate::convertValueToJValue", jlinstance);
 220                             return result;
 221                         }
 222                         result.l = instance->javaInstance();
 223                     }
 224                 } else if (object->classInfo() == RuntimeArray::info()) {
 225                     // Input is a JavaScript Array that was originally created from a Java Array
 226                     RuntimeArray* imp = static_cast<RuntimeArray*>(object);
 227                     JavaArray* array = static_cast<JavaArray*>(imp->getConcreteArray());
 228 
 229                     // Since array->javaArray() is WeakGlobalRef, creating a localref to safeguard javaInstance() from GC
 230                     JLObject jlinstancearray(array->javaArray(), true);
 231                     if (!jlinstancearray) {
 232                         LOG_ERROR("Could not get javaArrayInstance for %p in JNIUtilityPrivate::convertValueToJValue", jlinstancearray);
 233                         return result;
 234                     }
 235                     result.l = array->javaArray();
 236                 } else if ((!result.l && (!strcmp(javaClassName, "java.lang.Object")))
 237                            || (!strcmp(javaClassName, "netscape.javascript.JSObject"))) {
 238                     // Wrap objects in JSObject instances.
 239                     JNIEnv* env = getJNIEnv();
 240                     if (object->inherits(WebCore::JSNode::info())) {
 241                         WebCore::JSNode* jsnode = static_cast<WebCore::JSNode*>(object);
 242                         static JGClass nodeImplClass = env->FindClass("com/sun/webkit/dom/NodeImpl");
 243                         static jmethodID getImplID = env->GetStaticMethodID(nodeImplClass, "getCachedImpl",
 244                                                                      "(J)Lorg/w3c/dom/Node;");
 245                         WebCore::Node *peer = &jsnode->impl();
 246                         peer->ref(); //deref is in NodeImpl disposer
 247                         result.l = env->CallStaticObjectMethod(
 248                             nodeImplClass,
 249                             getImplID,
 250                             ptr_to_jlong(peer));
 251                     } else {
 252                         static JGClass jsObjectClass = env->FindClass(JSOBJECT_CLASSNAME);
 253                         static jmethodID constructorID = env->GetMethodID(jsObjectClass, "<init>", "(JI)V");
 254                         if (constructorID) {
 255                             rootObject->gcProtect(object);
 256                             jlong nativeHandle = ptr_to_jlong(object);
 257                             result.l = env->NewObject(jsObjectClass, constructorID,
 258                                 nativeHandle,
 259                                 com_sun_webkit_dom_JSObject_JS_CONTEXT_OBJECT);
 260                         }
 261                     }
 262                 }
 263             }
 264 
 265             // Create an appropriate Java object if target type is java.lang.Object or other wrapper Objects {Integer, Double, Boolean}.
 266             if (!result.l) {
 267                 if (value.isString() && !strcmp(javaClassName, "java.lang.Object")) {
 268                     String stringValue = asString(value)->value(exec);
 269                     JNIEnv* env = getJNIEnv();
 270                     jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.deprecatedCharacters(), stringValue.length());
 271                     result.l = javaString;
 272                 } else if (value.isNumber()) {
 273                     JNIEnv* env = getJNIEnv();
 274                     if (value.isInt32() && (!strcmp(javaClassName, "java.lang.Number") || !strcmp(javaClassName, "java.lang.Integer") || !strcmp(javaClassName, "java.lang.Object"))) {
 275                         static JGClass clazz(env->FindClass("java/lang/Integer"));
 276                         jmethodID meth = env->GetStaticMethodID(clazz, "valueOf", "(I)Ljava/lang/Integer;");
 277                         result.l = env->CallStaticObjectMethod(clazz, meth, (jint) value.asInt32());
 278                     } else if (!strcmp(javaClassName, "java.lang.Number") || !strcmp(javaClassName, "java.lang.Double") || !strcmp(javaClassName, "java.lang.Object")) {
 279                         jdouble doubleValue = (jdouble) value.asNumber();
 280                         static JGClass clazz = env->FindClass("java/lang/Double");
 281                         jmethodID meth = env->GetStaticMethodID(clazz, "valueOf", "(D)Ljava/lang/Double;");
 282                         jobject javaDouble = env->CallStaticObjectMethod(clazz, meth, doubleValue);
 283                         result.l = javaDouble;
 284                     }
 285                 } else if (value.isBoolean() && (!strcmp(javaClassName, "java.lang.Boolean") || !strcmp(javaClassName, "java.lang.Object"))) {
 286                     bool boolValue = value.asBoolean();
 287                     JNIEnv* env = getJNIEnv();
 288                     static JGClass clazz(env->FindClass("java/lang/Boolean"));
 289                     jmethodID meth = env->GetStaticMethodID(clazz, "valueOf", "(Z)Ljava/lang/Boolean;");
 290                     jobject javaBoolean = env->CallStaticObjectMethod(clazz, meth, boolValue);
 291                     result.l = javaBoolean;
 292                 } else if (value.isUndefined()) {
 293                     result.l = convertUndefinedToJObject();
 294                 }
 295             }
 296 
 297             // Convert value to a string if the target type is a java.lang.String, and we're not
 298             // converting from a null.
 299             if (!result.l && !strcmp(javaClassName, "java.lang.String")) {
 300                 if (!value.isNull()) {
 301                     String stringValue = value.toString(exec)->value(exec);
 302                     JNIEnv* env = getJNIEnv();
 303                     jobject javaString = env->functions->NewString(env, (const jchar*)stringValue.deprecatedCharacters(), stringValue.length());
 304                     result.l = javaString;
 305                 }
 306             }
 307         }
 308         break;
 309 
 310     case JavaTypeBoolean:
 311         {
 312             result.z = (jboolean)value.toNumber(exec);
 313         }
 314         break;
 315 
 316     case JavaTypeByte:
 317         {
 318             result.b = (jbyte)value.toNumber(exec);
 319         }
 320         break;
 321 
 322     case JavaTypeChar:
 323         {
 324             result.c = (jchar)value.toNumber(exec);
 325         }
 326         break;
 327 
 328     case JavaTypeShort:
 329         {
 330             result.s = (jshort)value.toNumber(exec);
 331         }
 332         break;
 333 
 334     case JavaTypeInt:
 335         {
 336             result.i = (jint)value.toNumber(exec);
 337         }
 338         break;
 339 
 340     case JavaTypeLong:
 341         {
 342             result.j = (jlong)value.toNumber(exec);
 343         }
 344         break;
 345 
 346     case JavaTypeFloat:
 347         {
 348             result.f = (jfloat)value.toNumber(exec);
 349         }
 350         break;
 351 
 352     case JavaTypeDouble:
 353         {
 354             result.d = (jdouble)value.toNumber(exec);
 355         }
 356         break;
 357 
 358     case JavaTypeInvalid:
 359     case JavaTypeVoid:
 360         break;
 361     }
 362     return result;
 363 }
 364 
 365 jobject jvalueToJObject(jvalue value, JavaType jtype) {
 366     JNIEnv* env = getJNIEnv();
 367     jmethodID meth;
 368     switch (jtype) {
 369     case JavaTypeObject:
 370     case JavaTypeArray:
 371         return value.l;
 372     case JavaTypeBoolean: {
 373       static JGClass clsZ(env->FindClass("java/lang/Boolean"));
 374       meth = env->GetStaticMethodID(clsZ, "valueOf", "(Z)Ljava/lang/Boolean;");
 375       return env->CallStaticObjectMethod(clsZ, meth, value.z);
 376     }
 377     case JavaTypeChar: {
 378       static JGClass clsC(env->FindClass("java/lang/Character"));
 379       meth = env->GetStaticMethodID(clsC, "valueOf",
 380                                     "(C)Ljava/lang/Character;");
 381       return env->CallStaticObjectMethod(clsC, meth, value.c);
 382     }
 383     case JavaTypeByte: {
 384       static JGClass clsB(env->FindClass("java/lang/Byte"));
 385       meth = env->GetStaticMethodID(clsB, "valueOf", "(B)Ljava/lang/Byte;");
 386       return env->CallStaticObjectMethod(clsB, meth, value.b);
 387     }
 388     case JavaTypeShort: {
 389       static JGClass clsS(env->FindClass("java/lang/Short"));
 390       meth = env->GetStaticMethodID(clsS, "valueOf", "(S)Ljava/lang/Short;");
 391       return env->CallStaticObjectMethod(clsS, meth, value.s);
 392     }
 393     case JavaTypeInt: {
 394       static JGClass clsI(env->FindClass("java/lang/Integer"));
 395       meth = env->GetStaticMethodID(clsI, "valueOf", "(I)Ljava/lang/Integer;");
 396       return env->CallStaticObjectMethod(clsI, meth, value.i);
 397     }
 398     case JavaTypeLong: {
 399       static JGClass clsJ(env->FindClass("java/lang/Long"));
 400       meth = env->GetStaticMethodID(clsJ, "valueOf", "(J)Ljava/lang/Long;");
 401       return env->CallStaticObjectMethod(clsJ, meth, value.j);
 402     }
 403     case JavaTypeFloat: {
 404       static JGClass clsF(env->FindClass("java/lang/Float"));
 405       meth = env->GetStaticMethodID(clsF, "valueOf", "(F)Ljava/lang/Float;");
 406       return env->CallStaticObjectMethod(clsF, meth, value.f);
 407     }
 408     case JavaTypeDouble: {
 409       static JGClass clsD(env->FindClass("java/lang/Double"));
 410       meth = env->GetStaticMethodID(clsD, "valueOf", "(D)Ljava/lang/Double;");
 411       return env->CallStaticObjectMethod(clsD, meth, value.d);
 412     }
 413     default:
 414         abort();
 415     }
 416 }
 417 
 418 jthrowable dispatchJNICall(int count, RootObject* rootObject, jobject obj, bool isStatic, JavaType returnType, jmethodID methodId, jobject* args, jvalue& result, jobject accessControlContext) {
 419 
 420     // Since obj is WeakGlobalRef, creating a localref to safeguard instance() from GC
 421     JLObject jlinstance(obj, true);
 422 
 423     if (!jlinstance) {
 424         LOG_ERROR("Could not get javaInstance for %p in JNIUtilityPrivate::dispatchJNICall", jlinstance);
 425         return NULL;
 426     }
 427 
 428     JNIEnv* env = getJNIEnv();
 429     jclass objClass = env->GetObjectClass(obj);
 430     jobject rmethod = env->ToReflectedMethod(objClass, methodId, isStatic);
 431     jclass utilityCls = env->FindClass("com/sun/webkit/Utilities");
 432     jclass objectCls = env->FindClass("java/lang/Object");
 433     jobjectArray argsArray = env->NewObjectArray(count, objectCls, NULL);
 434     for (int i = 0;  i < count; i++)
 435       env->SetObjectArrayElement(argsArray, i, args[i]);
 436     jmethodID invokeMethod =
 437         env->GetStaticMethodID(utilityCls, "fwkInvokeWithContext",
 438                                "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;Ljava/security/AccessControlContext;)Ljava/lang/Object;");
 439     jobject r = env->CallStaticObjectMethod(utilityCls, invokeMethod,
 440                                             rmethod, obj, argsArray,
 441                                             accessControlContext);
 442 
 443     jthrowable ex = env->ExceptionOccurred();
 444     env->ExceptionClear();
 445 
 446     switch (returnType) {
 447     case JavaTypeVoid:
 448         {
 449         }
 450         break;
 451     case JavaTypeArray:
 452     case JavaTypeObject:
 453         result.l = r;
 454         break;
 455 
 456     case JavaTypeBoolean:
 457         result.z = callJNIMethod<jboolean>(r, "booleanValue", "()Z");
 458         break;
 459 
 460     case JavaTypeByte:
 461         result.b = callJNIMethod<jbyte>(r, "byteValue", "()B");
 462         break;
 463 
 464     case JavaTypeChar:
 465         result.c = callJNIMethod<jchar>(r, "charValue", "()C");
 466         break;
 467 
 468     case JavaTypeShort:
 469         result.s = callJNIMethod<jshort>(r, "shortValue", "()S");
 470         break;
 471 
 472     case JavaTypeInt:
 473         result.i = callJNIMethod<jint>(r, "intValue", "()I");
 474         break;
 475 
 476     case JavaTypeLong:
 477         result.j = callJNIMethod<jlong>(r, "longValue", "()J");
 478         break;
 479 
 480     case JavaTypeFloat:
 481         result.f = callJNIMethod<jfloat>(r, "floatValue", "()F");
 482         break;
 483 
 484     case JavaTypeDouble:
 485         result.d = callJNIMethod<jdouble>(r, "doubleValue", "()D");
 486         break;
 487 
 488     case JavaTypeInvalid:
 489         /* Nothing to do */
 490         break;
 491     }
 492     return ex;
 493 }
 494 
 495 } // end of namespace Bindings
 496 
 497 } // end of namespace JSC
 498 
 499 #endif // ENABLE(JAVA_BRIDGE)