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)