Home | History | Annotate | Download | only in jsc
      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