1 /* 2 * Copyright (C) 2003, 2008, 2010 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "JavaInstanceJSC.h" 28 29 #if ENABLE(MAC_JAVA_BRIDGE) 30 31 #include "JNIBridgeJSC.h" 32 #include "JNIUtility.h" 33 #include "JNIUtilityPrivate.h" 34 #include "JavaClassJSC.h" 35 #include "runtime_object.h" 36 #include "runtime_root.h" 37 #include <runtime/ArgList.h> 38 #include <runtime/Error.h> 39 #include <runtime/JSLock.h> 40 41 #if PLATFORM(ANDROID) 42 #include <assert.h> 43 #endif 44 45 #ifdef NDEBUG 46 #define JS_LOG(formatAndArgs...) ((void)0) 47 #else 48 #define JS_LOG(formatAndArgs...) { \ 49 fprintf(stderr, "%s:%d -- %s: ", __FILE__, __LINE__, __FUNCTION__); \ 50 fprintf(stderr, formatAndArgs); \ 51 } 52 #endif 53 54 #if PLATFORM(ANDROID) 55 #include <cutils/log.h> 56 #define LOG_TAG JavaInstanceJSC.cpp 57 #endif 58 59 using namespace JSC::Bindings; 60 using namespace JSC; 61 62 JavaInstance::JavaInstance(jobject instance, PassRefPtr<RootObject> rootObject) 63 : Instance(rootObject) 64 { 65 m_instance = new JObjectWrapper(instance); 66 m_class = 0; 67 } 68 69 JavaInstance::~JavaInstance() 70 { 71 delete m_class; 72 } 73 74 #define NUM_LOCAL_REFS 64 75 76 void JavaInstance::virtualBegin() 77 { 78 getJNIEnv()->PushLocalFrame(NUM_LOCAL_REFS); 79 } 80 81 void JavaInstance::virtualEnd() 82 { 83 getJNIEnv()->PopLocalFrame(0); 84 } 85 86 Class* JavaInstance::getClass() const 87 { 88 if (!m_class) 89 m_class = new JavaClass (m_instance->m_instance); 90 return m_class; 91 } 92 93 JSValue JavaInstance::stringValue(ExecState* exec) const 94 { 95 JSLock lock(SilenceAssertionsOnly); 96 97 jstring stringValue = (jstring)callJNIMethod<jobject>(m_instance->m_instance, "toString", "()Ljava/lang/String;"); 98 99 // Should throw a JS exception, rather than returning ""? - but better than a null dereference. 100 if (!stringValue) 101 return jsString(exec, UString()); 102 103 JNIEnv* env = getJNIEnv(); 104 const jchar* c = getUCharactersFromJStringInEnv(env, stringValue); 105 UString u((const UChar*)c, (int)env->GetStringLength(stringValue)); 106 releaseUCharactersForJStringInEnv(env, stringValue, c); 107 return jsString(exec, u); 108 } 109 110 JSValue JavaInstance::numberValue(ExecState* exec) const 111 { 112 jdouble doubleValue = callJNIMethod<jdouble>(m_instance->m_instance, "doubleValue", "()D"); 113 return jsNumber(exec, doubleValue); 114 } 115 116 JSValue JavaInstance::booleanValue() const 117 { 118 jboolean booleanValue = callJNIMethod<jboolean>(m_instance->m_instance, "booleanValue", "()Z"); 119 return jsBoolean(booleanValue); 120 } 121 122 JSValue JavaInstance::invokeMethod(ExecState* exec, const MethodList& methodList, const ArgList &args) 123 { 124 int i, count = args.size(); 125 jvalue* jArgs; 126 JSValue resultValue; 127 Method* method = 0; 128 size_t numMethods = methodList.size(); 129 130 // Try to find a good match for the overloaded method. The 131 // fundamental problem is that JavaScript doesn't have the 132 // notion of method overloading and Java does. We could 133 // get a bit more sophisticated and attempt to does some 134 // type checking as we as checking the number of parameters. 135 Method* aMethod; 136 for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { 137 aMethod = methodList[methodIndex]; 138 if (aMethod->numParameters() == count) { 139 method = aMethod; 140 break; 141 } 142 } 143 if (!method) { 144 JS_LOG("unable to find an appropiate method\n"); 145 return jsUndefined(); 146 } 147 148 const JavaMethod* jMethod = static_cast<const JavaMethod*>(method); 149 JS_LOG("call %s %s on %p\n", UString(jMethod->name()).UTF8String().c_str(), jMethod->signature(), m_instance->m_instance); 150 151 if (count > 0) 152 jArgs = (jvalue*)malloc(count * sizeof(jvalue)); 153 else 154 jArgs = 0; 155 156 for (i = 0; i < count; i++) { 157 JavaParameter* aParameter = jMethod->parameterAt(i); 158 jArgs[i] = convertValueToJValue(exec, args.at(i), aParameter->getJNIType(), aParameter->type()); 159 JS_LOG("arg[%d] = %s\n", i, args.at(i).toString(exec).ascii()); 160 } 161 162 jvalue result; 163 164 // Try to use the JNI abstraction first, otherwise fall back to 165 // normal JNI. The JNI dispatch abstraction allows the Java plugin 166 // to dispatch the call on the appropriate internal VM thread. 167 RootObject* rootObject = this->rootObject(); 168 if (!rootObject) 169 return jsUndefined(); 170 171 bool handled = false; 172 if (rootObject->nativeHandle()) { 173 jobject obj = m_instance->m_instance; 174 JSValue exceptionDescription; 175 const char *callingURL = 0; // FIXME, need to propagate calling URL to Java 176 handled = dispatchJNICall(exec, rootObject->nativeHandle(), obj, jMethod->isStatic(), jMethod->JNIReturnType(), jMethod->methodID(obj), jArgs, result, callingURL, exceptionDescription); 177 if (exceptionDescription) { 178 throwError(exec, GeneralError, exceptionDescription.toString(exec)); 179 free(jArgs); 180 return jsUndefined(); 181 } 182 } 183 184 // The following code can be conditionally removed once we have a Tiger update that 185 // contains the new Java plugin. It is needed for builds prior to Tiger. 186 if (!handled) { 187 jobject obj = m_instance->m_instance; 188 switch (jMethod->JNIReturnType()) { 189 case void_type: 190 callJNIMethodIDA<void>(obj, jMethod->methodID(obj), jArgs); 191 break; 192 case object_type: 193 result.l = callJNIMethodIDA<jobject>(obj, jMethod->methodID(obj), jArgs); 194 break; 195 case boolean_type: 196 result.z = callJNIMethodIDA<jboolean>(obj, jMethod->methodID(obj), jArgs); 197 break; 198 case byte_type: 199 result.b = callJNIMethodIDA<jbyte>(obj, jMethod->methodID(obj), jArgs); 200 break; 201 case char_type: 202 result.c = callJNIMethodIDA<jchar>(obj, jMethod->methodID(obj), jArgs); 203 break; 204 case short_type: 205 result.s = callJNIMethodIDA<jshort>(obj, jMethod->methodID(obj), jArgs); 206 break; 207 case int_type: 208 result.i = callJNIMethodIDA<jint>(obj, jMethod->methodID(obj), jArgs); 209 break; 210 211 case long_type: 212 result.j = callJNIMethodIDA<jlong>(obj, jMethod->methodID(obj), jArgs); 213 break; 214 case float_type: 215 result.f = callJNIMethodIDA<jfloat>(obj, jMethod->methodID(obj), jArgs); 216 break; 217 case double_type: 218 result.d = callJNIMethodIDA<jdouble>(obj, jMethod->methodID(obj), jArgs); 219 break; 220 case invalid_type: 221 default: 222 break; 223 } 224 } 225 226 switch (jMethod->JNIReturnType()) { 227 case void_type: 228 { 229 resultValue = jsUndefined(); 230 } 231 break; 232 233 case object_type: 234 { 235 if (result.l) { 236 const char* arrayType = jMethod->returnType(); 237 if (arrayType[0] == '[') 238 resultValue = JavaArray::convertJObjectToArray(exec, result.l, arrayType, rootObject); 239 else 240 resultValue = JavaInstance::create(result.l, rootObject)->createRuntimeObject(exec); 241 } else 242 resultValue = jsUndefined(); 243 } 244 break; 245 246 case boolean_type: 247 { 248 resultValue = jsBoolean(result.z); 249 } 250 break; 251 252 case byte_type: 253 { 254 resultValue = jsNumber(exec, result.b); 255 } 256 break; 257 258 case char_type: 259 { 260 resultValue = jsNumber(exec, result.c); 261 } 262 break; 263 264 case short_type: 265 { 266 resultValue = jsNumber(exec, result.s); 267 } 268 break; 269 270 case int_type: 271 { 272 resultValue = jsNumber(exec, result.i); 273 } 274 break; 275 276 case long_type: 277 { 278 resultValue = jsNumber(exec, result.j); 279 } 280 break; 281 282 case float_type: 283 { 284 resultValue = jsNumber(exec, result.f); 285 } 286 break; 287 288 case double_type: 289 { 290 resultValue = jsNumber(exec, result.d); 291 } 292 break; 293 294 case invalid_type: 295 default: 296 { 297 resultValue = jsUndefined(); 298 } 299 break; 300 } 301 302 free(jArgs); 303 304 return resultValue; 305 } 306 307 JSValue JavaInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const 308 { 309 if (hint == PreferString) 310 return stringValue(exec); 311 if (hint == PreferNumber) 312 return numberValue(exec); 313 JavaClass* aClass = static_cast<JavaClass*>(getClass()); 314 if (aClass->isStringClass()) 315 return stringValue(exec); 316 if (aClass->isNumberClass()) 317 return numberValue(exec); 318 if (aClass->isBooleanClass()) 319 return booleanValue(); 320 return valueOf(exec); 321 } 322 323 JSValue JavaInstance::valueOf(ExecState* exec) const 324 { 325 return stringValue(exec); 326 } 327 328 JObjectWrapper::JObjectWrapper(jobject instance) 329 : m_refCount(0) 330 { 331 assert(instance); 332 #if PLATFORM(ANDROID) 333 if (!instance) 334 LOGE("Attempted to create JObjectWrapper for null object"); 335 #endif 336 337 // Cache the JNIEnv used to get the global ref for this java instance. 338 // It'll be used to delete the reference. 339 m_env = getJNIEnv(); 340 341 m_instance = m_env->NewGlobalRef(instance); 342 343 JS_LOG("new global ref %p for %p\n", m_instance, instance); 344 345 if (!m_instance) 346 #if PLATFORM(ANDROID) 347 LOGE("%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); 348 #else 349 fprintf(stderr, "%s: could not get GlobalRef for %p\n", __PRETTY_FUNCTION__, instance); 350 #endif 351 } 352 353 JObjectWrapper::~JObjectWrapper() 354 { 355 JS_LOG("deleting global ref %p\n", m_instance); 356 m_env->DeleteGlobalRef(m_instance); 357 } 358 359 #endif // ENABLE(MAC_JAVA_BRIDGE) 360