1/* 2 * Copyright 2010, The Android Open Source Project 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 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "JavaClassJobjectV8.h" 28 29#if ENABLE(JAVA_BRIDGE) 30 31#include "JavaFieldJobjectV8.h" 32#include "JavaMethodJobject.h" 33 34using namespace JSC::Bindings; 35 36#if PLATFORM(ANDROID) 37const char kJavaScriptInterfaceAnnotation[] = "android/webkit/JavascriptInterface"; 38const char kIsAnnotationPresent[] = "isAnnotationPresent"; 39const char kGetMethods[] = "getMethods"; 40 41static jclass safeAnnotationClazz = NULL; 42 43JavaClassJobject::JavaClassJobject(jobject anInstance, bool requireAnnotation) 44 : m_requireAnnotation(requireAnnotation) 45#else 46JavaClassJobject::JavaClassJobject(jobject anInstance) 47#endif 48{ 49 jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;"); 50 51 if (!aClass) { 52 LOG_ERROR("unable to call getClass on instance %p", anInstance); 53 return; 54 } 55 56 JNIEnv* env = getJNIEnv(); 57 58 // Get the fields 59 jarray fields = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;")); 60 int numFields = env->GetArrayLength(fields); 61 for (int i = 0; i < numFields; i++) { 62 jobject aJField = env->GetObjectArrayElement(static_cast<jobjectArray>(fields), i); 63 JavaField* aField = new JavaFieldJobject(env, aJField); // deleted in the JavaClass destructor 64 m_fields.set(aField->name(), aField); 65 env->DeleteLocalRef(aJField); 66 } 67 68 // Get the methods 69 jarray methods = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;")); 70 int numMethods = env->GetArrayLength(methods); 71#if PLATFORM(ANDROID) 72 jmethodID isAnnotationPresentMethodID = getAnnotationMethodID(env); 73 if (!isAnnotationPresentMethodID) { 74 LOG_ERROR("unable to find method %s on instance %p", kIsAnnotationPresent, anInstance); 75 return; 76 } 77#endif 78 for (int i = 0; i < numMethods; i++) { 79 jobject aJMethod = env->GetObjectArrayElement(static_cast<jobjectArray>(methods), i); 80#if PLATFORM(ANDROID) 81 if (jsAccessAllowed(env, isAnnotationPresentMethodID, aJMethod)) { 82#endif 83 JavaMethod* aMethod = new JavaMethodJobject(env, aJMethod); // deleted in the JavaClass destructor 84 MethodList* methodList = m_methods.get(aMethod->name()); 85 if (!methodList) { 86 methodList = new MethodList(); 87 m_methods.set(aMethod->name(), methodList); 88 } 89 methodList->append(aMethod); 90#if PLATFORM(ANDROID) 91 } 92#endif 93 env->DeleteLocalRef(aJMethod); 94 } 95 env->DeleteLocalRef(fields); 96 env->DeleteLocalRef(methods); 97 env->DeleteLocalRef(aClass); 98} 99 100JavaClassJobject::~JavaClassJobject() 101{ 102 deleteAllValues(m_fields); 103 m_fields.clear(); 104 105 MethodListMap::const_iterator end = m_methods.end(); 106 for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) { 107 const MethodList* methodList = it->second; 108 deleteAllValues(*methodList); 109 delete methodList; 110 } 111 m_methods.clear(); 112} 113 114#if PLATFORM(ANDROID) 115bool JavaClassJobject::jsAccessAllowed(JNIEnv* env, jmethodID mid, jobject aJMethod) 116{ 117 if (!m_requireAnnotation) 118 return true; 119 bool accessAllowed = env->CallBooleanMethod(aJMethod, mid, safeAnnotationClazz); 120 if (env->ExceptionCheck()) { 121 env->ExceptionDescribe(); 122 env->ExceptionClear(); 123 return false; 124 } 125 return accessAllowed; 126} 127 128jmethodID JavaClassJobject::getAnnotationMethodID(JNIEnv* env) 129{ 130 jclass methodClass = env->FindClass("java/lang/reflect/Method"); 131 jmethodID mid = 0; 132 if (methodClass) 133 mid = env->GetMethodID(methodClass, kIsAnnotationPresent, "(Ljava/lang/Class;)Z"); 134 if (!methodClass || !mid) { 135 env->ExceptionDescribe(); 136 env->ExceptionClear(); 137 } 138 env->DeleteLocalRef(methodClass); 139 return mid; 140} 141 142bool JavaClassJobject::RegisterJavaClassJobject(JNIEnv* env) { 143 safeAnnotationClazz = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaScriptInterfaceAnnotation))); 144 if (!safeAnnotationClazz) { 145 LOG_ERROR("failed to register %s", kJavaScriptInterfaceAnnotation); 146 return false; 147 } 148 return true; 149} 150#endif 151 152MethodList JavaClassJobject::methodsNamed(const char* name) const 153{ 154 MethodList* methodList = m_methods.get(name); 155 156 if (methodList) 157 return *methodList; 158 return MethodList(); 159} 160 161JavaField* JavaClassJobject::fieldNamed(const char* name) const 162{ 163 return m_fields.get(name); 164} 165 166#endif // ENABLE(JAVA_BRIDGE) 167