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