| OLD | NEW |
| (Empty) |
| 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 #include "content/browser/renderer_host/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#getNa
me(). | |
| 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 | |
| OLD | NEW |