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