Home | History | Annotate | Download | only in java
      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
    210