blob: 9da0102cf33bb36c59103fc9a3abce4ec5cca72b [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/android/java/java_method.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "content/browser/android/java/jni_helper.h"
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::GetClass;
using base::android::MethodID;
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
namespace content {
namespace {
const char kGetName[] = "getName";
const char kGetDeclaringClass[] = "getDeclaringClass";
const char kGetModifiers[] = "getModifiers";
const char kGetParameterTypes[] = "getParameterTypes";
const char kGetReturnType[] = "getReturnType";
const char kIntegerReturningBoolean[] = "(I)Z";
const char kIsStatic[] = "isStatic";
const char kJavaLangClass[] = "java/lang/Class";
const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
const char kJavaLangReflectModifier[] = "java/lang/reflect/Modifier";
const char kReturningInteger[] = "()I";
const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
const char kReturningJavaLangClassArray[] = "()[Ljava/lang/Class;";
const char kReturningJavaLangString[] = "()Ljava/lang/String;";
struct ModifierClassTraits :
public base::internal::LeakyLazyInstanceTraits<ScopedJavaGlobalRef<
jclass> > {
static ScopedJavaGlobalRef<jclass>* New(void* instance) {
JNIEnv* env = AttachCurrentThread();
// Use placement new to initialize our instance in our preallocated space.
return new (instance) ScopedJavaGlobalRef<jclass>(
GetClass(env, kJavaLangReflectModifier));
}
};
base::LazyInstance<ScopedJavaGlobalRef<jclass>, ModifierClassTraits>
g_java_lang_reflect_modifier_class = LAZY_INSTANCE_INITIALIZER;
std::string BinaryNameToJNISignature(const std::string& binary_name,
JavaType* type) {
DCHECK(type);
*type = JavaType::CreateFromBinaryName(binary_name);
return type->JNISignature();
}
} // namespace
JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method)
: java_method_(method),
have_calculated_num_parameters_(false),
id_(NULL) {
JNIEnv* env = AttachCurrentThread();
// On construction, we do nothing except get the name. Everything else is
// done lazily.
ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
env,
kJavaLangReflectMethod,
kGetName,
kReturningJavaLangString))));
name_ = ConvertJavaStringToUTF8(name);
}
JavaMethod::~JavaMethod() {
}
size_t JavaMethod::num_parameters() const {
EnsureNumParametersIsSetUp();
return num_parameters_;
}
bool JavaMethod::is_static() const {
EnsureTypesAndIDAreSetUp();
return is_static_;
}
const JavaType& JavaMethod::parameter_type(size_t index) const {
EnsureTypesAndIDAreSetUp();
return parameter_types_[index];
}
const JavaType& JavaMethod::return_type() const {
EnsureTypesAndIDAreSetUp();
return return_type_;
}
jmethodID JavaMethod::id() const {
EnsureTypesAndIDAreSetUp();
return id_;
}
void JavaMethod::EnsureNumParametersIsSetUp() const {
if (have_calculated_num_parameters_) {
return;
}
have_calculated_num_parameters_ = true;
// The number of parameters will be used frequently when determining
// whether to call this method. We don't get the ID etc until actually
// required.
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
env,
kJavaLangReflectMethod,
kGetParameterTypes,
kReturningJavaLangClassArray))));
num_parameters_ = env->GetArrayLength(parameters.obj());
}
void JavaMethod::EnsureTypesAndIDAreSetUp() const {
if (id_) {
return;
}
// Get the parameters
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
env,
kJavaLangReflectMethod,
kGetParameterTypes,
kReturningJavaLangClassArray))));
// Usually, this will already have been called.
EnsureNumParametersIsSetUp();
DCHECK_EQ(num_parameters_,
static_cast<size_t>(env->GetArrayLength(parameters.obj())));
// Java gives us the argument type using an extended version of the 'binary
// name'. See
// http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName().
// If we build the signature now, there's no need to store the binary name
// of the arguments. We just store the simple type.
std::string signature("(");
// Form the signature and record the parameter types.
parameter_types_.resize(num_parameters_);
for (size_t i = 0; i < num_parameters_; ++i) {
ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement(
parameters.obj(), i));
ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName(
env,
kJavaLangClass,
kGetName,
kReturningJavaLangString))));
std::string name_utf8 = ConvertJavaStringToUTF8(name);
signature += BinaryNameToJNISignature(name_utf8, &parameter_types_[i]);
}
signature += ")";
// Get the return type
ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
env,
kJavaLangReflectMethod,
kGetReturnType,
kReturningJavaLangClass))));
ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
env,
kJavaLangClass,
kGetName,
kReturningJavaLangString))));
signature += BinaryNameToJNISignature(ConvertJavaStringToUTF8(name),
&return_type_);
// Determine whether the method is static.
jint modifiers = env->CallIntMethod(
java_method_.obj(), GetMethodIDFromClassName(env,
kJavaLangReflectMethod,
kGetModifiers,
kReturningInteger));
is_static_ = env->CallStaticBooleanMethod(
g_java_lang_reflect_modifier_class.Get().obj(),
MethodID::Get<MethodID::TYPE_STATIC>(
env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
kIntegerReturningBoolean),
modifiers);
// Get the ID for this method.
ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
env,
kJavaLangReflectMethod,
kGetDeclaringClass,
kReturningJavaLangClass))));
id_ = is_static_ ?
MethodID::Get<MethodID::TYPE_STATIC>(
env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
MethodID::Get<MethodID::TYPE_INSTANCE>(
env, declaring_class.obj(), name_.c_str(), signature.c_str());
java_method_.Reset();
}
} // namespace content