1 // Copyright (c) 2012 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/renderer_host/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 13 using base::android::AttachCurrentThread; 14 using base::android::ConvertJavaStringToUTF8; 15 using base::android::GetClass; 16 using base::android::GetMethodIDFromClassName; 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 EmptyString(); 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 const JavaType& JavaMethod::parameter_type(size_t index) const { 119 EnsureTypesAndIDAreSetUp(); 120 return parameter_types_[index]; 121 } 122 123 const JavaType& JavaMethod::return_type() const { 124 EnsureTypesAndIDAreSetUp(); 125 return return_type_; 126 } 127 128 jmethodID JavaMethod::id() const { 129 EnsureTypesAndIDAreSetUp(); 130 return id_; 131 } 132 133 void JavaMethod::EnsureNumParametersIsSetUp() const { 134 if (have_calculated_num_parameters_) { 135 return; 136 } 137 have_calculated_num_parameters_ = true; 138 139 // The number of parameters will be used frequently when determining 140 // whether to call this method. We don't get the ID etc until actually 141 // required. 142 JNIEnv* env = AttachCurrentThread(); 143 ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>( 144 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 145 env, 146 kJavaLangReflectMethod, 147 kGetParameterTypes, 148 kReturningJavaLangClassArray)))); 149 num_parameters_ = env->GetArrayLength(parameters.obj()); 150 } 151 152 void JavaMethod::EnsureTypesAndIDAreSetUp() const { 153 if (id_) { 154 return; 155 } 156 157 // Get the parameters 158 JNIEnv* env = AttachCurrentThread(); 159 ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>( 160 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 161 env, 162 kJavaLangReflectMethod, 163 kGetParameterTypes, 164 kReturningJavaLangClassArray)))); 165 // Usually, this will already have been called. 166 EnsureNumParametersIsSetUp(); 167 DCHECK_EQ(num_parameters_, 168 static_cast<size_t>(env->GetArrayLength(parameters.obj()))); 169 170 // Java gives us the argument type using an extended version of the 'binary 171 // name'. See 172 // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName(). 173 // If we build the signature now, there's no need to store the binary name 174 // of the arguments. We just store the simple type. 175 std::string signature("("); 176 177 // Form the signature and record the parameter types. 178 parameter_types_.resize(num_parameters_); 179 for (size_t i = 0; i < num_parameters_; ++i) { 180 ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement( 181 parameters.obj(), i)); 182 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( 183 env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName( 184 env, 185 kJavaLangClass, 186 kGetName, 187 kReturningJavaLangString)))); 188 std::string name_utf8 = ConvertJavaStringToUTF8(name); 189 signature += BinaryNameToJNIName(name_utf8, ¶meter_types_[i]); 190 } 191 signature += ")"; 192 193 // Get the return type 194 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( 195 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 196 env, 197 kJavaLangReflectMethod, 198 kGetReturnType, 199 kReturningJavaLangClass)))); 200 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( 201 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( 202 env, 203 kJavaLangClass, 204 kGetName, 205 kReturningJavaLangString)))); 206 signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(name), 207 &return_type_); 208 209 // Determine whether the method is static. 210 jint modifiers = env->CallIntMethod( 211 java_method_.obj(), GetMethodIDFromClassName(env, 212 kJavaLangReflectMethod, 213 kGetModifiers, 214 kReturningInteger)); 215 bool is_static = env->CallStaticBooleanMethod( 216 g_java_lang_reflect_modifier_class.Get().obj(), 217 MethodID::Get<MethodID::TYPE_STATIC>( 218 env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic, 219 kIntegerReturningBoolean), 220 modifiers); 221 222 // Get the ID for this method. 223 ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>( 224 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName( 225 env, 226 kJavaLangReflectMethod, 227 kGetDeclaringClass, 228 kReturningJavaLangClass)))); 229 id_ = is_static ? 230 MethodID::Get<MethodID::TYPE_STATIC>( 231 env, declaring_class.obj(), name_.c_str(), signature.c_str()) : 232 MethodID::Get<MethodID::TYPE_INSTANCE>( 233 env, declaring_class.obj(), name_.c_str(), signature.c_str()); 234 java_method_.Reset(); 235 } 236 237 } // namespace content 238