Add Scoped Local and Global Ref
Summary: Add ScopedLocalRef, ScopedGlobalRef and some common methods to be used later. Reviewed By: amir-shalem Differential Revision: D17711284 fbshipit-source-id: be43d5e246bc2406765057783be11854877c41f1
This commit is contained in:
committed by
Facebook Github Bot
parent
7c2683fe52
commit
b9b0217a07
@@ -29,7 +29,10 @@ add_compile_options(
|
|||||||
-Wall
|
-Wall
|
||||||
-std=c++11)
|
-std=c++11)
|
||||||
|
|
||||||
add_library(yoga SHARED jni/YGJNI.cpp jni/YGJTypes.cpp jni/YGJNIVanilla.cpp)
|
file(GLOB jni_SRC
|
||||||
|
jni/*.cpp)
|
||||||
|
|
||||||
|
add_library(yoga SHARED ${jni_SRC})
|
||||||
|
|
||||||
target_include_directories(yoga PRIVATE
|
target_include_directories(yoga PRIVATE
|
||||||
${libfb_DIR}/include
|
${libfb_DIR}/include
|
||||||
|
140
java/jni/ScopedGlobalRef.h
Normal file
140
java/jni/ScopedGlobalRef.h
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "corefunctions.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScopedGlobalRef is a sort of smart reference that allows us to control the
|
||||||
|
* lifespan of a JNI global reference.
|
||||||
|
*
|
||||||
|
* This class is designed so that when a ScopedGlobalRef goes out of scoped, its
|
||||||
|
* destructor will delete -JNIEnv->DeleteGlobalRef()- the underlying JNI
|
||||||
|
* reference.
|
||||||
|
*
|
||||||
|
* This class should be used to wrap all the global references we create during
|
||||||
|
* normal JNI operations if we want reference to eventually go away (in JNI it
|
||||||
|
* is a common operation to cache some global references throughout the lifespan
|
||||||
|
* of a process, in which case using this class does not really make sense). The
|
||||||
|
* idea behind this is that in JNI we should be very explicit about the lifespan
|
||||||
|
* of global references. Global references can quickly get out of control if not
|
||||||
|
* freed properly, and the developer should always be very aware of the lifespan
|
||||||
|
* of each global reference that is created in JNI so that leaks are prevented.
|
||||||
|
*
|
||||||
|
* This class is very explicit in its behavior, and it does not allow to perform
|
||||||
|
* unexpected conversions or unexpected ownership transfer. In practice, this
|
||||||
|
* class acts as a unique pointer where the underying JNI reference can have one
|
||||||
|
* and just one owner. Transfering ownership is allowed but it is an explicit
|
||||||
|
* operation (implemneted via move semantics and also via explicity API calls).
|
||||||
|
*
|
||||||
|
* Note that this class doesn't receive an explicit JNIEnv at construction time.
|
||||||
|
* At destruction time it uses vanillajni::getCurrentEnv() to retrieve the
|
||||||
|
* JNIEnv.
|
||||||
|
*
|
||||||
|
* It is OK to cache a ScopedGlobalRef between different JNI native
|
||||||
|
* method calls.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class ScopedGlobalRef {
|
||||||
|
static_assert(
|
||||||
|
std::is_same<T, jclass>() || std::is_same<T, jobject>() ||
|
||||||
|
std::is_same<T, jstring>() || std::is_same<T, jthrowable>() ||
|
||||||
|
std::is_same<T, jbyteArray>() || std::is_same<T, jintArray>() ||
|
||||||
|
std::is_same<T, jshortArray>() || std::is_same<T, jcharArray>() ||
|
||||||
|
std::is_same<T, jlongArray>() || std::is_same<T, jfloatArray>() ||
|
||||||
|
std::is_same<T, jdoubleArray>() || std::is_same<T, jobjectArray>() ||
|
||||||
|
std::is_same<T, jbooleanArray>(),
|
||||||
|
"ScopedGlobalRef instantiated for invalid type");
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a ScopedGlobalRef with a JNI global reference.
|
||||||
|
*
|
||||||
|
* @param globalRef the global reference to wrap. Can be NULL.
|
||||||
|
*/
|
||||||
|
ScopedGlobalRef(T globalRef) : mGlobalRef(globalRef) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to ScopedGlobalRef(NULL)
|
||||||
|
*/
|
||||||
|
explicit ScopedGlobalRef() : mGlobalRef(NULL) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move construction is allowed.
|
||||||
|
*/
|
||||||
|
ScopedGlobalRef(ScopedGlobalRef&& s) : mGlobalRef(s.release()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move assignment is allowed.
|
||||||
|
*/
|
||||||
|
ScopedGlobalRef& operator=(ScopedGlobalRef&& s) {
|
||||||
|
reset(s.release());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedGlobalRef() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the currently held reference and reassigns a new one to the
|
||||||
|
* ScopedGlobalRef.
|
||||||
|
*/
|
||||||
|
void reset(T ptr = NULL) {
|
||||||
|
if (ptr != mGlobalRef) {
|
||||||
|
if (mGlobalRef != NULL) {
|
||||||
|
vanillajni::getCurrentEnv()->DeleteGlobalRef(mGlobalRef);
|
||||||
|
}
|
||||||
|
mGlobalRef = ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes this ScopedGlobalRef not own the underlying JNI global reference.
|
||||||
|
* After calling this method, the ScopedGlobalRef will not delete the JNI
|
||||||
|
* global reference when the ScopedGlobalRef goes out of scope.
|
||||||
|
*/
|
||||||
|
T release() {
|
||||||
|
T globalRef = mGlobalRef;
|
||||||
|
mGlobalRef = NULL;
|
||||||
|
return globalRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying JNI global reference.
|
||||||
|
*/
|
||||||
|
T get() const { return mGlobalRef; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the underlying JNI reference is not NULL.
|
||||||
|
*/
|
||||||
|
operator bool() const {
|
||||||
|
return mGlobalRef != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedGlobalRef(const ScopedGlobalRef& ref) = delete;
|
||||||
|
ScopedGlobalRef& operator=(const ScopedGlobalRef& other) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
T mGlobalRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ScopedGlobalRef<T> make_global_ref(T globalRef) {
|
||||||
|
return ScopedGlobalRef<T>(globalRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
140
java/jni/ScopedLocalRef.h
Normal file
140
java/jni/ScopedLocalRef.h
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* This is a modified version of Android's ScopedLocalRef class that can be
|
||||||
|
* found in the Android's JNI code.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScopedLocalRef is a sort of smart reference that allows us to control the
|
||||||
|
* lifespan of a JNI local reference.
|
||||||
|
*
|
||||||
|
* This class is designed so that when a ScopedLocalRef goes out of scope, its
|
||||||
|
* destructor will delete -JNIEnv->DeleteLocalRef()- the underlying JNI
|
||||||
|
* reference.
|
||||||
|
*
|
||||||
|
* This class should be used to wrap all the local references that JNI
|
||||||
|
* gives us other than those that are passed to native methods at
|
||||||
|
* invocation time. The idea behind this is that in JNI we should be very
|
||||||
|
* explicit about the lifespan of local references. Local references can quickly
|
||||||
|
* get out of control, and the developer should always be very aware of the
|
||||||
|
* lifespan of each local reference that is created in JNI so that leaks are
|
||||||
|
* prevented.
|
||||||
|
*
|
||||||
|
* This class is very explicit in its behavior, and it does not allow to perform
|
||||||
|
* unexpected conversions or unexpected ownership transfer. In practice, this
|
||||||
|
* class acts as a unique pointer where the underying JNI reference can have one
|
||||||
|
* and just one owner. Transfering ownership is allowed but it is an explicit
|
||||||
|
* operation (implemneted via move semantics and also via explicity API calls).
|
||||||
|
*
|
||||||
|
* As with standard JNI local references it is not a valid operation to keep a
|
||||||
|
* reference around between different native method calls.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class ScopedLocalRef {
|
||||||
|
static_assert(
|
||||||
|
std::is_same<T, jclass>() || std::is_same<T, jobject>() ||
|
||||||
|
std::is_same<T, jstring>() || std::is_same<T, jthrowable>() ||
|
||||||
|
std::is_same<T, jbyteArray>() || std::is_same<T, jintArray>() ||
|
||||||
|
std::is_same<T, jshortArray>() || std::is_same<T, jcharArray>() ||
|
||||||
|
std::is_same<T, jlongArray>() || std::is_same<T, jfloatArray>() ||
|
||||||
|
std::is_same<T, jdoubleArray>() || std::is_same<T, jobjectArray>() ||
|
||||||
|
std::is_same<T, jbooleanArray>(),
|
||||||
|
"ScopedLocalRef instantiated for invalid type");
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a ScopedLocalRef with a JNI local reference.
|
||||||
|
*
|
||||||
|
* @param localRef the local reference to wrap. Can be NULL.
|
||||||
|
*/
|
||||||
|
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to ScopedLocalRef(env, NULL)
|
||||||
|
*/
|
||||||
|
explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(NULL) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move construction is allowed.
|
||||||
|
*/
|
||||||
|
ScopedLocalRef(ScopedLocalRef&& s) : mEnv(s.mEnv), mLocalRef(s.release()) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move assignment is allowed.
|
||||||
|
*/
|
||||||
|
ScopedLocalRef& operator=(ScopedLocalRef&& s) {
|
||||||
|
reset(s.release());
|
||||||
|
mEnv = s.mEnv;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedLocalRef() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the currently held reference and reassigns a new one to the
|
||||||
|
* ScopedLocalRef.
|
||||||
|
*/
|
||||||
|
void reset(T ptr = NULL) {
|
||||||
|
if (ptr != mLocalRef) {
|
||||||
|
if (mLocalRef != NULL) {
|
||||||
|
mEnv->DeleteLocalRef(mLocalRef);
|
||||||
|
}
|
||||||
|
mLocalRef = ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes this ScopedLocalRef not own the underlying JNI local reference. After
|
||||||
|
* calling this method, the ScopedLocalRef will not delete the JNI local
|
||||||
|
* reference when the ScopedLocalRef goes out of scope.
|
||||||
|
*/
|
||||||
|
T release() {
|
||||||
|
T localRef = mLocalRef;
|
||||||
|
mLocalRef = NULL;
|
||||||
|
return localRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying JNI local reference.
|
||||||
|
*/
|
||||||
|
T get() const { return mLocalRef; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the underlying JNI reference is not NULL.
|
||||||
|
*/
|
||||||
|
operator bool() const {
|
||||||
|
return mLocalRef != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedLocalRef(const ScopedLocalRef& ref) = delete;
|
||||||
|
ScopedLocalRef& operator=(const ScopedLocalRef& other) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JNIEnv* mEnv;
|
||||||
|
T mLocalRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ScopedLocalRef<T> make_local_ref(JNIEnv* env, T localRef) {
|
||||||
|
return ScopedLocalRef<T>(env, localRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
100
java/jni/common.cpp
Normal file
100
java/jni/common.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
void registerNatives(
|
||||||
|
JNIEnv* env,
|
||||||
|
const char* className,
|
||||||
|
const JNINativeMethod methods[],
|
||||||
|
size_t numMethods) {
|
||||||
|
jclass clazz = env->FindClass(className);
|
||||||
|
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
|
||||||
|
env->RegisterNatives(clazz, methods, numMethods);
|
||||||
|
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID getStaticMethodId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* methodName,
|
||||||
|
const char* methodDescriptor) {
|
||||||
|
jmethodID methodId =
|
||||||
|
env->GetStaticMethodID(clazz, methodName, methodDescriptor);
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
return methodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID getMethodId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* methodName,
|
||||||
|
const char* methodDescriptor) {
|
||||||
|
jmethodID methodId = env->GetMethodID(clazz, methodName, methodDescriptor);
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
return methodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID getFieldId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* fieldName,
|
||||||
|
const char* fieldSignature) {
|
||||||
|
jfieldID fieldId = env->GetFieldID(clazz, fieldName, fieldSignature);
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
return fieldId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_CALL_METHOD_FOR_PRIMITIVE_IMPLEMENTATION(jnitype, readableType) \
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(jnitype, readableType) { \
|
||||||
|
va_list args; \
|
||||||
|
va_start(args, methodId); \
|
||||||
|
jnitype result = env->Call##readableType##MethodV(obj, methodId, args); \
|
||||||
|
va_end(args); \
|
||||||
|
assertNoPendingJniException(env); \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_IMPLEMENTATION(jlong, Long);
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_IMPLEMENTATION(jfloat, Float);
|
||||||
|
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(void, Void) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, methodId);
|
||||||
|
env->CallVoidMethodV(obj, methodId, args);
|
||||||
|
va_end(args);
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject>
|
||||||
|
callStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID methodId, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, methodId);
|
||||||
|
jobject result = env->CallStaticObjectMethodV(clazz, methodId, args);
|
||||||
|
va_end(args);
|
||||||
|
assertNoPendingJniException(env);
|
||||||
|
return make_local_ref(env, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedGlobalRef<jobject> newGlobalRef(JNIEnv* env, jobject obj) {
|
||||||
|
jobject result = env->NewGlobalRef(obj);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
logErrorMessageAndDie("Could not obtain global reference from object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_global_ref(result);
|
||||||
|
}
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
73
java/jni/common.h
Normal file
73
java/jni/common.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "ScopedGlobalRef.h"
|
||||||
|
#include "ScopedLocalRef.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of methods for a JNI class. Aborts if registration fails.
|
||||||
|
*/
|
||||||
|
void registerNatives(
|
||||||
|
JNIEnv* env,
|
||||||
|
const char* className,
|
||||||
|
const JNINativeMethod methods[],
|
||||||
|
size_t numMethods);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a jmethodID for a class static method. Aborts if any error happens.
|
||||||
|
*/
|
||||||
|
jmethodID getStaticMethodId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* methodName,
|
||||||
|
const char* methodDescriptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a jmethodID for a class non-static method. Aborts if any error
|
||||||
|
* happens.
|
||||||
|
*/
|
||||||
|
jmethodID getMethodId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* methodName,
|
||||||
|
const char* methodDescriptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a class non-static field ID. Aborts if any error happens.
|
||||||
|
*/
|
||||||
|
jfieldID getFieldId(
|
||||||
|
JNIEnv* env,
|
||||||
|
jclass clazz,
|
||||||
|
const char* fieldName,
|
||||||
|
const char* fieldSignature);
|
||||||
|
|
||||||
|
// Helper methods to call a non-static method on an object depending on the
|
||||||
|
// return type. Each method will abort the execution if an error
|
||||||
|
// (such as a Java pending exception) is detected after invoking the
|
||||||
|
// Java method.
|
||||||
|
#define DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(jnitype, readableType) \
|
||||||
|
jnitype call##readableType##Method( \
|
||||||
|
JNIEnv* env, jobject obj, jmethodID methodId, ...)
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(void, Void);
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(jlong, Long);
|
||||||
|
DEFINE_CALL_METHOD_FOR_PRIMITIVE_INTERFACE(jfloat, Float);
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject>
|
||||||
|
callStaticObjectMethod(JNIEnv* env, jclass clazz, jmethodID methodId, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a local or a global reference, this method creates a new global
|
||||||
|
* reference out of it. If any error happens, aborts the process.
|
||||||
|
*/
|
||||||
|
ScopedGlobalRef<jobject> newGlobalRef(JNIEnv* env, jobject obj);
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
74
java/jni/corefunctions.cpp
Normal file
74
java/jni/corefunctions.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include "corefunctions.h"
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
JavaVM* globalVm = NULL;
|
||||||
|
struct JavaVMInitializer {
|
||||||
|
JavaVMInitializer(JavaVM* vm) {
|
||||||
|
if (!vm) {
|
||||||
|
logErrorMessageAndDie(
|
||||||
|
"You cannot pass a NULL JavaVM to ensureInitialized");
|
||||||
|
}
|
||||||
|
globalVm = vm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
jint ensureInitialized(JNIEnv** env, JavaVM* vm) {
|
||||||
|
static JavaVMInitializer init(vm);
|
||||||
|
|
||||||
|
if (!env) {
|
||||||
|
logErrorMessageAndDie(
|
||||||
|
"Need to pass a valid JNIEnv pointer to vanillajni initialization "
|
||||||
|
"routine");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(env), JNI_VERSION_1_6) != JNI_OK) {
|
||||||
|
logErrorMessageAndDie(
|
||||||
|
"Error retrieving JNIEnv during initialization of vanillajni");
|
||||||
|
}
|
||||||
|
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv* getCurrentEnv() {
|
||||||
|
JNIEnv* env;
|
||||||
|
jint ret = globalVm->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||||
|
if (ret != JNI_OK) {
|
||||||
|
logErrorMessageAndDie(
|
||||||
|
"There was an error retrieving the current JNIEnv. Make sure the "
|
||||||
|
"current thread is attached");
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logErrorMessageAndDie(const char* message) {
|
||||||
|
VANILLAJNI_LOG_ERROR(
|
||||||
|
"VanillaJni",
|
||||||
|
"Aborting due to error detected in native code: %s",
|
||||||
|
message);
|
||||||
|
VANILLAJNI_DIE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void assertNoPendingJniException(JNIEnv* env) {
|
||||||
|
// This method cannot call any other method of the library, since other
|
||||||
|
// methods of the library use it to check for exceptions too
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
logErrorMessageAndDie("Aborting due to pending Java exception in JNI");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
51
java/jni/corefunctions.h
Normal file
51
java/jni/corefunctions.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace yoga {
|
||||||
|
namespace vanillajni {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has to be called before using the vanillajni library. This method
|
||||||
|
* is typically called when doing initialization in the "on load" JNI hook of a
|
||||||
|
* particular library.
|
||||||
|
*
|
||||||
|
* This method is thread safe, and after the first time it's called it has no
|
||||||
|
* initialization effect.
|
||||||
|
*
|
||||||
|
* @param env use this output parameter to get a JNIEnv to use for things such
|
||||||
|
* as registering native methods and such.
|
||||||
|
* @param vm the VM instance passed by JNI. This is usually the VM instance
|
||||||
|
* that is passed to the "on load" JNI hook.
|
||||||
|
* @return an integer value to return from the "on load" hook.
|
||||||
|
*/
|
||||||
|
jint ensureInitialized(JNIEnv** env, JavaVM* vm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JNIEnv* suitable for the current thread. If the current thread is
|
||||||
|
* not attached to the Java VM, this method aborts execution.
|
||||||
|
*/
|
||||||
|
JNIEnv* getCurrentEnv();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an error message and aborts the current process.
|
||||||
|
*/
|
||||||
|
void logErrorMessageAndDie(const char* message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether there is a pending JNI exception. If so, it logs an error
|
||||||
|
* message and aborts the current process. Otherwise it does nothing.
|
||||||
|
*/
|
||||||
|
void assertNoPendingJniException(JNIEnv* env);
|
||||||
|
|
||||||
|
} // namespace vanillajni
|
||||||
|
} // namespace yoga
|
||||||
|
} // namespace facebook
|
22
java/jni/macros.h
Normal file
22
java/jni/macros.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#define VANILLAJNI_LOG_ERROR(tag, format, ...) \
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, tag, format, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define VANILLAJNI_LOG_ERROR(tag, format, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VANILLAJNI_DIE() std::abort()
|
Reference in New Issue
Block a user