Add JavaBoundObject

This represents a Java object for use in the Java bridge. It handles all of the
JNI required to interrogate the Java object and call methods on it.

Also adds some JNI utility methods to simpify the new code.

BUG=96703

Review URL: http://codereview.chromium.org/8509019

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@109645 0039d316-1c4b-4281-b951-d872f2087c98
parent 3826a8a0
......@@ -4,6 +4,7 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/logging.h"
namespace {
......@@ -46,6 +47,12 @@ jobject GetApplicationContext() {
return g_application_context;
}
MethodID::MethodID(JNIEnv* env, const char* class_name, const char* method,
const char* jni_signature) {
ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
id_ = GetMethodID(env, clazz.obj(), method, jni_signature);
}
jmethodID GetMethodID(JNIEnv* env,
jclass clazz,
const char* const method,
......@@ -66,6 +73,16 @@ jmethodID GetStaticMethodID(JNIEnv* env,
return id;
}
jfieldID GetFieldID(JNIEnv* env,
jclass clazz,
const char* field,
const char* jni_signature) {
jfieldID id = env->GetFieldID(clazz, field, jni_signature);
DCHECK(id) << field;
CheckException(env);
return id;
}
bool CheckException(JNIEnv* env) {
if (env->ExceptionCheck() == JNI_FALSE)
return false;
......
......@@ -30,6 +30,20 @@ void InitApplicationContext(jobject context);
// Returns the application context assigned by InitApplicationContext().
jobject GetApplicationContext();
// Wraps a method ID.
class MethodID {
public:
jmethodID id() { return id_; }
protected:
// Gets the method ID from the class name. Clears the pending Java exception
// and returns NULL if the method is not found. Note that GetMethodID() below
// avoids a class lookup, so should be used in preference when possible.
MethodID(JNIEnv* env, const char* class_name, const char* method,
const char* jni_signature);
private:
jmethodID id_;
};
// Get the method ID for a method. Will clear the pending Java
// exception and return 0 if the method is not found.
jmethodID GetMethodID(JNIEnv* env,
......@@ -44,8 +58,15 @@ jmethodID GetStaticMethodID(JNIEnv* env,
const char* const method,
const char* const jni_signature);
// Returns true if an exception is pending in the provided JNIEnv*.
// If an exception is pending, it is printed.
// Gets the field ID for a class field. Clears the pending Java exception and
// returns NULL if the field is not found.
jfieldID GetFieldID(JNIEnv* env,
jclass clazz,
const char* field,
const char* jni_signature);
// Returns true if an exception is pending in the provided JNIEnv*. If an
// exception is pending, this function prints and then clears it.
bool CheckException(JNIEnv* env);
} // namespace android
......
This diff is collapsed.
// Copyright (c) 2011 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
#include <jni.h>
#include <map>
#include <string>
#include "base/android/scoped_java_ref.h"
#include "base/memory/linked_ptr.h"
#include "content/browser/renderer_host/java/java_method.h"
#include "third_party/npapi/bindings/npruntime.h"
// Wrapper around a Java object.
//
// Represents a Java object for use in the Java bridge. Holds a global ref to
// the Java object and provides the ability to invoke methods on it.
// Interrogation of the Java object for its methods is done lazily. This class
// is not generally threadsafe. However, it does allow for instances to be
// created and destroyed on different threads.
class JavaBoundObject {
public:
// Takes a Java object and creates a JavaBoundObject around it. Returns an
// NPObject with a ref count of one which owns the JavaBoundObject.
static NPObject* Create(const base::android::JavaRef<jobject>& object);
virtual ~JavaBoundObject();
// Gets the underlying JavaObject from a JavaBoundObject wrapped as an
// NPObject.
static jobject GetJavaObject(NPObject* object);
// Methods to implement the NPObject callbacks.
bool HasMethod(const std::string& name) const;
bool Invoke(const std::string& name, const NPVariant* args, size_t arg_count,
NPVariant* result);
private:
explicit JavaBoundObject(const base::android::JavaRef<jobject>& object);
void EnsureMethodsAreSetUp() const;
// Global ref to the underlying Java object. We use a naked jobject, rather
// than a ScopedJavaGlobalRef, as the global ref will be added and dropped on
// different threads.
jobject java_object_;
// Map of public methods, from method name to Method instance. Multiple
// entries will be present for overloaded methods. Note that we can't use
// scoped_ptr in STL containers as we can't copy it.
typedef std::multimap<std::string, linked_ptr<JavaMethod> > JavaMethodMap;
mutable JavaMethodMap methods_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JavaBoundObject);
};
#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
// Copyright (c) 2011 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/renderer_host/java/java_method.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/memory/singleton.h"
#include "base/string_util.h" // For ReplaceSubstringsAfterOffset
using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF8;
using base::android::MethodID;
using base::android::ScopedJavaLocalRef;
namespace {
// Java's reflection API represents types as a string using an extended 'binary
// name'. This converts to an enum which we store in place of the binary name
// for simplicity.
JavaType::Type BinaryNameToType(const std::string& binary_name) {
if (binary_name == "boolean") {
return JavaType::TypeBoolean;
} else if (binary_name == "byte") {
return JavaType::TypeByte;
} else if (binary_name == "char") {
return JavaType::TypeChar;
} else if (binary_name == "short") {
return JavaType::TypeShort;
} else if (binary_name == "int") {
return JavaType::TypeInt;
} else if (binary_name == "long") {
return JavaType::TypeLong;
} else if (binary_name == "float") {
return JavaType::TypeFloat;
} else if (binary_name == "double") {
return JavaType::TypeDouble;
} else if (binary_name == "void") {
return JavaType::TypeVoid;
} else if (binary_name[0] == '[') {
return JavaType::TypeArray;
} else if (binary_name == "java.lang.String") {
return JavaType::TypeString;
}
return JavaType::TypeObject;
}
std::string BinaryNameToJNIName(const std::string& binary_name,
JavaType::Type* type) {
DCHECK(type);
*type = BinaryNameToType(binary_name);
switch (*type) {
case JavaType::TypeBoolean:
return "Z";
case JavaType::TypeByte:
return "B";
case JavaType::TypeChar:
return "C";
case JavaType::TypeShort:
return "S";
case JavaType::TypeInt:
return "I";
case JavaType::TypeLong:
return "J";
case JavaType::TypeFloat:
return "F";
case JavaType::TypeDouble:
return "D";
case JavaType::TypeVoid:
return "V";
case JavaType::TypeArray:
return "[";
default:
DCHECK (*type == JavaType::TypeString || *type == JavaType::TypeObject);
std::string jni_name = "L" + binary_name + ";";
ReplaceSubstringsAfterOffset(&jni_name, 0, ".", "/");
return jni_name;
}
}
class MethodGetParameterTypesID : public MethodID {
public:
static MethodGetParameterTypesID* GetInstance() {
return Singleton<MethodGetParameterTypesID>::get();
}
private:
friend struct DefaultSingletonTraits<MethodGetParameterTypesID>;
MethodGetParameterTypesID()
: MethodID(AttachCurrentThread(), "java/lang/reflect/Method",
"getParameterTypes", "()[Ljava/lang/Class;") {
}
DISALLOW_COPY_AND_ASSIGN(MethodGetParameterTypesID);
};
class MethodGetNameID : public MethodID {
public:
static MethodGetNameID* GetInstance() {
return Singleton<MethodGetNameID>::get();
}
private:
friend struct DefaultSingletonTraits<MethodGetNameID>;
MethodGetNameID()
: MethodID(AttachCurrentThread(), "java/lang/reflect/Method",
"getName", "()Ljava/lang/String;") {
}
DISALLOW_COPY_AND_ASSIGN(MethodGetNameID);
};
class MethodGetReturnTypeID : public MethodID {
public:
static MethodGetReturnTypeID* GetInstance() {
return Singleton<MethodGetReturnTypeID>::get();
}
private:
friend struct DefaultSingletonTraits<MethodGetReturnTypeID>;
MethodGetReturnTypeID()
: MethodID(AttachCurrentThread(), "java/lang/reflect/Method",
"getReturnType", "()Ljava/lang/Class;") {
}
DISALLOW_COPY_AND_ASSIGN(MethodGetReturnTypeID);
};
class MethodGetDeclaringClassID : public MethodID {
public:
static MethodGetDeclaringClassID* GetInstance() {
return Singleton<MethodGetDeclaringClassID>::get();
}
private:
friend struct DefaultSingletonTraits<MethodGetDeclaringClassID>;
MethodGetDeclaringClassID()
: MethodID(AttachCurrentThread(), "java/lang/reflect/Method",
"getDeclaringClass", "()Ljava/lang/Class;") {
}
DISALLOW_COPY_AND_ASSIGN(MethodGetDeclaringClassID);
};
class ClassGetNameID : public MethodID {
public:
static ClassGetNameID* GetInstance() {
return Singleton<ClassGetNameID>::get();
}
private:
friend struct DefaultSingletonTraits<ClassGetNameID>;
ClassGetNameID()
: MethodID(AttachCurrentThread(), "java/lang/Class", "getName",
"()Ljava/lang/String;") {
}
DISALLOW_COPY_AND_ASSIGN(ClassGetNameID);
};
} // namespace
JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method)
: java_method_(method),
have_calculated_num_parameters_(false),
id_(NULL) {
JNIEnv* env = java_method_.env();
// 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(),
MethodGetNameID::GetInstance()->id())));
name_ = ConvertJavaStringToUTF8(env, name.obj());
}
JavaMethod::~JavaMethod() {
}
size_t JavaMethod::num_parameters() const {
EnsureNumParametersIsSetUp();
return num_parameters_;
}
JavaType::Type JavaMethod::parameter_type(size_t index) const {
EnsureTypesAndIDAreSetUp();
return parameter_types_[index];
}
JavaType::Type 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 = java_method_.env();
ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
env->CallObjectMethod(java_method_.obj(),
MethodGetParameterTypesID::GetInstance()->id())));
num_parameters_ = env->GetArrayLength(parameters.obj());
}
void JavaMethod::EnsureTypesAndIDAreSetUp() const {
if (id_) {
return;
}
// Get the parameters
JNIEnv* env = java_method_.env();
ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
env->CallObjectMethod(java_method_.obj(),
MethodGetParameterTypesID::GetInstance()->id())));
// 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(),
ClassGetNameID::GetInstance()->id())));
std::string name_utf8 = ConvertJavaStringToUTF8(env, name.obj());
signature += BinaryNameToJNIName(name_utf8, &parameter_types_[i]);
}
signature += ")";
// Get the return type
ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
env->CallObjectMethod(java_method_.obj(),
MethodGetReturnTypeID::GetInstance()->id())));
ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
env->CallObjectMethod(clazz.obj(), ClassGetNameID::GetInstance()->id())));
signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(env, name.obj()),
&return_type_);
// Get the ID for this method.
ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
env->CallObjectMethod(java_method_.obj(),
MethodGetDeclaringClassID::GetInstance()->id())));
id_ = base::android::GetMethodID(env, declaring_class.obj(), name_.c_str(),
signature.c_str());
java_method_.Reset();
}
// Copyright (c) 2011 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
#include <jni.h>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
namespace JavaType {
enum Type {
TypeBoolean,
TypeByte,
TypeChar,
TypeShort,
TypeInt,
TypeLong,
TypeFloat,
TypeDouble,
// This is only used as a return type, so we should never convert from
// JavaScript with this type.
TypeVoid,
TypeArray,
// We special-case strings, as they get special handling when coercing.
TypeString,
TypeObject,
};
} // namespace JavaType
// Wrapper around java.lang.reflect.Method. This class must be used on a single
// thread only.
class JavaMethod {
public:
explicit JavaMethod(const base::android::JavaRef<jobject>& method);
~JavaMethod();
const std::string& name() const { return name_; }
size_t num_parameters() const;
JavaType::Type parameter_type(size_t index) const;
JavaType::Type return_type() const;
jmethodID id() const;
private:
void EnsureNumParametersIsSetUp() const;
void EnsureTypesAndIDAreSetUp() const;
std::string name_;
mutable base::android::ScopedJavaGlobalRef<jobject> java_method_;
mutable bool have_calculated_num_parameters_;
mutable size_t num_parameters_;
mutable std::vector<JavaType::Type> parameter_types_;
mutable JavaType::Type return_type_;
mutable jmethodID id_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JavaMethod);
};
#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
......@@ -346,12 +346,16 @@
'browser/renderer_host/gtk_window_utils.h',
'browser/renderer_host/image_transport_client.cc',
'browser/renderer_host/image_transport_client.h',
'browser/renderer_host/java/java_bound_object.cc',
'browser/renderer_host/java/java_bound_object.h',
'browser/renderer_host/java/java_bridge_channel_host.cc',
'browser/renderer_host/java/java_bridge_channel_host.h',
'browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc',
'browser/renderer_host/java/java_bridge_dispatcher_host_manager.h',
'browser/renderer_host/java/java_bridge_dispatcher_host.cc',
'browser/renderer_host/java/java_bridge_dispatcher_host.h',
'browser/renderer_host/java/java_method.cc',
'browser/renderer_host/java/java_method.h',
'browser/renderer_host/media/audio_common.cc',
'browser/renderer_host/media/audio_common.h',
'browser/renderer_host/media/audio_input_device_manager.cc',
......@@ -768,12 +772,16 @@
],
}, {
'sources!': [
'browser/renderer_host/java/java_bound_object.cc',
'browser/renderer_host/java/java_bound_object.h',
'browser/renderer_host/java/java_bridge_channel_host.cc',
'browser/renderer_host/java/java_bridge_channel_host.h',
'browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc',
'browser/renderer_host/java/java_bridge_dispatcher_host_manager.h',
'browser/renderer_host/java/java_bridge_dispatcher_host.cc',
'browser/renderer_host/java/java_bridge_dispatcher_host.h',
'browser/renderer_host/java/java_method.cc',
'browser/renderer_host/java/java_method.h',
],
}],
],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment