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.
5 #include "content/browser/renderer_host/java/java_method.h"
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"
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
;
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;";
39 struct ModifierClassTraits
:
40 public base::internal::LeakyLazyInstanceTraits
<ScopedJavaGlobalRef
<
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
));
50 base::LazyInstance
<ScopedJavaGlobalRef
<jclass
>, ModifierClassTraits
>
51 g_java_lang_reflect_modifier_class
= LAZY_INSTANCE_INITIALIZER
;
53 std::string
BinaryNameToJNIName(const std::string
& binary_name
,
56 *type
= JavaType::CreateFromBinaryName(binary_name
);
58 case JavaType::TypeBoolean
:
60 case JavaType::TypeByte
:
62 case JavaType::TypeChar
:
64 case JavaType::TypeShort
:
66 case JavaType::TypeInt
:
68 case JavaType::TypeLong
:
70 case JavaType::TypeFloat
:
72 case JavaType::TypeDouble
:
74 case JavaType::TypeVoid
:
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, ".", "/");
82 case JavaType::TypeString
:
83 case JavaType::TypeObject
:
84 std::string jni_name
= "L" + binary_name
+ ";";
85 ReplaceSubstringsAfterOffset(&jni_name
, 0, ".", "/");
94 JavaMethod::JavaMethod(const base::android::JavaRef
<jobject
>& method
)
95 : java_method_(method
),
96 have_calculated_num_parameters_(false),
98 JNIEnv
* env
= AttachCurrentThread();
99 // On construction, we do nothing except get the name. Everything else is
101 ScopedJavaLocalRef
<jstring
> name(env
, static_cast<jstring
>(
102 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
104 kJavaLangReflectMethod
,
106 kReturningJavaLangString
))));
107 name_
= ConvertJavaStringToUTF8(name
);
110 JavaMethod::~JavaMethod() {
113 size_t JavaMethod::num_parameters() const {
114 EnsureNumParametersIsSetUp();
115 return num_parameters_
;
118 const JavaType
& JavaMethod::parameter_type(size_t index
) const {
119 EnsureTypesAndIDAreSetUp();
120 return parameter_types_
[index
];
123 const JavaType
& JavaMethod::return_type() const {
124 EnsureTypesAndIDAreSetUp();
128 jmethodID
JavaMethod::id() const {
129 EnsureTypesAndIDAreSetUp();
133 void JavaMethod::EnsureNumParametersIsSetUp() const {
134 if (have_calculated_num_parameters_
) {
137 have_calculated_num_parameters_
= true;
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
142 JNIEnv
* env
= AttachCurrentThread();
143 ScopedJavaLocalRef
<jarray
> parameters(env
, static_cast<jarray
>(
144 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
146 kJavaLangReflectMethod
,
148 kReturningJavaLangClassArray
))));
149 num_parameters_
= env
->GetArrayLength(parameters
.obj());
152 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
157 // Get the parameters
158 JNIEnv
* env
= AttachCurrentThread();
159 ScopedJavaLocalRef
<jobjectArray
> parameters(env
, static_cast<jobjectArray
>(
160 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
162 kJavaLangReflectMethod
,
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())));
170 // Java gives us the argument type using an extended version of the 'binary
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("(");
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(
187 kReturningJavaLangString
))));
188 std::string name_utf8
= ConvertJavaStringToUTF8(name
);
189 signature
+= BinaryNameToJNIName(name_utf8
, ¶meter_types_
[i
]);
193 // Get the return type
194 ScopedJavaLocalRef
<jclass
> clazz(env
, static_cast<jclass
>(
195 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
197 kJavaLangReflectMethod
,
199 kReturningJavaLangClass
))));
200 ScopedJavaLocalRef
<jstring
> name(env
, static_cast<jstring
>(
201 env
->CallObjectMethod(clazz
.obj(), GetMethodIDFromClassName(
205 kReturningJavaLangString
))));
206 signature
+= BinaryNameToJNIName(ConvertJavaStringToUTF8(name
),
209 // Determine whether the method is static.
210 jint modifiers
= env
->CallIntMethod(
211 java_method_
.obj(), GetMethodIDFromClassName(env
,
212 kJavaLangReflectMethod
,
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
),
222 // Get the ID for this method.
223 ScopedJavaLocalRef
<jclass
> declaring_class(env
, static_cast<jclass
>(
224 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
226 kJavaLangReflectMethod
,
228 kReturningJavaLangClass
))));
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();
237 } // namespace content