1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/android/java/java_method.h" 6 7#include "base/android/jni_android.h" 8#include "base/android/jni_string.h" 9#include "base/lazy_instance.h" 10#include "base/memory/singleton.h" 11#include "content/browser/android/java/jni_helper.h" 12 13using base::android::AttachCurrentThread; 14using base::android::ConvertJavaStringToUTF8; 15using base::android::GetClass; 16using base::android::MethodID; 17using base::android::ScopedJavaGlobalRef; 18using base::android::ScopedJavaLocalRef; 19 20namespace content { 21namespace { 22 23const char kGetName[] = "getName"; 24const char kGetDeclaringClass[] = "getDeclaringClass"; 25const char kGetModifiers[] = "getModifiers"; 26const char kGetParameterTypes[] = "getParameterTypes"; 27const char kGetReturnType[] = "getReturnType"; 28const char kIntegerReturningBoolean[] = "(I)Z"; 29const char kIsStatic[] = "isStatic"; 30const char kJavaLangClass[] = "java/lang/Class"; 31const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; 32const char kJavaLangReflectModifier[] = "java/lang/reflect/Modifier"; 33const char kReturningInteger[] = "()I"; 34const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; 35const char kReturningJavaLangClassArray[] = "()[Ljava/lang/Class;"; 36const char kReturningJavaLangString[] = "()Ljava/lang/String;"; 37 38struct ModifierClassTraits : 39 public base::internal::LeakyLazyInstanceTraits<ScopedJavaGlobalRef< 40 jclass> > { 41 static ScopedJavaGlobalRef<jclass>* New(void* instance) { 42 JNIEnv* env = AttachCurrentThread(); 43 // Use placement new to initialize our instance in our preallocated space. 44 return new (instance) ScopedJavaGlobalRef<jclass>( 45 GetClass(env, kJavaLangReflectModifier)); 46 } 47}; 48 49base::LazyInstance<ScopedJavaGlobalRef<jclass>, ModifierClassTraits> 50 g_java_lang_reflect_modifier_class = LAZY_INSTANCE_INITIALIZER; 51 52std::string BinaryNameToJNISignature(const std::string& binary_name, 53 JavaType* type) { 54 DCHECK(type); 55 *type = JavaType::CreateFromBinaryName(binary_name); 56 return type->JNISignature(); 57} 58 59} // namespace 60 61JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method) 62 : java_method_(method), 63 have_calculated_num_parameters_(false), 64 id_(NULL) { 65 JNIEnv* env = AttachCurrentThread(); 66 // On construction, we do nothing except get the name. Everything else is 67 // done lazily. 68 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( 69 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 70 env, 71 kJavaLangReflectMethod, 72 kGetName, 73 kReturningJavaLangString)))); 74 name_ = ConvertJavaStringToUTF8(name); 75} 76 77JavaMethod::~JavaMethod() { 78} 79 80size_t JavaMethod::num_parameters() const { 81 EnsureNumParametersIsSetUp(); 82 return num_parameters_; 83} 84 85bool JavaMethod::is_static() const { 86 EnsureTypesAndIDAreSetUp(); 87 return is_static_; 88} 89 90const JavaType& JavaMethod::parameter_type(size_t index) const { 91 EnsureTypesAndIDAreSetUp(); 92 return parameter_types_[index]; 93} 94 95const JavaType& JavaMethod::return_type() const { 96 EnsureTypesAndIDAreSetUp(); 97 return return_type_; 98} 99 100jmethodID JavaMethod::id() const { 101 EnsureTypesAndIDAreSetUp(); 102 return id_; 103} 104 105void JavaMethod::EnsureNumParametersIsSetUp() const { 106 if (have_calculated_num_parameters_) { 107 return; 108 } 109 have_calculated_num_parameters_ = true; 110 111 // The number of parameters will be used frequently when determining 112 // whether to call this method. We don't get the ID etc until actually 113 // required. 114 JNIEnv* env = AttachCurrentThread(); 115 ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>( 116 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 117 env, 118 kJavaLangReflectMethod, 119 kGetParameterTypes, 120 kReturningJavaLangClassArray)))); 121 num_parameters_ = env->GetArrayLength(parameters.obj()); 122} 123 124void JavaMethod::EnsureTypesAndIDAreSetUp() const { 125 if (id_) { 126 return; 127 } 128 129 // Get the parameters 130 JNIEnv* env = AttachCurrentThread(); 131 ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>( 132 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 133 env, 134 kJavaLangReflectMethod, 135 kGetParameterTypes, 136 kReturningJavaLangClassArray)))); 137 // Usually, this will already have been called. 138 EnsureNumParametersIsSetUp(); 139 DCHECK_EQ(num_parameters_, 140 static_cast<size_t>(env->GetArrayLength(parameters.obj()))); 141 142 // Java gives us the argument type using an extended version of the 'binary 143 // name'. See 144 // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName(). 145 // If we build the signature now, there's no need to store the binary name 146 // of the arguments. We just store the simple type. 147 std::string signature("("); 148 149 // Form the signature and record the parameter types. 150 parameter_types_.resize(num_parameters_); 151 for (size_t i = 0; i < num_parameters_; ++i) { 152 ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement( 153 parameters.obj(), i)); 154 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( 155 env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName( 156 env, 157 kJavaLangClass, 158 kGetName, 159 kReturningJavaLangString)))); 160 std::string name_utf8 = ConvertJavaStringToUTF8(name); 161 signature += BinaryNameToJNISignature(name_utf8, ¶meter_types_[i]); 162 } 163 signature += ")"; 164 165 // Get the return type 166 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( 167 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 168 env, 169 kJavaLangReflectMethod, 170 kGetReturnType, 171 kReturningJavaLangClass)))); 172 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( 173 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( 174 env, 175 kJavaLangClass, 176 kGetName, 177 kReturningJavaLangString)))); 178 signature += BinaryNameToJNISignature(ConvertJavaStringToUTF8(name), 179 &return_type_); 180 181 // Determine whether the method is static. 182 jint modifiers = env->CallIntMethod( 183 java_method_.obj(), GetMethodIDFromClassName(env, 184 kJavaLangReflectMethod, 185 kGetModifiers, 186 kReturningInteger)); 187 is_static_ = env->CallStaticBooleanMethod( 188 g_java_lang_reflect_modifier_class.Get().obj(), 189 MethodID::Get<MethodID::TYPE_STATIC>( 190 env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic, 191 kIntegerReturningBoolean), 192 modifiers); 193 194 // Get the ID for this method. 195 ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>( 196 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 197 env, 198 kJavaLangReflectMethod, 199 kGetDeclaringClass, 200 kReturningJavaLangClass)))); 201 id_ = is_static_ ? 202 MethodID::Get<MethodID::TYPE_STATIC>( 203 env, declaring_class.obj(), name_.c_str(), signature.c_str()) : 204 MethodID::Get<MethodID::TYPE_INSTANCE>( 205 env, declaring_class.obj(), name_.c_str(), signature.c_str()); 206 java_method_.Reset(); 207} 208 209} // namespace content 210