Home | History | Annotate | Download | only in java
      1 // Copyright (c) 2012 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/renderer_host/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 
     13 using base::android::AttachCurrentThread;
     14 using base::android::ConvertJavaStringToUTF8;
     15 using base::android::GetClass;
     16 using base::android::GetMethodIDFromClassName;
     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 EmptyString();
     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 const JavaType& JavaMethod::parameter_type(size_t index) const {
    119   EnsureTypesAndIDAreSetUp();
    120   return parameter_types_[index];
    121 }
    122 
    123 const JavaType& JavaMethod::return_type() const {
    124   EnsureTypesAndIDAreSetUp();
    125   return return_type_;
    126 }
    127 
    128 jmethodID JavaMethod::id() const {
    129   EnsureTypesAndIDAreSetUp();
    130   return id_;
    131 }
    132 
    133 void JavaMethod::EnsureNumParametersIsSetUp() const {
    134   if (have_calculated_num_parameters_) {
    135     return;
    136   }
    137   have_calculated_num_parameters_ = true;
    138 
    139   // The number of parameters will be used frequently when determining
    140   // whether to call this method. We don't get the ID etc until actually
    141   // required.
    142   JNIEnv* env = AttachCurrentThread();
    143   ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
    144       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
    145           env,
    146           kJavaLangReflectMethod,
    147           kGetParameterTypes,
    148           kReturningJavaLangClassArray))));
    149   num_parameters_ = env->GetArrayLength(parameters.obj());
    150 }
    151 
    152 void JavaMethod::EnsureTypesAndIDAreSetUp() const {
    153   if (id_) {
    154     return;
    155   }
    156 
    157   // Get the parameters
    158   JNIEnv* env = AttachCurrentThread();
    159   ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
    160       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
    161           env,
    162           kJavaLangReflectMethod,
    163           kGetParameterTypes,
    164           kReturningJavaLangClassArray))));
    165   // Usually, this will already have been called.
    166   EnsureNumParametersIsSetUp();
    167   DCHECK_EQ(num_parameters_,
    168             static_cast<size_t>(env->GetArrayLength(parameters.obj())));
    169 
    170   // Java gives us the argument type using an extended version of the 'binary
    171   // name'. See
    172   // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName().
    173   // If we build the signature now, there's no need to store the binary name
    174   // of the arguments. We just store the simple type.
    175   std::string signature("(");
    176 
    177   // Form the signature and record the parameter types.
    178   parameter_types_.resize(num_parameters_);
    179   for (size_t i = 0; i < num_parameters_; ++i) {
    180     ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement(
    181         parameters.obj(), i));
    182     ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
    183         env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName(
    184             env,
    185             kJavaLangClass,
    186             kGetName,
    187             kReturningJavaLangString))));
    188     std::string name_utf8 = ConvertJavaStringToUTF8(name);
    189     signature += BinaryNameToJNIName(name_utf8, &parameter_types_[i]);
    190   }
    191   signature += ")";
    192 
    193   // Get the return type
    194   ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
    195       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
    196           env,
    197           kJavaLangReflectMethod,
    198           kGetReturnType,
    199           kReturningJavaLangClass))));
    200   ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
    201       env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
    202           env,
    203           kJavaLangClass,
    204           kGetName,
    205           kReturningJavaLangString))));
    206   signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(name),
    207                                    &return_type_);
    208 
    209   // Determine whether the method is static.
    210   jint modifiers = env->CallIntMethod(
    211       java_method_.obj(), GetMethodIDFromClassName(env,
    212                                                    kJavaLangReflectMethod,
    213                                                    kGetModifiers,
    214                                                    kReturningInteger));
    215   bool is_static = env->CallStaticBooleanMethod(
    216       g_java_lang_reflect_modifier_class.Get().obj(),
    217       MethodID::Get<MethodID::TYPE_STATIC>(
    218           env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
    219           kIntegerReturningBoolean),
    220       modifiers);
    221 
    222   // Get the ID for this method.
    223   ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
    224       env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
    225           env,
    226           kJavaLangReflectMethod,
    227           kGetDeclaringClass,
    228           kReturningJavaLangClass))));
    229   id_ = is_static ?
    230       MethodID::Get<MethodID::TYPE_STATIC>(
    231           env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
    232       MethodID::Get<MethodID::TYPE_INSTANCE>(
    233           env, declaring_class.obj(), name_.c_str(), signature.c_str());
    234   java_method_.Reset();
    235 }
    236 
    237 }  // namespace content
    238