// Copyright 2014 The Chromium Authors
// 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 "content/browser/android/java/jni_reflect.h"
using base::android::AttachCurrentThread;
using base::android::MethodID;
using base::android::ScopedJavaLocalRef;
namespace content {
namespace {
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.
name_ = GetMethodName(env, java_method_);
}
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<jobjectArray> parameters(
GetMethodParameterTypes(env, java_method_));
num_parameters_ = env->GetArrayLength(parameters.obj());
}
void JavaMethod::EnsureTypesAndIDAreSetUp() const {
if (id_) {
return;
}
// Get the parameters
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> parameters(
GetMethodParameterTypes(env, java_method_));
// 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<jclass> parameter(
env,
static_cast<jclass>(env->GetObjectArrayElement(parameters.obj(), i)));
signature += BinaryNameToJNISignature(GetClassName(env, parameter),
¶meter_types_[i]);
if (parameter_types_[i].type == JavaType::TypeObject) {
parameter_types_[i].class_ref.Reset(parameter);
}
}
signature += ")";
// Get the return type
ScopedJavaLocalRef<jclass> clazz(GetMethodReturnType(env, java_method_));
signature +=
BinaryNameToJNISignature(GetClassName(env, clazz), &return_type_);
// Determine whether the method is static.
is_static_ = IsMethodStatic(env, java_method_);
// Get the ID for this method.
ScopedJavaLocalRef<jclass> declaring_class(
GetMethodDeclaringClass(env, java_method_));
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