Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / android / java / java_method.cc
blob3b4b6c0185e4513ae6af1016a0a5156c5b6657ce
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;
20 namespace content {
21 namespace {
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<
40 jclass> > {
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,
53 JavaType* type) {
54 DCHECK(type);
55 *type = JavaType::CreateFromBinaryName(binary_name);
56 return type->JNISignature();
59 } // namespace
61 JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method)
62 : java_method_(method),
63 have_calculated_num_parameters_(false),
64 id_(NULL) {
65 JNIEnv* env = AttachCurrentThread();
66 // On construction, we do nothing except get the name. Everything else is
67 // done lazily.
68 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
69 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
70 env,
71 kJavaLangReflectMethod,
72 kGetName,
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();
87 return is_static_;
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();
97 return return_type_;
100 jmethodID JavaMethod::id() const {
101 EnsureTypesAndIDAreSetUp();
102 return id_;
105 void JavaMethod::EnsureNumParametersIsSetUp() const {
106 if (have_calculated_num_parameters_) {
107 return;
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
113 // required.
114 JNIEnv* env = AttachCurrentThread();
115 ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
116 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
117 env,
118 kJavaLangReflectMethod,
119 kGetParameterTypes,
120 kReturningJavaLangClassArray))));
121 num_parameters_ = env->GetArrayLength(parameters.obj());
124 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
125 if (id_) {
126 return;
129 // Get the parameters
130 JNIEnv* env = AttachCurrentThread();
131 ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
132 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
133 env,
134 kJavaLangReflectMethod,
135 kGetParameterTypes,
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
143 // name'. See
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<jclass> parameter(
153 env,
154 static_cast<jclass>(env->GetObjectArrayElement(parameters.obj(), i)));
155 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
156 env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName(
157 env,
158 kJavaLangClass,
159 kGetName,
160 kReturningJavaLangString))));
161 std::string name_utf8 = ConvertJavaStringToUTF8(name);
162 signature += BinaryNameToJNISignature(name_utf8, &parameter_types_[i]);
163 if (parameter_types_[i].type == JavaType::TypeObject) {
164 parameter_types_[i].class_ref.Reset(parameter);
167 signature += ")";
169 // Get the return type
170 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
171 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
172 env,
173 kJavaLangReflectMethod,
174 kGetReturnType,
175 kReturningJavaLangClass))));
176 ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
177 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
178 env,
179 kJavaLangClass,
180 kGetName,
181 kReturningJavaLangString))));
182 signature += BinaryNameToJNISignature(ConvertJavaStringToUTF8(name),
183 &return_type_);
185 // Determine whether the method is static.
186 jint modifiers = env->CallIntMethod(
187 java_method_.obj(), GetMethodIDFromClassName(env,
188 kJavaLangReflectMethod,
189 kGetModifiers,
190 kReturningInteger));
191 is_static_ = env->CallStaticBooleanMethod(
192 g_java_lang_reflect_modifier_class.Get().obj(),
193 MethodID::Get<MethodID::TYPE_STATIC>(
194 env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
195 kIntegerReturningBoolean),
196 modifiers);
198 // Get the ID for this method.
199 ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
200 env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
201 env,
202 kJavaLangReflectMethod,
203 kGetDeclaringClass,
204 kReturningJavaLangClass))));
205 id_ = is_static_ ?
206 MethodID::Get<MethodID::TYPE_STATIC>(
207 env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
208 MethodID::Get<MethodID::TYPE_INSTANCE>(
209 env, declaring_class.obj(), name_.c_str(), signature.c_str());
210 java_method_.Reset();
213 } // namespace content