1 // Copyright 2014 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/android/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 "content/browser/android/java/jni_helper.h"
13 using base::android::AttachCurrentThread
;
14 using base::android::ConvertJavaStringToUTF8
;
15 using base::android::GetClass
;
16 using base::android::MethodID
;
17 using base::android::ScopedJavaGlobalRef
;
18 using base::android::ScopedJavaLocalRef
;
23 const char kGetName
[] = "getName";
24 const char kGetDeclaringClass
[] = "getDeclaringClass";
25 const char kGetModifiers
[] = "getModifiers";
26 const char kGetParameterTypes
[] = "getParameterTypes";
27 const char kGetReturnType
[] = "getReturnType";
28 const char kIntegerReturningBoolean
[] = "(I)Z";
29 const char kIsStatic
[] = "isStatic";
30 const char kJavaLangClass
[] = "java/lang/Class";
31 const char kJavaLangReflectMethod
[] = "java/lang/reflect/Method";
32 const char kJavaLangReflectModifier
[] = "java/lang/reflect/Modifier";
33 const char kReturningInteger
[] = "()I";
34 const char kReturningJavaLangClass
[] = "()Ljava/lang/Class;";
35 const char kReturningJavaLangClassArray
[] = "()[Ljava/lang/Class;";
36 const char kReturningJavaLangString
[] = "()Ljava/lang/String;";
38 struct ModifierClassTraits
:
39 public base::internal::LeakyLazyInstanceTraits
<ScopedJavaGlobalRef
<
41 static ScopedJavaGlobalRef
<jclass
>* New(void* instance
) {
42 JNIEnv
* env
= AttachCurrentThread();
43 // Use placement new to initialize our instance in our preallocated space.
44 return new (instance
) ScopedJavaGlobalRef
<jclass
>(
45 GetClass(env
, kJavaLangReflectModifier
));
49 base::LazyInstance
<ScopedJavaGlobalRef
<jclass
>, ModifierClassTraits
>
50 g_java_lang_reflect_modifier_class
= LAZY_INSTANCE_INITIALIZER
;
52 std::string
BinaryNameToJNISignature(const std::string
& binary_name
,
55 *type
= JavaType::CreateFromBinaryName(binary_name
);
56 return type
->JNISignature();
61 JavaMethod::JavaMethod(const base::android::JavaRef
<jobject
>& method
)
62 : java_method_(method
),
63 have_calculated_num_parameters_(false),
65 JNIEnv
* env
= AttachCurrentThread();
66 // On construction, we do nothing except get the name. Everything else is
68 ScopedJavaLocalRef
<jstring
> name(env
, static_cast<jstring
>(
69 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
71 kJavaLangReflectMethod
,
73 kReturningJavaLangString
))));
74 name_
= ConvertJavaStringToUTF8(name
);
77 JavaMethod::~JavaMethod() {
80 size_t JavaMethod::num_parameters() const {
81 EnsureNumParametersIsSetUp();
82 return num_parameters_
;
85 bool JavaMethod::is_static() const {
86 EnsureTypesAndIDAreSetUp();
90 const JavaType
& JavaMethod::parameter_type(size_t index
) const {
91 EnsureTypesAndIDAreSetUp();
92 return parameter_types_
[index
];
95 const JavaType
& JavaMethod::return_type() const {
96 EnsureTypesAndIDAreSetUp();
100 jmethodID
JavaMethod::id() const {
101 EnsureTypesAndIDAreSetUp();
105 void JavaMethod::EnsureNumParametersIsSetUp() const {
106 if (have_calculated_num_parameters_
) {
109 have_calculated_num_parameters_
= true;
111 // The number of parameters will be used frequently when determining
112 // whether to call this method. We don't get the ID etc until actually
114 JNIEnv
* env
= AttachCurrentThread();
115 ScopedJavaLocalRef
<jarray
> parameters(env
, static_cast<jarray
>(
116 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
118 kJavaLangReflectMethod
,
120 kReturningJavaLangClassArray
))));
121 num_parameters_
= env
->GetArrayLength(parameters
.obj());
124 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
129 // Get the parameters
130 JNIEnv
* env
= AttachCurrentThread();
131 ScopedJavaLocalRef
<jobjectArray
> parameters(env
, static_cast<jobjectArray
>(
132 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
134 kJavaLangReflectMethod
,
136 kReturningJavaLangClassArray
))));
137 // Usually, this will already have been called.
138 EnsureNumParametersIsSetUp();
139 DCHECK_EQ(num_parameters_
,
140 static_cast<size_t>(env
->GetArrayLength(parameters
.obj())));
142 // Java gives us the argument type using an extended version of the 'binary
144 // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName().
145 // If we build the signature now, there's no need to store the binary name
146 // of the arguments. We just store the simple type.
147 std::string
signature("(");
149 // Form the signature and record the parameter types.
150 parameter_types_
.resize(num_parameters_
);
151 for (size_t i
= 0; i
< num_parameters_
; ++i
) {
152 ScopedJavaLocalRef
<jobject
> parameter(env
, env
->GetObjectArrayElement(
153 parameters
.obj(), i
));
154 ScopedJavaLocalRef
<jstring
> name(env
, static_cast<jstring
>(
155 env
->CallObjectMethod(parameter
.obj(), GetMethodIDFromClassName(
159 kReturningJavaLangString
))));
160 std::string name_utf8
= ConvertJavaStringToUTF8(name
);
161 signature
+= BinaryNameToJNISignature(name_utf8
, ¶meter_types_
[i
]);
165 // Get the return type
166 ScopedJavaLocalRef
<jclass
> clazz(env
, static_cast<jclass
>(
167 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
169 kJavaLangReflectMethod
,
171 kReturningJavaLangClass
))));
172 ScopedJavaLocalRef
<jstring
> name(env
, static_cast<jstring
>(
173 env
->CallObjectMethod(clazz
.obj(), GetMethodIDFromClassName(
177 kReturningJavaLangString
))));
178 signature
+= BinaryNameToJNISignature(ConvertJavaStringToUTF8(name
),
181 // Determine whether the method is static.
182 jint modifiers
= env
->CallIntMethod(
183 java_method_
.obj(), GetMethodIDFromClassName(env
,
184 kJavaLangReflectMethod
,
187 is_static_
= env
->CallStaticBooleanMethod(
188 g_java_lang_reflect_modifier_class
.Get().obj(),
189 MethodID::Get
<MethodID::TYPE_STATIC
>(
190 env
, g_java_lang_reflect_modifier_class
.Get().obj(), kIsStatic
,
191 kIntegerReturningBoolean
),
194 // Get the ID for this method.
195 ScopedJavaLocalRef
<jclass
> declaring_class(env
, static_cast<jclass
>(
196 env
->CallObjectMethod(java_method_
.obj(), GetMethodIDFromClassName(
198 kJavaLangReflectMethod
,
200 kReturningJavaLangClass
))));
202 MethodID::Get
<MethodID::TYPE_STATIC
>(
203 env
, declaring_class
.obj(), name_
.c_str(), signature
.c_str()) :
204 MethodID::Get
<MethodID::TYPE_INSTANCE
>(
205 env
, declaring_class
.obj(), name_
.c_str(), signature
.c_str());
206 java_method_
.Reset();
209 } // namespace content