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 "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> > {
     42   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 
     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 
     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 
    110 JavaMethod::~JavaMethod() {
    111 }
    112 
    113 size_t JavaMethod::num_parameters() const {
    114   EnsureNumParametersIsSetUp();
    115   return num_parameters_;
    116 }
    117 
    118 bool JavaMethod::is_static() const {
    119   EnsureTypesAndIDAreSetUp();
    120   return is_static_;
    121 }
    122 
    123 const JavaType& JavaMethod::parameter_type(size_t index) const {
    124   EnsureTypesAndIDAreSetUp();
    125   return parameter_types_[index];
    126 }
    127 
    128 const JavaType& JavaMethod::return_type() const {
    129   EnsureTypesAndIDAreSetUp();
    130   return return_type_;
    131 }
    132 
    133 jmethodID JavaMethod::id() const {
    134   EnsureTypesAndIDAreSetUp();
    135   return id_;
    136 }
    137 
    138 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 
    157 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