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:
Родитель
67a3ad38e3
Коммит
4a69b3e636
|
@ -9,10 +9,7 @@ include $(CLEAR_VARS)
|
|||
|
||||
LOCAL_MODULE := yoga
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
jni/YGJNI.cpp \
|
||||
jni/YGJTypes.cpp \
|
||||
jni/YGJNIVanilla.cpp
|
||||
LOCAL_SRC_FILES := $(wildcard jni/*.cpp)
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/jni
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
Загрузка…
Ссылка в новой задаче