• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/strings/string_util.h"  // For ReplaceSubstringsAfterOffset
12 #include "content/browser/android/java/jni_helper.h"
13 
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;
20 
21 namespace content {
22 namespace {
23 
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;";
38 
39 struct ModifierClassTraits :
40       public base::internal::LeakyLazyInstanceTraits<ScopedJavaGlobalRef<
41                                                          jclass> > {
Newcontent::__anon0e4d19630111::ModifierClassTraits42   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));
47   }
48 };
49 
50 base::LazyInstance<ScopedJavaGlobalRef<jclass>, ModifierClassTraits>
51     g_java_lang_reflect_modifier_class = LAZY_INSTANCE_INITIALIZER;
52 
BinaryNameToJNIName(const std::string & binary_name,JavaType * type)53 std::string BinaryNameToJNIName(const std::string& binary_name,
54                                 JavaType* type) {
55   DCHECK(type);
56   *type = JavaType::CreateFromBinaryName(binary_name);
57   switch (type->type) {
58     case JavaType::TypeBoolean:
59       return "Z";
60     case JavaType::TypeByte:
61       return "B";
62     case JavaType::TypeChar:
63       return "C";
64     case JavaType::TypeShort:
65       return "S";
66     case JavaType::TypeInt:
67       return "I";
68     case JavaType::TypeLong:
69       return "J";
70     case JavaType::TypeFloat:
71       return "F";
72     case JavaType::TypeDouble:
73       return "D";
74     case JavaType::TypeVoid:
75       return "V";
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, ".", "/");
80       return jni_name;
81     }
82     case JavaType::TypeString:
83     case JavaType::TypeObject:
84       std::string jni_name = "L" + binary_name + ";";
85       ReplaceSubstringsAfterOffset(&jni_name, 0, ".", "/");
86       return jni_name;
87   }
88   NOTREACHED();
89   return std::string();
90 }
91 
92 }  // namespace
93 
JavaMethod(const base::android::JavaRef<jobject> & method)94 JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method)
95     : java_method_(method),
96       have_calculated_num_parameters_(false),
97       id_(NULL) {
98   JNIEnv* env = AttachCurrentThread();
99   // On construction, we do nothing except get the name. Everything else is
100   // done lazily.
101   ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
102       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
103           env,
104           kJavaLangReflectMethod,
105           kGetName,
106           kReturningJavaLangString))));
107   name_ = ConvertJavaStringToUTF8(name);
108 }
109 
~JavaMethod()110 JavaMethod::~JavaMethod() {
111 }
112 
num_parameters() const113 size_t JavaMethod::num_parameters() const {
114   EnsureNumParametersIsSetUp();
115   return num_parameters_;
116 }
117 
is_static() const118 bool JavaMethod::is_static() const {
119   EnsureTypesAndIDAreSetUp();
120   return is_static_;
121 }
122 
parameter_type(size_t index) const123 const JavaType& JavaMethod::parameter_type(size_t index) const {
124   EnsureTypesAndIDAreSetUp();
125   return parameter_types_[index];
126 }
127 
return_type() const128 const JavaType& JavaMethod::return_type() const {
129   EnsureTypesAndIDAreSetUp();
130   return return_type_;
131 }
132 
id() const133 jmethodID JavaMethod::id() const {
134   EnsureTypesAndIDAreSetUp();
135   return id_;
136 }
137 
EnsureNumParametersIsSetUp() const138 void JavaMethod::EnsureNumParametersIsSetUp() const {
139   if (have_calculated_num_parameters_) {
140     return;
141   }
142   have_calculated_num_parameters_ = true;
143 
144   // The number of parameters will be used frequently when determining
145   // whether to call this method. We don't get the ID etc until actually
146   // required.
147   JNIEnv* env = AttachCurrentThread();
148   ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
149       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
150           env,
151           kJavaLangReflectMethod,
152           kGetParameterTypes,
153           kReturningJavaLangClassArray))));
154   num_parameters_ = env->GetArrayLength(parameters.obj());
155 }
156 
EnsureTypesAndIDAreSetUp() const157 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
158   if (id_) {
159     return;
160   }
161 
162   // Get the parameters
163   JNIEnv* env = AttachCurrentThread();
164   ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
165       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
166           env,
167           kJavaLangReflectMethod,
168           kGetParameterTypes,
169           kReturningJavaLangClassArray))));
170   // Usually, this will already have been called.
171   EnsureNumParametersIsSetUp();
172   DCHECK_EQ(num_parameters_,
173             static_cast<size_t>(env->GetArrayLength(parameters.obj())));
174 
175   // Java gives us the argument type using an extended version of the 'binary
176   // name'. See
177   // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName().
178   // If we build the signature now, there's no need to store the binary name
179   // of the arguments. We just store the simple type.
180   std::string signature("(");
181 
182   // Form the signature and record the parameter types.
183   parameter_types_.resize(num_parameters_);
184   for (size_t i = 0; i < num_parameters_; ++i) {
185     ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement(
186         parameters.obj(), i));
187     ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
188         env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName(
189             env,
190             kJavaLangClass,
191             kGetName,
192             kReturningJavaLangString))));
193     std::string name_utf8 = ConvertJavaStringToUTF8(name);
194     signature += BinaryNameToJNIName(name_utf8, &parameter_types_[i]);
195   }
196   signature += ")";
197 
198   // Get the return type
199   ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
200       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
201           env,
202           kJavaLangReflectMethod,
203           kGetReturnType,
204           kReturningJavaLangClass))));
205   ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
206       env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
207           env,
208           kJavaLangClass,
209           kGetName,
210           kReturningJavaLangString))));
211   signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(name),
212                                    &return_type_);
213 
214   // Determine whether the method is static.
215   jint modifiers = env->CallIntMethod(
216       java_method_.obj(), GetMethodIDFromClassName(env,
217                                                    kJavaLangReflectMethod,
218                                                    kGetModifiers,
219                                                    kReturningInteger));
220   is_static_ = env->CallStaticBooleanMethod(
221       g_java_lang_reflect_modifier_class.Get().obj(),
222       MethodID::Get<MethodID::TYPE_STATIC>(
223           env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
224           kIntegerReturningBoolean),
225       modifiers);
226 
227   // Get the ID for this method.
228   ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
229       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
230           env,
231           kJavaLangReflectMethod,
232           kGetDeclaringClass,
233           kReturningJavaLangClass))));
234   id_ = is_static_ ?
235       MethodID::Get<MethodID::TYPE_STATIC>(
236           env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
237       MethodID::Get<MethodID::TYPE_INSTANCE>(
238           env, declaring_class.obj(), name_.c_str(), signature.c_str());
239   java_method_.Reset();
240 }
241 
242 }  // namespace content
243