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 34 using namespace JSC::Bindings; 35 36 #if PLATFORM(ANDROID) 37 const char kJavaScriptInterfaceAnnotation[] = "android/webkit/JavascriptInterface"; 38 const char kIsAnnotationPresent[] = "isAnnotationPresent"; 39 const char kGetMethods[] = "getMethods"; 40 41 static jclass safeAnnotationClazz = NULL; 42 43 JavaClassJobject::JavaClassJobject(jobject anInstance, bool requireAnnotation) 44 : m_requireAnnotation(requireAnnotation) 45 #else 46 JavaClassJobject::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 100 JavaClassJobject::~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) 115 bool 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 128 jmethodID 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 142 bool 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 152 MethodList 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 161 JavaField* JavaClassJobject::fieldNamed(const char* name) const 162 { 163 return m_fields.get(name); 164 } 165 166 #endif // ENABLE(JAVA_BRIDGE) 167