Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / android / java / java_method.cc
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.
4
5 #include "content/browser/android/java/java_method.h"
6
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"
12
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;
19
20 namespace content {
21 namespace {
22
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;";
37
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));
46   }
47 };
48
49 base::LazyInstance<ScopedJavaGlobalRef<jclass>, ModifierClassTraits>
50     g_java_lang_reflect_modifier_class = LAZY_INSTANCE_INITIALIZER;
51
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();
57 }
58
59 }  // namespace
60
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);
75 }
76
77 JavaMethod::~JavaMethod() {
78 }
79
80 size_t JavaMethod::num_parameters() const {
81   EnsureNumParametersIsSetUp();
82   return num_parameters_;
83 }
84
85 bool JavaMethod::is_static() const {
86   EnsureTypesAndIDAreSetUp();
87   return is_static_;
88 }
89
90 const JavaType& JavaMethod::parameter_type(size_t index) const {
91   EnsureTypesAndIDAreSetUp();
92   return parameter_types_[index];
93 }
94
95 const JavaType& JavaMethod::return_type() const {
96   EnsureTypesAndIDAreSetUp();
97   return return_type_;
98 }
99
100 jmethodID JavaMethod::id() const {
101   EnsureTypesAndIDAreSetUp();
102   return id_;
103 }
104
105 void JavaMethod::EnsureNumParametersIsSetUp() const {
106   if (have_calculated_num_parameters_) {
107     return;
108   }
109   have_calculated_num_parameters_ = true;
110
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());
122 }
123
124 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
125   if (id_) {
126     return;
127   }
128
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())));
141
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("(");
148
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(
156             env,
157             kJavaLangClass,
158             kGetName,
159             kReturningJavaLangString))));
160     std::string name_utf8 = ConvertJavaStringToUTF8(name);
161     signature += BinaryNameToJNISignature(name_utf8, &parameter_types_[i]);
162   }
163   signature += ")";
164
165   // Get the return type
166   ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
167       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
168           env,
169           kJavaLangReflectMethod,
170           kGetReturnType,
171           kReturningJavaLangClass))));
172   ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
173       env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
174           env,
175           kJavaLangClass,
176           kGetName,
177           kReturningJavaLangString))));
178   signature += BinaryNameToJNISignature(ConvertJavaStringToUTF8(name),
179                                         &return_type_);
180
181   // Determine whether the method is static.
182   jint modifiers = env->CallIntMethod(
183       java_method_.obj(), GetMethodIDFromClassName(env,
184                                                    kJavaLangReflectMethod,
185                                                    kGetModifiers,
186                                                    kReturningInteger));
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),
192       modifiers);
193
194   // Get the ID for this method.
195   ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
196       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
197           env,
198           kJavaLangReflectMethod,
199           kGetDeclaringClass,
200           kReturningJavaLangClass))));
201   id_ = is_static_ ?
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();
207 }
208
209 }  // namespace content