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 const_cast<JavaMethod*>(this)->EnsureNumParametersIsSetUp(); | |
M-A Ruel
2011/11/11 16:22:28
Why make the member function const if it isn't?
I
Steve Block
2011/11/11 16:31:34
I think that conceptually, the method is const - w
M-A Ruel
2011/11/11 16:43:29
Then you would probably better to put your member
Steve Block
2011/11/11 17:05:43
The reason I preferred the const_cast to using mut
| |
172 return num_parameters_; | |
173 } | |
174 | |
175 JavaType::Type JavaMethod::parameter_type(size_t index) const { | |
176 const_cast<JavaMethod*>(this)->EnsureTypesAndIDAreSetUp(); | |
177 return parameter_types_[index]; | |
178 } | |
179 | |
180 JavaType::Type JavaMethod::return_type() const { | |
181 const_cast<JavaMethod*>(this)->EnsureTypesAndIDAreSetUp(); | |
182 return return_type_; | |
183 } | |
184 | |
185 jmethodID JavaMethod::id() const { | |
186 const_cast<JavaMethod*>(this)->EnsureTypesAndIDAreSetUp(); | |
187 return id_; | |
188 } | |
189 | |
190 void JavaMethod::EnsureNumParametersIsSetUp() { | |
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() { | |
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 |