blob: 68ff653714547a9ea85e9c07e8d893981a90480c [file] [log] [blame]
* Copyright (C) 2011 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.
#include <jni.h>
#include <hash_map>
#include <string>
#include "base/utilities.h"
#include "core/value.h"
// We add this JNI_NULL macro to allow consistent code separation of Java and
// C++ types.
#if 0
// Pointer to current JavaVM. Do not use this directly. Instead use the funciton
// GetCurrentJavaVM().
extern JavaVM* g_current_java_vm_;
// Wrapper around a java object pointer, which includes the environment
// pointer in which the object "lives". This is used for passing down Java
// objects from the Java layer to C++.
// While an instance of this class does not own the underlying java object, it
// does hold a global reference to it, so that the Java garbage collector does
// not destroy it. It uses reference counting to determine when it can destroy
// the reference.
// TODO: Add multi-thread support!
class JavaObject {
// Creates a NULL JavaObject.
// Creates a wrapper around the given object in the given JNI environment.
JavaObject(jobject object, JNIEnv* env);
// Copy constructor.
JavaObject(const JavaObject& java_obj);
// Destructor.
// Assignment operator.
JavaObject& operator=(const JavaObject& java_obj);
// Access to the object (non-const as JNI functions are non-const).
jobject object() const {
return object_;
// Resets this object to the NULL JavaObject.
void Reset();
// Retain the instance, i.e. increase reference count.
void Retain();
// Release the instance, i.e. decrease reference count.
void Release();
// The object pointer (not owned).
jobject object_;
// The reference count of this object
int* ref_count_;
// ObjectPool template class. This class keeps track of C++ instances that are
// coupled to Java objects. This is done by using an "id" field in the Java
// object, which is then mapped to the correct instance here. It should not be
// necessary to use this class directly. Instead, the convenience functions
// below can be used.
template<class T>
class ObjectPool {
// Create a new ObjectPool for a specific object type. Pass the path to the
// Java equivalent class of the C++ class, and the name of the java member
// field that will store the object's ID.
static void Setup(const std::string& jclass_name,
const std::string& id_fld_name) {
instance_ = new ObjectPool<T>(jclass_name, id_fld_name);
// Return the shared instance to this type's pool.
static ObjectPool* Instance() {
return instance_;
// Delete this type's pool.
static void TearDown() {
delete instance_;
// Register a new C++ object with the pool. This does not affect the Java
// layer. Use WrapObject() instead to perform the necessary Java-side
// assignments. Pass true to owns if the object pool owns the object.
int RegisterObject(T* object, bool owns) {
const int id = next_id_;
objects_[id] = object;
owns_[id] = owns;
return id;
// Return the object in the pool with the specified ID.
T* ObjectWithID(int obj_id) const {
typename CObjMap::const_iterator iter = objects_.find(obj_id);
return iter == objects_.end() ? NULL : iter->second;
// Get the ID of a Java object. This ID can be used to look-up the C++
// object.
int GetObjectID(JNIEnv* env, jobject j_object) {
jclass cls = env->GetObjectClass(j_object);
jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I");
const int result = env->GetIntField(j_object, id_field);
return result;
// Take a C++ object and wrap it with a given Java object. This will
// essentially set the ID member of the Java object to the ID of the C++
// object. Pass true to owns if the object pool owns the object.
bool WrapObject(T* c_object, JNIEnv* env, jobject j_object, bool owns) {
const int id = RegisterObject(c_object, owns);
jclass cls = env->GetObjectClass(j_object);
jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I");
env->SetIntField(j_object, id_field, id);
return true;
// Remove the object with the given ID from this pool, and delete it. This
// does not affect the Java layer.
bool DeleteObjectWithID(int obj_id) {
typename CObjMap::iterator iter = objects_.find(obj_id);
const bool found = iter != objects_.end();
if (found) {
if (owns_[obj_id])
delete iter->second;
return found;
// Instantiates a new java object for this class. The Java class must have
// a default constructor for this to succeed.
jobject CreateJavaObject(JNIEnv* env) {
jclass cls = env->FindClass(jclass_name_.c_str());
jmethodID constructor = env->GetMethodID(
jobject result = env->NewObject(cls, constructor, JNI_NULL);
return result;
int GetObjectCount() const {
return objects_.size();
const std::string& GetJavaClassName() const {
return jclass_name_;
explicit ObjectPool(const std::string& jclass_name,
const std::string& id_fld_name)
: jclass_name_(jclass_name),
next_id_(0) { }
typedef std::hash_map<int, T*> CObjMap;
typedef std::hash_map<int, bool> FlagMap;
static ObjectPool* instance_;
std::string jclass_name_;
std::string id_field_name_;
int next_id_;
CObjMap objects_;
FlagMap owns_;
template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL;
// Convenience Functions ///////////////////////////////////////////////////////
// This function "links" the C++ instance and the Java instance, so that they
// can be mapped to one another. This must be called for every C++ instance
// which is wrapped by a Java front-end interface. Pass true to owns, if the
// Java layer should own the object.
template<typename T>
bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) {
ObjectPool<T>* pool = ObjectPool<T>::Instance();
return pool ? pool->WrapObject(c_object, env, j_object, owns) : false;
// Creates a new Java instance, which wraps the passed C++ instance. Returns
// the wrapped object or JNI_NULL if there was an error. Pass true to owns, if
// the Java layer should own the object.
template<typename T>
jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) {
ObjectPool<T>* pool = ObjectPool<T>::Instance();
if (pool) {
jobject result = pool->CreateJavaObject(env);
if (WrapObjectInJava(c_object, env, result, owns))
return result;
return JNI_NULL;
// Use ConvertFromJava to obtain a C++ instance given a Java object. This
// instance must have been wrapped in Java using the WrapObjectInJava()
// function.
template<typename T>
T* ConvertFromJava(JNIEnv* env, jobject j_object) {
ObjectPool<T>* pool = ObjectPool<T>::Instance();
return pool && j_object
? pool->ObjectWithID(pool->GetObjectID(env, j_object))
// Delete the native object given a Java instance. This should be called from
// the Java object's finalizer.
template<typename T>
bool DeleteNativeObject(JNIEnv* env, jobject j_object) {
ObjectPool<T>* pool = ObjectPool<T>::Instance();
return pool && j_object
? pool->DeleteObjectWithID(pool->GetObjectID(env, j_object))
: false;
#if 0
// Get the current JNI VM, or NULL if there is no current VM
JavaVM* GetCurrentJavaVM();
// Get the current JNI environment, or NULL if this is not a JNI thread
JNIEnv* GetCurrentJNIEnv();
// Convert C++ boolean to Java boolean.
jboolean ToJBool(bool value);
// Convert Java boolean to C++ boolean.
bool ToCppBool(jboolean value);
// Convert Java String to C++ string.
jstring ToJString(JNIEnv* env, const std::string& value);
// Convert C++ string to Java String.
std::string ToCppString(JNIEnv* env, jstring value);
// Convert Java object to a (C) Value object.
Value ToCValue(JNIEnv* env, jobject object);
// Convert a (C) Value object to a Java object.
jobject ToJObject(JNIEnv* env, const Value& value);
// Returns true, iff the passed object is an instance of the class specified
// by its fully qualified class name.
bool IsJavaInstanceOf(JNIEnv* env, jobject object,
const std::string& class_name);