OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/memory/singleton.h" |
| 10 #include "base/string_util.h" // For ReplaceSubstringsAfterOffset |
| 11 |
| 12 using base::android::AttachCurrentThread; |
| 13 using base::android::ConvertJavaStringToUTF8; |
| 14 using base::android::MethodID; |
| 15 using base::android::ScopedJavaLocalRef; |
| 16 |
| 17 namespace { |
| 18 |
| 19 // Java's reflection API represents types as a string using an extended 'binary |
| 20 // name'. This converts to an enum which we store in place of the binary name |
| 21 // for simplicity. |
| 22 JavaType::Type BinaryNameToType(const std::string& binary_name) { |
| 23 if (binary_name == "boolean") { |
| 24 return JavaType::TypeBoolean; |
| 25 } else if (binary_name == "byte") { |
| 26 return JavaType::TypeByte; |
| 27 } else if (binary_name == "char") { |
| 28 return JavaType::TypeChar; |
| 29 } else if (binary_name == "short") { |
| 30 return JavaType::TypeShort; |
| 31 } else if (binary_name == "int") { |
| 32 return JavaType::TypeInt; |
| 33 } else if (binary_name == "long") { |
| 34 return JavaType::TypeLong; |
| 35 } else if (binary_name == "float") { |
| 36 return JavaType::TypeFloat; |
| 37 } else if (binary_name == "double") { |
| 38 return JavaType::TypeDouble; |
| 39 } else if (binary_name == "void") { |
| 40 return JavaType::TypeVoid; |
| 41 } else if (binary_name[0] == '[') { |
| 42 return JavaType::TypeArray; |
| 43 } else if (binary_name == "java.lang.String") { |
| 44 return JavaType::TypeString; |
| 45 } |
| 46 return JavaType::TypeObject; |
| 47 } |
| 48 |
| 49 std::string BinaryNameToJNIName(const std::string& binary_name, |
| 50 JavaType::Type* type) { |
| 51 DCHECK(type); |
| 52 *type = BinaryNameToType(binary_name); |
| 53 switch (*type) { |
| 54 case JavaType::TypeBoolean: |
| 55 return "Z"; |
| 56 case JavaType::TypeByte: |
| 57 return "B"; |
| 58 case JavaType::TypeChar: |
| 59 return "C"; |
| 60 case JavaType::TypeShort: |
| 61 return "S"; |
| 62 case JavaType::TypeInt: |
| 63 return "I"; |
| 64 case JavaType::TypeLong: |
| 65 return "J"; |
| 66 case JavaType::TypeFloat: |
| 67 return "F"; |
| 68 case JavaType::TypeDouble: |
| 69 return "D"; |
| 70 case JavaType::TypeVoid: |
| 71 return "V"; |
| 72 case JavaType::TypeArray: |
| 73 return "["; |
| 74 default: |
| 75 DCHECK (*type == JavaType::TypeString || *type == JavaType::TypeObject); |
| 76 std::string jni_name = "L" + binary_name + ";"; |
| 77 ReplaceSubstringsAfterOffset(&jni_name, 0, ".", "/"); |
| 78 return jni_name; |
| 79 } |
| 80 } |
| 81 |
| 82 class MethodGetParameterTypesID : public MethodID { |
| 83 public: |
| 84 static MethodGetParameterTypesID* GetInstance() { |
| 85 return Singleton<MethodGetParameterTypesID>::get(); |
| 86 } |
| 87 private: |
| 88 friend struct DefaultSingletonTraits<MethodGetParameterTypesID>; |
| 89 MethodGetParameterTypesID() |
| 90 : MethodID(AttachCurrentThread(), "java/lang/reflect/Method", |
| 91 "getParameterTypes", "()[Ljava/lang/Class;") { |
| 92 } |
| 93 DISALLOW_COPY_AND_ASSIGN(MethodGetParameterTypesID); |
| 94 }; |
| 95 |
| 96 class MethodGetNameID : public MethodID { |
| 97 public: |
| 98 static MethodGetNameID* GetInstance() { |
| 99 return Singleton<MethodGetNameID>::get(); |
| 100 } |
| 101 private: |
| 102 friend struct DefaultSingletonTraits<MethodGetNameID>; |
| 103 MethodGetNameID() |
| 104 : MethodID(AttachCurrentThread(), "java/lang/reflect/Method", |
| 105 "getName", "()Ljava/lang/String;") { |
| 106 } |
| 107 DISALLOW_COPY_AND_ASSIGN(MethodGetNameID); |
| 108 }; |
| 109 |
| 110 class MethodGetReturnTypeID : public MethodID { |
| 111 public: |
| 112 static MethodGetReturnTypeID* GetInstance() { |
| 113 return Singleton<MethodGetReturnTypeID>::get(); |
| 114 } |
| 115 private: |
| 116 friend struct DefaultSingletonTraits<MethodGetReturnTypeID>; |
| 117 MethodGetReturnTypeID() |
| 118 : MethodID(AttachCurrentThread(), "java/lang/reflect/Method", |
| 119 "getReturnType", "()Ljava/lang/Class;") { |
| 120 } |
| 121 DISALLOW_COPY_AND_ASSIGN(MethodGetReturnTypeID); |
| 122 }; |
| 123 |
| 124 class MethodGetDeclaringClassID : public MethodID { |
| 125 public: |
| 126 static MethodGetDeclaringClassID* GetInstance() { |
| 127 return Singleton<MethodGetDeclaringClassID>::get(); |
| 128 } |
| 129 private: |
| 130 friend struct DefaultSingletonTraits<MethodGetDeclaringClassID>; |
| 131 MethodGetDeclaringClassID() |
| 132 : MethodID(AttachCurrentThread(), "java/lang/reflect/Method", |
| 133 "getDeclaringClass", "()Ljava/lang/Class;") { |
| 134 } |
| 135 DISALLOW_COPY_AND_ASSIGN(MethodGetDeclaringClassID); |
| 136 }; |
| 137 |
| 138 class ClassGetNameID : public MethodID { |
| 139 public: |
| 140 static ClassGetNameID* GetInstance() { |
| 141 return Singleton<ClassGetNameID>::get(); |
| 142 } |
| 143 private: |
| 144 friend struct DefaultSingletonTraits<ClassGetNameID>; |
| 145 ClassGetNameID() |
| 146 : MethodID(AttachCurrentThread(), "java/lang/Class", "getName", |
| 147 "()Ljava/lang/String;") { |
| 148 } |
| 149 DISALLOW_COPY_AND_ASSIGN(ClassGetNameID); |
| 150 }; |
| 151 |
| 152 } // namespace |
| 153 |
| 154 JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method) |
| 155 : java_method_(method), |
| 156 have_calculated_num_parameters_(false), |
| 157 id_(NULL) { |
| 158 JNIEnv* env = java_method_.env(); |
| 159 // On construction, we do nothing except get the name. Everything else is |
| 160 // done lazily. |
| 161 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( |
| 162 env->CallObjectMethod(java_method_.obj(), |
| 163 MethodGetNameID::GetInstance()->id()))); |
| 164 name_ = ConvertJavaStringToUTF8(env, name.obj()); |
| 165 } |
| 166 |
| 167 JavaMethod::~JavaMethod() { |
| 168 } |
| 169 |
| 170 size_t JavaMethod::num_parameters() const { |
| 171 EnsureNumParametersIsSetUp(); |
| 172 return num_parameters_; |
| 173 } |
| 174 |
| 175 JavaType::Type JavaMethod::parameter_type(size_t index) const { |
| 176 EnsureTypesAndIDAreSetUp(); |
| 177 return parameter_types_[index]; |
| 178 } |
| 179 |
| 180 JavaType::Type JavaMethod::return_type() const { |
| 181 EnsureTypesAndIDAreSetUp(); |
| 182 return return_type_; |
| 183 } |
| 184 |
| 185 jmethodID JavaMethod::id() const { |
| 186 EnsureTypesAndIDAreSetUp(); |
| 187 return id_; |
| 188 } |
| 189 |
| 190 void JavaMethod::EnsureNumParametersIsSetUp() const { |
| 191 if (have_calculated_num_parameters_) { |
| 192 return; |
| 193 } |
| 194 have_calculated_num_parameters_ = true; |
| 195 |
| 196 // The number of parameters will be used frequently when determining |
| 197 // whether to call this method. We don't get the ID etc until actually |
| 198 // required. |
| 199 JNIEnv* env = java_method_.env(); |
| 200 ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>( |
| 201 env->CallObjectMethod(java_method_.obj(), |
| 202 MethodGetParameterTypesID::GetInstance()->id()))); |
| 203 num_parameters_ = env->GetArrayLength(parameters.obj()); |
| 204 } |
| 205 |
| 206 void JavaMethod::EnsureTypesAndIDAreSetUp() const { |
| 207 if (id_) { |
| 208 return; |
| 209 } |
| 210 |
| 211 // Get the parameters |
| 212 JNIEnv* env = java_method_.env(); |
| 213 ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>( |
| 214 env->CallObjectMethod(java_method_.obj(), |
| 215 MethodGetParameterTypesID::GetInstance()->id()))); |
| 216 // Usually, this will already have been called. |
| 217 EnsureNumParametersIsSetUp(); |
| 218 DCHECK_EQ(num_parameters_, |
| 219 static_cast<size_t>(env->GetArrayLength(parameters.obj()))); |
| 220 |
| 221 // Java gives us the argument type using an extended version of the 'binary |
| 222 // name'. See |
| 223 // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getNa
me(). |
| 224 // If we build the signature now, there's no need to store the binary name |
| 225 // of the arguments. We just store the simple type. |
| 226 std::string signature("("); |
| 227 |
| 228 // Form the signature and record the parameter types. |
| 229 parameter_types_.resize(num_parameters_); |
| 230 for (size_t i = 0; i < num_parameters_; ++i) { |
| 231 ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement( |
| 232 parameters.obj(), i)); |
| 233 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( |
| 234 env->CallObjectMethod(parameter.obj(), |
| 235 ClassGetNameID::GetInstance()->id()))); |
| 236 std::string name_utf8 = ConvertJavaStringToUTF8(env, name.obj()); |
| 237 signature += BinaryNameToJNIName(name_utf8, ¶meter_types_[i]); |
| 238 } |
| 239 signature += ")"; |
| 240 |
| 241 // Get the return type |
| 242 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( |
| 243 env->CallObjectMethod(java_method_.obj(), |
| 244 MethodGetReturnTypeID::GetInstance()->id()))); |
| 245 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>( |
| 246 env->CallObjectMethod(clazz.obj(), ClassGetNameID::GetInstance()->id()))); |
| 247 signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(env, name.obj()), |
| 248 &return_type_); |
| 249 |
| 250 // Get the ID for this method. |
| 251 ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>( |
| 252 env->CallObjectMethod(java_method_.obj(), |
| 253 MethodGetDeclaringClassID::GetInstance()->id()))); |
| 254 id_ = base::android::GetMethodID(env, declaring_class.obj(), name_.c_str(), |
| 255 signature.c_str()); |
| 256 |
| 257 java_method_.Reset(); |
| 258 } |
OLD | NEW |