Copy fbjni library from react-native
This commit is contained in:
15
lib/fb/Doxyfile
Normal file
15
lib/fb/Doxyfile
Normal file
@@ -0,0 +1,15 @@
|
||||
PROJECT_NAME = "Facebook Android Support"
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
RECURSIVE = YES
|
||||
EXCLUDE = tests
|
||||
EXCLUDE_PATTERNS = *.cpp
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_SCOPE_NAMES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
#ENABLED_SECTIONS = INTERNAL
|
41
lib/fb/assert.cpp
Normal file
41
lib/fb/assert.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <cstdarg>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
#define ASSERT_BUF_SIZE 4096
|
||||
static char sAssertBuf[ASSERT_BUF_SIZE];
|
||||
static AssertHandler gAssertHandler;
|
||||
|
||||
void assertInternal(const char* formatstr ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, formatstr);
|
||||
vsnprintf(sAssertBuf, sizeof(sAssertBuf), formatstr, va_args);
|
||||
va_end(va_args);
|
||||
if (gAssertHandler != NULL) {
|
||||
gAssertHandler(sAssertBuf);
|
||||
}
|
||||
FBLOG(LOG_FATAL, "fbassert", "%s", sAssertBuf);
|
||||
// crash at this specific address so that we can find our crashes easier
|
||||
*(int*)0xdeadb00c = 0;
|
||||
// let the compiler know we won't reach the end of the function
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void setAssertHandler(AssertHandler assertHandler) {
|
||||
gAssertHandler = assertHandler;
|
||||
}
|
||||
|
||||
} // namespace facebook
|
83
lib/fb/include/fb/ALog.h
Normal file
83
lib/fb/include/fb/ALog.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/** @file ALog.h
|
||||
*
|
||||
* Very simple android only logging. Define LOG_TAG to enable the macros.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace alog {
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void log(int level, const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
__android_log_print(level, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void log(int level, const char* tag, const char* msg) noexcept {
|
||||
__android_log_write(level, tag, msg);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void logv(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_VERBOSE, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void logd(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_DEBUG, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void logi(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_INFO, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void logw(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_WARN, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void loge(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_ERROR, tag, msg, args...);
|
||||
}
|
||||
|
||||
template<typename... ARGS>
|
||||
inline void logf(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||
log(ANDROID_LOG_FATAL, tag, msg, args...);
|
||||
}
|
||||
|
||||
|
||||
#ifdef LOG_TAG
|
||||
# define ALOGV(...) ::facebook::alog::logv(LOG_TAG, __VA_ARGS__)
|
||||
# define ALOGD(...) ::facebook::alog::logd(LOG_TAG, __VA_ARGS__)
|
||||
# define ALOGI(...) ::facebook::alog::logi(LOG_TAG, __VA_ARGS__)
|
||||
# define ALOGW(...) ::facebook::alog::logw(LOG_TAG, __VA_ARGS__)
|
||||
# define ALOGE(...) ::facebook::alog::loge(LOG_TAG, __VA_ARGS__)
|
||||
# define ALOGF(...) ::facebook::alog::logf(LOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
}}
|
||||
|
||||
#else
|
||||
# define ALOGV(...) ((void)0)
|
||||
# define ALOGD(...) ((void)0)
|
||||
# define ALOGI(...) ((void)0)
|
||||
# define ALOGW(...) ((void)0)
|
||||
# define ALOGE(...) ((void)0)
|
||||
# define ALOGF(...) ((void)0)
|
||||
#endif
|
47
lib/fb/include/fb/Countable.h
Normal file
47
lib/fb/include/fb/Countable.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <fb/assert.h>
|
||||
#include <fb/noncopyable.h>
|
||||
#include <fb/nonmovable.h>
|
||||
#include <fb/RefPtr.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
class Countable : public noncopyable, public nonmovable {
|
||||
public:
|
||||
// RefPtr expects refcount to start at 0
|
||||
Countable() : m_refcount(0) {}
|
||||
virtual ~Countable()
|
||||
{
|
||||
FBASSERT(m_refcount == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
void ref() {
|
||||
++m_refcount;
|
||||
}
|
||||
|
||||
void unref() {
|
||||
if (0 == --m_refcount) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasOnlyOneRef() const {
|
||||
return m_refcount == 1;
|
||||
}
|
||||
|
||||
template <typename T> friend class RefPtr;
|
||||
std::atomic<int> m_refcount;
|
||||
};
|
||||
|
||||
}
|
18
lib/fb/include/fb/Doxyfile
Normal file
18
lib/fb/include/fb/Doxyfile
Normal file
@@ -0,0 +1,18 @@
|
||||
PROJECT_NAME = "Facebook JNI"
|
||||
PROJECT_BRIEF = "Helper library to provide safe and convenient access to JNI with very low overhead"
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
RECURSIVE = YES
|
||||
EXCLUDE = tests Asserts.h Countable.h GlobalReference.h LocalReference.h LocalString.h Registration.h WeakReference.h jni_helpers.h Environment.h
|
||||
EXCLUDE_PATTERNS = *-inl.h *.cpp
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_SCOPE_NAMES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
PREDEFINED = LOG_TAG=fbjni
|
||||
EXAMPLE_PATH = samples
|
||||
#ENABLED_SECTIONS = INTERNAL
|
77
lib/fb/include/fb/Environment.h
Normal file
77
lib/fb/include/fb/Environment.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// Keeps a thread-local reference to the current thread's JNIEnv.
|
||||
struct Environment {
|
||||
// May be null if this thread isn't attached to the JVM
|
||||
FBEXPORT static JNIEnv* current();
|
||||
static void initialize(JavaVM* vm);
|
||||
|
||||
// There are subtle issues with calling the next functions directly. It is
|
||||
// much better to always use a ThreadScope to manage attaching/detaching for
|
||||
// you.
|
||||
FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached();
|
||||
FBEXPORT static void detachCurrentThread();
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it
|
||||
* exits will cause a crash, as will calling Detach an extra time, and this guard class helps
|
||||
* keep that straight. In addition, it remembers whether it performed the attach or not, so it
|
||||
* is safe to nest it with itself or with non-fbjni code that manages the attachment correctly.
|
||||
*
|
||||
* Potential concerns:
|
||||
* - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the
|
||||
* app is not busy.
|
||||
* - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that
|
||||
* there is no Java code on the current stack or you run the risk of a crash like:
|
||||
* ERROR: detaching thread with interp frames (count=18)
|
||||
* (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)
|
||||
* ThreadScope won't do a detach if the thread was already attached before the guard is
|
||||
* instantiated, but there's probably some usage that could trip this up.
|
||||
* - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language
|
||||
* classes, not any of our application's classes. This will be different behavior than threads
|
||||
* that were initiated on the Java side. A workaround is to pass a global reference for a
|
||||
* class or instance to the new thread; this bypasses the need for the class loader.
|
||||
* (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
|
||||
* If you need access to the application's classes, you can use ThreadScope::WithClassLoader.
|
||||
*/
|
||||
class FBEXPORT ThreadScope {
|
||||
public:
|
||||
ThreadScope();
|
||||
ThreadScope(ThreadScope&) = delete;
|
||||
ThreadScope(ThreadScope&&) = default;
|
||||
ThreadScope& operator=(ThreadScope&) = delete;
|
||||
ThreadScope& operator=(ThreadScope&&) = delete;
|
||||
~ThreadScope();
|
||||
|
||||
/**
|
||||
* This runs the closure in a scope with fbjni's classloader. This should be
|
||||
* the same classloader as the rest of the application and thus anything
|
||||
* running in the closure will have access to the same classes as in a normal
|
||||
* java-create thread.
|
||||
*/
|
||||
static void WithClassLoader(std::function<void()>&& runnable);
|
||||
|
||||
static void OnLoad();
|
||||
private:
|
||||
bool attachedWithThisScope_;
|
||||
};
|
||||
}
|
||||
}
|
50
lib/fb/include/fb/ProgramLocation.h
Normal file
50
lib/fb/include/fb/ProgramLocation.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
#define FROM_HERE facebook::ProgramLocation(__FUNCTION__, __FILE__, __LINE__)
|
||||
|
||||
class ProgramLocation {
|
||||
public:
|
||||
ProgramLocation() : m_functionName("Unspecified"), m_fileName("Unspecified"), m_lineNumber(0) {}
|
||||
|
||||
ProgramLocation(const char* functionName, const char* fileName, int line) :
|
||||
m_functionName(functionName),
|
||||
m_fileName(fileName),
|
||||
m_lineNumber(line)
|
||||
{}
|
||||
|
||||
const char* functionName() const { return m_functionName; }
|
||||
const char* fileName() const { return m_fileName; }
|
||||
int lineNumber() const { return m_lineNumber; }
|
||||
|
||||
std::string asFormattedString() const {
|
||||
std::stringstream str;
|
||||
str << "Function " << m_functionName << " in file " << m_fileName << ":" << m_lineNumber;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
bool operator==(const ProgramLocation& other) const {
|
||||
// Assumes that the strings are static
|
||||
return (m_functionName == other.m_functionName) && (m_fileName == other.m_fileName) && m_lineNumber == other.m_lineNumber;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* m_functionName;
|
||||
const char* m_fileName;
|
||||
int m_lineNumber;
|
||||
};
|
||||
|
||||
}
|
274
lib/fb/include/fb/RefPtr.h
Normal file
274
lib/fb/include/fb/RefPtr.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include <fb/assert.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
// Reference counting smart pointer. This is designed to work with the
|
||||
// Countable class or other implementations in the future. It is designed in a
|
||||
// way to be both efficient and difficult to misuse. Typical usage is very
|
||||
// simple once you learn the patterns (and the compiler will help!):
|
||||
//
|
||||
// By default, the internal pointer is null.
|
||||
// RefPtr<Foo> ref;
|
||||
//
|
||||
// Object creation requires explicit construction:
|
||||
// RefPtr<Foo> ref = createNew<Foo>(...);
|
||||
//
|
||||
// Or if the constructor is not public:
|
||||
// RefPtr<Foo> ref = adoptRef(new Foo(...));
|
||||
//
|
||||
// But you can implicitly create from nullptr:
|
||||
// RefPtr<Foo> maybeRef = cond ? ref : nullptr;
|
||||
//
|
||||
// Move/Copy Construction/Assignment are straightforward:
|
||||
// RefPtr<Foo> ref2 = ref;
|
||||
// ref = std::move(ref2);
|
||||
//
|
||||
// Destruction automatically drops the RefPtr's reference as expected.
|
||||
//
|
||||
// Upcasting is implicit but downcasting requires an explicit cast:
|
||||
// struct Bar : public Foo {};
|
||||
// RefPtr<Bar> barRef = static_cast<RefPtr<Bar>>(ref);
|
||||
// ref = barRef;
|
||||
//
|
||||
template <class T>
|
||||
class RefPtr {
|
||||
public:
|
||||
constexpr RefPtr() :
|
||||
m_ptr(nullptr)
|
||||
{}
|
||||
|
||||
// Allow implicit construction from a pointer only from nullptr
|
||||
constexpr RefPtr(std::nullptr_t ptr) :
|
||||
m_ptr(nullptr)
|
||||
{}
|
||||
|
||||
RefPtr(const RefPtr<T>& ref) :
|
||||
m_ptr(ref.m_ptr)
|
||||
{
|
||||
refIfNecessary(m_ptr);
|
||||
}
|
||||
|
||||
// Only allow implicit upcasts. A downcast will result in a compile error
|
||||
// unless you use static_cast (which will end up invoking the explicit
|
||||
// operator below).
|
||||
template <typename U>
|
||||
RefPtr(const RefPtr<U>& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :
|
||||
m_ptr(ref.get())
|
||||
{
|
||||
refIfNecessary(m_ptr);
|
||||
}
|
||||
|
||||
RefPtr(RefPtr<T>&& ref) :
|
||||
m_ptr(nullptr)
|
||||
{
|
||||
*this = std::move(ref);
|
||||
}
|
||||
|
||||
// Only allow implicit upcasts. A downcast will result in a compile error
|
||||
// unless you use static_cast (which will end up invoking the explicit
|
||||
// operator below).
|
||||
template <typename U>
|
||||
RefPtr(RefPtr<U>&& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :
|
||||
m_ptr(nullptr)
|
||||
{
|
||||
*this = std::move(ref);
|
||||
}
|
||||
|
||||
~RefPtr() {
|
||||
unrefIfNecessary(m_ptr);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<T>& operator=(const RefPtr<T>& ref) {
|
||||
if (m_ptr != ref.m_ptr) {
|
||||
unrefIfNecessary(m_ptr);
|
||||
m_ptr = ref.m_ptr;
|
||||
refIfNecessary(m_ptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// The STL assumes rvalue references are unique and for simplicity's sake, we
|
||||
// make the same assumption here, that &ref != this.
|
||||
RefPtr<T>& operator=(RefPtr<T>&& ref) {
|
||||
unrefIfNecessary(m_ptr);
|
||||
m_ptr = ref.m_ptr;
|
||||
ref.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RefPtr<T>& operator=(RefPtr<U>&& ref) {
|
||||
unrefIfNecessary(m_ptr);
|
||||
m_ptr = ref.m_ptr;
|
||||
ref.m_ptr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
unrefIfNecessary(m_ptr);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
explicit operator RefPtr<U> () const;
|
||||
|
||||
explicit operator bool() const {
|
||||
return m_ptr ? true : false;
|
||||
}
|
||||
|
||||
bool isTheLastRef() const {
|
||||
FBASSERT(m_ptr);
|
||||
return m_ptr->hasOnlyOneRef();
|
||||
}
|
||||
|
||||
// Creates a strong reference from a raw pointer, assuming that is already
|
||||
// referenced from some other RefPtr. This should be used sparingly.
|
||||
static inline RefPtr<T> assumeAlreadyReffed(T* ptr) {
|
||||
return RefPtr<T>(ptr, ConstructionMode::External);
|
||||
}
|
||||
|
||||
// Creates a strong reference from a raw pointer, assuming that it points to a
|
||||
// freshly-created object. See the documentation for RefPtr for usage.
|
||||
static inline RefPtr<T> adoptRef(T* ptr) {
|
||||
return RefPtr<T>(ptr, ConstructionMode::Adopted);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ConstructionMode {
|
||||
Adopted,
|
||||
External
|
||||
};
|
||||
|
||||
RefPtr(T* ptr, ConstructionMode mode) :
|
||||
m_ptr(ptr)
|
||||
{
|
||||
FBASSERTMSGF(ptr, "Got null pointer in %s construction mode", mode == ConstructionMode::Adopted ? "adopted" : "external");
|
||||
ptr->ref();
|
||||
if (mode == ConstructionMode::Adopted) {
|
||||
FBASSERT(ptr->hasOnlyOneRef());
|
||||
}
|
||||
}
|
||||
|
||||
static inline void refIfNecessary(T* ptr) {
|
||||
if (ptr) {
|
||||
ptr->ref();
|
||||
}
|
||||
}
|
||||
static inline void unrefIfNecessary(T* ptr) {
|
||||
if (ptr) {
|
||||
ptr->unref();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U> friend class RefPtr;
|
||||
|
||||
T* m_ptr;
|
||||
};
|
||||
|
||||
// Creates a strong reference from a raw pointer, assuming that is already
|
||||
// referenced from some other RefPtr and that it is non-null. This should be
|
||||
// used sparingly.
|
||||
template <typename T>
|
||||
static inline RefPtr<T> assumeAlreadyReffed(T* ptr) {
|
||||
return RefPtr<T>::assumeAlreadyReffed(ptr);
|
||||
}
|
||||
|
||||
// As above, but tolerant of nullptr.
|
||||
template <typename T>
|
||||
static inline RefPtr<T> assumeAlreadyReffedOrNull(T* ptr) {
|
||||
return ptr ? RefPtr<T>::assumeAlreadyReffed(ptr) : nullptr;
|
||||
}
|
||||
|
||||
// Creates a strong reference from a raw pointer, assuming that it points to a
|
||||
// freshly-created object. See the documentation for RefPtr for usage.
|
||||
template <typename T>
|
||||
static inline RefPtr<T> adoptRef(T* ptr) {
|
||||
return RefPtr<T>::adoptRef(ptr);
|
||||
}
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
static inline RefPtr<T> createNew(Args&&... arguments) {
|
||||
return RefPtr<T>::adoptRef(new T(std::forward<Args>(arguments)...));
|
||||
}
|
||||
|
||||
template <typename T> template <typename U>
|
||||
RefPtr<T>::operator RefPtr<U>() const {
|
||||
static_assert(std::is_base_of<T, U>::value, "Invalid static cast");
|
||||
return assumeAlreadyReffedOrNull<U>(static_cast<U*>(m_ptr));
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(const RefPtr<T>& a, const RefPtr<U>& b) {
|
||||
return a.get() == b.get();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(const RefPtr<T>& a, const RefPtr<U>& b) {
|
||||
return a.get() != b.get();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(const RefPtr<T>& ref, U* ptr) {
|
||||
return ref.get() == ptr;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(const RefPtr<T>& ref, U* ptr) {
|
||||
return ref.get() != ptr;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(U* ptr, const RefPtr<T>& ref) {
|
||||
return ref.get() == ptr;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline bool operator!=(U* ptr, const RefPtr<T>& ref) {
|
||||
return ref.get() != ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator==(const RefPtr<T>& ref, std::nullptr_t ptr) {
|
||||
return ref.get() == ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator!=(const RefPtr<T>& ref, std::nullptr_t ptr) {
|
||||
return ref.get() != ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator==(std::nullptr_t ptr, const RefPtr<T>& ref) {
|
||||
return ref.get() == ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool operator!=(std::nullptr_t ptr, const RefPtr<T>& ref) {
|
||||
return ref.get() != ptr;
|
||||
}
|
||||
|
||||
}
|
40
lib/fb/include/fb/StaticInitialized.h
Normal file
40
lib/fb/include/fb/StaticInitialized.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <fb/assert.h>
|
||||
#include <utility>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
// Class that lets you declare a global but does not add a static constructor
|
||||
// to the binary. Eventually I'd like to have this auto-initialize in a
|
||||
// multithreaded environment but for now it's easiest just to use manual
|
||||
// initialization.
|
||||
template <typename T>
|
||||
class StaticInitialized {
|
||||
public:
|
||||
constexpr StaticInitialized() :
|
||||
m_instance(nullptr)
|
||||
{}
|
||||
|
||||
template <typename ...Args>
|
||||
void initialize(Args&&... arguments) {
|
||||
FBASSERT(!m_instance);
|
||||
m_instance = new T(std::forward<Args>(arguments)...);
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
return m_instance;
|
||||
}
|
||||
private:
|
||||
T* m_instance;
|
||||
};
|
||||
|
||||
}
|
118
lib/fb/include/fb/ThreadLocal.h
Normal file
118
lib/fb/include/fb/ThreadLocal.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <fb/assert.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A thread-local object is a "global" object within a thread. This is useful
|
||||
* for writing apartment-threaded code, where nothing is actullay shared
|
||||
* between different threads (hence no locking) but those variables are not
|
||||
* on stack in local scope. To use it, just do something like this,
|
||||
*
|
||||
* ThreadLocal<MyClass> static_object;
|
||||
* static_object->data_ = ...;
|
||||
* static_object->doSomething();
|
||||
*
|
||||
* ThreadLocal<int> static_number;
|
||||
* int value = *static_number;
|
||||
*
|
||||
* So, syntax-wise it's similar to pointers. T can be primitive types, and if
|
||||
* it's a class, there has to be a default constructor.
|
||||
*/
|
||||
template<typename T>
|
||||
class ThreadLocal {
|
||||
public:
|
||||
/**
|
||||
* Constructor that has to be called from a thread-neutral place.
|
||||
*/
|
||||
ThreadLocal() :
|
||||
m_key(0),
|
||||
m_cleanup(OnThreadExit) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* As above but with a custom cleanup function
|
||||
*/
|
||||
typedef void (*CleanupFunction)(void* obj);
|
||||
explicit ThreadLocal(CleanupFunction cleanup) :
|
||||
m_key(0),
|
||||
m_cleanup(cleanup) {
|
||||
FBASSERT(cleanup);
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access object's member or method through this operator overload.
|
||||
*/
|
||||
T *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
T &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
T *get() const {
|
||||
return (T*)pthread_getspecific(m_key);
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* obj = get();
|
||||
pthread_setspecific(m_key, NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void reset(T* other = NULL) {
|
||||
T* old = (T*)pthread_getspecific(m_key);
|
||||
if (old != other) {
|
||||
FBASSERT(m_cleanup);
|
||||
m_cleanup(old);
|
||||
pthread_setspecific(m_key, other);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void initialize() {
|
||||
int ret = pthread_key_create(&m_key, m_cleanup);
|
||||
if (ret != 0) {
|
||||
const char *msg = "(unknown error)";
|
||||
switch (ret) {
|
||||
case EAGAIN:
|
||||
msg = "PTHREAD_KEYS_MAX (1024) is exceeded";
|
||||
break;
|
||||
case ENOMEM:
|
||||
msg = "Out-of-memory";
|
||||
break;
|
||||
}
|
||||
(void) msg;
|
||||
FBASSERTMSGF(0, "pthread_key_create failed: %d %s", ret, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnThreadExit(void *obj) {
|
||||
if (NULL != obj) {
|
||||
delete (T*)obj;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_key_t m_key;
|
||||
CleanupFunction m_cleanup;
|
||||
};
|
||||
|
||||
}
|
36
lib/fb/include/fb/assert.h
Normal file
36
lib/fb/include/fb/assert.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#ifndef FBASSERT_H
|
||||
#define FBASSERT_H
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
#define ENABLE_FBASSERT 1
|
||||
|
||||
#if ENABLE_FBASSERT
|
||||
#define FBASSERTMSGF(expr, msg, ...) !(expr) ? facebook::assertInternal("Assert (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__) : (void) 0
|
||||
#else
|
||||
#define FBASSERTMSGF(expr, msg, ...)
|
||||
#endif // ENABLE_FBASSERT
|
||||
|
||||
#define FBASSERT(expr) FBASSERTMSGF(expr, "%s", #expr)
|
||||
|
||||
#define FBCRASH(msg, ...) facebook::assertInternal("Fatal error (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#define FBUNREACHABLE() facebook::assertInternal("This code should be unreachable (%s:%d)", __FILE__, __LINE__)
|
||||
|
||||
FBEXPORT void assertInternal(const char* formatstr, ...) __attribute__((noreturn));
|
||||
|
||||
// This allows storing the assert message before the current process terminates due to a crash
|
||||
typedef void (*AssertHandler)(const char* message);
|
||||
void setAssertHandler(AssertHandler assertHandler);
|
||||
|
||||
} // namespace facebook
|
||||
#endif // FBASSERT_H
|
24
lib/fb/include/fb/fbjni.h
Normal file
24
lib/fb/include/fb/fbjni.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/ALog.h>
|
||||
#include <fb/fbjni/Common.h>
|
||||
#include <fb/fbjni/Exceptions.h>
|
||||
#include <fb/fbjni/ReferenceAllocators.h>
|
||||
#include <fb/fbjni/References.h>
|
||||
#include <fb/fbjni/Meta.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
#include <fb/fbjni/Iterator.h>
|
||||
#include <fb/fbjni/Hybrid.h>
|
||||
#include <fb/fbjni/Registration.h>
|
66
lib/fb/include/fb/fbjni/Boxed.h
Normal file
66
lib/fb/include/fb/fbjni/Boxed.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename jprim>
|
||||
struct JPrimitive : JavaClass<T> {
|
||||
using typename JavaClass<T>::javaobject;
|
||||
using JavaClass<T>::javaClassStatic;
|
||||
static local_ref<javaobject> valueOf(jprim val) {
|
||||
static auto cls = javaClassStatic();
|
||||
static auto method =
|
||||
cls->template getStaticMethod<javaobject(jprim)>("valueOf");
|
||||
return method(cls, val);
|
||||
}
|
||||
jprim value() const {
|
||||
static auto method =
|
||||
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
|
||||
return method(this->self());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
#define DEFINE_BOXED_PRIMITIVE(LITTLE, BIG) \
|
||||
struct J ## BIG : detail::JPrimitive<J ## BIG, j ## LITTLE> { \
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/" #BIG ";"; \
|
||||
static auto constexpr kValueMethod = #LITTLE "Value"; \
|
||||
j ## LITTLE LITTLE ## Value() const { \
|
||||
return value(); \
|
||||
} \
|
||||
}; \
|
||||
inline local_ref<jobject> autobox(j ## LITTLE val) { \
|
||||
return J ## BIG::valueOf(val); \
|
||||
}
|
||||
|
||||
DEFINE_BOXED_PRIMITIVE(boolean, Boolean)
|
||||
DEFINE_BOXED_PRIMITIVE(byte, Byte)
|
||||
DEFINE_BOXED_PRIMITIVE(char, Character)
|
||||
DEFINE_BOXED_PRIMITIVE(short, Short)
|
||||
DEFINE_BOXED_PRIMITIVE(int, Integer)
|
||||
DEFINE_BOXED_PRIMITIVE(long, Long)
|
||||
DEFINE_BOXED_PRIMITIVE(float, Float)
|
||||
DEFINE_BOXED_PRIMITIVE(double, Double)
|
||||
|
||||
#undef DEFINE_BOXED_PRIMITIVE
|
||||
|
||||
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
|
||||
return make_local(val);
|
||||
}
|
||||
|
||||
}}
|
||||
|
34
lib/fb/include/fb/fbjni/ByteBuffer.h
Normal file
34
lib/fb/include/fb/fbjni/ByteBuffer.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// JNI's NIO support has some awkward preconditions and error reporting. This
|
||||
// class provides much more user-friendly access.
|
||||
class FBEXPORT JByteBuffer : public JavaClass<JByteBuffer> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
|
||||
|
||||
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
|
||||
|
||||
bool isDirect();
|
||||
|
||||
uint8_t* getDirectBytes();
|
||||
size_t getDirectSize();
|
||||
};
|
||||
|
||||
}}
|
106
lib/fb/include/fb/fbjni/Common.h
Normal file
106
lib/fb/include/fb/fbjni/Common.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/** @file Common.h
|
||||
*
|
||||
* Defining the stuff that don't deserve headers of their own...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
#include <fb/Environment.h>
|
||||
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
# ifdef __ANDROID__
|
||||
# include <android/log.h>
|
||||
# else
|
||||
# include <cstdio>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as
|
||||
// a C++ exception.
|
||||
#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \
|
||||
::facebook::jni::throwPendingJniExceptionAsCppException()
|
||||
|
||||
// If the condition is true, throws a JniException object, which wraps the pending JNI Java
|
||||
// exception if any. If no pending exception is found, throws a JniException object that wraps a
|
||||
// RuntimeException throwable.
|
||||
#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \
|
||||
::facebook::jni::throwCppExceptionIf(CONDITION)
|
||||
|
||||
/// @cond INTERNAL
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
FBEXPORT void throwPendingJniExceptionAsCppException();
|
||||
FBEXPORT void throwCppExceptionIf(bool condition);
|
||||
|
||||
[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);
|
||||
[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);
|
||||
template<typename... Args>
|
||||
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
|
||||
|
||||
|
||||
/**
|
||||
* This needs to be called at library load time, typically in your JNI_OnLoad method.
|
||||
*
|
||||
* The intended use is to return the result of initialize() directly
|
||||
* from JNI_OnLoad and to do nothing else there. Library specific
|
||||
* initialization code should go in the function passed to initialize
|
||||
* (which can be, and probably should be, a C++ lambda). This approach
|
||||
* provides correct error handling and translation errors during
|
||||
* initialization into Java exceptions when appropriate.
|
||||
*
|
||||
* Failure to call this will cause your code to crash in a remarkably
|
||||
* unhelpful way (typically a segfault) while trying to handle an exception
|
||||
* which occurs later.
|
||||
*/
|
||||
FBEXPORT jint initialize(JavaVM*, std::function<void()>&&) noexcept;
|
||||
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* Retrieve a pointer the JNI environment of the current thread.
|
||||
*
|
||||
* @pre The current thread must be attached to the VM
|
||||
*/
|
||||
inline JNIEnv* getEnv() noexcept {
|
||||
// TODO(T6594868) Benchmark against raw JNI access
|
||||
return Environment::current();
|
||||
}
|
||||
|
||||
// Define to get extremely verbose logging of references and to enable reference stats
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
template<typename... Args>
|
||||
inline void dbglog(const char* msg, Args... args) {
|
||||
# ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "fbjni_dbg", msg, args...);
|
||||
# else
|
||||
std::fprintf(stderr, msg, args...);
|
||||
# endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename... Args>
|
||||
inline void dbglog(const char*, Args...) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}}}
|
||||
|
||||
/// @endcond
|
35
lib/fb/include/fb/fbjni/Context.h
Normal file
35
lib/fb/include/fb/fbjni/Context.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "File.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class AContext : public JavaClass<AContext> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
|
||||
|
||||
// Define a method that calls into the represented Java class
|
||||
local_ref<JFile::javaobject> getCacheDir() {
|
||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
|
||||
return method(self());
|
||||
}
|
||||
|
||||
local_ref<JFile::javaobject> getFilesDir() {
|
||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
|
||||
return method(self());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
663
lib/fb/include/fb/fbjni/CoreClasses-inl.h
Normal file
663
lib/fb/include/fb/fbjni/CoreClasses-inl.h
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <type_traits>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Meta.h"
|
||||
#include "MetaConvert.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// jobject /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
|
||||
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
|
||||
}
|
||||
|
||||
inline local_ref<JClass> JObject::getClass() const noexcept {
|
||||
return adopt_local(internal::getEnv()->GetObjectClass(self()));
|
||||
}
|
||||
|
||||
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
|
||||
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T JObject::getFieldValue(JField<T> field) const noexcept {
|
||||
return field.get(self());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
|
||||
return adopt_local(field.get(self()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
|
||||
field.set(self(), value);
|
||||
}
|
||||
|
||||
inline std::string JObject::toString() const {
|
||||
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
|
||||
|
||||
return method(self())->toStdString();
|
||||
}
|
||||
|
||||
|
||||
// Class is here instead of CoreClasses.h because we need
|
||||
// alias_ref to be complete.
|
||||
class MonitorLock {
|
||||
public:
|
||||
inline MonitorLock() noexcept;
|
||||
inline MonitorLock(alias_ref<JObject> object) noexcept;
|
||||
inline ~MonitorLock() noexcept;
|
||||
|
||||
inline MonitorLock(MonitorLock&& other) noexcept;
|
||||
inline MonitorLock& operator=(MonitorLock&& other) noexcept;
|
||||
|
||||
inline MonitorLock(const MonitorLock&) = delete;
|
||||
inline MonitorLock& operator=(const MonitorLock&) = delete;
|
||||
|
||||
private:
|
||||
inline void reset() noexcept;
|
||||
alias_ref<JObject> owned_;
|
||||
};
|
||||
|
||||
MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
|
||||
|
||||
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
|
||||
: owned_(object) {
|
||||
internal::getEnv()->MonitorEnter(object.get());
|
||||
}
|
||||
|
||||
void MonitorLock::reset() noexcept {
|
||||
if (owned_) {
|
||||
internal::getEnv()->MonitorExit(owned_.get());
|
||||
if (internal::getEnv()->ExceptionCheck()) {
|
||||
abort(); // Lock mismatch
|
||||
}
|
||||
owned_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MonitorLock::~MonitorLock() noexcept {
|
||||
reset();
|
||||
}
|
||||
|
||||
MonitorLock::MonitorLock(MonitorLock&& other) noexcept
|
||||
: owned_(other.owned_)
|
||||
{
|
||||
other.owned_ = nullptr;
|
||||
}
|
||||
|
||||
MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
|
||||
reset();
|
||||
owned_ = other.owned_;
|
||||
other.owned_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MonitorLock JObject::lock() const noexcept {
|
||||
return MonitorLock(this_);
|
||||
}
|
||||
|
||||
inline jobject JObject::self() const noexcept {
|
||||
return this_;
|
||||
}
|
||||
|
||||
inline void swap(JObject& a, JObject& b) noexcept {
|
||||
using std::swap;
|
||||
swap(a.this_, b.this_);
|
||||
}
|
||||
|
||||
// JavaClass ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
template<typename JC, typename... Args>
|
||||
static local_ref<JC> newInstance(Args... args) {
|
||||
static auto cls = JC::javaClassStatic();
|
||||
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
|
||||
return cls->newObject(constructor, args...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename B, typename J>
|
||||
auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
|
||||
return static_cast<javaobject>(JObject::self());
|
||||
}
|
||||
|
||||
// jclass //////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This is not a real type. It is used so people won't accidentally
|
||||
// use a void* to initialize a NativeMethod.
|
||||
struct NativeMethodWrapper;
|
||||
|
||||
}
|
||||
|
||||
struct NativeMethod {
|
||||
const char* name;
|
||||
std::string descriptor;
|
||||
detail::NativeMethodWrapper* wrapper;
|
||||
};
|
||||
|
||||
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
|
||||
return adopt_local(internal::getEnv()->GetSuperclass(self()));
|
||||
}
|
||||
|
||||
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
|
||||
const auto env = internal::getEnv();
|
||||
|
||||
JNINativeMethod jnimethods[methods.size()];
|
||||
size_t i = 0;
|
||||
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
|
||||
jnimethods[i].name = it->name;
|
||||
jnimethods[i].signature = it->descriptor.c_str();
|
||||
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
|
||||
}
|
||||
|
||||
auto result = env->RegisterNatives(self(), jnimethods, methods.size());
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
|
||||
}
|
||||
|
||||
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
const auto result = env->IsAssignableFrom(self(), other.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JConstructor<F> JClass::getConstructor() const {
|
||||
return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JConstructor<F> JClass::getConstructor(const char* descriptor) const {
|
||||
constexpr auto constructor_method_name = "<init>";
|
||||
return getMethod<F>(constructor_method_name, descriptor);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JMethod<F> JClass::getMethod(const char* name) const {
|
||||
return getMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JMethod<F> JClass::getMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {
|
||||
return getStaticMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JStaticMethod<F> JClass::getStaticMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetStaticMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JStaticMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(const char* name) const {
|
||||
return getNonvirtualMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||
return JNonvirtualMethod<F>{method};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JField<enable_if_t<IsJniScalar<T>(), T>>
|
||||
JClass::getField(const char* name) const {
|
||||
return getField<T>(name, jtype_traits<T>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto field = env->GetFieldID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||
return JField<T>{field};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
||||
const char* name) const {
|
||||
return getStaticField<T>(name, jtype_traits<T>::descriptor().c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
||||
const char* name,
|
||||
const char* descriptor) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto field = env->GetStaticFieldID(self(), name, descriptor);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||
return JStaticField<T>{field};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
|
||||
return field.get(self());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {
|
||||
return adopt_local(field.get(self()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
|
||||
field.set(self(), value);
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline local_ref<R> JClass::newObject(
|
||||
JConstructor<R(Args...)> constructor,
|
||||
Args... args) const {
|
||||
const auto env = internal::getEnv();
|
||||
auto object = env->NewObject(self(), constructor.getId(),
|
||||
detail::callToJni(
|
||||
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
|
||||
return adopt_local(static_cast<R>(object));
|
||||
}
|
||||
|
||||
inline jclass JClass::self() const noexcept {
|
||||
return static_cast<jclass>(JObject::self());
|
||||
}
|
||||
|
||||
inline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {
|
||||
findClassLocal(name)->registerNatives(methods);
|
||||
}
|
||||
|
||||
|
||||
// jstring /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {
|
||||
return make_jstring(modifiedUtf8.c_str());
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// convert to std::string from jstring
|
||||
template <>
|
||||
struct Convert<std::string> {
|
||||
typedef jstring jniType;
|
||||
static std::string fromJni(jniType t) {
|
||||
return wrap_alias(t)->toStdString();
|
||||
}
|
||||
static jniType toJniRet(const std::string& t) {
|
||||
return make_jstring(t).release();
|
||||
}
|
||||
static local_ref<JString> toCall(const std::string& t) {
|
||||
return make_jstring(t);
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from const char*
|
||||
template <>
|
||||
struct Convert<const char*> {
|
||||
typedef jstring jniType;
|
||||
// no automatic synthesis of const char*. (It can't be freed.)
|
||||
static jniType toJniRet(const char* t) {
|
||||
return make_jstring(t).release();
|
||||
}
|
||||
static local_ref<JString> toCall(const char* t) {
|
||||
return make_jstring(t);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
|
||||
static auto meth = javaClassStatic()->getMethod<javaobject(javaobject)>("initCause");
|
||||
return meth(self(), cause.get());
|
||||
}
|
||||
|
||||
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail {
|
||||
inline size_t JArray::size() const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
return env->GetArrayLength(self());
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy(
|
||||
Target* target,
|
||||
size_t idx)
|
||||
: target_{target}, idx_{idx} {}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
|
||||
target_->setElement(idx_, o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {
|
||||
target_->setElement(idx_, o.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {
|
||||
target_->setElement(idx_, o.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const ElementProxy<Target>& o) {
|
||||
auto src = o.target_->getElement(o.idx_);
|
||||
target_->setElement(idx_, src.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {
|
||||
return target_->getElement(idx_);
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
inline ElementProxy<Target>::ElementProxy::operator local_ref<T> () {
|
||||
return target_->getElement(idx_);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string JArrayClass<T>::get_instantiated_java_descriptor() {
|
||||
return "[" + jtype_traits<T>::descriptor();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::string JArrayClass<T>::get_instantiated_base_name() {
|
||||
return get_instantiated_java_descriptor();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
|
||||
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||
const auto env = internal::getEnv();
|
||||
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
|
||||
return adopt_local(static_cast<javaobject>(rawArray));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
|
||||
const auto env = internal::getEnv();
|
||||
env->SetObjectArrayElement(this->self(), idx, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
|
||||
const auto env = internal::getEnv();
|
||||
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
|
||||
return adopt_local(static_cast<T>(rawElement));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {
|
||||
return detail::ElementProxy<JArrayClass<T>>(this, index);
|
||||
}
|
||||
|
||||
// jarray /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
|
||||
-> std::unique_ptr<T[]> {
|
||||
using T = typename jtype_traits<JArrayType>::entry_type;
|
||||
auto buf = std::unique_ptr<T[]>{new T[length]};
|
||||
getRegion(start, length, buf.get());
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
|
||||
return jtype_traits<JArrayType>::descriptor();
|
||||
}
|
||||
template <typename JArrayType>
|
||||
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
|
||||
return JPrimitiveArray::get_instantiated_java_descriptor();
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
|
||||
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
|
||||
}
|
||||
|
||||
template <typename JArrayType>
|
||||
auto JPrimitiveArray<JArrayType>::pinCritical()
|
||||
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
|
||||
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class PinnedArrayAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
(void) start;
|
||||
(void) length;
|
||||
*elements = array->getElements(isCopy);
|
||||
*size = array->size();
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
(void) start;
|
||||
(void) size;
|
||||
array->releaseElements(elements, mode);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PinnedCriticalAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
const auto env = internal::getEnv();
|
||||
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
|
||||
*size = array->size();
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
const auto env = internal::getEnv();
|
||||
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class PinnedRegionAlloc {
|
||||
public:
|
||||
static void allocate(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
jsize start,
|
||||
jsize length,
|
||||
T** elements,
|
||||
size_t* size,
|
||||
jboolean* isCopy) {
|
||||
auto buf = array->getRegion(start, length);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
|
||||
*elements = buf.release();
|
||||
*size = length;
|
||||
*isCopy = true;
|
||||
}
|
||||
static void release(
|
||||
alias_ref<typename jtype_traits<T>::array_type> array,
|
||||
T* elements,
|
||||
jint start,
|
||||
jint size,
|
||||
jint mode) {
|
||||
std::unique_ptr<T[]> holder;
|
||||
if (mode == 0 || mode == JNI_ABORT) {
|
||||
holder.reset(elements);
|
||||
}
|
||||
if (mode == 0 || mode == JNI_COMMIT) {
|
||||
array->setRegion(start, size, elements);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
|
||||
*this = std::move(o);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
PinnedPrimitiveArray<T, Alloc>&
|
||||
PinnedPrimitiveArray<T, Alloc>::operator=(PinnedPrimitiveArray&& o) {
|
||||
if (array_) {
|
||||
release();
|
||||
}
|
||||
array_ = std::move(o.array_);
|
||||
elements_ = o.elements_;
|
||||
isCopy_ = o.isCopy_;
|
||||
size_ = o.size_;
|
||||
start_ = o.start_;
|
||||
o.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
T* PinnedPrimitiveArray<T, Alloc>::get() {
|
||||
return elements_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::release() {
|
||||
releaseImpl(0);
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::commit() {
|
||||
releaseImpl(JNI_COMMIT);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::abort() {
|
||||
releaseImpl(JNI_ABORT);
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);
|
||||
Alloc::release(array_, elements_, start_, size_, mode);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {
|
||||
array_ = nullptr;
|
||||
elements_ = nullptr;
|
||||
isCopy_ = false;
|
||||
start_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
|
||||
return elements_[index];
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
|
||||
return isCopy_ == JNI_TRUE;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
|
||||
if (elements_) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(alias_ref<typename jtype_traits<T>::array_type> array, jint start, jint length) {
|
||||
array_ = array;
|
||||
start_ = start;
|
||||
Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);
|
||||
}
|
||||
|
||||
template<typename T, typename Base, typename JType>
|
||||
inline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {
|
||||
static auto cls = findClassStatic(jtype_traits<typename T::javaobject>::base_name().c_str());
|
||||
return cls;
|
||||
}
|
||||
|
||||
template<typename T, typename Base, typename JType>
|
||||
inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
|
||||
std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());
|
||||
return findClassLocal(className.c_str());
|
||||
}
|
||||
|
||||
}}
|
595
lib/fb/include/fb/fbjni/CoreClasses.h
Normal file
595
lib/fb/include/fb/fbjni/CoreClasses.h
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** @file CoreClasses.h
|
||||
*
|
||||
* In CoreClasses.h wrappers for the core classes (jobject, jclass, and jstring) is defined
|
||||
* to provide access to corresponding JNI functions + some conveniance.
|
||||
*/
|
||||
|
||||
#include "References-forward.h"
|
||||
#include "Meta-forward.h"
|
||||
#include "TypeTraits.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JClass;
|
||||
class JObject;
|
||||
|
||||
/// Lookup a class by name. Note this functions returns an alias_ref that
|
||||
/// points to a leaked global reference. This is appropriate for classes
|
||||
/// that are never unloaded (which is any class in an Android app and most
|
||||
/// Java programs).
|
||||
///
|
||||
/// The most common use case for this is storing the result
|
||||
/// in a "static auto" variable, or a static global.
|
||||
///
|
||||
/// @return Returns a leaked global reference to the class
|
||||
FBEXPORT alias_ref<JClass> findClassStatic(const char* name);
|
||||
|
||||
/// Lookup a class by name. Note this functions returns a local reference,
|
||||
/// which means that it must not be stored in a static variable.
|
||||
///
|
||||
/// The most common use case for this is one-time initialization
|
||||
/// (like caching method ids).
|
||||
///
|
||||
/// @return Returns a global reference to the class
|
||||
FBEXPORT local_ref<JClass> findClassLocal(const char* name);
|
||||
|
||||
/// Check to see if two references refer to the same object. Comparison with nullptr
|
||||
/// returns true if and only if compared to another nullptr. A weak reference that
|
||||
/// refers to a reclaimed object count as nullptr.
|
||||
FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
|
||||
|
||||
// Together, these classes allow convenient use of any class with the fbjni
|
||||
// helpers. To use:
|
||||
//
|
||||
// struct MyClass : public JavaClass<MyClass> {
|
||||
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
||||
// };
|
||||
//
|
||||
// Then, an alias_ref<MyClass::javaobject> will be backed by an instance of
|
||||
// MyClass. JavaClass provides a convenient way to add functionality to these
|
||||
// smart references.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// struct MyClass : public JavaClass<MyClass> {
|
||||
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
||||
//
|
||||
// void foo() {
|
||||
// static auto method = javaClassStatic()->getMethod<void()>("foo");
|
||||
// method(self());
|
||||
// }
|
||||
//
|
||||
// static local_ref<javaobject> create(int i) {
|
||||
// return newInstance(i);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// auto obj = MyClass::create(10);
|
||||
// obj->foo();
|
||||
//
|
||||
// While users of a JavaClass-type can lookup methods and fields through the
|
||||
// underlying JClass, those calls can only be checked at runtime. It is recommended
|
||||
// that the JavaClass-type instead explicitly expose it's methods as in the example
|
||||
// above.
|
||||
|
||||
namespace detail {
|
||||
template<typename JC, typename... Args>
|
||||
static local_ref<JC> newInstance(Args... args);
|
||||
}
|
||||
|
||||
class MonitorLock;
|
||||
|
||||
class FBEXPORT JObject : detail::JObjectBase {
|
||||
public:
|
||||
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
|
||||
|
||||
static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }
|
||||
static constexpr const char* get_instantiated_base_name() { return nullptr; }
|
||||
|
||||
/// Get a @ref local_ref of the object's class
|
||||
local_ref<JClass> getClass() const noexcept;
|
||||
|
||||
/// Checks if the object is an instance of a class
|
||||
bool isInstanceOf(alias_ref<JClass> cls) const noexcept;
|
||||
|
||||
/// Get the primitive value of a field
|
||||
template<typename T>
|
||||
T getFieldValue(JField<T> field) const noexcept;
|
||||
|
||||
/// Get and wrap the value of a field in a @ref local_ref
|
||||
template<typename T>
|
||||
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
|
||||
|
||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
||||
/// and raw reference types.
|
||||
template<typename T>
|
||||
void setFieldValue(JField<T> field, T value) noexcept;
|
||||
|
||||
/// Convenience method to create a std::string representing the object
|
||||
std::string toString() const;
|
||||
|
||||
// Take this object's monitor lock
|
||||
MonitorLock lock() const noexcept;
|
||||
|
||||
typedef _jobject _javaobject;
|
||||
typedef _javaobject* javaobject;
|
||||
|
||||
protected:
|
||||
jobject self() const noexcept;
|
||||
private:
|
||||
friend void swap(JObject& a, JObject& b) noexcept;
|
||||
template<typename>
|
||||
friend struct detail::ReprAccess;
|
||||
template<typename, typename, typename>
|
||||
friend class JavaClass;
|
||||
|
||||
template <typename, typename>
|
||||
friend class JObjectWrapper;
|
||||
};
|
||||
|
||||
// This is only to maintain backwards compatibility with things that are
|
||||
// already providing a specialization of JObjectWrapper. Any such instances
|
||||
// should be updated to use a JavaClass.
|
||||
template<>
|
||||
class JObjectWrapper<jobject> : public JObject {
|
||||
};
|
||||
|
||||
|
||||
namespace detail {
|
||||
template <typename, typename Base, typename JType>
|
||||
struct JTypeFor {
|
||||
static_assert(
|
||||
std::is_base_of<
|
||||
std::remove_pointer<jobject>::type,
|
||||
typename std::remove_pointer<JType>::type
|
||||
>::value, "");
|
||||
using _javaobject = typename std::remove_pointer<JType>::type;
|
||||
using javaobject = JType;
|
||||
};
|
||||
|
||||
template <typename T, typename Base>
|
||||
struct JTypeFor<T, Base, void> {
|
||||
// JNI pattern for jobject assignable pointer
|
||||
struct _javaobject : Base::_javaobject {
|
||||
// This allows us to map back to the defining type (in ReprType, for
|
||||
// example).
|
||||
typedef T JniRefRepr;
|
||||
};
|
||||
using javaobject = _javaobject*;
|
||||
};
|
||||
}
|
||||
|
||||
// JavaClass provides a method to inform fbjni about user-defined Java types.
|
||||
// Given a class:
|
||||
// struct Foo : JavaClass<Foo> {
|
||||
// static constexpr auto kJavaDescriptor = "Lcom/example/package/Foo;";
|
||||
// };
|
||||
// fbjni can determine the java type/method signatures for Foo::javaobject and
|
||||
// smart refs (like alias_ref<Foo::javaobject>) will hold an instance of Foo
|
||||
// and provide access to it through the -> and * operators.
|
||||
//
|
||||
// The "Base" template argument can be used to specify the JavaClass superclass
|
||||
// of this type (for instance, JString's Base is JObject).
|
||||
//
|
||||
// The "JType" template argument is used to provide a jni type (like jstring,
|
||||
// jthrowable) to be used as javaobject. This should only be necessary for
|
||||
// built-in jni types and not user-defined ones.
|
||||
template <typename T, typename Base = JObject, typename JType = void>
|
||||
class FBEXPORT JavaClass : public Base {
|
||||
using JObjType = typename detail::JTypeFor<T, Base, JType>;
|
||||
public:
|
||||
using _javaobject = typename JObjType::_javaobject;
|
||||
using javaobject = typename JObjType::javaobject;
|
||||
|
||||
using JavaBase = JavaClass;
|
||||
|
||||
static alias_ref<JClass> javaClassStatic();
|
||||
static local_ref<JClass> javaClassLocal();
|
||||
protected:
|
||||
/// Allocates a new object and invokes the specified constructor
|
||||
/// Like JClass's getConstructor, this function can only check at runtime if
|
||||
/// the class actually has a constructor that accepts the corresponding types.
|
||||
/// While a JavaClass-type can expose this function directly, it is recommended
|
||||
/// to instead to use this to explicitly only expose those constructors that
|
||||
/// the Java class actually has (i.e. with static create() functions).
|
||||
template<typename... Args>
|
||||
static local_ref<T> newInstance(Args... args) {
|
||||
return detail::newInstance<T>(args...);
|
||||
}
|
||||
|
||||
javaobject self() const noexcept;
|
||||
};
|
||||
|
||||
/// Wrapper to provide functionality to jclass references
|
||||
struct NativeMethod;
|
||||
|
||||
class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {
|
||||
public:
|
||||
/// Java type descriptor
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
|
||||
|
||||
/// Get a @local_ref to the super class of this class
|
||||
local_ref<JClass> getSuperclass() const noexcept;
|
||||
|
||||
/// Register native methods for the class. Usage looks like this:
|
||||
///
|
||||
/// classRef->registerNatives({
|
||||
/// makeNativeMethod("nativeMethodWithAutomaticDescriptor",
|
||||
/// methodWithAutomaticDescriptor),
|
||||
/// makeNativeMethod("nativeMethodWithExplicitDescriptor",
|
||||
/// "(Lcom/facebook/example/MyClass;)V",
|
||||
/// methodWithExplicitDescriptor),
|
||||
/// });
|
||||
///
|
||||
/// By default, C++ exceptions raised will be converted to Java exceptions.
|
||||
/// To avoid this and get the "standard" JNI behavior of a crash when a C++
|
||||
/// exception is crashing out of the JNI method, declare the method noexcept.
|
||||
void registerNatives(std::initializer_list<NativeMethod> methods);
|
||||
|
||||
/// Check to see if the class is assignable from another class
|
||||
/// @pre cls != nullptr
|
||||
bool isAssignableFrom(alias_ref<JClass> cls) const noexcept;
|
||||
|
||||
/// Convenience method to lookup the constructor with descriptor as specified by the
|
||||
/// type arguments
|
||||
template<typename F>
|
||||
JConstructor<F> getConstructor() const;
|
||||
|
||||
/// Convenience method to lookup the constructor with specified descriptor
|
||||
template<typename F>
|
||||
JConstructor<F> getConstructor(const char* descriptor) const;
|
||||
|
||||
/// Look up the method with given name and descriptor as specified with the type arguments
|
||||
template<typename F>
|
||||
JMethod<F> getMethod(const char* name) const;
|
||||
|
||||
/// Look up the method with given name and descriptor
|
||||
template<typename F>
|
||||
JMethod<F> getMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Lookup the field with the given name and deduced descriptor
|
||||
template<typename T>
|
||||
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name) const;
|
||||
|
||||
/// Lookup the field with the given name and descriptor
|
||||
template<typename T>
|
||||
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Lookup the static field with the given name and deduced descriptor
|
||||
template<typename T>
|
||||
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(const char* name) const;
|
||||
|
||||
/// Lookup the static field with the given name and descriptor
|
||||
template<typename T>
|
||||
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(
|
||||
const char* name,
|
||||
const char* descriptor) const;
|
||||
|
||||
/// Get the primitive value of a static field
|
||||
template<typename T>
|
||||
T getStaticFieldValue(JStaticField<T> field) const noexcept;
|
||||
|
||||
/// Get and wrap the value of a field in a @ref local_ref
|
||||
template<typename T>
|
||||
local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;
|
||||
|
||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
||||
/// and raw reference types.
|
||||
template<typename T>
|
||||
void setStaticFieldValue(JStaticField<T> field, T value) noexcept;
|
||||
|
||||
/// Allocates a new object and invokes the specified constructor
|
||||
template<typename R, typename... Args>
|
||||
local_ref<R> newObject(JConstructor<R(Args...)> constructor, Args... args) const;
|
||||
|
||||
/// Look up the static method with given name and descriptor as specified with the type arguments
|
||||
template<typename F>
|
||||
JStaticMethod<F> getStaticMethod(const char* name) const;
|
||||
|
||||
/// Look up the static method with given name and descriptor
|
||||
template<typename F>
|
||||
JStaticMethod<F> getStaticMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
/// Look up the non virtual method with given name and descriptor as specified with the
|
||||
/// type arguments
|
||||
template<typename F>
|
||||
JNonvirtualMethod<F> getNonvirtualMethod(const char* name) const;
|
||||
|
||||
/// Look up the non virtual method with given name and descriptor
|
||||
template<typename F>
|
||||
JNonvirtualMethod<F> getNonvirtualMethod(const char* name, const char* descriptor) const;
|
||||
|
||||
private:
|
||||
jclass self() const noexcept;
|
||||
};
|
||||
|
||||
// Convenience method to register methods on a class without holding
|
||||
// onto the class object.
|
||||
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
|
||||
|
||||
/// Wrapper to provide functionality to jstring references
|
||||
class FBEXPORT JString : public JavaClass<JString, JObject, jstring> {
|
||||
public:
|
||||
/// Java type descriptor
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
|
||||
|
||||
/// Convenience method to convert a jstring object to a std::string
|
||||
std::string toStdString() const;
|
||||
};
|
||||
|
||||
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a
|
||||
/// jstring
|
||||
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);
|
||||
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
|
||||
|
||||
/// Wrapper to provide functionality to jthrowable references
|
||||
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
|
||||
|
||||
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename Target>
|
||||
class ElementProxy {
|
||||
private:
|
||||
Target* target_;
|
||||
size_t idx_;
|
||||
|
||||
public:
|
||||
using T = typename Target::javaentry;
|
||||
ElementProxy(Target* target, size_t idx);
|
||||
|
||||
ElementProxy& operator=(const T& o);
|
||||
|
||||
ElementProxy& operator=(alias_ref<T>& o);
|
||||
|
||||
ElementProxy& operator=(alias_ref<T>&& o);
|
||||
|
||||
ElementProxy& operator=(const ElementProxy& o);
|
||||
|
||||
operator const local_ref<T> () const;
|
||||
|
||||
operator local_ref<T> ();
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {
|
||||
public:
|
||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
||||
// signature). Use a more derived type instead (like JArrayInt or
|
||||
// JArrayClass<T>).
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
size_t size() const noexcept;
|
||||
};
|
||||
|
||||
// This is used so that the JArrayClass<T> javaobject extends jni's
|
||||
// jobjectArray. This class should not be used directly. A general Object[]
|
||||
// should use JArrayClass<jobject>.
|
||||
class FBEXPORT JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
|
||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
||||
// signature).
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class JArrayClass : public JavaClass<JArrayClass<T>, detail::JTypeArray> {
|
||||
public:
|
||||
static_assert(is_plain_jni_reference<T>(), "");
|
||||
// javaentry is the jni type of an entry in the array (i.e. jint).
|
||||
using javaentry = T;
|
||||
// javaobject is the jni type of the array.
|
||||
using javaobject = typename JavaClass<JArrayClass<T>, detail::JTypeArray>::javaobject;
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
/// Allocate a new array from Java heap, for passing as a JNI parameter or return value.
|
||||
/// NOTE: if using as a return value, you want to call release() instead of get() on the
|
||||
/// smart pointer.
|
||||
static local_ref<javaobject> newArray(size_t count);
|
||||
|
||||
/// Assign an object to the array.
|
||||
/// Typically you will use the shorthand (*ref)[idx]=value;
|
||||
void setElement(size_t idx, const T& value);
|
||||
|
||||
/// Read an object from the array.
|
||||
/// Typically you will use the shorthand
|
||||
/// T value = (*ref)[idx];
|
||||
/// If you use auto, you'll get an ElementProxy, which may need to be cast.
|
||||
local_ref<T> getElement(size_t idx);
|
||||
|
||||
/// EXPERIMENTAL SUBSCRIPT SUPPORT
|
||||
/// This implementation of [] returns a proxy object which then has a bunch of specializations
|
||||
/// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can
|
||||
/// make code look like it is dealing with a T rather than an obvious proxy. In particular, the
|
||||
/// proxy in this iteration does not read a value and therefore does not create a LocalRef
|
||||
/// until one of these other operators is used. There are certainly holes that you may find
|
||||
/// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand,
|
||||
/// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests
|
||||
/// for some examples.
|
||||
detail::ElementProxy<JArrayClass> operator[](size_t idx);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using jtypeArray = typename JArrayClass<T>::javaobject;
|
||||
|
||||
template<typename T>
|
||||
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
|
||||
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
|
||||
}
|
||||
|
||||
template<typename Target>
|
||||
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
|
||||
return static_cast<local_ref<typename Target::javaentry>>(elementProxy);
|
||||
}
|
||||
|
||||
template <typename T, typename PinAlloc>
|
||||
class PinnedPrimitiveArray;
|
||||
|
||||
template <typename T> class PinnedArrayAlloc;
|
||||
template <typename T> class PinnedRegionAlloc;
|
||||
template <typename T> class PinnedCriticalAlloc;
|
||||
|
||||
/// Wrapper to provide functionality to jarray references.
|
||||
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
|
||||
/// the elements of the array.
|
||||
template <typename JArrayType>
|
||||
class FBEXPORT JPrimitiveArray :
|
||||
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
|
||||
static_assert(is_jni_primitive_array<JArrayType>(), "");
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
using T = typename jtype_traits<JArrayType>::entry_type;
|
||||
|
||||
static local_ref<JArrayType> newArray(size_t count);
|
||||
|
||||
void getRegion(jsize start, jsize length, T* buf);
|
||||
std::unique_ptr<T[]> getRegion(jsize start, jsize length);
|
||||
void setRegion(jsize start, jsize length, const T* buf);
|
||||
|
||||
/// Returns a view of the underlying array. This will either be a "pinned"
|
||||
/// version of the array (in which case changes to one immediately affect the
|
||||
/// other) or a copy of the array (in which cases changes to the view will take
|
||||
/// affect when destroyed or on calls to release()/commit()).
|
||||
PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> pin();
|
||||
|
||||
/// Returns a view of part of the underlying array. A pinned region is always
|
||||
/// backed by a copy of the region.
|
||||
PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> pinRegion(jsize start, jsize length);
|
||||
|
||||
/// Returns a view of the underlying array like pin(). However, while the pin
|
||||
/// is held, the code is considered within a "critical region". In a critical
|
||||
/// region, native code must not call JNI functions or make any calls that may
|
||||
/// block on other Java threads. These restrictions make it more likely that
|
||||
/// the view will be "pinned" rather than copied (for example, the VM may
|
||||
/// suspend garbage collection within a critical region).
|
||||
PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> pinCritical();
|
||||
|
||||
private:
|
||||
friend class PinnedArrayAlloc<T>;
|
||||
T* getElements(jboolean* isCopy);
|
||||
void releaseElements(T* elements, jint mode);
|
||||
};
|
||||
|
||||
FBEXPORT local_ref<jbooleanArray> make_boolean_array(jsize size);
|
||||
FBEXPORT local_ref<jbyteArray> make_byte_array(jsize size);
|
||||
FBEXPORT local_ref<jcharArray> make_char_array(jsize size);
|
||||
FBEXPORT local_ref<jshortArray> make_short_array(jsize size);
|
||||
FBEXPORT local_ref<jintArray> make_int_array(jsize size);
|
||||
FBEXPORT local_ref<jlongArray> make_long_array(jsize size);
|
||||
FBEXPORT local_ref<jfloatArray> make_float_array(jsize size);
|
||||
FBEXPORT local_ref<jdoubleArray> make_double_array(jsize size);
|
||||
|
||||
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
|
||||
using JArrayByte = JPrimitiveArray<jbyteArray>;
|
||||
using JArrayChar = JPrimitiveArray<jcharArray>;
|
||||
using JArrayShort = JPrimitiveArray<jshortArray>;
|
||||
using JArrayInt = JPrimitiveArray<jintArray>;
|
||||
using JArrayLong = JPrimitiveArray<jlongArray>;
|
||||
using JArrayFloat = JPrimitiveArray<jfloatArray>;
|
||||
using JArrayDouble = JPrimitiveArray<jdoubleArray>;
|
||||
|
||||
/// RAII class for pinned primitive arrays
|
||||
/// This currently only supports read/write access to existing java arrays. You can't create a
|
||||
/// primitive array this way yet. This class also pins the entire array into memory during the
|
||||
/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the
|
||||
/// release() or abort() functions. During a long-running block of code, you
|
||||
/// should unpin the array as soon as you're done with it, to avoid holding up
|
||||
/// the Java garbage collector.
|
||||
template <typename T, typename PinAlloc>
|
||||
class PinnedPrimitiveArray {
|
||||
public:
|
||||
static_assert(is_jni_primitive<T>::value,
|
||||
"PinnedPrimitiveArray requires primitive jni type.");
|
||||
|
||||
using ArrayType = typename jtype_traits<T>::array_type;
|
||||
|
||||
PinnedPrimitiveArray(PinnedPrimitiveArray&&);
|
||||
PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;
|
||||
~PinnedPrimitiveArray() noexcept;
|
||||
|
||||
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&);
|
||||
PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;
|
||||
|
||||
T* get();
|
||||
void release();
|
||||
/// Unpins the array. If the array is a copy, pending changes are discarded.
|
||||
void abort();
|
||||
/// If the array is a copy, copies pending changes to the underlying java array.
|
||||
void commit();
|
||||
|
||||
bool isCopy() const noexcept;
|
||||
|
||||
const T& operator[](size_t index) const;
|
||||
T& operator[](size_t index);
|
||||
size_t size() const noexcept;
|
||||
|
||||
private:
|
||||
alias_ref<ArrayType> array_;
|
||||
size_t start_;
|
||||
T* elements_;
|
||||
jboolean isCopy_;
|
||||
size_t size_;
|
||||
|
||||
void allocate(alias_ref<ArrayType>, jint start, jint length);
|
||||
void releaseImpl(jint mode);
|
||||
void clear() noexcept;
|
||||
|
||||
PinnedPrimitiveArray(alias_ref<ArrayType>, jint start, jint length);
|
||||
|
||||
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
|
||||
};
|
||||
|
||||
#pragma push_macro("PlainJniRefMap")
|
||||
#undef PlainJniRefMap
|
||||
#define PlainJniRefMap(rtype, jtype) \
|
||||
namespace detail { \
|
||||
template<> \
|
||||
struct RefReprType<jtype> { \
|
||||
using type = rtype; \
|
||||
}; \
|
||||
}
|
||||
|
||||
PlainJniRefMap(JArrayBoolean, jbooleanArray);
|
||||
PlainJniRefMap(JArrayByte, jbyteArray);
|
||||
PlainJniRefMap(JArrayChar, jcharArray);
|
||||
PlainJniRefMap(JArrayShort, jshortArray);
|
||||
PlainJniRefMap(JArrayInt, jintArray);
|
||||
PlainJniRefMap(JArrayLong, jlongArray);
|
||||
PlainJniRefMap(JArrayFloat, jfloatArray);
|
||||
PlainJniRefMap(JArrayDouble, jdoubleArray);
|
||||
PlainJniRefMap(JObject, jobject);
|
||||
PlainJniRefMap(JClass, jclass);
|
||||
PlainJniRefMap(JString, jstring);
|
||||
PlainJniRefMap(JThrowable, jthrowable);
|
||||
|
||||
#pragma pop_macro("PlainJniRefMap")
|
||||
|
||||
}}
|
||||
|
||||
#include "CoreClasses-inl.h"
|
118
lib/fb/include/fb/fbjni/Exceptions.h
Normal file
118
lib/fb/include/fb/fbjni/Exceptions.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file Exceptions.h
|
||||
*
|
||||
* After invoking a JNI function that can throw a Java exception, the macro
|
||||
* @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref FACEBOOK_JNI_THROW_EXCEPTION_IF()
|
||||
* should be invoked.
|
||||
*
|
||||
* IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!
|
||||
* To use these methods you MUST call initExceptionHelpers() when your library is loaded.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "References.h"
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JThrowable;
|
||||
|
||||
class JCppException : public JavaClass<JCppException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;";
|
||||
|
||||
static local_ref<JCppException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
|
||||
static local_ref<JCppException> create(const std::exception& ex) {
|
||||
return newInstance(make_jstring(ex.what()));
|
||||
}
|
||||
};
|
||||
|
||||
// JniException ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This class wraps a Java exception into a C++ exception; if the exception is routed back
|
||||
* to the Java side, it can be unwrapped and just look like a pure Java interaction. The class
|
||||
* is resilient to errors while creating the exception, falling back to some pre-allocated
|
||||
* exceptions if a new one cannot be allocated or populated.
|
||||
*
|
||||
* Note: the what() method of this class is not thread-safe (t6900503).
|
||||
*/
|
||||
class FBEXPORT JniException : public std::exception {
|
||||
public:
|
||||
JniException();
|
||||
~JniException();
|
||||
|
||||
explicit JniException(alias_ref<jthrowable> throwable);
|
||||
|
||||
JniException(JniException &&rhs);
|
||||
|
||||
JniException(const JniException &other);
|
||||
|
||||
local_ref<JThrowable> getThrowable() const noexcept;
|
||||
|
||||
virtual const char* what() const noexcept;
|
||||
|
||||
void setJavaException() const noexcept;
|
||||
|
||||
private:
|
||||
global_ref<JThrowable> throwable_;
|
||||
mutable std::string what_;
|
||||
mutable bool isMessageExtracted_;
|
||||
const static std::string kExceptionMessageFailure_;
|
||||
|
||||
void populateWhat() const noexcept;
|
||||
};
|
||||
|
||||
// Exception throwing & translating functions //////////////////////////////////////////////////////
|
||||
|
||||
// Functions that throw C++ exceptions
|
||||
|
||||
static const int kMaxExceptionMessageBufferSize = 512;
|
||||
|
||||
// These methods are the preferred way to throw a Java exception from
|
||||
// a C++ function. They create and throw a C++ exception which wraps
|
||||
// a Java exception, so the C++ flow is interrupted. Then, when
|
||||
// translatePendingCppExceptionToJavaException is called at the
|
||||
// topmost level of the native stack, the wrapped Java exception is
|
||||
// thrown to the java caller.
|
||||
template<typename... Args>
|
||||
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {
|
||||
int msgSize = snprintf(nullptr, 0, fmt, args...);
|
||||
|
||||
char *msg = (char*) alloca(msgSize + 1);
|
||||
snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
|
||||
throwNewJavaException(throwableName, msg);
|
||||
}
|
||||
|
||||
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
|
||||
// be thrown, it aborts the program. This is a noexcept function at C++ level.
|
||||
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept;
|
||||
|
||||
// For convenience, some exception names in java.lang are available here.
|
||||
|
||||
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
|
||||
|
||||
}}
|
30
lib/fb/include/fb/fbjni/File.h
Normal file
30
lib/fb/include/fb/fbjni/File.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JFile : public JavaClass<JFile> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
|
||||
|
||||
// Define a method that calls into the represented Java class
|
||||
std::string getAbsolutePath() {
|
||||
static auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
|
||||
return method(self())->toStdString();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
233
lib/fb/include/fb/fbjni/Hybrid.h
Normal file
233
lib/fb/include/fb/fbjni/Hybrid.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class BaseHybridClass {
|
||||
public:
|
||||
virtual ~BaseHybridClass() {}
|
||||
};
|
||||
|
||||
struct FBEXPORT HybridData : public JavaClass<HybridData> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
||||
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
|
||||
BaseHybridClass* getNativePointer();
|
||||
static local_ref<HybridData> create();
|
||||
};
|
||||
|
||||
template <typename Base, typename Enabled = void>
|
||||
struct HybridTraits {
|
||||
// This static assert should actually always fail if we don't use one of the
|
||||
// specializations below.
|
||||
static_assert(
|
||||
std::is_base_of<JObject, Base>::value ||
|
||||
std::is_base_of<BaseHybridClass, Base>::value,
|
||||
"The base of a HybridClass must be either another HybridClass or derived from JObject.");
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HybridTraits<BaseHybridClass> {
|
||||
using CxxBase = BaseHybridClass;
|
||||
using JavaBase = JObject;
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct HybridTraits<
|
||||
Base,
|
||||
typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {
|
||||
using CxxBase = Base;
|
||||
using JavaBase = typename Base::JavaPart;
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct HybridTraits<
|
||||
Base,
|
||||
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
|
||||
using CxxBase = BaseHybridClass;
|
||||
using JavaBase = Base;
|
||||
};
|
||||
|
||||
// convert to HybridClass* from jhybridobject
|
||||
template <typename T>
|
||||
struct FBEXPORT Convert<
|
||||
T, typename std::enable_if<
|
||||
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
||||
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
||||
static T fromJni(jniType t) {
|
||||
if (t == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return wrap_alias(t)->cthis();
|
||||
}
|
||||
// There is no automatic return conversion for objects.
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {
|
||||
static_assert(std::is_same<T, void>::value,
|
||||
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. "
|
||||
"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.");
|
||||
using Repr = T;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
template <typename T, typename Base = detail::BaseHybridClass>
|
||||
class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
||||
public:
|
||||
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
||||
// At this point, T is incomplete, and so we cannot access
|
||||
// T::kJavaDescriptor directly. jtype_traits support this escape hatch for
|
||||
// such a case.
|
||||
static constexpr const char* kJavaDescriptor = nullptr;
|
||||
static std::string get_instantiated_java_descriptor();
|
||||
static std::string get_instantiated_base_name();
|
||||
|
||||
using HybridType = T;
|
||||
|
||||
// This will reach into the java object and extract the C++ instance from
|
||||
// the mHybridData and return it.
|
||||
T* cthis();
|
||||
|
||||
friend class HybridClass;
|
||||
};
|
||||
|
||||
using jhybridobject = typename JavaPart::javaobject;
|
||||
using javaobject = typename JavaPart::javaobject;
|
||||
typedef detail::HybridData::javaobject jhybriddata;
|
||||
|
||||
static alias_ref<JClass> javaClassStatic() {
|
||||
return JavaPart::javaClassStatic();
|
||||
}
|
||||
|
||||
static local_ref<JClass> javaClassLocal() {
|
||||
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
|
||||
return findClassLocal(className.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef HybridClass HybridBase;
|
||||
|
||||
// This ensures that a C++ hybrid part cannot be created on its own
|
||||
// by default. If a hybrid wants to enable this, it can provide its
|
||||
// own public ctor, or change the accessibility of this to public.
|
||||
using detail::HybridTraits<Base>::CxxBase::CxxBase;
|
||||
|
||||
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
|
||||
javaClassStatic()->registerNatives(methods);
|
||||
}
|
||||
|
||||
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
||||
auto hybridData = detail::HybridData::create();
|
||||
hybridData->setNativePointer(std::move(cxxPart));
|
||||
return hybridData;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
|
||||
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
|
||||
public:
|
||||
// Factory method for creating a hybrid object where the arguments
|
||||
// are used to initialize the C++ part directly without passing them
|
||||
// through java. This method requires the Java part to have a ctor
|
||||
// which takes a HybridData, and for the C++ part to have a ctor
|
||||
// compatible with the arguments passed here. For safety, the ctor
|
||||
// can be private, and the hybrid declared a friend of its base, so
|
||||
// the hybrid can only be created from here.
|
||||
//
|
||||
// Exception behavior: This can throw an exception if creating the
|
||||
// C++ object fails, or any JNI methods throw.
|
||||
template <typename... Args>
|
||||
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||
return JavaPart::newInstance(hybridData);
|
||||
}
|
||||
|
||||
// TODO? Create reusable interface for Allocatable classes and use it to
|
||||
// strengthen type-checking (and possibly provide a default
|
||||
// implementation of allocate().)
|
||||
template <typename... Args>
|
||||
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
|
||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
||||
static auto allocateMethod =
|
||||
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
|
||||
return allocateMethod(javaClassStatic(), hybridData.get());
|
||||
}
|
||||
|
||||
// Factory method for creating a hybrid object where the arguments
|
||||
// are passed to the java ctor.
|
||||
template <typename... Args>
|
||||
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
|
||||
return JavaPart::newInstance(std::move(args)...);
|
||||
}
|
||||
|
||||
// If a hybrid class throws an exception which derives from
|
||||
// std::exception, it will be passed to mapException on the hybrid
|
||||
// class, or nearest ancestor. This allows boilerplate exception
|
||||
// translation code (for example, calling throwNewJavaException on a
|
||||
// particular java class) to be hoisted to a common function. If
|
||||
// mapException returns, then the std::exception will be translated
|
||||
// to Java.
|
||||
static void mapException(const std::exception& ex) {}
|
||||
};
|
||||
|
||||
template <typename T, typename B>
|
||||
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
||||
static auto field =
|
||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||
auto hybridData = this->getFieldValue(field);
|
||||
if (!hybridData) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||
T* value = static_cast<T*>(hybridData->getNativePointer());
|
||||
// This would require some serious programmer error.
|
||||
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
|
||||
return value;
|
||||
};
|
||||
|
||||
template <typename T, typename B>
|
||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
|
||||
return T::kJavaDescriptor;
|
||||
}
|
||||
|
||||
template <typename T, typename B>
|
||||
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
|
||||
auto name = get_instantiated_java_descriptor();
|
||||
return name.substr(1, name.size() - 2);
|
||||
}
|
||||
|
||||
// Given a *_ref object which refers to a hybrid class, this will reach inside
|
||||
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
|
||||
// the appropriate type, and return it.
|
||||
template <typename T>
|
||||
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
|
||||
return jthis->cthis();
|
||||
}
|
||||
|
||||
void HybridDataOnLoad();
|
||||
|
||||
}
|
||||
}
|
199
lib/fb/include/fb/fbjni/Iterator-inl.h
Normal file
199
lib/fb/include/fb/fbjni/Iterator-inl.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E>
|
||||
struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;";
|
||||
|
||||
typedef local_ref<E> value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
|
||||
typedef JavaClass<IteratorHelper<E>> JavaBase_;
|
||||
|
||||
bool hasNext() const {
|
||||
static auto hasNextMethod =
|
||||
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
|
||||
return hasNextMethod(JavaBase_::self());
|
||||
}
|
||||
|
||||
value_type next() {
|
||||
static auto elementField =
|
||||
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
|
||||
return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField));
|
||||
}
|
||||
|
||||
static void reset(value_type& v) {
|
||||
v.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct MapIteratorHelper : public JavaClass<MapIteratorHelper<K,V>> {
|
||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;";
|
||||
|
||||
typedef std::pair<local_ref<K>, local_ref<V>> value_type;
|
||||
|
||||
typedef JavaClass<MapIteratorHelper<K,V>> JavaBase_;
|
||||
|
||||
bool hasNext() const {
|
||||
static auto hasNextMethod =
|
||||
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
|
||||
return hasNextMethod(JavaBase_::self());
|
||||
}
|
||||
|
||||
value_type next() {
|
||||
static auto keyField = JavaBase_::javaClassStatic()->template getField<jobject>("mKey");
|
||||
static auto valueField = JavaBase_::javaClassStatic()->template getField<jobject>("mValue");
|
||||
return std::make_pair(dynamic_ref_cast<K>(JavaBase_::getFieldValue(keyField)),
|
||||
dynamic_ref_cast<V>(JavaBase_::getFieldValue(valueField)));
|
||||
}
|
||||
|
||||
static void reset(value_type& v) {
|
||||
v.first.reset();
|
||||
v.second.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Iterator {
|
||||
public:
|
||||
typedef typename T::value_type value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
|
||||
// begin ctor
|
||||
Iterator(global_ref<typename T::javaobject>&& helper)
|
||||
: helper_(std::move(helper))
|
||||
, i_(-1) {
|
||||
++(*this);
|
||||
}
|
||||
|
||||
// end ctor
|
||||
Iterator()
|
||||
: i_(-1) {}
|
||||
|
||||
bool operator==(const Iterator& it) const { return i_ == it.i_; }
|
||||
bool operator!=(const Iterator& it) const { return !(*this == it); }
|
||||
const value_type& operator*() const { assert(i_ != -1); return entry_; }
|
||||
const value_type* operator->() const { assert(i_ != -1); return &entry_; }
|
||||
Iterator& operator++() { // preincrement
|
||||
bool hasNext = helper_->hasNext();
|
||||
if (hasNext) {
|
||||
++i_;
|
||||
entry_ = helper_->next();
|
||||
} else {
|
||||
i_ = -1;
|
||||
helper_->reset(entry_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Iterator operator++(int) { // postincrement
|
||||
Iterator ret;
|
||||
ret.i_ = i_;
|
||||
ret.entry_ = std::move(entry_);
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
global_ref<typename T::javaobject> helper_;
|
||||
// set to -1 at end
|
||||
std::ptrdiff_t i_;
|
||||
value_type entry_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct JIterator<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
|
||||
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
typename JIterator<E>::Iterator JIterator<E>::begin() const {
|
||||
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
|
||||
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
|
||||
typename JIterator<E>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename JIterator<E>::Iterator JIterator<E>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct JIterable<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
|
||||
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
typename JIterable<E>::Iterator JIterable<E>::begin() const {
|
||||
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
|
||||
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
|
||||
typename JIterable<E>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename JIterable<E>::Iterator JIterable<E>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
size_t JCollection<E>::size() const {
|
||||
static auto sizeMethod =
|
||||
JCollection<E>::javaClassStatic()->template getMethod<jint()>("size");
|
||||
return sizeMethod(this->self());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
struct JMap<K,V>::Iterator : public detail::Iterator<detail::MapIteratorHelper<K,V>> {
|
||||
using detail::Iterator<detail::MapIteratorHelper<K,V>>::Iterator;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
size_t JMap<K,V>::size() const {
|
||||
static auto sizeMethod =
|
||||
JMap<K,V>::javaClassStatic()->template getMethod<jint()>("size");
|
||||
return sizeMethod(this->self());
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
typename JMap<K,V>::Iterator JMap<K,V>::begin() const {
|
||||
static auto ctor = detail::MapIteratorHelper<K,V>::javaClassStatic()->
|
||||
template getConstructor<typename detail::MapIteratorHelper<K,V>::javaobject(
|
||||
typename JMap<K,V>::javaobject)>();
|
||||
return Iterator(
|
||||
make_global(
|
||||
detail::MapIteratorHelper<K,V>::javaClassStatic()->newObject(ctor, this->self())));
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
typename JMap<K,V>::Iterator JMap<K,V>::end() const {
|
||||
return Iterator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
146
lib/fb/include/fb/fbjni/Iterator.h
Normal file
146
lib/fb/include/fb/fbjni/Iterator.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/**
|
||||
* JavaClass which represents a reference to a java.util.Iterator instance. It
|
||||
* provides begin()/end() methods to provide C++-style iteration over the
|
||||
* underlying collection. The class has a template parameter for the element
|
||||
* type, which defaults to jobject. For example:
|
||||
*
|
||||
* alias_ref<JIterator<jstring>::javaobject> my_iter = ...;
|
||||
*
|
||||
* In the simplest case, it can be used just as alias_ref<JIterator<>::javaobject>,
|
||||
* for example in a method declaration.
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JIterator : JavaClass<JIterator<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Iterator;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
/**
|
||||
* To iterate:
|
||||
*
|
||||
* for (const auto& element : *jiter) { ... }
|
||||
*
|
||||
* The JIterator iterator value_type is local_ref<E>, containing a reference
|
||||
* to an element instance.
|
||||
*
|
||||
* If the Iterator returns objects whch are not convertible to the given
|
||||
* element type, iteration will throw a java ClassCastException.
|
||||
*
|
||||
* For example, to convert an iterator over a collection of java strings to
|
||||
* an std::vector of std::strings:
|
||||
*
|
||||
* std::vector<std::string> vs;
|
||||
* for (const auto& elem : *jiter) {
|
||||
* vs.push_back(elem->toStdString());
|
||||
* }
|
||||
*
|
||||
* Or if you prefer using std algorithms:
|
||||
*
|
||||
* std::vector<std::string> vs;
|
||||
* std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs),
|
||||
* [](const local_ref<jstring>& elem) { return elem->toStdString(); });
|
||||
*
|
||||
* The iterator is a InputIterator.
|
||||
*/
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to JIterator, except this represents any object which implements the
|
||||
* java.lang.Iterable interface. It will create the Java Iterator as a part of
|
||||
* begin().
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JIterable : JavaClass<JIterable<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/lang/Iterable;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* JavaClass types which represent Collection, List, and Set are also provided.
|
||||
* These preserve the Java class heirarchy.
|
||||
*/
|
||||
template <typename E = jobject>
|
||||
struct JCollection : JavaClass<JCollection<E>, JIterable<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Collection;";
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the collection.
|
||||
*/
|
||||
size_t size() const;
|
||||
};
|
||||
|
||||
template <typename E = jobject>
|
||||
struct JList : JavaClass<JList<E>, JCollection<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/List;";
|
||||
};
|
||||
|
||||
template <typename E = jobject>
|
||||
struct JSet : JavaClass<JSet<E>, JCollection<E>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Set;";
|
||||
};
|
||||
|
||||
/**
|
||||
* JavaClass which represents a reference to a java.util.Map instance. It adds
|
||||
* wrappers around Java methods, including begin()/end() methods to provide
|
||||
* C++-style iteration over the Java Map. The class has template parameters
|
||||
* for the key and value types, which default to jobject. For example:
|
||||
*
|
||||
* alias_ref<JMap<jstring, MyJClass::javaobject>::javaobject> my_map = ...;
|
||||
*
|
||||
* In the simplest case, it can be used just as alias_ref<JMap<>::javaobject>,
|
||||
* for example in a method declaration.
|
||||
*/
|
||||
template <typename K = jobject, typename V = jobject>
|
||||
struct JMap : JavaClass<JMap<K,V>> {
|
||||
constexpr static auto kJavaDescriptor = "Ljava/util/Map;";
|
||||
|
||||
struct Iterator;
|
||||
|
||||
/**
|
||||
* Returns the number of pairs in the map.
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* To iterate over the Map:
|
||||
*
|
||||
* for (const auto& entry : *jmap) { ... }
|
||||
*
|
||||
* The JMap iterator value_type is std::pair<local_ref<K>, local_ref<V>>
|
||||
* containing references to key and value instances.
|
||||
*
|
||||
* If the Map contains objects whch are not convertible to the given key and
|
||||
* value types, iteration will throw a java ClassCastException.
|
||||
*
|
||||
* The iterator is a InputIterator.
|
||||
*/
|
||||
Iterator begin() const;
|
||||
Iterator end() const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "Iterator-inl.h"
|
39
lib/fb/include/fb/fbjni/JThread.h
Normal file
39
lib/fb/include/fb/fbjni/JThread.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "NativeRunnable.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class JThread : public JavaClass<JThread> {
|
||||
public:
|
||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
|
||||
|
||||
void start() {
|
||||
static auto method = javaClassStatic()->getMethod<void()>("start");
|
||||
method(self());
|
||||
}
|
||||
|
||||
void join() {
|
||||
static auto method = javaClassStatic()->getMethod<void()>("join");
|
||||
method(self());
|
||||
}
|
||||
|
||||
static local_ref<JThread> create(std::function<void()>&& runnable) {
|
||||
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
|
||||
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
36
lib/fb/include/fb/fbjni/Meta-forward.h
Normal file
36
lib/fb/include/fb/fbjni/Meta-forward.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename F>
|
||||
class JMethod;
|
||||
template<typename F>
|
||||
class JStaticMethod;
|
||||
template<typename F>
|
||||
class JNonvirtualMethod;
|
||||
template<typename F>
|
||||
struct JConstructor;
|
||||
template<typename F>
|
||||
class JField;
|
||||
template<typename F>
|
||||
class JStaticField;
|
||||
|
||||
/// Type traits for Java types (currently providing Java type descriptors)
|
||||
template<typename T>
|
||||
struct jtype_traits;
|
||||
|
||||
/// Type traits for Java methods (currently providing Java type descriptors)
|
||||
template<typename F>
|
||||
struct jmethod_traits;
|
||||
|
||||
}}
|
432
lib/fb/include/fb/fbjni/Meta-inl.h
Normal file
432
lib/fb/include/fb/fbjni/Meta-inl.h
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Exceptions.h"
|
||||
#include "MetaConvert.h"
|
||||
#include "References.h"
|
||||
#include "Boxed.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// JMethod /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline JMethodBase::JMethodBase(jmethodID method_id) noexcept
|
||||
: method_id_{method_id}
|
||||
{}
|
||||
|
||||
inline JMethodBase::operator bool() const noexcept {
|
||||
return method_id_ != nullptr;
|
||||
}
|
||||
|
||||
inline jmethodID JMethodBase::getId() const noexcept {
|
||||
return method_id_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <int idx, typename... Args>
|
||||
struct ArgsArraySetter;
|
||||
|
||||
template <int idx, typename Arg, typename... Args>
|
||||
struct ArgsArraySetter<idx, Arg, Args...> {
|
||||
static void set(alias_ref<JArrayClass<jobject>::javaobject> array, Arg arg0, Args... args) {
|
||||
// TODO(xxxxxxxx): Use Convert<Args>... to do conversions like the fast path.
|
||||
(*array)[idx] = autobox(arg0);
|
||||
ArgsArraySetter<idx + 1, Args...>::set(array, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <int idx>
|
||||
struct ArgsArraySetter<idx> {
|
||||
static void set(alias_ref<JArrayClass<jobject>::javaobject> array) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
|
||||
auto arr = JArrayClass<jobject>::newArray(sizeof...(args));
|
||||
ArgsArraySetter<0, Args...>::set(arr, args...);
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
inline bool needsSlowPath(alias_ref<jobject> obj) {
|
||||
#if defined(__ANDROID__)
|
||||
// On Android 6.0, art crashes when attempting to call a function on a Proxy.
|
||||
// So, when we detect that case we must use the safe, slow workaround. That is,
|
||||
// we resolve the method id to the corresponding java.lang.reflect.Method object
|
||||
// and make the call via it's invoke() method.
|
||||
static auto android_sdk = ([] {
|
||||
char sdk_version_str[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.build.version.sdk", sdk_version_str);
|
||||
return atoi(sdk_version_str);
|
||||
})();
|
||||
static auto is_bad_android = android_sdk == 23;
|
||||
if (!is_bad_android) return false;
|
||||
static auto proxy_class = findClassStatic("java/lang/reflect/Proxy");
|
||||
return obj->isInstanceOf(proxy_class);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
|
||||
const auto env = Environment::current();
|
||||
env->CallVoidMethod(
|
||||
self.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
|
||||
#undef DEFINE_PRIMITIVE_CALL
|
||||
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \
|
||||
template<typename... Args> \
|
||||
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->Call ## METHOD ## Method( \
|
||||
self.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean)
|
||||
DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte)
|
||||
DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter)
|
||||
DEFINE_PRIMITIVE_CALL(jshort, Short, JShort)
|
||||
DEFINE_PRIMITIVE_CALL(jint, Int, JInteger)
|
||||
DEFINE_PRIMITIVE_CALL(jlong, Long, JLong)
|
||||
DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat)
|
||||
DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
|
||||
|
||||
/// JMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JMethod<R(Args...)> : public JMethodBase {
|
||||
public:
|
||||
// TODO: static_assert is jobject-derived or local_ref jobject
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "JniRet must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JMethod() noexcept {};
|
||||
JMethod(const JMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args);
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) -> local_ref<JniRet> {
|
||||
const auto env = Environment::current();
|
||||
auto result = env->CallObjectMethod(
|
||||
self.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
env->CallStaticVoidMethod(
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_CALL")
|
||||
#undef DEFINE_PRIMITIVE_STATIC_CALL
|
||||
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->CallStatic ## METHOD ## Method( \
|
||||
cls.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_CALL")
|
||||
|
||||
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JStaticMethod<R(Args...)> : public JMethodBase {
|
||||
|
||||
public:
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JStaticMethod() noexcept {};
|
||||
JStaticMethod(const JStaticMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
auto result = env->CallStaticObjectMethod(
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
inline void
|
||||
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) {
|
||||
const auto env = internal::getEnv();
|
||||
env->CallNonvirtualVoidMethod(
|
||||
self.get(),
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
|
||||
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_CALL
|
||||
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
|
||||
template<typename... Args> \
|
||||
inline TYPE \
|
||||
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \
|
||||
const auto env = internal::getEnv(); \
|
||||
auto result = env->CallNonvirtual ## METHOD ## Method( \
|
||||
self.get(), \
|
||||
cls.get(), \
|
||||
getId(), \
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return result; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jbyte, Byte)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jchar, Char)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jshort, Short)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jint, Int)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jlong, Long)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float)
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
|
||||
|
||||
/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref
|
||||
template<typename R, typename... Args>
|
||||
class JNonvirtualMethod<R(Args...)> : public JMethodBase {
|
||||
public:
|
||||
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
|
||||
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
|
||||
using JMethodBase::JMethodBase;
|
||||
JNonvirtualMethod() noexcept {};
|
||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
|
||||
|
||||
/// Invoke a method and return a local reference wrapping the result
|
||||
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args){
|
||||
const auto env = internal::getEnv();
|
||||
auto result = env->CallNonvirtualObjectMethod(
|
||||
self.get(),
|
||||
cls.get(),
|
||||
getId(),
|
||||
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(static_cast<JniRet>(result));
|
||||
}
|
||||
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {
|
||||
static auto invoke = findClassStatic("java/lang/reflect/Method")
|
||||
->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>("invoke");
|
||||
// TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.
|
||||
auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method");
|
||||
auto argsArray = makeArgsArray(args...);
|
||||
// No need to check for exceptions since invoke is itself a JMethod that will do that for us.
|
||||
return invoke(reflected, self.get(), argsArray.get());
|
||||
}
|
||||
|
||||
|
||||
// JField<T> ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline JField<T>::JField(jfieldID field) noexcept
|
||||
: field_id_{field}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline JField<T>::operator bool() const noexcept {
|
||||
return field_id_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline jfieldID JField<T>::getId() const noexcept {
|
||||
return field_id_;
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
|
||||
#undef DEFINE_FIELD_PRIMITIVE_GET_SET
|
||||
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||
template<> \
|
||||
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
return env->Get ## METHOD ## Field(object, field_id_); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
env->Set ## METHOD ## Field(object, field_id_, value); \
|
||||
}
|
||||
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jchar, Char)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jshort, Short)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jint, Int)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jlong, Long)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
|
||||
DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
|
||||
|
||||
template<typename T>
|
||||
inline T JField<T>::get(jobject object) const noexcept {
|
||||
return static_cast<T>(internal::getEnv()->GetObjectField(object, field_id_));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JField<T>::set(jobject object, T value) noexcept {
|
||||
internal::getEnv()->SetObjectField(object, field_id_, static_cast<jobject>(value));
|
||||
}
|
||||
|
||||
// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<T>::JStaticField(jfieldID field) noexcept
|
||||
: field_id_{field}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline JStaticField<T>::operator bool() const noexcept {
|
||||
return field_id_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline jfieldID JStaticField<T>::getId() const noexcept {
|
||||
return field_id_;
|
||||
}
|
||||
|
||||
#pragma push_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
|
||||
#undef DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET
|
||||
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||
template<> \
|
||||
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
|
||||
const auto env = internal::getEnv(); \
|
||||
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
|
||||
}
|
||||
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jchar, Char)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jshort, Short)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jint, Int)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jlong, Long)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
|
||||
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
||||
#pragma pop_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
|
||||
|
||||
template<typename T>
|
||||
inline T JStaticField<T>::get(jclass jcls) const noexcept {
|
||||
const auto env = internal::getEnv();
|
||||
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
|
||||
internal::getEnv()->SetStaticObjectField(jcls, field_id_, value);
|
||||
}
|
||||
|
||||
|
||||
// jmethod_traits //////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO(T6608405) Adapt this to implement a register natives method that requires no descriptor
|
||||
namespace internal {
|
||||
|
||||
template<typename Head>
|
||||
inline std::string JavaDescriptor() {
|
||||
return jtype_traits<Head>::descriptor();
|
||||
}
|
||||
|
||||
template<typename Head, typename Elem, typename... Tail>
|
||||
inline std::string JavaDescriptor() {
|
||||
return JavaDescriptor<Head>() + JavaDescriptor<Elem, Tail...>();
|
||||
}
|
||||
|
||||
template<typename R, typename Arg1, typename... Args>
|
||||
inline std::string JMethodDescriptor() {
|
||||
return "(" + JavaDescriptor<Arg1, Args...>() + ")" + JavaDescriptor<R>();
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
inline std::string JMethodDescriptor() {
|
||||
return "()" + JavaDescriptor<R>();
|
||||
}
|
||||
|
||||
} // internal
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline std::string jmethod_traits<R(Args...)>::descriptor() {
|
||||
return internal::JMethodDescriptor<R, Args...>();
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
inline std::string jmethod_traits<R(Args...)>::constructor_descriptor() {
|
||||
return internal::JMethodDescriptor<void, Args...>();
|
||||
}
|
||||
|
||||
}}
|
340
lib/fb/include/fb/fbjni/Meta.h
Normal file
340
lib/fb/include/fb/fbjni/Meta.h
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/** @file meta.h
|
||||
*
|
||||
* Provides wrappers for meta data such as methods and fields.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "References-forward.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# include <android/log.h>
|
||||
# define XLOG_TAG "fb-jni"
|
||||
# define XLOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGI(...) __android_log_print(ANDROID_LOG_INFO, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGW(...) __android_log_print(ANDROID_LOG_WARN, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGE(...) __android_log_print(ANDROID_LOG_ERROR, XLOG_TAG, __VA_ARGS__)
|
||||
# define XLOGWTF(...) __android_log_print(ANDROID_LOG_FATAL, XLOG_TAG, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
// This will get the reflected Java Method from the method_id, get it's invoke
|
||||
// method, and call the method via that. This shouldn't ever be needed, but
|
||||
// Android 6.0 crashes when calling a method on a java.lang.Proxy via jni.
|
||||
template <typename... Args>
|
||||
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args);
|
||||
|
||||
class JObject;
|
||||
|
||||
|
||||
/// Wrapper of a jmethodID. Provides a common base for JMethod specializations
|
||||
class JMethodBase {
|
||||
public:
|
||||
/// Verify that the method is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jmethodID getId() const noexcept;
|
||||
|
||||
protected:
|
||||
/// Create a wrapper of a method id
|
||||
explicit JMethodBase(jmethodID method_id = nullptr) noexcept;
|
||||
|
||||
private:
|
||||
jmethodID method_id_;
|
||||
};
|
||||
|
||||
|
||||
/// Representation of a jmethodID
|
||||
template<typename F>
|
||||
class JMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_METHOD_CLASS
|
||||
|
||||
// Defining JMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
public: \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"TYPE must be primitive or void"); \
|
||||
\
|
||||
using JMethodBase::JMethodBase; \
|
||||
JMethod() noexcept {}; \
|
||||
JMethod(const JMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jobject> self, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/// Convenience type representing constructors
|
||||
/// These should only be used with JClass::getConstructor and JClass::newObject.
|
||||
template<typename F>
|
||||
struct JConstructor : private JMethod<F> {
|
||||
using JMethod<F>::JMethod;
|
||||
private:
|
||||
JConstructor(const JMethod<F>& other) : JMethod<F>(other.getId()) {}
|
||||
friend class JClass;
|
||||
};
|
||||
|
||||
/// Representation of a jStaticMethodID
|
||||
template<typename F>
|
||||
class JStaticMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_STATIC_METHOD_CLASS
|
||||
|
||||
// Defining JStaticMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"T must be a JNI primitive or void"); \
|
||||
\
|
||||
public: \
|
||||
using JMethodBase::JMethodBase; \
|
||||
JStaticMethod() noexcept {}; \
|
||||
JStaticMethod(const JStaticMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jclass> cls, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/// Representation of a jNonvirtualMethodID
|
||||
template<typename F>
|
||||
class JNonvirtualMethod;
|
||||
|
||||
/// @cond INTERNAL
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
|
||||
|
||||
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS
|
||||
|
||||
// Defining JNonvirtualMethod specializations based on return value
|
||||
#define DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(TYPE) \
|
||||
template<typename... Args> \
|
||||
class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
|
||||
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
|
||||
"T must be a JNI primitive or void"); \
|
||||
\
|
||||
public: \
|
||||
using JMethodBase::JMethodBase; \
|
||||
JNonvirtualMethod() noexcept {}; \
|
||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
|
||||
\
|
||||
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args); \
|
||||
\
|
||||
friend class JClass; \
|
||||
}
|
||||
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jboolean);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jbyte);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jchar);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jshort);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jint);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jlong);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jfloat);
|
||||
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble);
|
||||
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
|
||||
/// @endcond
|
||||
|
||||
|
||||
/**
|
||||
* JField represents typed fields and simplifies their access. Note that object types return
|
||||
* raw pointers which generally should promptly get a wrap_local treatment.
|
||||
*/
|
||||
template<typename T>
|
||||
class JField {
|
||||
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
|
||||
|
||||
public:
|
||||
/// Wraps an existing field id
|
||||
explicit JField(jfieldID field = nullptr) noexcept;
|
||||
|
||||
/// Verify that the id is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jfieldID getId() const noexcept;
|
||||
|
||||
private:
|
||||
jfieldID field_id_;
|
||||
|
||||
/// Get field value
|
||||
/// @pre object != nullptr
|
||||
T get(jobject object) const noexcept;
|
||||
|
||||
/// Set field value
|
||||
/// @pre object != nullptr
|
||||
void set(jobject object, T value) noexcept;
|
||||
|
||||
friend class JObject;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* JStaticField represents typed fields and simplifies their access. Note that object types
|
||||
* return raw pointers which generally should promptly get a wrap_local treatment.
|
||||
*/
|
||||
template<typename T>
|
||||
class JStaticField {
|
||||
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
|
||||
|
||||
public:
|
||||
/// Wraps an existing field id
|
||||
explicit JStaticField(jfieldID field = nullptr) noexcept;
|
||||
|
||||
/// Verify that the id is valid
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the wrapped id
|
||||
jfieldID getId() const noexcept;
|
||||
|
||||
private:
|
||||
jfieldID field_id_;
|
||||
|
||||
/// Get field value
|
||||
/// @pre object != nullptr
|
||||
T get(jclass jcls) const noexcept;
|
||||
|
||||
/// Set field value
|
||||
/// @pre object != nullptr
|
||||
void set(jclass jcls, T value) noexcept;
|
||||
|
||||
friend class JClass;
|
||||
friend class JObject;
|
||||
};
|
||||
|
||||
|
||||
/// Template magic to provide @ref jmethod_traits
|
||||
template<typename R, typename... Args>
|
||||
struct jmethod_traits<R(Args...)> {
|
||||
static std::string descriptor();
|
||||
static std::string constructor_descriptor();
|
||||
};
|
||||
|
||||
|
||||
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
struct jtype_traits {
|
||||
private:
|
||||
using Repr = ReprType<T>;
|
||||
public:
|
||||
// The jni type signature (described at
|
||||
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
|
||||
static std::string descriptor() {
|
||||
std::string descriptor;
|
||||
if (Repr::kJavaDescriptor == nullptr) {
|
||||
descriptor = Repr::get_instantiated_java_descriptor();
|
||||
} else {
|
||||
descriptor = Repr::kJavaDescriptor;
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
// The signature used for class lookups. See
|
||||
// http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName().
|
||||
static std::string base_name() {
|
||||
if (Repr::kJavaDescriptor != nullptr) {
|
||||
std::string base_name = Repr::kJavaDescriptor;
|
||||
return base_name.substr(1, base_name.size() - 2);
|
||||
}
|
||||
return Repr::get_instantiated_base_name();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma push_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
|
||||
#undef DEFINE_FIELD_AND_ARRAY_TRAIT
|
||||
|
||||
#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC) \
|
||||
template<> \
|
||||
struct jtype_traits<TYPE> { \
|
||||
static std::string descriptor() { return std::string{#DSC}; } \
|
||||
static std::string base_name() { return descriptor(); } \
|
||||
using array_type = TYPE ## Array; \
|
||||
}; \
|
||||
template<> \
|
||||
struct jtype_traits<TYPE ## Array> { \
|
||||
static std::string descriptor() { return std::string{"[" #DSC}; } \
|
||||
static std::string base_name() { return descriptor(); } \
|
||||
using entry_type = TYPE; \
|
||||
};
|
||||
|
||||
// There is no voidArray, handle that without the macro.
|
||||
template<>
|
||||
struct jtype_traits<void> {
|
||||
static std::string descriptor() { return std::string{"V"}; };
|
||||
};
|
||||
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jbyte, B)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jchar, C)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jshort, S)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jint, I)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jlong, J)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jfloat, F)
|
||||
DEFINE_FIELD_AND_ARRAY_TRAIT(jdouble, D)
|
||||
|
||||
#pragma pop_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct jmethod_traits_from_cxx;
|
||||
|
||||
}}
|
||||
|
||||
#include "Meta-inl.h"
|
122
lib/fb/include/fb/fbjni/MetaConvert.h
Normal file
122
lib/fb/include/fb/fbjni/MetaConvert.h
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "References.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// In order to avoid potentially filling the jni locals table,
|
||||
// temporary objects (right now, this is just jstrings) need to be
|
||||
// released. This is done by returning a holder which autoconverts to
|
||||
// jstring.
|
||||
template <typename T>
|
||||
inline T callToJni(T&& t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline JniType<T> callToJni(local_ref<T>&& sref) {
|
||||
return sref.get();
|
||||
}
|
||||
|
||||
// Normally, pass through types unmolested.
|
||||
template <typename T, typename Enabled = void>
|
||||
struct Convert {
|
||||
typedef T jniType;
|
||||
static jniType fromJni(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toJniRet(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toCall(jniType t) {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed for return conversion
|
||||
template <>
|
||||
struct Convert<void> {
|
||||
typedef void jniType;
|
||||
};
|
||||
|
||||
// jboolean is an unsigned char, not a bool. Allow it to work either way.
|
||||
template<>
|
||||
struct Convert<bool> {
|
||||
typedef jboolean jniType;
|
||||
static bool fromJni(jniType t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toJniRet(bool t) {
|
||||
return t;
|
||||
}
|
||||
static jniType toCall(bool t) {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
// convert to alias_ref<T> from T
|
||||
template <typename T>
|
||||
struct Convert<alias_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
static alias_ref<jniType> fromJni(jniType t) {
|
||||
return wrap_alias(t);
|
||||
}
|
||||
static jniType toJniRet(alias_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
static jniType toCall(alias_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from local_ref<T>
|
||||
template <typename T>
|
||||
struct Convert<local_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
// No automatic synthesis of local_ref
|
||||
static jniType toJniRet(local_ref<jniType> t) {
|
||||
return t.release();
|
||||
}
|
||||
static jniType toCall(local_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
// convert return from global_ref<T>
|
||||
template <typename T>
|
||||
struct Convert<global_ref<T>> {
|
||||
typedef JniType<T> jniType;
|
||||
// No automatic synthesis of global_ref
|
||||
static jniType toJniRet(global_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
static jniType toCall(global_ref<jniType> t) {
|
||||
return t.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct jni_sig_from_cxx_t;
|
||||
template <typename R, typename... Args>
|
||||
struct jni_sig_from_cxx_t<R(Args...)> {
|
||||
using JniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
using JniSig = JniRet(typename Convert<typename std::decay<Args>::type>::jniType...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using jni_sig_from_cxx = typename jni_sig_from_cxx_t<T>::JniSig;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct jmethod_traits_from_cxx<R(Args...)> : jmethod_traits<detail::jni_sig_from_cxx<R(Args...)>> {
|
||||
};
|
||||
|
||||
}}
|
47
lib/fb/include/fb/fbjni/NativeRunnable.h
Normal file
47
lib/fb/include/fb/fbjni/NativeRunnable.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreClasses.h"
|
||||
#include "Hybrid.h"
|
||||
#include "Registration.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
struct JRunnable : public JavaClass<JRunnable> {
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/Runnable;";
|
||||
};
|
||||
|
||||
struct JNativeRunnable : public HybridClass<JNativeRunnable, JRunnable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/NativeRunnable;";
|
||||
|
||||
JNativeRunnable(std::function<void()>&& runnable) : runnable_(std::move(runnable)) {}
|
||||
|
||||
static void OnLoad() {
|
||||
registerHybrid({
|
||||
makeNativeMethod("run", JNativeRunnable::run),
|
||||
});
|
||||
}
|
||||
|
||||
void run() {
|
||||
runnable_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> runnable_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace jni
|
||||
} // namespace facebook
|
120
lib/fb/include/fb/fbjni/ReferenceAllocators-inl.h
Normal file
120
lib/fb/include/fb/fbjni/ReferenceAllocators-inl.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <new>
|
||||
#include <atomic>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace internal {
|
||||
|
||||
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
|
||||
struct ReferenceStats {
|
||||
std::atomic_uint locals_deleted, globals_deleted, weaks_deleted;
|
||||
|
||||
void reset() noexcept;
|
||||
};
|
||||
|
||||
extern ReferenceStats g_reference_stats;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
|
||||
// LocalReferenceAllocator /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Local new: %p", original);
|
||||
auto ref = internal::getEnv()->NewLocalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void LocalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Local release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.locals_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteLocalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType;
|
||||
}
|
||||
|
||||
|
||||
// GlobalReferenceAllocator ////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Global new: %p", original);
|
||||
auto ref = internal::getEnv()->NewGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void GlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Global release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.globals_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteGlobalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType;
|
||||
}
|
||||
|
||||
|
||||
// WeakGlobalReferenceAllocator ////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
|
||||
internal::dbglog("Weak global new: %p", original);
|
||||
auto ref = internal::getEnv()->NewWeakGlobalRef(original);
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return ref;
|
||||
}
|
||||
|
||||
inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
|
||||
internal::dbglog("Weak Global release: %p", reference);
|
||||
|
||||
if (reference) {
|
||||
#ifdef FBJNI_DEBUG_REFS
|
||||
++internal::g_reference_stats.weaks_deleted;
|
||||
#endif
|
||||
assert(verifyReference(reference));
|
||||
internal::getEnv()->DeleteWeakGlobalRef(reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
||||
return true;
|
||||
}
|
||||
return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType;
|
||||
}
|
||||
|
||||
}}
|
62
lib/fb/include/fb/fbjni/ReferenceAllocators.h
Normal file
62
lib/fb/include/fb/fbjni/ReferenceAllocators.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ReferenceAllocators.h
|
||||
*
|
||||
* Reference allocators are used to create and delete various classes of JNI references (local,
|
||||
* global, and weak global).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
/// Allocator that handles local references
|
||||
class FBEXPORT LocalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// Allocator that handles global references
|
||||
class FBEXPORT GlobalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// Allocator that handles weak global references
|
||||
class FBEXPORT WeakGlobalReferenceAllocator {
|
||||
public:
|
||||
jobject newReference(jobject original) const;
|
||||
void deleteReference(jobject reference) const noexcept;
|
||||
bool verifyReference(jobject reference) const noexcept;
|
||||
};
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* @return true iff env->GetObjectRefType is expected to work properly.
|
||||
*/
|
||||
FBEXPORT bool doesGetObjectRefTypeWork();
|
||||
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
}}
|
||||
|
||||
#include "ReferenceAllocators-inl.h"
|
67
lib/fb/include/fb/fbjni/References-forward.h
Normal file
67
lib/fb/include/fb/fbjni/References-forward.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ReferenceAllocators.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
class JObjectWrapper;
|
||||
|
||||
namespace detail {
|
||||
struct JObjectBase {
|
||||
jobject get() const noexcept;
|
||||
void set(jobject reference) noexcept;
|
||||
jobject this_;
|
||||
};
|
||||
|
||||
// RefReprType maps a type to the representation used by fbjni smart references.
|
||||
template <typename T, typename Enable = void>
|
||||
struct RefReprType;
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType;
|
||||
|
||||
template <typename T>
|
||||
struct ReprAccess;
|
||||
}
|
||||
|
||||
// Given T, either a jobject-like type or a JavaClass-derived type, ReprType<T>
|
||||
// is the corresponding JavaClass-derived type and JniType<T> is the
|
||||
// jobject-like type.
|
||||
template <typename T>
|
||||
using ReprType = typename detail::RefReprType<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using JniType = typename detail::JavaObjectType<T>::type;
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class base_owned_ref;
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class basic_strong_ref;
|
||||
|
||||
template<typename T>
|
||||
class weak_ref;
|
||||
|
||||
template<typename T>
|
||||
class alias_ref;
|
||||
|
||||
/// A smart unique reference owning a local JNI reference
|
||||
template<typename T>
|
||||
using local_ref = basic_strong_ref<T, LocalReferenceAllocator>;
|
||||
|
||||
/// A smart unique reference owning a global JNI reference
|
||||
template<typename T>
|
||||
using global_ref = basic_strong_ref<T, GlobalReferenceAllocator>;
|
||||
|
||||
}} // namespace facebook::jni
|
510
lib/fb/include/fb/fbjni/References-inl.h
Normal file
510
lib/fb/include/fb/fbjni/References-inl.h
Normal file
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include "CoreClasses.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<typename T>
|
||||
inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline JniType<T> getPlainJniReference(alias_ref<T> ref) {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
|
||||
namespace detail {
|
||||
template <typename Repr>
|
||||
struct ReprAccess {
|
||||
using javaobject = JniType<Repr>;
|
||||
static void set(Repr& repr, javaobject obj) noexcept {
|
||||
repr.JObjectBase::set(obj);
|
||||
}
|
||||
static javaobject get(const Repr& repr) {
|
||||
return static_cast<javaobject>(repr.JObject::get());
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <typename Repr>
|
||||
void StaticAssertValidRepr() noexcept {
|
||||
static_assert(std::is_base_of<JObject, Repr>::value,
|
||||
"A smart ref representation must be derived from JObject.");
|
||||
static_assert(IsPlainJniReference<JniType<Repr>>(), "T must be a JNI reference");
|
||||
static_assert(sizeof(Repr) == sizeof(JObjectBase), "");
|
||||
static_assert(alignof(Repr) == alignof(JObjectBase), "");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
ReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {
|
||||
StaticAssertValidRepr<Repr>();
|
||||
set(obj);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
void ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {
|
||||
new (&storage_) Repr;
|
||||
ReprAccess<Repr>::set(get(), obj);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
Repr& ReprStorage<Repr>::get() noexcept {
|
||||
return *reinterpret_cast<Repr*>(&storage_);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
const Repr& ReprStorage<Repr>::get() const noexcept {
|
||||
return *reinterpret_cast<const Repr*>(&storage_);
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
JniType<Repr> ReprStorage<Repr>::jobj() const noexcept {
|
||||
ReprAccess<Repr>::get(get());
|
||||
return ReprAccess<Repr>::get(get());
|
||||
}
|
||||
|
||||
template <typename Repr>
|
||||
void ReprStorage<Repr>::swap(ReprStorage& other) noexcept {
|
||||
StaticAssertValidRepr<Repr>();
|
||||
using std::swap;
|
||||
swap(get(), other.get());
|
||||
}
|
||||
|
||||
inline void JObjectBase::set(jobject reference) noexcept {
|
||||
this_ = reference;
|
||||
}
|
||||
|
||||
inline jobject JObjectBase::get() const noexcept {
|
||||
return this_;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T& reference) {
|
||||
auto old_reference = getPlainJniReference(reference);
|
||||
if (!old_reference) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ref = Alloc{}.newReference(old_reference);
|
||||
if (!ref) {
|
||||
// Note that we end up here if we pass a weak ref that refers to a collected object.
|
||||
// Thus, it's hard to come up with a reason why this function should be used with
|
||||
// weak references.
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
|
||||
return static_cast<plain_jni_reference_t<T>>(ref);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
inline local_ref<T> adopt_local(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return local_ref<T>{ref};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline global_ref<T> adopt_global(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return global_ref<T>{ref};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T> adopt_weak_global(T ref) noexcept {
|
||||
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
|
||||
return weak_ref<T>{ref};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {
|
||||
return alias_ref<T>(ref);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
|
||||
make_local(const T& ref) {
|
||||
return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
|
||||
make_global(const T& ref) {
|
||||
return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
|
||||
make_weak(const T& ref) {
|
||||
return adopt_weak_global(detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator==(const T1& a, const T2& b) {
|
||||
return isSameObject(getPlainJniReference(a), getPlainJniReference(b));
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator!=(const T1& a, const T2& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
|
||||
// base_owned_ref ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
|
||||
: base_owned_ref(nullptr)
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
|
||||
: base_owned_ref(static_cast<javaobject>(nullptr))
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)
|
||||
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
|
||||
{}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
template<typename U>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)
|
||||
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
|
||||
{
|
||||
static_assert(std::is_convertible<JniType<U>, javaobject>::value, "");
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
|
||||
javaobject reference) noexcept
|
||||
: storage_(reference) {
|
||||
assert(Alloc{}.verifyReference(reference));
|
||||
internal::dbglog("New wrapped ref=%p this=%p", get(), this);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::base_owned_ref(
|
||||
base_owned_ref<T, Alloc>&& other) noexcept
|
||||
: storage_(other.get()) {
|
||||
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
|
||||
internal::dbglog("New move to ref=%p this=%p", get(), this);
|
||||
// JObject is a simple type and does not support move semantics so we explicitly
|
||||
// clear other
|
||||
other.set(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
template<typename U>
|
||||
base_owned_ref<T, Alloc>::base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept
|
||||
: storage_(other.get()) {
|
||||
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
|
||||
internal::dbglog("New move to ref=%p this=%p", get(), this);
|
||||
// JObject is a simple type and does not support move semantics so we explicitly
|
||||
// clear other
|
||||
other.set(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {
|
||||
reset();
|
||||
internal::dbglog("Ref destruct ref=%p this=%p", get(), this);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {
|
||||
auto value = get();
|
||||
internal::dbglog("Ref release ref=%p this=%p", value, this);
|
||||
set(nullptr);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T,Alloc>::reset() noexcept {
|
||||
reset(nullptr);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T,Alloc>::reset(javaobject reference) noexcept {
|
||||
if (get()) {
|
||||
assert(Alloc{}.verifyReference(reference));
|
||||
Alloc{}.deleteReference(get());
|
||||
}
|
||||
set(reference);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {
|
||||
return storage_.jobj();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {
|
||||
storage_.set(ref);
|
||||
}
|
||||
|
||||
|
||||
// weak_ref ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T>& weak_ref<T>::operator=(
|
||||
const weak_ref& other) {
|
||||
auto otherCopy = other;
|
||||
swap(*this, otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline weak_ref<T>& weak_ref<T>::operator=(
|
||||
weak_ref<T>&& other) noexcept {
|
||||
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
|
||||
get(), this, other.get(), &other);
|
||||
reset(other.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
local_ref<T> weak_ref<T>::lockLocal() const {
|
||||
return adopt_local(
|
||||
static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
global_ref<T> weak_ref<T>::lockGlobal() const {
|
||||
return adopt_global(
|
||||
static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swap(
|
||||
weak_ref<T>& a,
|
||||
weak_ref<T>& b) noexcept {
|
||||
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
|
||||
a.get(), &a, b.get(), &b);
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
|
||||
// basic_strong_ref ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
|
||||
const basic_strong_ref& other) {
|
||||
auto otherCopy = other;
|
||||
swap(*this, otherCopy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
|
||||
basic_strong_ref<T, Alloc>&& other) noexcept {
|
||||
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
|
||||
get(), this, other.get(), &other);
|
||||
reset(other.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline alias_ref<T> basic_strong_ref<T, Alloc>::releaseAlias() noexcept {
|
||||
return wrap_alias(release());
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline basic_strong_ref<T, Alloc>::operator bool() const noexcept {
|
||||
return get() != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {
|
||||
return &storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator->() const noexcept -> const Repr* {
|
||||
return &storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline auto basic_strong_ref<T, Alloc>::operator*() const noexcept -> const Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
inline void swap(
|
||||
basic_strong_ref<T, Alloc>& a,
|
||||
basic_strong_ref<T, Alloc>& b) noexcept {
|
||||
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
|
||||
a.get(), &a, b.get(), &b);
|
||||
using std::swap;
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
|
||||
// alias_ref //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref() noexcept
|
||||
: storage_{nullptr}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(std::nullptr_t) noexcept
|
||||
: storage_{nullptr}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::alias_ref(javaobject ref) noexcept
|
||||
: storage_(ref) {
|
||||
assert(
|
||||
LocalReferenceAllocator{}.verifyReference(ref) ||
|
||||
GlobalReferenceAllocator{}.verifyReference(ref));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename TOther, typename /* for SFINAE */>
|
||||
inline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
template<typename TOther, typename AOther, typename /* for SFINAE */>
|
||||
inline alias_ref<T>::alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept
|
||||
: storage_{other.get()}
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>& alias_ref<T>::operator=(alias_ref other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline alias_ref<T>::operator bool() const noexcept {
|
||||
return get() != nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {
|
||||
return storage_.jobj();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator->() noexcept -> Repr* {
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator->() const noexcept -> const Repr* {
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator*() noexcept -> Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto alias_ref<T>::operator*() const noexcept -> const Repr& {
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void alias_ref<T>::set(javaobject ref) noexcept {
|
||||
storage_.set(ref);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
|
||||
a.storage_.swap(b.storage_);
|
||||
}
|
||||
|
||||
// Could reduce code duplication by using a pointer-to-function
|
||||
// template argument. I'm not sure whether that would make the code
|
||||
// more maintainable (DRY), or less (too clever/confusing.).
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
|
||||
static_ref_cast(const local_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return make_local(p);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
|
||||
static_ref_cast(const global_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return make_global(p);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
|
||||
static_ref_cast(const alias_ref<U>& ref) noexcept
|
||||
{
|
||||
T p = static_cast<T>(ref.get());
|
||||
return wrap_alias(p);
|
||||
}
|
||||
|
||||
template<typename T, typename RefType>
|
||||
auto dynamic_ref_cast(const RefType& ref) ->
|
||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
|
||||
{
|
||||
if (! ref) {
|
||||
return decltype(static_ref_cast<T>(ref))();
|
||||
}
|
||||
|
||||
std::string target_class_name{jtype_traits<T>::base_name()};
|
||||
|
||||
// If not found, will throw an exception.
|
||||
alias_ref<jclass> target_class = findClassStatic(target_class_name.c_str());
|
||||
|
||||
local_ref<jclass> source_class = ref->getClass();
|
||||
|
||||
if ( ! source_class->isAssignableFrom(target_class)) {
|
||||
throwNewJavaException("java/lang/ClassCastException",
|
||||
"Tried to cast from %s to %s.",
|
||||
source_class->toString().c_str(),
|
||||
target_class_name.c_str());
|
||||
}
|
||||
|
||||
return static_ref_cast<T>(ref);
|
||||
}
|
||||
|
||||
}}
|
588
lib/fb/include/fb/fbjni/References.h
Normal file
588
lib/fb/include/fb/fbjni/References.h
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
|
||||
/** @file References.h
|
||||
*
|
||||
* Functionality similar to smart pointers, but for references into the VM. Four main reference
|
||||
* types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic
|
||||
* templates that and refer to objects in the jobject hierarchy. The type of the referred objects
|
||||
* are specified using the template parameter. All reference types except alias_ref own their
|
||||
* underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context
|
||||
* of std smart pointers, these references behave like unique_ptr, and have basically the same
|
||||
* interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying
|
||||
* JNI reference (like the parameters passed directly to JNI functions), is released. The alias
|
||||
* references provides no ownership and is a simple wrapper for plain JNI references.
|
||||
*
|
||||
* All but the weak references provides access to the underlying object using dereferencing, and a
|
||||
* get() method. It is also possible to convert these references to booleans to test for nullity.
|
||||
* To access the underlying object of a weak reference, the reference must either be released, or
|
||||
* the weak reference can be used to create a local or global reference.
|
||||
*
|
||||
* An owning reference is created either by moving the reference from an existing owned reference,
|
||||
* by copying an existing owned reference (which creates a new underlying reference), by using the
|
||||
* default constructor which initialize the reference to nullptr, or by using a helper function. The
|
||||
* helper function exist in two flavors: make_XXX or adopt_XXX.
|
||||
*
|
||||
* Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the
|
||||
* plain JNI reference so be sure that no one else owns the reference when you adopt it, and make
|
||||
* sure that you know what kind of reference it is.
|
||||
*
|
||||
* New owned references can be created from existing plain JNI references, alias references, local
|
||||
* references, and global references (i.e. non-weak references) using the make_local, make_global,
|
||||
* and make_weak functions.
|
||||
*
|
||||
* Alias references can be implicitly initialized using global, local and plain JNI references using
|
||||
* the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather
|
||||
* create a separate reference that we do own, leaving the passed-in reference to its fate.
|
||||
*
|
||||
* Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart
|
||||
* reference of the same type. In the case of copy assignment a new reference is created. Alias
|
||||
* reference can also be assigned new values, but since they are simple wrappers of plain JNI
|
||||
* references there is no move semantics involved.
|
||||
*
|
||||
* Alias references are special in that they do not own the object and can therefore safely be
|
||||
* converted to and from its corresponding plain JNI reference. They are useful as parameters of
|
||||
* functions that do not affect the lifetime of a reference. Usage can be compared with using plain
|
||||
* JNI pointers as parameters where a function does not take ownership of the underlying object.
|
||||
*
|
||||
* The local, global, and alias references makes it possible to access methods in the underlying
|
||||
* objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are
|
||||
* supported (see example below). The wrappers also supports inheritance so a wrapper can inherit
|
||||
* from another wrapper to gain access to its functionality. As an example the jstring wrapper
|
||||
* inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for
|
||||
* example call the toString() method using the jclass wrapper, or any other class that inherits
|
||||
* from the jobject wrapper.
|
||||
*
|
||||
* Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if
|
||||
* you have a jobject that refers to a Java String you will need to cast it to jstring to get the
|
||||
* jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one
|
||||
* stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast
|
||||
* wisely.
|
||||
*
|
||||
* @include WrapperSample.cpp
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#include "ReferenceAllocators.h"
|
||||
#include "TypeTraits.h"
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// Convenience function to wrap an existing local reference
|
||||
template<typename T>
|
||||
local_ref<T> adopt_local(T ref) noexcept;
|
||||
|
||||
/// Convenience function to wrap an existing global reference
|
||||
template<typename T>
|
||||
global_ref<T> adopt_global(T ref) noexcept;
|
||||
|
||||
/// Convenience function to wrap an existing weak reference
|
||||
template<typename T>
|
||||
weak_ref<T> adopt_weak_global(T ref) noexcept;
|
||||
|
||||
|
||||
/// Swaps two owning references of the same type
|
||||
template<typename T>
|
||||
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
|
||||
|
||||
/// Swaps two owning references of the same type
|
||||
template<typename T, typename Alloc>
|
||||
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
|
||||
|
||||
/**
|
||||
* Retrieve the plain reference from a plain reference.
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
|
||||
|
||||
/**
|
||||
* Retrieve the plain reference from an alias reference.
|
||||
*/
|
||||
template<typename T>
|
||||
JniType<T> getPlainJniReference(alias_ref<T> ref);
|
||||
|
||||
/**
|
||||
* Retrieve the plain JNI reference from any reference owned reference.
|
||||
*/
|
||||
template<typename T, typename Alloc>
|
||||
JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
|
||||
|
||||
class JObject;
|
||||
class JClass;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct HasJniRefRepr : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {
|
||||
using type = typename T::JniRefRepr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RefReprType<T*> {
|
||||
using type = typename std::conditional<HasJniRefRepr<T>::value, typename HasJniRefRepr<T>::type, JObjectWrapper<T*>>::type;
|
||||
static_assert(std::is_base_of<JObject, type>::value,
|
||||
"Repr type missing JObject base.");
|
||||
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
|
||||
"RefReprType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RefReprType<T, typename std::enable_if<std::is_base_of<JObject, T>::value, void>::type> {
|
||||
using type = T;
|
||||
static_assert(std::is_base_of<JObject, type>::value,
|
||||
"Repr type missing JObject base.");
|
||||
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
|
||||
"RefReprType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType {
|
||||
using type = typename RefReprType<T>::type::javaobject;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType<JObjectWrapper<T>> {
|
||||
using type = T;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct JavaObjectType<T*> {
|
||||
using type = T*;
|
||||
static_assert(IsPlainJniReference<type>(),
|
||||
"JavaObjectType<T> not a plain jni reference");
|
||||
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
|
||||
"JavaObjectType<T> not idempotent");
|
||||
};
|
||||
|
||||
template <typename Repr>
|
||||
struct ReprStorage {
|
||||
explicit ReprStorage(JniType<Repr> obj) noexcept;
|
||||
|
||||
void set(JniType<Repr> obj) noexcept;
|
||||
|
||||
Repr& get() noexcept;
|
||||
const Repr& get() const noexcept;
|
||||
JniType<Repr> jobj() const noexcept;
|
||||
|
||||
void swap(ReprStorage& other) noexcept;
|
||||
private:
|
||||
ReprStorage() = delete;
|
||||
ReprStorage(const ReprStorage&) = delete;
|
||||
ReprStorage(ReprStorage&&) = delete;
|
||||
ReprStorage& operator=(const ReprStorage&) = delete;
|
||||
ReprStorage& operator=(ReprStorage&&) = delete;
|
||||
|
||||
using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
|
||||
Storage storage_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Create a new local reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned local reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the JNI reference could not be created
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
|
||||
make_local(const T& r);
|
||||
|
||||
/**
|
||||
* Create a new global reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned global reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the JNI reference could not be created
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
|
||||
make_global(const T& r);
|
||||
|
||||
/**
|
||||
* Create a new weak global reference from an existing reference
|
||||
*
|
||||
* @param ref a plain JNI, alias, or strong reference
|
||||
* @return an owned weak global reference (referring to null if the input does)
|
||||
* @throws std::bad_alloc if the returned reference is null
|
||||
*/
|
||||
template<typename T>
|
||||
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
|
||||
make_weak(const T& r);
|
||||
|
||||
/**
|
||||
* Compare two references to see if they refer to the same object
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator==(const T1& a, const T2& b);
|
||||
|
||||
/**
|
||||
* Compare two references to see if they don't refer to the same object
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||
operator!=(const T1& a, const T2& b);
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
class base_owned_ref {
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
/**
|
||||
* Release the ownership and set the reference to null. Thus no deleter is invoked.
|
||||
* @return Returns the reference
|
||||
*/
|
||||
javaobject release() noexcept;
|
||||
|
||||
/**
|
||||
* Reset the reference to refer to nullptr.
|
||||
*/
|
||||
void reset() noexcept;
|
||||
|
||||
protected:
|
||||
using Repr = ReprType<T>;
|
||||
detail::ReprStorage<Repr> storage_;
|
||||
|
||||
javaobject get() const noexcept;
|
||||
void set(javaobject ref) noexcept;
|
||||
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit base_owned_ref(javaobject reference) noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
base_owned_ref() noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
explicit base_owned_ref(std::nullptr_t) noexcept;
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
base_owned_ref(const base_owned_ref& other);
|
||||
template<typename U>
|
||||
base_owned_ref(const base_owned_ref<U, Alloc>& other);
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
base_owned_ref(base_owned_ref&& other) noexcept;
|
||||
template<typename U>
|
||||
base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;
|
||||
|
||||
/// The delete the underlying reference if applicable
|
||||
~base_owned_ref() noexcept;
|
||||
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
base_owned_ref& operator=(const base_owned_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
|
||||
|
||||
void reset(javaobject reference) noexcept;
|
||||
|
||||
friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref);
|
||||
|
||||
template<typename U, typename UAlloc>
|
||||
friend class base_owned_ref;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A smart reference that owns its underlying JNI reference. The class provides basic
|
||||
* functionality to handle a reference but gives no access to it unless the reference is
|
||||
* released, thus no longer owned. The API is stolen with pride from unique_ptr and the
|
||||
* semantics should be basically the same. This class should not be used directly, instead use
|
||||
* @ref weak_ref
|
||||
*/
|
||||
template<typename T>
|
||||
class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
using Allocator = WeakGlobalReferenceAllocator;
|
||||
|
||||
// This inherits non-default, non-copy, non-move ctors.
|
||||
using base_owned_ref<T, Allocator>::base_owned_ref;
|
||||
|
||||
/// Create a null reference
|
||||
weak_ref() noexcept
|
||||
: base_owned_ref<T, Allocator>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
explicit weak_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Allocator>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
weak_ref(const weak_ref& other)
|
||||
: base_owned_ref<T, Allocator>{other} {}
|
||||
|
||||
// This needs to be explicit to change its visibility.
|
||||
template<typename U>
|
||||
weak_ref(const weak_ref<U>& other)
|
||||
: base_owned_ref<T, Allocator>{other} {}
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
weak_ref(weak_ref&& other) noexcept
|
||||
: base_owned_ref<T, Allocator>{std::move(other)} {}
|
||||
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
weak_ref& operator=(const weak_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
weak_ref& operator=(weak_ref&& rhs) noexcept;
|
||||
|
||||
// Creates an owned local reference to the referred object or to null if the object is reclaimed
|
||||
local_ref<T> lockLocal() const;
|
||||
|
||||
// Creates an owned global reference to the referred object or to null if the object is reclaimed
|
||||
global_ref<T> lockGlobal() const;
|
||||
|
||||
private:
|
||||
// get/release/reset on weak_ref are not exposed to users.
|
||||
using base_owned_ref<T, Allocator>::get;
|
||||
using base_owned_ref<T, Allocator>::release;
|
||||
using base_owned_ref<T, Allocator>::reset;
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit weak_ref(javaobject reference) noexcept
|
||||
: base_owned_ref<T, Allocator>{reference} {}
|
||||
|
||||
template<typename T2> friend class weak_ref;
|
||||
friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;
|
||||
friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A class representing owned strong references to Java objects. This class
|
||||
* should not be used directly, instead use @ref local_ref, or @ref global_ref.
|
||||
*/
|
||||
template<typename T, typename Alloc>
|
||||
class basic_strong_ref : public base_owned_ref<T, Alloc> {
|
||||
using typename base_owned_ref<T, Alloc>::Repr;
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
using Allocator = Alloc;
|
||||
|
||||
// This inherits non-default, non-copy, non-move ctors.
|
||||
using base_owned_ref<T, Alloc>::base_owned_ref;
|
||||
using base_owned_ref<T, Alloc>::release;
|
||||
using base_owned_ref<T, Alloc>::reset;
|
||||
|
||||
/// Create a null reference
|
||||
basic_strong_ref() noexcept
|
||||
: base_owned_ref<T, Alloc>{} {}
|
||||
|
||||
/// Create a null reference
|
||||
explicit basic_strong_ref(std::nullptr_t) noexcept
|
||||
: base_owned_ref<T, Alloc>{nullptr} {}
|
||||
|
||||
/// Copy constructor (note creates a new reference)
|
||||
basic_strong_ref(const basic_strong_ref& other)
|
||||
: base_owned_ref<T, Alloc>{other} {}
|
||||
|
||||
// This needs to be explicit to change its visibility.
|
||||
template<typename U>
|
||||
basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
|
||||
: base_owned_ref<T, Alloc>{other} {}
|
||||
|
||||
/// Transfers ownership of an underlying reference from one unique reference to another
|
||||
basic_strong_ref(basic_strong_ref&& other) noexcept
|
||||
: base_owned_ref<T, Alloc>{std::move(other)} {}
|
||||
|
||||
/// Assignment operator (note creates a new reference)
|
||||
basic_strong_ref& operator=(const basic_strong_ref& other);
|
||||
|
||||
/// Assignment by moving a reference thus not creating a new reference
|
||||
basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
|
||||
|
||||
/// Get the plain JNI reference
|
||||
using base_owned_ref<T, Allocator>::get;
|
||||
|
||||
/// Release the ownership of the reference and return the wrapped reference in an alias
|
||||
alias_ref<T> releaseAlias() noexcept;
|
||||
|
||||
/// Checks if the reference points to a non-null object
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
Repr* operator->() noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
const Repr* operator->() const noexcept;
|
||||
|
||||
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
|
||||
Repr& operator*() noexcept;
|
||||
|
||||
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
|
||||
/// before invoking)
|
||||
const Repr& operator*() const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
using base_owned_ref<T, Alloc>::storage_;
|
||||
|
||||
/*
|
||||
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
|
||||
* NB! Does not create a new reference
|
||||
*/
|
||||
explicit basic_strong_ref(javaobject reference) noexcept
|
||||
: base_owned_ref<T, Alloc>{reference} {}
|
||||
|
||||
|
||||
friend local_ref<T> adopt_local<T>(T ref) noexcept;
|
||||
friend global_ref<T> adopt_global<T>(T ref) noexcept;
|
||||
friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
|
||||
|
||||
/// Swaps to alias reference of the same type
|
||||
template<typename T>
|
||||
void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
|
||||
|
||||
/**
|
||||
* A non-owning variant of the smart references (a dumb reference). These references still provide
|
||||
* access to the functionality of the @ref JObjectWrapper specializations including exception
|
||||
* handling and ease of use. Use this representation when you don't want to claim ownership of the
|
||||
* underlying reference (compare to using raw pointers instead of smart pointers.) For symmetry use
|
||||
* @ref alias_ref instead of this class.
|
||||
*/
|
||||
template<typename T>
|
||||
class alias_ref {
|
||||
using Repr = ReprType<T>;
|
||||
|
||||
public:
|
||||
using javaobject = JniType<T>;
|
||||
|
||||
/// Create a null reference
|
||||
alias_ref() noexcept;
|
||||
|
||||
/// Create a null reference
|
||||
alias_ref(std::nullptr_t) noexcept;
|
||||
|
||||
/// Copy constructor
|
||||
alias_ref(const alias_ref& other) noexcept;
|
||||
|
||||
/// Wrap an existing plain JNI reference
|
||||
/* implicit */ alias_ref(javaobject ref) noexcept;
|
||||
|
||||
/// Wrap an existing smart reference of any type convertible to T
|
||||
template<
|
||||
typename TOther,
|
||||
typename = enable_if_t<
|
||||
IsConvertible<JniType<TOther>, javaobject>(), T>
|
||||
>
|
||||
alias_ref(alias_ref<TOther> other) noexcept;
|
||||
|
||||
/// Wrap an existing alias reference of a type convertible to T
|
||||
template<
|
||||
typename TOther,
|
||||
typename AOther,
|
||||
typename = enable_if_t<
|
||||
IsConvertible<JniType<TOther>, javaobject>(), T>
|
||||
>
|
||||
alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
|
||||
|
||||
/// Assignment operator
|
||||
alias_ref& operator=(alias_ref other) noexcept;
|
||||
|
||||
/// Checks if the reference points to a non-null object
|
||||
explicit operator bool() const noexcept;
|
||||
|
||||
/// Converts back to a plain JNI reference
|
||||
javaobject get() const noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
Repr* operator->() noexcept;
|
||||
|
||||
/// Access the functionality provided by the object wrappers
|
||||
const Repr* operator->() const noexcept;
|
||||
|
||||
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
|
||||
Repr& operator*() noexcept;
|
||||
|
||||
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
|
||||
const Repr& operator*() const noexcept;
|
||||
|
||||
private:
|
||||
void set(javaobject ref) noexcept;
|
||||
|
||||
detail::ReprStorage<Repr> storage_;
|
||||
|
||||
friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.
|
||||
*
|
||||
* This is useful when you have a call which is initiated from C++-land, and therefore
|
||||
* doesn't automatically get a local JNI frame managed for you by the JNI framework.
|
||||
*/
|
||||
class FBEXPORT JniLocalScope {
|
||||
public:
|
||||
JniLocalScope(JNIEnv* p_env, jint capacity);
|
||||
~JniLocalScope();
|
||||
|
||||
private:
|
||||
JNIEnv* env_;
|
||||
bool hasFrame_;
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
|
||||
static_ref_cast(const local_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
|
||||
static_ref_cast(const global_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename U>
|
||||
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
|
||||
static_ref_cast(const alias_ref<U>& ref) noexcept;
|
||||
|
||||
template<typename T, typename RefType>
|
||||
auto dynamic_ref_cast(const RefType& ref) ->
|
||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> ;
|
||||
|
||||
}}
|
||||
|
||||
#include "References-inl.h"
|
186
lib/fb/include/fb/fbjni/Registration-inl.h
Normal file
186
lib/fb/include/fb/fbjni/Registration-inl.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Exceptions.h"
|
||||
#include "Hybrid.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef __i386__
|
||||
// X86 ABI forces 16 byte stack allignment on calls. Unfortunately
|
||||
// sometimes Dalvik chooses not to obey the ABI:
|
||||
// - https://code.google.com/p/android/issues/detail?id=61012
|
||||
// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/
|
||||
// Therefore, we tell the compiler to re-align the stack on entry
|
||||
// to our JNI functions.
|
||||
#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define JNI_ENTRY_POINT
|
||||
#endif
|
||||
|
||||
// registration wrapper for legacy JNI-style functions
|
||||
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, Args... args) {
|
||||
// Note that if func was declared noexcept, then both gcc and clang are smart
|
||||
// enough to elide the try/catch.
|
||||
try {
|
||||
(*func)(env, static_cast<C>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
|
||||
try {
|
||||
return (*func)(env, static_cast<JniType<C>>(obj), args...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return R{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
// registration wrappers for functions, with autoconversion of arguments.
|
||||
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref<C>, Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...);
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
|
||||
struct funcWrapper {
|
||||
|
||||
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType call(JNIEnv*, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
return Convert<typename std::decay<R>::type>::toJniRet(
|
||||
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
|
||||
} catch (...) {
|
||||
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return jniRet{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
// registration wrappers for non-static methods, with autoconvertion of arguments.
|
||||
|
||||
template<typename M, M method, typename C, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) {
|
||||
struct funcWrapper {
|
||||
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
try {
|
||||
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(facebook::jni::cthis(aref));
|
||||
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...);
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
|
||||
struct funcWrapper {
|
||||
|
||||
JNI_ENTRY_POINT static typename Convert<typename std::decay<R>::type>::jniType call(JNIEnv* env, jobject obj,
|
||||
typename Convert<typename std::decay<Args>::type>::jniType... args) {
|
||||
try {
|
||||
try {
|
||||
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
|
||||
// This is usually a noop, but if the hybrid object is a
|
||||
// base class of other classes which register JNI methods,
|
||||
// this will get the right type for the registered method.
|
||||
auto cobj = static_cast<C*>(facebook::jni::cthis(aref));
|
||||
return Convert<typename std::decay<R>::type>::toJniRet(
|
||||
(cobj->*method)(Convert<typename std::decay<Args>::type>::fromJni(args)...));
|
||||
} catch (const std::exception& ex) {
|
||||
C::mapException(ex);
|
||||
throw;
|
||||
}
|
||||
} catch (...) {
|
||||
using jniRet = typename Convert<typename std::decay<R>::type>::jniType;
|
||||
translatePendingCppExceptionToJavaException();
|
||||
return jniRet{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This intentionally erases the real type; JNI will do it anyway
|
||||
return reinterpret_cast<NativeMethodWrapper*>(&(funcWrapper::call));
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) {
|
||||
return jmethod_traits<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (*)(alias_ref<C>, Args... args)) {
|
||||
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
template<typename R, typename C, typename... Args>
|
||||
inline std::string makeDescriptor(R (C::*)(Args... args)) {
|
||||
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}
|
87
lib/fb/include/fb/fbjni/Registration.h
Normal file
87
lib/fb/include/fb/fbjni/Registration.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include "References.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// This uses the real JNI function as a non-type template parameter to
|
||||
// cause a (static member) function to exist with the same signature,
|
||||
// but with try/catch exception translation.
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(JNIEnv*, jobject, Args... args));
|
||||
|
||||
// Same as above, but for non-void return types.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
|
||||
|
||||
// Automatically wrap object argument, and don't take env explicitly.
|
||||
template<typename F, F func, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(alias_ref<C>, Args... args));
|
||||
|
||||
// Automatically wrap object argument, and don't take env explicitly,
|
||||
// non-void return type.
|
||||
template<typename F, F func, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... args));
|
||||
|
||||
// Extract C++ instance from object, and invoke given method on it.
|
||||
template<typename M, M method, typename C, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args));
|
||||
|
||||
// Extract C++ instance from object, and invoke given method on it,
|
||||
// non-void return type
|
||||
template<typename M, M method, typename C, typename R, typename... Args>
|
||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (*func)(JNIEnv*, C, Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));
|
||||
|
||||
// This uses deduction to figure out the descriptor name if the types
|
||||
// are primitive or have JObjectWrapper specializations.
|
||||
template<typename R, typename C, typename... Args>
|
||||
std::string makeDescriptor(R (C::*method0)(Args... args));
|
||||
|
||||
}
|
||||
|
||||
// We have to use macros here, because the func needs to be used
|
||||
// as both a decltype expression argument and as a non-type template
|
||||
// parameter, since C++ provides no way for translateException
|
||||
// to deduce the type of its non-type template parameter.
|
||||
// The empty string in the macros below ensures that name
|
||||
// is always a string literal (because that syntax is only
|
||||
// valid when name is a string literal).
|
||||
#define makeNativeMethod2(name, func) \
|
||||
{ name "", ::facebook::jni::detail::makeDescriptor(&func), \
|
||||
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
|
||||
|
||||
#define makeNativeMethod3(name, desc, func) \
|
||||
{ name "", desc, \
|
||||
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
|
||||
|
||||
// Variadic template hacks to get macros with different numbers of
|
||||
// arguments. Usage instructions are in CoreClasses.h.
|
||||
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
|
||||
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||
|
||||
}}
|
||||
|
||||
#include "Registration-inl.h"
|
160
lib/fb/include/fb/fbjni/TypeTraits.h
Normal file
160
lib/fb/include/fb/fbjni/TypeTraits.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "References-forward.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
/// Generic std::enable_if helper
|
||||
template<bool B, typename T>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
/// Generic std::is_convertible helper
|
||||
template<typename From, typename To>
|
||||
constexpr bool IsConvertible() {
|
||||
return std::is_convertible<From, To>::value;
|
||||
}
|
||||
|
||||
template<template<typename...> class TT, typename T>
|
||||
struct is_instantiation_of : std::false_type {};
|
||||
|
||||
template<template<typename...> class TT, typename... Ts>
|
||||
struct is_instantiation_of<TT, TT<Ts...>> : std::true_type {};
|
||||
|
||||
template<template<typename...> class TT, typename... Ts>
|
||||
constexpr bool IsInstantiationOf() {
|
||||
return is_instantiation_of<TT, Ts...>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a type is a JNI reference or not
|
||||
template<typename T>
|
||||
struct is_plain_jni_reference :
|
||||
std::integral_constant<bool,
|
||||
std::is_pointer<T>::value &&
|
||||
std::is_base_of<
|
||||
typename std::remove_pointer<jobject>::type,
|
||||
typename std::remove_pointer<T>::type>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_plain_jni_reference
|
||||
template<typename T>
|
||||
constexpr bool IsPlainJniReference() {
|
||||
return is_plain_jni_reference<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a type is a primitive JNI type or not
|
||||
template<typename T>
|
||||
struct is_jni_primitive :
|
||||
std::integral_constant<bool,
|
||||
std::is_same<jboolean, T>::value ||
|
||||
std::is_same<jbyte, T>::value ||
|
||||
std::is_same<jchar, T>::value ||
|
||||
std::is_same<jshort, T>::value ||
|
||||
std::is_same<jint, T>::value ||
|
||||
std::is_same<jlong, T>::value ||
|
||||
std::is_same<jfloat, T>::value ||
|
||||
std::is_same<jdouble, T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_primitive
|
||||
template<typename T>
|
||||
constexpr bool IsJniPrimitive() {
|
||||
return is_jni_primitive<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine whether a type is a JNI array of primitives or not
|
||||
template <typename T>
|
||||
struct is_jni_primitive_array :
|
||||
std::integral_constant<bool,
|
||||
std::is_same<jbooleanArray, T>::value ||
|
||||
std::is_same<jbyteArray, T>::value ||
|
||||
std::is_same<jcharArray, T>::value ||
|
||||
std::is_same<jshortArray, T>::value ||
|
||||
std::is_same<jintArray, T>::value ||
|
||||
std::is_same<jlongArray, T>::value ||
|
||||
std::is_same<jfloatArray, T>::value ||
|
||||
std::is_same<jdoubleArray, T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_primitive_array
|
||||
template <typename T>
|
||||
constexpr bool IsJniPrimitiveArray() {
|
||||
return is_jni_primitive_array<T>::value;
|
||||
}
|
||||
|
||||
/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type
|
||||
template<typename T>
|
||||
struct is_jni_scalar :
|
||||
std::integral_constant<bool,
|
||||
is_plain_jni_reference<T>::value ||
|
||||
is_jni_primitive<T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_scalar
|
||||
template<typename T>
|
||||
constexpr bool IsJniScalar() {
|
||||
return is_jni_scalar<T>::value;
|
||||
}
|
||||
|
||||
// Metafunction to determine if a type is a JNI type
|
||||
template<typename T>
|
||||
struct is_jni_type :
|
||||
std::integral_constant<bool,
|
||||
is_jni_scalar<T>::value ||
|
||||
std::is_void<T>::value> {};
|
||||
|
||||
/// Helper to simplify use of is_jni_type
|
||||
template<typename T>
|
||||
constexpr bool IsJniType() {
|
||||
return is_jni_type<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct is_non_weak_reference :
|
||||
std::integral_constant<bool,
|
||||
IsPlainJniReference<T>() ||
|
||||
IsInstantiationOf<basic_strong_ref, T>() ||
|
||||
IsInstantiationOf<alias_ref, T>()> {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsNonWeakReference() {
|
||||
return is_non_weak_reference<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct is_any_reference :
|
||||
std::integral_constant<bool,
|
||||
IsPlainJniReference<T>() ||
|
||||
IsInstantiationOf<weak_ref, T>() ||
|
||||
IsInstantiationOf<basic_strong_ref, T>() ||
|
||||
IsInstantiationOf<alias_ref, T>()> {};
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsAnyReference() {
|
||||
return is_any_reference<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct reference_traits {
|
||||
using plain_jni_reference_t = JniType<T>;
|
||||
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
|
||||
};
|
||||
|
||||
template<template <typename...> class R, typename T, typename... A>
|
||||
struct reference_traits<R<T, A...>> {
|
||||
using plain_jni_reference_t = JniType<T>;
|
||||
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;
|
||||
|
||||
} // namespace jni
|
||||
} // namespace facebook
|
346
lib/fb/include/fb/log.h
Normal file
346
lib/fb/include/fb/log.h
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2005 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FB Wrapper for logging functions.
|
||||
*
|
||||
* The android logging API uses the macro "LOG()" for its logic, which means
|
||||
* that it conflicts with random other places that use LOG for their own
|
||||
* purposes and doesn't work right half the places you include it
|
||||
*
|
||||
* FBLOG uses exactly the same semantics (FBLOGD for debug etc) but because of
|
||||
* the FB prefix it's strictly better. FBLOGV also gets stripped out based on
|
||||
* whether NDEBUG is set, but can be overridden by FBLOG_NDEBUG
|
||||
*
|
||||
* Most of the rest is a copy of <cutils/log.h> with minor changes.
|
||||
*/
|
||||
|
||||
//
|
||||
// C/C++ logging functions. See the logging documentation for API details.
|
||||
//
|
||||
// We'd like these to be available from C code (in case we import some from
|
||||
// somewhere), so this has a C interface.
|
||||
//
|
||||
// The output will be correct when the log file is shared between multiple
|
||||
// threads and/or multiple processes so long as the operating system
|
||||
// supports O_APPEND. These calls have mutex-protected data structures
|
||||
// and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#else
|
||||
// These declarations are needed for our internal use even on non-Android
|
||||
// builds.
|
||||
// (they are borrowed from <android/log.h>)
|
||||
|
||||
/*
|
||||
* Android log priority values, in ascending priority order.
|
||||
*/
|
||||
typedef enum android_LogPriority {
|
||||
ANDROID_LOG_UNKNOWN = 0,
|
||||
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
|
||||
ANDROID_LOG_VERBOSE,
|
||||
ANDROID_LOG_DEBUG,
|
||||
ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_WARN,
|
||||
ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL,
|
||||
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
|
||||
} android_LogPriority;
|
||||
|
||||
/*
|
||||
* Send a simple string to the log.
|
||||
*/
|
||||
int __android_log_write(int prio, const char *tag, const char *text);
|
||||
|
||||
/*
|
||||
* Send a formatted string to the log, used like printf(fmt,...)
|
||||
*/
|
||||
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((format(printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Normally we strip FBLOGV (VERBOSE messages) from release builds.
|
||||
* You can modify this (for example with "#define FBLOG_NDEBUG 0"
|
||||
* at the top of your source file) to change that behavior.
|
||||
*/
|
||||
#ifndef FBLOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define FBLOG_NDEBUG 1
|
||||
#else
|
||||
#define FBLOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef FBLOGV
|
||||
#if FBLOG_NDEBUG
|
||||
#define FBLOGV(...) ((void)0)
|
||||
#else
|
||||
#define FBLOGV(...) ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond) != 0, 0))
|
||||
|
||||
#ifndef FBLOGV_IF
|
||||
#if FBLOG_NDEBUG
|
||||
#define FBLOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define FBLOGV_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef FBLOGD
|
||||
#define FBLOGD(...) ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef FBLOGD_IF
|
||||
#define FBLOGD_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) : (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef FBLOGI
|
||||
#define FBLOGI(...) ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef FBLOGI_IF
|
||||
#define FBLOGI_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) : (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef FBLOGW
|
||||
#define FBLOGW(...) ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef FBLOGW_IF
|
||||
#define FBLOGW_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) : (void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef FBLOGE
|
||||
#define FBLOGE(...) ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef FBLOGE_IF
|
||||
#define FBLOGE_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) : (void)0)
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* verbose priority.
|
||||
*/
|
||||
#ifndef IF_FBLOGV
|
||||
#if FBLOG_NDEBUG
|
||||
#define IF_FBLOGV() if (false)
|
||||
#else
|
||||
#define IF_FBLOGV() IF_FBLOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* debug priority.
|
||||
*/
|
||||
#ifndef IF_FBLOGD
|
||||
#define IF_FBLOGD() IF_FBLOG(LOG_DEBUG, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* info priority.
|
||||
*/
|
||||
#ifndef IF_FBLOGI
|
||||
#define IF_FBLOGI() IF_FBLOG(LOG_INFO, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* warn priority.
|
||||
*/
|
||||
#ifndef IF_FBLOGW
|
||||
#define IF_FBLOGW() IF_FBLOG(LOG_WARN, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* error priority.
|
||||
*/
|
||||
#ifndef IF_FBLOGE
|
||||
#define IF_FBLOGE() IF_FBLOG(LOG_ERROR, LOG_TAG)
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Log a fatal error. If the given condition fails, this stops program
|
||||
* execution like a normal assertion, but also generating the given message.
|
||||
* It is NOT stripped from release builds. Note that the condition test
|
||||
* is -inverted- from the normal assert() semantics.
|
||||
*/
|
||||
#define FBLOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
((CONDITION(cond)) ? ((void)fb_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0)
|
||||
|
||||
#define FBLOG_ALWAYS_FATAL(...) \
|
||||
(((void)fb_printAssert(NULL, LOG_TAG, __VA_ARGS__)))
|
||||
|
||||
/*
|
||||
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
|
||||
* are stripped out of release builds.
|
||||
*/
|
||||
#if FBLOG_NDEBUG
|
||||
|
||||
#define FBLOG_FATAL_IF(cond, ...) ((void)0)
|
||||
#define FBLOG_FATAL(...) ((void)0)
|
||||
|
||||
#else
|
||||
|
||||
#define FBLOG_FATAL_IF(cond, ...) FBLOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
|
||||
#define FBLOG_FATAL(...) FBLOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assertion that generates a log message when the assertion fails.
|
||||
* Stripped out of release builds. Uses the current LOG_TAG.
|
||||
*/
|
||||
#define FBLOG_ASSERT(cond, ...) FBLOG_FATAL_IF(!(cond), __VA_ARGS__)
|
||||
//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Basic log message macro.
|
||||
*
|
||||
* Example:
|
||||
* FBLOG(LOG_WARN, NULL, "Failed with error %d", errno);
|
||||
*
|
||||
* The second argument may be NULL or "" to indicate the "global" tag.
|
||||
*/
|
||||
#ifndef FBLOG
|
||||
#define FBLOG(priority, tag, ...) \
|
||||
FBLOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef FBLOG_BY_DELIMS
|
||||
#define FBLOG_BY_DELIMS(priority, tag, delims, msg, ...) \
|
||||
logPrintByDelims(ANDROID_##priority, tag, delims, msg, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to specify a number for the priority.
|
||||
*/
|
||||
#ifndef FBLOG_PRI
|
||||
#define FBLOG_PRI(priority, tag, ...) fb_printLog(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to pass in a varargs ("args" is a va_list).
|
||||
*/
|
||||
#ifndef FBLOG_PRI_VA
|
||||
#define FBLOG_PRI_VA(priority, tag, fmt, args) \
|
||||
fb_vprintLog(priority, NULL, tag, fmt, args)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional given a desired logging priority and tag.
|
||||
*/
|
||||
#ifndef IF_FBLOG
|
||||
#define IF_FBLOG(priority, tag) if (fb_testLog(ANDROID_##priority, tag))
|
||||
#endif
|
||||
|
||||
typedef void (*LogHandler)(int priority, const char* tag, const char* message);
|
||||
FBEXPORT void setLogHandler(LogHandler logHandler);
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
*
|
||||
* The stuff in the rest of this file should not be used directly.
|
||||
*/
|
||||
FBEXPORT int fb_printLog(int prio, const char* tag, const char* fmt, ...)
|
||||
#if defined(__GNUC__)
|
||||
__attribute__((format(printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
#define fb_vprintLog(prio, cond, tag, fmt...) \
|
||||
__android_log_vprint(prio, tag, fmt)
|
||||
|
||||
#define fb_printAssert(cond, tag, fmt...) __android_log_assert(cond, tag, fmt)
|
||||
|
||||
#define fb_writeLog(prio, tag, text) __android_log_write(prio, tag, text)
|
||||
|
||||
#define fb_bWriteLog(tag, payload, len) __android_log_bwrite(tag, payload, len)
|
||||
#define fb_btWriteLog(tag, type, payload, len) \
|
||||
__android_log_btwrite(tag, type, payload, len)
|
||||
|
||||
#define fb_testLog(prio, tag) (1)
|
||||
|
||||
/*
|
||||
* FB extensions
|
||||
*/
|
||||
void logPrintByDelims(int priority, const char* tag, const char* delims,
|
||||
const char* msg, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
168
lib/fb/include/fb/lyra.h
Normal file
168
lib/fb/include/fb/lyra.h
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace lyra {
|
||||
|
||||
constexpr size_t kDefaultLimit = 64;
|
||||
|
||||
using InstructionPointer = const void*;
|
||||
|
||||
class FBEXPORT StackTraceElement {
|
||||
public:
|
||||
StackTraceElement(InstructionPointer absoluteProgramCounter,
|
||||
InstructionPointer libraryBase,
|
||||
InstructionPointer functionAddress, std::string libraryName,
|
||||
std::string functionName)
|
||||
: absoluteProgramCounter_{absoluteProgramCounter},
|
||||
libraryBase_{libraryBase},
|
||||
functionAddress_{functionAddress},
|
||||
libraryName_{std::move(libraryName)},
|
||||
functionName_{std::move(functionName)} {}
|
||||
|
||||
InstructionPointer libraryBase() const noexcept { return libraryBase_; }
|
||||
|
||||
InstructionPointer functionAddress() const noexcept {
|
||||
return functionAddress_;
|
||||
}
|
||||
|
||||
InstructionPointer absoluteProgramCounter() const noexcept {
|
||||
return absoluteProgramCounter_;
|
||||
}
|
||||
|
||||
const std::string& libraryName() const noexcept { return libraryName_; }
|
||||
|
||||
const std::string& functionName() const noexcept { return functionName_; }
|
||||
|
||||
/**
|
||||
* The offset of the program counter to the base of the library (i.e. the
|
||||
* address that addr2line takes as input>
|
||||
*/
|
||||
std::ptrdiff_t libraryOffset() const noexcept {
|
||||
auto absoluteLibrary = static_cast<const char*>(libraryBase_);
|
||||
auto absoluteabsoluteProgramCounter =
|
||||
static_cast<const char*>(absoluteProgramCounter_);
|
||||
return absoluteabsoluteProgramCounter - absoluteLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset within the current function
|
||||
*/
|
||||
int functionOffset() const noexcept {
|
||||
auto absoluteSymbol = static_cast<const char*>(functionAddress_);
|
||||
auto absoluteabsoluteProgramCounter =
|
||||
static_cast<const char*>(absoluteProgramCounter_);
|
||||
return absoluteabsoluteProgramCounter - absoluteSymbol;
|
||||
}
|
||||
|
||||
private:
|
||||
const InstructionPointer absoluteProgramCounter_;
|
||||
const InstructionPointer libraryBase_;
|
||||
const InstructionPointer functionAddress_;
|
||||
const std::string libraryName_;
|
||||
const std::string functionName_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the vector with the current stack trace
|
||||
*
|
||||
* Note that this trace needs to be symbolicated to get the library offset even
|
||||
* if it is to be symbolicated off-line.
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param stackTrace The vector that will receive the stack trace. Before
|
||||
* filling the vector it will be cleared. The vector will never grow so the
|
||||
* number of frames captured is limited by the capacity of it.
|
||||
*
|
||||
* @param skip The number of frames to skip before capturing the trace
|
||||
*/
|
||||
FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,
|
||||
size_t skip = 0);
|
||||
|
||||
/**
|
||||
* Creates a vector and populates it with the current stack trace
|
||||
*
|
||||
* Note that this trace needs to be symbolicated to get the library offset even
|
||||
* if it is to be symbolicated off-line.
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param skip The number of frames to skip before capturing the trace
|
||||
*
|
||||
* @limit The maximum number of frames captured
|
||||
*/
|
||||
FBEXPORT inline std::vector<InstructionPointer> getStackTrace(
|
||||
size_t skip = 0,
|
||||
size_t limit = kDefaultLimit) {
|
||||
auto stackTrace = std::vector<InstructionPointer>{};
|
||||
stackTrace.reserve(limit);
|
||||
getStackTrace(stackTrace, skip + 1);
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symbolicates a stack trace into a given vector
|
||||
*
|
||||
* @param symbols The vector to receive the output. The vector is cleared and
|
||||
* enough room to keep the frames are reserved.
|
||||
*
|
||||
* @param stackTrace The input stack trace
|
||||
*/
|
||||
FBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
|
||||
const std::vector<InstructionPointer>& trace);
|
||||
|
||||
/**
|
||||
* Symbolicates a stack trace into a new vector
|
||||
*
|
||||
* @param stackTrace The input stack trace
|
||||
*/
|
||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||
const std::vector<InstructionPointer>& trace) {
|
||||
auto symbols = std::vector<StackTraceElement>{};
|
||||
getStackTraceSymbols(symbols, trace);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Captures and symbolicates a stack trace
|
||||
*
|
||||
* Beware of a bug on some platforms, which makes the trace loop until the
|
||||
* buffer is full when it reaches a noexpr function. It seems to be fixed in
|
||||
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
*
|
||||
* @param skip The number of frames before capturing the trace
|
||||
*
|
||||
* @param limit The maximum number of frames captured
|
||||
*/
|
||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||
size_t skip = 0,
|
||||
size_t limit = kDefaultLimit) {
|
||||
return getStackTraceSymbols(getStackTrace(skip + 1, limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatting a stack trace element
|
||||
*/
|
||||
FBEXPORT std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
|
||||
|
||||
/**
|
||||
* Formatting a stack trace
|
||||
*/
|
||||
FBEXPORT std::ostream& operator<<(std::ostream& out,
|
||||
const std::vector<StackTraceElement>& trace);
|
||||
}
|
||||
}
|
21
lib/fb/include/fb/noncopyable.h
Normal file
21
lib/fb/include/fb/noncopyable.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
|
||||
struct noncopyable {
|
||||
noncopyable(const noncopyable&) = delete;
|
||||
noncopyable& operator=(const noncopyable&) = delete;
|
||||
protected:
|
||||
noncopyable() = default;
|
||||
};
|
||||
|
||||
}
|
21
lib/fb/include/fb/nonmovable.h
Normal file
21
lib/fb/include/fb/nonmovable.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace facebook {
|
||||
|
||||
struct nonmovable {
|
||||
nonmovable(nonmovable&&) = delete;
|
||||
nonmovable& operator=(nonmovable&&) = delete;
|
||||
protected:
|
||||
nonmovable() = default;
|
||||
};
|
||||
|
||||
}
|
12
lib/fb/include/fb/visibility.h
Normal file
12
lib/fb/include/fb/visibility.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FBEXPORT __attribute__((visibility("default")))
|
36
lib/fb/include/jni/Countable.h
Normal file
36
lib/fb/include/jni/Countable.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Countable.h>
|
||||
#include <fb/RefPtr.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
FBEXPORT const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj);
|
||||
|
||||
template <typename T> RefPtr<T> extractRefPtr(JNIEnv* env, jobject obj) {
|
||||
return static_cast<RefPtr<T>>(countableFromJava(env, obj));
|
||||
}
|
||||
|
||||
template <typename T> RefPtr<T> extractPossiblyNullRefPtr(JNIEnv* env, jobject obj) {
|
||||
return obj ? extractRefPtr<T>(env, obj) : nullptr;
|
||||
}
|
||||
|
||||
FBEXPORT void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable);
|
||||
|
||||
void CountableOnLoad(JNIEnv* env);
|
||||
|
||||
} }
|
||||
|
91
lib/fb/include/jni/GlobalReference.h
Normal file
91
lib/fb/include/jni/GlobalReference.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
|
||||
namespace facebook { namespace jni {
|
||||
|
||||
template<typename T>
|
||||
class GlobalReference {
|
||||
static_assert(std::is_convertible<T, jobject>::value,
|
||||
"GlobalReference<T> instantiated with type that is not "
|
||||
"convertible to jobject");
|
||||
|
||||
public:
|
||||
explicit GlobalReference(T globalReference) :
|
||||
reference_(globalReference? Environment::current()->NewGlobalRef(globalReference) : nullptr) {
|
||||
}
|
||||
|
||||
~GlobalReference() {
|
||||
reset();
|
||||
}
|
||||
|
||||
GlobalReference() :
|
||||
reference_(nullptr) {
|
||||
}
|
||||
|
||||
// enable move constructor and assignment
|
||||
GlobalReference(GlobalReference&& rhs) :
|
||||
reference_(std::move(rhs.reference_)) {
|
||||
rhs.reference_ = nullptr;
|
||||
}
|
||||
|
||||
GlobalReference& operator=(GlobalReference&& rhs) {
|
||||
if (this != &rhs) {
|
||||
reset();
|
||||
reference_ = std::move(rhs.reference_);
|
||||
rhs.reference_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GlobalReference(const GlobalReference<T>& rhs) :
|
||||
reference_{} {
|
||||
reset(rhs.get());
|
||||
}
|
||||
|
||||
GlobalReference& operator=(const GlobalReference<T>& rhs) {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
reset(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (reference_ != nullptr);
|
||||
}
|
||||
|
||||
T get() const {
|
||||
return reinterpret_cast<T>(reference_);
|
||||
}
|
||||
|
||||
void reset(T globalReference = nullptr) {
|
||||
if (reference_) {
|
||||
Environment::current()->DeleteGlobalRef(reference_);
|
||||
}
|
||||
if (globalReference) {
|
||||
reference_ = Environment::current()->NewGlobalRef(globalReference);
|
||||
} else {
|
||||
reference_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
jobject reference_;
|
||||
};
|
||||
|
||||
}}
|
37
lib/fb/include/jni/LocalReference.h
Normal file
37
lib/fb/include/jni/LocalReference.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/Environment.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
template<class T>
|
||||
struct LocalReferenceDeleter {
|
||||
static_assert(std::is_convertible<T, jobject>::value,
|
||||
"LocalReferenceDeleter<T> instantiated with type that is not convertible to jobject");
|
||||
void operator()(T localReference) {
|
||||
if (localReference != nullptr) {
|
||||
Environment::current()->DeleteLocalRef(localReference);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using LocalReference =
|
||||
std::unique_ptr<typename std::remove_pointer<T>::type, LocalReferenceDeleter<T>>;
|
||||
|
||||
} }
|
94
lib/fb/include/jni/LocalString.h
Normal file
94
lib/fb/include/jni/LocalString.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength);
|
||||
size_t modifiedLength(const std::string& str);
|
||||
size_t modifiedLength(const uint8_t* str, size_t* length);
|
||||
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept;
|
||||
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
|
||||
|
||||
}
|
||||
|
||||
// JNI represents strings encoded with modified version of UTF-8. The difference between UTF-8 and
|
||||
// Modified UTF-8 is that the latter support only 1-byte, 2-byte, and 3-byte formats. Supplementary
|
||||
// character (4 bytes in unicode) needs to be represented in the form of surrogate pairs. To create
|
||||
// a Modified UTF-8 surrogate pair that Dalvik would understand we take 4-byte unicode character,
|
||||
// encode it with UTF-16 which gives us two 2 byte chars (surrogate pair) and then we encode each
|
||||
// pair as UTF-8. This result in 2 x 3 byte characters. To convert modified UTF-8 to standard
|
||||
// UTF-8, this mus tbe reversed.
|
||||
//
|
||||
// The second difference is that Modified UTF-8 is encoding NUL byte in 2-byte format.
|
||||
//
|
||||
// In order to avoid complex error handling, only a minimum of validity checking is done to avoid
|
||||
// crashing. If the input is invalid, the output may be invalid as well.
|
||||
//
|
||||
// Relevant links:
|
||||
// - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
||||
// - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8
|
||||
|
||||
class FBEXPORT LocalString {
|
||||
public:
|
||||
// Assumes UTF8 encoding and make a required convertion to modified UTF-8 when the string
|
||||
// contains unicode supplementary characters.
|
||||
explicit LocalString(const std::string& str);
|
||||
explicit LocalString(const char* str);
|
||||
jstring string() const {
|
||||
return m_string;
|
||||
}
|
||||
~LocalString();
|
||||
private:
|
||||
jstring m_string;
|
||||
};
|
||||
|
||||
// JString to UTF16 extractor using RAII idiom
|
||||
class JStringUtf16Extractor {
|
||||
public:
|
||||
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
|
||||
: env_(env)
|
||||
, javaString_(javaString)
|
||||
, utf16String_(nullptr) {
|
||||
if (env_ && javaString_) {
|
||||
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
~JStringUtf16Extractor() {
|
||||
if (utf16String_) {
|
||||
env_->ReleaseStringCritical(javaString_, utf16String_);
|
||||
}
|
||||
}
|
||||
|
||||
operator const jchar* () const {
|
||||
return utf16String_;
|
||||
}
|
||||
|
||||
private:
|
||||
JNIEnv* env_;
|
||||
jstring javaString_;
|
||||
const jchar* utf16String_;
|
||||
};
|
||||
|
||||
// The string from JNI is converted to standard UTF-8 if the string contains supplementary
|
||||
// characters.
|
||||
FBEXPORT std::string fromJString(JNIEnv* env, jstring str);
|
||||
|
||||
} }
|
27
lib/fb/include/jni/Registration.h
Normal file
27
lib/fb/include/jni/Registration.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <jni.h>
|
||||
#include <initializer_list>
|
||||
#include <fb/assert.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
static inline void registerNatives(JNIEnv* env, jclass cls, std::initializer_list<JNINativeMethod> methods) {
|
||||
auto result = env->RegisterNatives(cls, methods.begin(), methods.size());
|
||||
FBASSERT(result == 0);
|
||||
}
|
||||
|
||||
static inline void registerNatives(JNIEnv* env, const char* cls, std::initializer_list<JNINativeMethod> list) {
|
||||
registerNatives(env, env->FindClass(cls), list);
|
||||
}
|
||||
|
||||
} }
|
55
lib/fb/include/jni/WeakReference.h
Normal file
55
lib/fb/include/jni/WeakReference.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
#include <fb/noncopyable.h>
|
||||
#include <fb/Countable.h>
|
||||
#include <fb/visibility.h>
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
class FBEXPORT WeakReference : public Countable {
|
||||
public:
|
||||
typedef RefPtr<WeakReference> Ptr;
|
||||
WeakReference(jobject strongRef);
|
||||
~WeakReference();
|
||||
jweak weakRef() {
|
||||
return m_weakReference;
|
||||
}
|
||||
|
||||
private:
|
||||
jweak m_weakReference;
|
||||
};
|
||||
|
||||
// This class is intended to take a weak reference and turn it into a strong
|
||||
// local reference. Consequently, it should only be allocated on the stack.
|
||||
class FBEXPORT ResolvedWeakReference : public noncopyable {
|
||||
public:
|
||||
ResolvedWeakReference(jobject weakRef);
|
||||
ResolvedWeakReference(const RefPtr<WeakReference>& weakRef);
|
||||
~ResolvedWeakReference();
|
||||
|
||||
operator jobject () {
|
||||
return m_strongReference;
|
||||
}
|
||||
|
||||
explicit operator bool () {
|
||||
return m_strongReference != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
jobject m_strongReference;
|
||||
};
|
||||
|
||||
} }
|
||||
|
139
lib/fb/include/jni/jni_helpers.h
Normal file
139
lib/fb/include/jni/jni_helpers.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <fb/visibility.h>
|
||||
|
||||
namespace facebook {
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName class name to throw
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a NoClassDefFoundError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a RuntimeException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a IllegalArgumentException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a IllegalStateException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an IOException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an AssertionError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an OutOfMemoryError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
FBEXPORT jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...);
|
||||
|
||||
/**
|
||||
* Finds the specified class. If it's not found, instructs the JNI environment to throw an
|
||||
* exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName the classname to find in JNI format (e.g. "java/lang/String")
|
||||
* @return the class or NULL if not found (in which case a pending exception will be queued). This
|
||||
* returns a global reference (JNIEnv::NewGlobalRef).
|
||||
*/
|
||||
FBEXPORT jclass findClassOrThrow(JNIEnv *pEnv, const char* szClassName);
|
||||
|
||||
/**
|
||||
* Finds the specified field of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the field in
|
||||
* @param szFieldName the name of the field to find
|
||||
* @param szSig the signature of the field
|
||||
* @return the field or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
FBEXPORT jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig);
|
||||
|
||||
/**
|
||||
* Finds the specified method of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the method in
|
||||
* @param szMethodName the name of the method to find
|
||||
* @param szSig the signature of the method
|
||||
* @return the method or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
FBEXPORT jmethodID getMethodIdOrThrow(
|
||||
JNIEnv* pEnv,
|
||||
jclass clazz,
|
||||
const char* szMethodName,
|
||||
const char* szSig);
|
||||
|
||||
} // namespace facebook
|
||||
|
77
lib/fb/jni/ByteBuffer.cpp
Normal file
77
lib/fb/jni/ByteBuffer.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/ByteBuffer.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fb/fbjni/References.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
local_ref<JByteBuffer> createEmpty() {
|
||||
static auto cls = JByteBuffer::javaClassStatic();
|
||||
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
|
||||
return meth(cls, 0);
|
||||
}
|
||||
}
|
||||
|
||||
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
||||
// env->NewDirectByteBuffer requires that size is positive. Android's
|
||||
// dalvik returns an invalid result and Android's art aborts if size == 0.
|
||||
// Workaround this by using a slow path through Java in that case.
|
||||
if (!size) {
|
||||
return createEmpty();
|
||||
}
|
||||
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!res) {
|
||||
throw std::runtime_error("Direct byte buffers are unsupported.");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t* JByteBuffer::getDirectBytes() {
|
||||
if (!self()) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
void* bytes = Environment::current()->GetDirectBufferAddress(self());
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (!bytes) {
|
||||
throw std::runtime_error(
|
||||
isDirect() ?
|
||||
"Attempt to get direct bytes of non-direct byte buffer." :
|
||||
"Error getting direct bytes of byte buffer.");
|
||||
}
|
||||
return static_cast<uint8_t*>(bytes);
|
||||
}
|
||||
|
||||
size_t JByteBuffer::getDirectSize() {
|
||||
if (!self()) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
int size = Environment::current()->GetDirectBufferCapacity(self());
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
if (size < 0) {
|
||||
throw std::runtime_error(
|
||||
isDirect() ?
|
||||
"Attempt to get direct size of non-direct byte buffer." :
|
||||
"Error getting direct size of byte buffer.");
|
||||
}
|
||||
return static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
bool JByteBuffer::isDirect() {
|
||||
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
|
||||
return meth(self());
|
||||
}
|
||||
|
||||
}}
|
69
lib/fb/jni/Countable.cpp
Normal file
69
lib/fb/jni/Countable.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <jni/Countable.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <jni/Registration.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
static jfieldID gCountableNativePtr;
|
||||
|
||||
static RefPtr<Countable>* rawCountableFromJava(JNIEnv* env, jobject obj) {
|
||||
FBASSERT(obj);
|
||||
return reinterpret_cast<RefPtr<Countable>*>(env->GetLongField(obj, gCountableNativePtr));
|
||||
}
|
||||
|
||||
const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj) {
|
||||
FBASSERT(obj);
|
||||
return *rawCountableFromJava(env, obj);
|
||||
}
|
||||
|
||||
void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable) {
|
||||
int oldValue = env->GetLongField(obj, gCountableNativePtr);
|
||||
FBASSERTMSGF(oldValue == 0, "Cannot reinitialize object; expected nullptr, got %x", oldValue);
|
||||
|
||||
FBASSERT(countable);
|
||||
uintptr_t fieldValue = (uintptr_t) new RefPtr<Countable>(std::move(countable));
|
||||
env->SetLongField(obj, gCountableNativePtr, fieldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: THREAD SAFETY (this comment also exists at Countable.java)
|
||||
*
|
||||
* This method deletes the corresponding native object on whatever thread the method is called
|
||||
* on. In the common case when this is called by Countable#finalize(), this will be called on the
|
||||
* system finalizer thread. If you manually call dispose on the Java object, the native object
|
||||
* will be deleted synchronously on that thread.
|
||||
*/
|
||||
void dispose(JNIEnv* env, jobject obj) {
|
||||
// Grab the pointer
|
||||
RefPtr<Countable>* countable = rawCountableFromJava(env, obj);
|
||||
if (!countable) {
|
||||
// That was easy.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out the old value to avoid double-frees
|
||||
env->SetLongField(obj, gCountableNativePtr, 0);
|
||||
|
||||
delete countable;
|
||||
}
|
||||
|
||||
void CountableOnLoad(JNIEnv* env) {
|
||||
jclass countable = env->FindClass("com/facebook/jni/Countable");
|
||||
gCountableNativePtr = env->GetFieldID(countable, "mInstance", "J");
|
||||
registerNatives(env, countable, {
|
||||
{ "dispose", "()V", (void*) dispose },
|
||||
});
|
||||
}
|
||||
|
||||
} }
|
132
lib/fb/jni/Environment.cpp
Normal file
132
lib/fb/jni/Environment.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fb/log.h>
|
||||
#include <fb/StaticInitialized.h>
|
||||
#include <fb/ThreadLocal.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
#include <fb/fbjni/NativeRunnable.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
StaticInitialized<ThreadLocal<JNIEnv>> g_env;
|
||||
JavaVM* g_vm = nullptr;
|
||||
|
||||
struct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/ThreadScopeSupport;";
|
||||
|
||||
// These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead.
|
||||
static void runStdFunction(std::function<void()>&& func) {
|
||||
static auto method = javaClassStatic()->getStaticMethod<void(jlong)>("runStdFunction");
|
||||
method(javaClassStatic(), reinterpret_cast<jlong>(&func));
|
||||
}
|
||||
|
||||
static void runStdFunctionImpl(alias_ref<JClass>, jlong ptr) {
|
||||
(*reinterpret_cast<std::function<void()>*>(ptr))();
|
||||
}
|
||||
|
||||
static void OnLoad() {
|
||||
// We need the javaClassStatic so that the class lookup is cached and that
|
||||
// runStdFunction can be called from a ThreadScope-attached thread.
|
||||
javaClassStatic()->registerNatives({
|
||||
makeNativeMethod("runStdFunctionImpl", runStdFunctionImpl),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::current() {
|
||||
JNIEnv* env = g_env->get();
|
||||
if ((env == nullptr) && (g_vm != nullptr)) {
|
||||
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
FBLOGE("Error retrieving JNI Environment, thread is probably not attached to JVM");
|
||||
// TODO(cjhopman): This should throw an exception.
|
||||
env = nullptr;
|
||||
} else {
|
||||
g_env->reset(env);
|
||||
}
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Environment::detachCurrentThread() {
|
||||
auto env = g_env->get();
|
||||
if (env) {
|
||||
FBASSERT(g_vm);
|
||||
g_vm->DetachCurrentThread();
|
||||
g_env->reset();
|
||||
}
|
||||
}
|
||||
|
||||
struct EnvironmentInitializer {
|
||||
EnvironmentInitializer(JavaVM* vm) {
|
||||
FBASSERT(!g_vm);
|
||||
FBASSERT(vm);
|
||||
g_vm = vm;
|
||||
g_env.initialize([] (void*) {});
|
||||
}
|
||||
};
|
||||
|
||||
/* static */
|
||||
void Environment::initialize(JavaVM* vm) {
|
||||
static EnvironmentInitializer init(vm);
|
||||
}
|
||||
|
||||
/* static */
|
||||
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
|
||||
auto env = g_env->get();
|
||||
if (!env) {
|
||||
FBASSERT(g_vm);
|
||||
g_vm->AttachCurrentThread(&env, nullptr);
|
||||
g_env->reset(env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
ThreadScope::ThreadScope()
|
||||
: attachedWithThisScope_(false) {
|
||||
JNIEnv* env = nullptr;
|
||||
if (g_vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_EDETACHED) {
|
||||
return;
|
||||
}
|
||||
env = facebook::jni::Environment::ensureCurrentThreadIsAttached();
|
||||
FBASSERT(env);
|
||||
attachedWithThisScope_ = true;
|
||||
}
|
||||
|
||||
ThreadScope::~ThreadScope() {
|
||||
if (attachedWithThisScope_) {
|
||||
Environment::detachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ThreadScope::OnLoad() {
|
||||
// These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.
|
||||
JThreadScopeSupport::OnLoad();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void ThreadScope::WithClassLoader(std::function<void()>&& runnable) {
|
||||
// TODO(cjhopman): If the classloader is already available in this scope, we
|
||||
// shouldn't have to jump through java.
|
||||
ThreadScope ts;
|
||||
JThreadScopeSupport::runStdFunction(std::move(runnable));
|
||||
}
|
||||
|
||||
} }
|
||||
|
285
lib/fb/jni/Exceptions.cpp
Normal file
285
lib/fb/jni/Exceptions.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/CoreClasses.h>
|
||||
|
||||
#include <fb/assert.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
#include <alloca.h>
|
||||
#include <cstdlib>
|
||||
#include <ios>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
class JRuntimeException : public JavaClass<JRuntimeException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/RuntimeException;";
|
||||
|
||||
static local_ref<JRuntimeException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
|
||||
static local_ref<JRuntimeException> create() {
|
||||
return newInstance();
|
||||
}
|
||||
};
|
||||
|
||||
class JIOException : public JavaClass<JIOException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/io/IOException;";
|
||||
|
||||
static local_ref<JIOException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JOutOfMemoryError : public JavaClass<JOutOfMemoryError, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/OutOfMemoryError;";
|
||||
|
||||
static local_ref<JOutOfMemoryError> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JArrayIndexOutOfBoundsException : public JavaClass<JArrayIndexOutOfBoundsException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Ljava/lang/ArrayIndexOutOfBoundsException;";
|
||||
|
||||
static local_ref<JArrayIndexOutOfBoundsException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JUnknownCppException : public JavaClass<JUnknownCppException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/UnknownCppException;";
|
||||
|
||||
static local_ref<JUnknownCppException> create() {
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
static local_ref<JUnknownCppException> create(const char* str) {
|
||||
return newInstance(make_jstring(str));
|
||||
}
|
||||
};
|
||||
|
||||
class JCppSystemErrorException : public JavaClass<JCppSystemErrorException, JThrowable> {
|
||||
public:
|
||||
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppSystemErrorException;";
|
||||
|
||||
static local_ref<JCppSystemErrorException> create(const std::system_error& e) {
|
||||
return newInstance(make_jstring(e.what()), e.code().value());
|
||||
}
|
||||
};
|
||||
|
||||
// Exception throwing & translating functions //////////////////////////////////////////////////////
|
||||
|
||||
// Functions that throw Java exceptions
|
||||
|
||||
void setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {
|
||||
auto env = Environment::current();
|
||||
if (throwable) {
|
||||
env->Throw(throwable.get());
|
||||
}
|
||||
if (env->ExceptionCheck() != JNI_TRUE) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Functions that throw C++ exceptions
|
||||
|
||||
// TODO(T6618159) Take a stack dump here to save context if it results in a crash when propagated
|
||||
void throwPendingJniExceptionAsCppException() {
|
||||
JNIEnv* env = Environment::current();
|
||||
if (env->ExceptionCheck() == JNI_FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto throwable = adopt_local(env->ExceptionOccurred());
|
||||
if (!throwable) {
|
||||
throw std::runtime_error("Unable to get pending JNI exception.");
|
||||
}
|
||||
env->ExceptionClear();
|
||||
|
||||
throw JniException(throwable);
|
||||
}
|
||||
|
||||
void throwCppExceptionIf(bool condition) {
|
||||
if (!condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto env = Environment::current();
|
||||
if (env->ExceptionCheck() == JNI_TRUE) {
|
||||
throwPendingJniExceptionAsCppException();
|
||||
return;
|
||||
}
|
||||
|
||||
throw JniException();
|
||||
}
|
||||
|
||||
void throwNewJavaException(jthrowable throwable) {
|
||||
throw JniException(wrap_alias(throwable));
|
||||
}
|
||||
|
||||
void throwNewJavaException(const char* throwableName, const char* msg) {
|
||||
// If anything of the fbjni calls fail, an exception of a suitable
|
||||
// form will be thrown, which is what we want.
|
||||
auto throwableClass = findClassLocal(throwableName);
|
||||
auto throwable = throwableClass->newObject(
|
||||
throwableClass->getConstructor<jthrowable(jstring)>(),
|
||||
make_jstring(msg).release());
|
||||
throwNewJavaException(throwable.get());
|
||||
}
|
||||
|
||||
// Translate C++ to Java Exception
|
||||
|
||||
namespace {
|
||||
|
||||
// The implementation std::rethrow_if_nested uses a dynamic_cast to determine
|
||||
// if the exception is a nested_exception. If the exception is from a library
|
||||
// built with -fno-rtti, then that will crash. This avoids that.
|
||||
void rethrow_if_nested() {
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::nested_exception& e) {
|
||||
e.rethrow_nested();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
// For each exception in the chain of the currently handled exception, func
|
||||
// will be called with that exception as the currently handled exception (in
|
||||
// reverse order, i.e. innermost first).
|
||||
void denest(std::function<void()> func) {
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::exception& e) {
|
||||
try {
|
||||
rethrow_if_nested();
|
||||
} catch (...) {
|
||||
denest(func);
|
||||
}
|
||||
func();
|
||||
} catch (...) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void translatePendingCppExceptionToJavaException() noexcept {
|
||||
local_ref<JThrowable> previous;
|
||||
auto func = [&previous] () {
|
||||
local_ref<JThrowable> current;
|
||||
try {
|
||||
throw;
|
||||
} catch(const JniException& ex) {
|
||||
current = ex.getThrowable();
|
||||
} catch(const std::ios_base::failure& ex) {
|
||||
current = JIOException::create(ex.what());
|
||||
} catch(const std::bad_alloc& ex) {
|
||||
current = JOutOfMemoryError::create(ex.what());
|
||||
} catch(const std::out_of_range& ex) {
|
||||
current = JArrayIndexOutOfBoundsException::create(ex.what());
|
||||
} catch(const std::system_error& ex) {
|
||||
current = JCppSystemErrorException::create(ex);
|
||||
} catch(const std::runtime_error& ex) {
|
||||
current = JRuntimeException::create(ex.what());
|
||||
} catch(const std::exception& ex) {
|
||||
current = JCppException::create(ex.what());
|
||||
} catch(const char* msg) {
|
||||
current = JUnknownCppException::create(msg);
|
||||
} catch(...) {
|
||||
current = JUnknownCppException::create();
|
||||
}
|
||||
if (previous) {
|
||||
current->initCause(previous);
|
||||
}
|
||||
previous = current;
|
||||
};
|
||||
|
||||
try {
|
||||
denest(func);
|
||||
setJavaExceptionAndAbortOnFailure(previous);
|
||||
} catch (std::exception& e) {
|
||||
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException: %s", e.what());
|
||||
// rethrow the exception and let the noexcept handling abort.
|
||||
throw;
|
||||
} catch (...) {
|
||||
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// JniException ////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const std::string JniException::kExceptionMessageFailure_ = "Unable to get exception message.";
|
||||
|
||||
JniException::JniException() : JniException(JRuntimeException::create()) { }
|
||||
|
||||
JniException::JniException(alias_ref<jthrowable> throwable) : isMessageExtracted_(false) {
|
||||
throwable_ = make_global(throwable);
|
||||
}
|
||||
|
||||
JniException::JniException(JniException &&rhs)
|
||||
: throwable_(std::move(rhs.throwable_)),
|
||||
what_(std::move(rhs.what_)),
|
||||
isMessageExtracted_(rhs.isMessageExtracted_) {
|
||||
}
|
||||
|
||||
JniException::JniException(const JniException &rhs)
|
||||
: what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {
|
||||
throwable_ = make_global(rhs.throwable_);
|
||||
}
|
||||
|
||||
JniException::~JniException() {
|
||||
ThreadScope ts;
|
||||
throwable_.reset();
|
||||
}
|
||||
|
||||
local_ref<JThrowable> JniException::getThrowable() const noexcept {
|
||||
return make_local(throwable_);
|
||||
}
|
||||
|
||||
// TODO 6900503: consider making this thread-safe.
|
||||
void JniException::populateWhat() const noexcept {
|
||||
ThreadScope ts;
|
||||
try {
|
||||
what_ = throwable_->toString();
|
||||
isMessageExtracted_ = true;
|
||||
} catch(...) {
|
||||
what_ = kExceptionMessageFailure_;
|
||||
}
|
||||
}
|
||||
|
||||
const char* JniException::what() const noexcept {
|
||||
if (!isMessageExtracted_) {
|
||||
populateWhat();
|
||||
}
|
||||
return what_.c_str();
|
||||
}
|
||||
|
||||
void JniException::setJavaException() const noexcept {
|
||||
setJavaExceptionAndAbortOnFailure(throwable_);
|
||||
}
|
||||
|
||||
}}
|
65
lib/fb/jni/Hybrid.cpp
Normal file
65
lib/fb/jni/Hybrid.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include "fb/fbjni.h"
|
||||
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace detail {
|
||||
|
||||
void HybridData::setNativePointer(std::unique_ptr<BaseHybridClass> new_value) {
|
||||
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
|
||||
auto* old_value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (new_value) {
|
||||
// Modify should only ever be called once with a non-null
|
||||
// new_value. If this happens again it's a programmer error, so
|
||||
// blow up.
|
||||
FBASSERTMSGF(old_value == 0, "Attempt to set C++ native pointer twice");
|
||||
} else if (old_value == 0) {
|
||||
return;
|
||||
}
|
||||
// delete on a null pointer is defined to be a noop.
|
||||
delete old_value;
|
||||
// This releases ownership from the unique_ptr, and passes the pointer, and
|
||||
// ownership of it, to HybridData which is managed by the java GC. The
|
||||
// finalizer on hybridData calls resetNative which will delete the object, if
|
||||
// resetNative has not already been called.
|
||||
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
|
||||
}
|
||||
|
||||
BaseHybridClass* HybridData::getNativePointer() {
|
||||
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
|
||||
auto* value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
|
||||
if (!value) {
|
||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
local_ref<HybridData> HybridData::create() {
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
void resetNative(alias_ref<detail::HybridData> jthis) {
|
||||
jthis->setNativePointer(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void HybridDataOnLoad() {
|
||||
registerNatives("com/facebook/jni/HybridData", {
|
||||
makeNativeMethod("resetNative", resetNative),
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
312
lib/fb/jni/LocalString.cpp
Normal file
312
lib/fb/jni/LocalString.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <jni/LocalString.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/assert.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint16_t kUtf8OneByteBoundary = 0x80;
|
||||
const uint16_t kUtf8TwoBytesBoundary = 0x800;
|
||||
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
|
||||
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
|
||||
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
|
||||
|
||||
inline void encode3ByteUTF8(char32_t code, uint8_t* out) {
|
||||
FBASSERTMSGF((code & 0xffff0000) == 0, "3 byte utf-8 encodings only valid for up to 16 bits");
|
||||
|
||||
out[0] = 0xE0 | (code >> 12);
|
||||
out[1] = 0x80 | ((code >> 6) & 0x3F);
|
||||
out[2] = 0x80 | (code & 0x3F);
|
||||
}
|
||||
|
||||
inline char32_t decode3ByteUTF8(const uint8_t* in) {
|
||||
return (((in[0] & 0x0f) << 12) |
|
||||
((in[1] & 0x3f) << 6) |
|
||||
( in[2] & 0x3f));
|
||||
}
|
||||
|
||||
inline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) {
|
||||
FBASSERTMSGF((code & 0xfff80000) == 0, "4 byte utf-8 encodings only valid for up to 21 bits");
|
||||
|
||||
out[offset] = (char) (0xF0 | (code >> 18));
|
||||
out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
|
||||
out[offset + 2] = (char) (0x80 | ((code >> 6) & 0x3F));
|
||||
out[offset + 3] = (char) (0x80 | (code & 0x3F));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isFourByteUTF8Encoding(const T* utf8) {
|
||||
return ((*utf8 & 0xF8) == 0xF0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
size_t modifiedLength(const std::string& str) {
|
||||
// Scan for supplementary characters
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < str.size(); ) {
|
||||
if (str[i] == 0) {
|
||||
i += 1;
|
||||
j += 2;
|
||||
} else if (i + 4 > str.size() ||
|
||||
!isFourByteUTF8Encoding(&(str[i]))) {
|
||||
// See the code in utf8ToModifiedUTF8 for what's happening here.
|
||||
i += 1;
|
||||
j += 1;
|
||||
} else {
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
// returns modified utf8 length; *length is set to strlen(str)
|
||||
size_t modifiedLength(const uint8_t* str, size_t* length) {
|
||||
// NUL-terminated: Scan for length and supplementary characters
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
while (str[i] != 0) {
|
||||
if (str[i + 1] == 0 ||
|
||||
str[i + 2] == 0 ||
|
||||
str[i + 3] == 0 ||
|
||||
!isFourByteUTF8Encoding(&(str[i]))) {
|
||||
i += 1;
|
||||
j += 1;
|
||||
} else {
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
}
|
||||
|
||||
*length = i;
|
||||
return j;
|
||||
}
|
||||
|
||||
void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size_t modifiedBufLen)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < len; ) {
|
||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
||||
if (utf8[i] == 0) {
|
||||
FBASSERTMSGF(j + 1 < modifiedBufLen, "output buffer is too short");
|
||||
modified[j] = 0xc0;
|
||||
modified[j + 1] = 0x80;
|
||||
i += 1;
|
||||
j += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 4 > len ||
|
||||
!isFourByteUTF8Encoding(utf8 + i)) {
|
||||
// If the input is too short for this to be a four-byte
|
||||
// encoding, or it isn't one for real, just copy it on through.
|
||||
modified[j] = utf8[i];
|
||||
i++;
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert 4 bytes of input to 2 * 3 bytes of output
|
||||
char32_t code = (((utf8[i] & 0x07) << 18) |
|
||||
((utf8[i + 1] & 0x3f) << 12) |
|
||||
((utf8[i + 2] & 0x3f) << 6) |
|
||||
( utf8[i + 3] & 0x3f));
|
||||
char32_t first;
|
||||
char32_t second;
|
||||
|
||||
if (code > 0x10ffff) {
|
||||
// These could be valid utf-8, but cannot be represented as modified UTF-8, due to the 20-bit
|
||||
// limit on that representation. Encode two replacement characters, so the expected output
|
||||
// length lines up.
|
||||
const char32_t kUnicodeReplacementChar = 0xfffd;
|
||||
first = kUnicodeReplacementChar;
|
||||
second = kUnicodeReplacementChar;
|
||||
} else {
|
||||
// split into surrogate pair
|
||||
first = ((code - 0x010000) >> 10) | 0xd800;
|
||||
second = ((code - 0x010000) & 0x3ff) | 0xdc00;
|
||||
}
|
||||
|
||||
// encode each as a 3 byte surrogate value
|
||||
FBASSERTMSGF(j + 5 < modifiedBufLen, "output buffer is too short");
|
||||
encode3ByteUTF8(first, modified + j);
|
||||
encode3ByteUTF8(second, modified + j + 3);
|
||||
i += 4;
|
||||
j += 6;
|
||||
}
|
||||
|
||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
||||
modified[j++] = '\0';
|
||||
}
|
||||
|
||||
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept {
|
||||
// Converting from modified utf8 to utf8 will always shrink, so this will always be sufficient
|
||||
std::string utf8(len, 0);
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < len; ) {
|
||||
// surrogate pair: 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
|
||||
// encoded pair: 1110 1101 1010 xxxx 10xx xxxx 1110 1101 1011 xxxx 10xx xxxx
|
||||
|
||||
if (len >= i + 6 &&
|
||||
modified[i] == 0xed &&
|
||||
(modified[i + 1] & 0xf0) == 0xa0 &&
|
||||
modified[i + 3] == 0xed &&
|
||||
(modified[i + 4] & 0xf0) == 0xb0) {
|
||||
// Valid surrogate pair
|
||||
char32_t pair1 = decode3ByteUTF8(modified + i);
|
||||
char32_t pair2 = decode3ByteUTF8(modified + i + 3);
|
||||
char32_t ch = 0x10000 + (((pair1 & 0x3ff) << 10) |
|
||||
( pair2 & 0x3ff));
|
||||
encode4ByteUTF8(ch, utf8, j);
|
||||
i += 6;
|
||||
j += 4;
|
||||
continue;
|
||||
} else if (len >= i + 2 &&
|
||||
modified[i] == 0xc0 &&
|
||||
modified[i + 1] == 0x80) {
|
||||
utf8[j] = 0;
|
||||
i += 2;
|
||||
j += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy one byte. This might be a one, two, or three-byte encoding. It might be an invalid
|
||||
// encoding of some sort, but garbage in garbage out is ok.
|
||||
|
||||
utf8[j] = (char) modified[i];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
utf8.resize(j);
|
||||
|
||||
return utf8;
|
||||
}
|
||||
|
||||
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
|
||||
// UTF16 string
|
||||
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
|
||||
if (!utf16String || utf16StringLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t utf8StringLen = 0;
|
||||
auto utf16StringEnd = utf16String + utf16StringLen;
|
||||
auto idx16 = utf16String;
|
||||
while (idx16 < utf16StringEnd) {
|
||||
auto ch = *idx16++;
|
||||
if (ch < kUtf8OneByteBoundary) {
|
||||
utf8StringLen++;
|
||||
} else if (ch < kUtf8TwoBytesBoundary) {
|
||||
utf8StringLen += 2;
|
||||
} else if (
|
||||
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
|
||||
(idx16 < utf16StringEnd) &&
|
||||
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
|
||||
utf8StringLen += 4;
|
||||
idx16++;
|
||||
} else {
|
||||
utf8StringLen += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return utf8StringLen;
|
||||
}
|
||||
|
||||
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {
|
||||
if (!utf16String || utf16StringLen <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
|
||||
auto idx8 = utf8String.begin();
|
||||
auto idx16 = utf16String;
|
||||
auto utf16StringEnd = utf16String + utf16StringLen;
|
||||
while (idx16 < utf16StringEnd) {
|
||||
auto ch = *idx16++;
|
||||
if (ch < kUtf8OneByteBoundary) {
|
||||
*idx8++ = (ch & 0x7F);
|
||||
} else if (ch < kUtf8TwoBytesBoundary) {
|
||||
*idx8++ = 0b11000000 | (ch >> 6);
|
||||
*idx8++ = 0b10000000 | (ch & 0x3F);
|
||||
} else if (
|
||||
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
|
||||
(idx16 < utf16StringEnd) &&
|
||||
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
|
||||
auto ch2 = *idx16++;
|
||||
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
|
||||
*idx8++ = 0b11110000 | (trunc_byte >> 2);
|
||||
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
|
||||
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
|
||||
*idx8++ = 0b10000000 | (ch2 & 0x3F);
|
||||
} else {
|
||||
*idx8++ = 0b11100000 | (ch >> 12);
|
||||
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
|
||||
*idx8++ = 0b10000000 | (ch & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
return utf8String;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LocalString::LocalString(const std::string& str)
|
||||
{
|
||||
size_t modlen = detail::modifiedLength(str);
|
||||
if (modlen == str.size()) {
|
||||
// no supplementary characters, build jstring from input buffer
|
||||
m_string = Environment::current()->NewStringUTF(str.data());
|
||||
return;
|
||||
}
|
||||
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
|
||||
detail::utf8ToModifiedUTF8(
|
||||
reinterpret_cast<const uint8_t*>(str.data()), str.size(),
|
||||
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
|
||||
m_string = Environment::current()->NewStringUTF(modified.data());
|
||||
}
|
||||
|
||||
LocalString::LocalString(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(str), &len);
|
||||
if (modlen == len) {
|
||||
// no supplementary characters, build jstring from input buffer
|
||||
m_string = Environment::current()->NewStringUTF(str);
|
||||
return;
|
||||
}
|
||||
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
|
||||
detail::utf8ToModifiedUTF8(
|
||||
reinterpret_cast<const uint8_t*>(str), len,
|
||||
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
|
||||
m_string = Environment::current()->NewStringUTF(modified.data());
|
||||
}
|
||||
|
||||
LocalString::~LocalString() {
|
||||
Environment::current()->DeleteLocalRef(m_string);
|
||||
}
|
||||
|
||||
std::string fromJString(JNIEnv* env, jstring str) {
|
||||
auto utf16String = JStringUtf16Extractor(env, str);
|
||||
auto length = env->GetStringLength(str);
|
||||
return detail::utf16toUTF8(utf16String, length);
|
||||
}
|
||||
|
||||
} }
|
22
lib/fb/jni/OnLoad.cpp
Normal file
22
lib/fb/jni/OnLoad.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <jni/Countable.h>
|
||||
#include <fb/Environment.h>
|
||||
#include <fb/fbjni.h>
|
||||
#include <fb/fbjni/NativeRunnable.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
void initialize_fbjni() {
|
||||
CountableOnLoad(Environment::current());
|
||||
HybridDataOnLoad();
|
||||
JNativeRunnable::OnLoad();
|
||||
ThreadScope::OnLoad();
|
||||
}
|
41
lib/fb/jni/References.cpp
Normal file
41
lib/fb/jni/References.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni/References.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
JniLocalScope::JniLocalScope(JNIEnv* env, jint capacity)
|
||||
: env_(env) {
|
||||
hasFrame_ = false;
|
||||
auto pushResult = env->PushLocalFrame(capacity);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(pushResult < 0);
|
||||
hasFrame_ = true;
|
||||
}
|
||||
|
||||
JniLocalScope::~JniLocalScope() {
|
||||
if (hasFrame_) {
|
||||
env_->PopLocalFrame(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Default implementation always returns true.
|
||||
// Platform-specific sources can override this.
|
||||
bool doesGetObjectRefTypeWork() __attribute__ ((weak));
|
||||
bool doesGetObjectRefTypeWork() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
43
lib/fb/jni/WeakReference.cpp
Normal file
43
lib/fb/jni/WeakReference.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/Environment.h>
|
||||
#include <jni/WeakReference.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
WeakReference::WeakReference(jobject strongRef) :
|
||||
m_weakReference(Environment::current()->NewWeakGlobalRef(strongRef))
|
||||
{
|
||||
}
|
||||
|
||||
WeakReference::~WeakReference() {
|
||||
auto env = Environment::current();
|
||||
FBASSERTMSGF(env, "Attempt to delete jni::WeakReference from non-JNI thread");
|
||||
env->DeleteWeakGlobalRef(m_weakReference);
|
||||
}
|
||||
|
||||
ResolvedWeakReference::ResolvedWeakReference(jobject weakRef) :
|
||||
m_strongReference(Environment::current()->NewLocalRef(weakRef))
|
||||
{
|
||||
}
|
||||
|
||||
ResolvedWeakReference::ResolvedWeakReference(const RefPtr<WeakReference>& weakRef) :
|
||||
m_strongReference(Environment::current()->NewLocalRef(weakRef->weakRef()))
|
||||
{
|
||||
}
|
||||
|
||||
ResolvedWeakReference::~ResolvedWeakReference() {
|
||||
if (m_strongReference)
|
||||
Environment::current()->DeleteLocalRef(m_strongReference);
|
||||
}
|
||||
|
||||
} }
|
||||
|
196
lib/fb/jni/fbjni.cpp
Normal file
196
lib/fb/jni/fbjni.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <jni/LocalString.h>
|
||||
#include <fb/log.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace jni {
|
||||
|
||||
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
|
||||
static std::once_flag flag{};
|
||||
// TODO (t7832883): DTRT when we have exception pointers
|
||||
static auto error_msg = std::string{"Failed to initialize fbjni"};
|
||||
static auto error_occured = false;
|
||||
|
||||
std::call_once(flag, [vm] {
|
||||
try {
|
||||
Environment::initialize(vm);
|
||||
} catch (std::exception& ex) {
|
||||
error_occured = true;
|
||||
try {
|
||||
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
|
||||
} catch (...) {
|
||||
// Ignore, we already have a fall back message
|
||||
}
|
||||
} catch (...) {
|
||||
error_occured = true;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
if (error_occured) {
|
||||
throw std::runtime_error(error_msg);
|
||||
}
|
||||
|
||||
init_fn();
|
||||
} catch (const std::exception& e) {
|
||||
FBLOGE("error %s", e.what());
|
||||
translatePendingCppExceptionToJavaException();
|
||||
} catch (...) {
|
||||
translatePendingCppExceptionToJavaException();
|
||||
// So Java will handle the translated exception, fall through and
|
||||
// return a good version number.
|
||||
}
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
alias_ref<JClass> findClassStatic(const char* name) {
|
||||
const auto env = internal::getEnv();
|
||||
if (!env) {
|
||||
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
||||
}
|
||||
auto cls = env->FindClass(name);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
||||
auto leaking_ref = (jclass)env->NewGlobalRef(cls);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
|
||||
return wrap_alias(leaking_ref);
|
||||
}
|
||||
|
||||
local_ref<JClass> findClassLocal(const char* name) {
|
||||
const auto env = internal::getEnv();
|
||||
if (!env) {
|
||||
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
||||
}
|
||||
auto cls = env->FindClass(name);
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
||||
return adopt_local(cls);
|
||||
}
|
||||
|
||||
|
||||
// jstring /////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string JString::toStdString() const {
|
||||
const auto env = internal::getEnv();
|
||||
auto utf16String = JStringUtf16Extractor(env, self());
|
||||
auto length = env->GetStringLength(self());
|
||||
return detail::utf16toUTF8(utf16String, length);
|
||||
}
|
||||
|
||||
local_ref<JString> make_jstring(const char* utf8) {
|
||||
if (!utf8) {
|
||||
return {};
|
||||
}
|
||||
const auto env = internal::getEnv();
|
||||
size_t len;
|
||||
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
|
||||
jstring result;
|
||||
if (modlen == len) {
|
||||
// The only difference between utf8 and modifiedUTF8 is in encoding 4-byte UTF8 chars
|
||||
// and '\0' that is encoded on 2 bytes.
|
||||
//
|
||||
// Since modifiedUTF8-encoded string can be no shorter than it's UTF8 conterpart we
|
||||
// know that if those two strings are of the same length we don't need to do any
|
||||
// conversion -> no 4-byte chars nor '\0'.
|
||||
result = env->NewStringUTF(utf8);
|
||||
} else {
|
||||
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
|
||||
detail::utf8ToModifiedUTF8(
|
||||
reinterpret_cast<const uint8_t*>(utf8), len,
|
||||
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
|
||||
result = env->NewStringUTF(modified.data());
|
||||
}
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||
return adopt_local(result);
|
||||
}
|
||||
|
||||
|
||||
// JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma push_macro("DEFINE_PRIMITIVE_METHODS")
|
||||
#undef DEFINE_PRIMITIVE_METHODS
|
||||
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
|
||||
\
|
||||
template<> \
|
||||
FBEXPORT \
|
||||
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
|
||||
auto env = internal::getEnv(); \
|
||||
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
return res; \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
FBEXPORT \
|
||||
void JPrimitiveArray<TYPE ## Array>::releaseElements( \
|
||||
TYPE* elements, jint mode) { \
|
||||
auto env = internal::getEnv(); \
|
||||
env->Release ## NAME ## ArrayElements(self(), elements, mode); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
FBEXPORT \
|
||||
void JPrimitiveArray<TYPE ## Array>::getRegion( \
|
||||
jsize start, jsize length, TYPE* buf) { \
|
||||
auto env = internal::getEnv(); \
|
||||
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
FBEXPORT \
|
||||
void JPrimitiveArray<TYPE ## Array>::setRegion( \
|
||||
jsize start, jsize length, const TYPE* elements) { \
|
||||
auto env = internal::getEnv(); \
|
||||
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
|
||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||
} \
|
||||
\
|
||||
FBEXPORT \
|
||||
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
|
||||
auto array = internal::getEnv()->New ## NAME ## Array(size); \
|
||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
|
||||
return adopt_local(array); \
|
||||
} \
|
||||
\
|
||||
template<> \
|
||||
FBEXPORT \
|
||||
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
|
||||
return make_ ## SMALLNAME ## _array(count); \
|
||||
} \
|
||||
\
|
||||
|
||||
DEFINE_PRIMITIVE_METHODS(jboolean, Boolean, boolean)
|
||||
DEFINE_PRIMITIVE_METHODS(jbyte, Byte, byte)
|
||||
DEFINE_PRIMITIVE_METHODS(jchar, Char, char)
|
||||
DEFINE_PRIMITIVE_METHODS(jshort, Short, short)
|
||||
DEFINE_PRIMITIVE_METHODS(jint, Int, int)
|
||||
DEFINE_PRIMITIVE_METHODS(jlong, Long, long)
|
||||
DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
|
||||
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
|
||||
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
|
||||
|
||||
// Internal debug /////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace internal {
|
||||
|
||||
FBEXPORT ReferenceStats g_reference_stats;
|
||||
|
||||
FBEXPORT void facebook::jni::internal::ReferenceStats::reset() noexcept {
|
||||
locals_deleted = globals_deleted = weaks_deleted = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}
|
20
lib/fb/jni/java/CppException.java
Normal file
20
lib/fb/jni/java/CppException.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public class CppException extends RuntimeException {
|
||||
@DoNotStrip
|
||||
public CppException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
27
lib/fb/jni/java/CppSystemErrorException.java
Normal file
27
lib/fb/jni/java/CppSystemErrorException.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public class CppSystemErrorException extends CppException {
|
||||
int errorCode;
|
||||
|
||||
@DoNotStrip
|
||||
public CppSystemErrorException(String message, int errorCode) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
}
|
25
lib/fb/jni/java/UnknownCppException.java
Normal file
25
lib/fb/jni/java/UnknownCppException.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
package com.facebook.jni;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
|
||||
@DoNotStrip
|
||||
public class UnknownCppException extends CppException {
|
||||
@DoNotStrip
|
||||
public UnknownCppException() {
|
||||
super("Unknown");
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
public UnknownCppException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
197
lib/fb/jni/jni_helpers.cpp
Normal file
197
lib/fb/jni/jni_helpers.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <stddef.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <jni/jni_helpers.h>
|
||||
|
||||
#define MSG_SIZE 1024
|
||||
|
||||
namespace facebook {
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName class name to throw
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args) {
|
||||
char szMsg[MSG_SIZE];
|
||||
vsnprintf(szMsg, MSG_SIZE, szFmt, va_args);
|
||||
jclass exClass = pEnv->FindClass(szClassName);
|
||||
return pEnv->ThrowNew(exClass, szMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a NoClassDefFoundError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/NoClassDefFoundError", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw a RuntimeException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/RuntimeException", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an IllegalArgumentException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/IllegalArgumentException", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an IllegalStateException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/IllegalStateException", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an OutOfMemoryError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/OutOfMemoryError", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an AssertionError.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/lang/AssertionError", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the JNI environment to throw an IOException.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szFmt sprintf-style format string
|
||||
* @param ... sprintf-style args
|
||||
* @return 0 on success; a negative value on failure
|
||||
*/
|
||||
jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...) {
|
||||
va_list va_args;
|
||||
va_start(va_args, szFmt);
|
||||
jint ret = throwException(pEnv, "java/io/IOException", szFmt, va_args);
|
||||
va_end(va_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified class. If it's not found, instructs the JNI environment to throw an
|
||||
* exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param szClassName the classname to find in JNI format (e.g. "java/lang/String")
|
||||
* @return the class or NULL if not found (in which case a pending exception will be queued). This
|
||||
* returns a global reference (JNIEnv::NewGlobalRef).
|
||||
*/
|
||||
jclass findClassOrThrow(JNIEnv* pEnv, const char* szClassName) {
|
||||
jclass clazz = pEnv->FindClass(szClassName);
|
||||
if (!clazz) {
|
||||
return NULL;
|
||||
}
|
||||
return (jclass) pEnv->NewGlobalRef(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified field of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the field in
|
||||
* @param szFieldName the name of the field to find
|
||||
* @param szSig the signature of the field
|
||||
* @return the field or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig) {
|
||||
return pEnv->GetFieldID(clazz, szFieldName, szSig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified method of the specified class. If it's not found, instructs the JNI
|
||||
* environment to throw an exception.
|
||||
*
|
||||
* @param pEnv JNI environment
|
||||
* @param clazz the class to lookup the method in
|
||||
* @param szMethodName the name of the method to find
|
||||
* @param szSig the signature of the method
|
||||
* @return the method or NULL if not found (in which case a pending exception will be queued)
|
||||
*/
|
||||
jmethodID getMethodIdOrThrow(
|
||||
JNIEnv* pEnv,
|
||||
jclass clazz,
|
||||
const char* szMethodName,
|
||||
const char* szSig) {
|
||||
return pEnv->GetMethodID(clazz, szMethodName, szSig);
|
||||
}
|
||||
|
||||
} // namespace facebook
|
100
lib/fb/log.cpp
Normal file
100
lib/fb/log.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <fb/log.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LOG_BUFFER_SIZE 4096
|
||||
static LogHandler gLogHandler;
|
||||
|
||||
void setLogHandler(LogHandler logHandler) {
|
||||
gLogHandler = logHandler;
|
||||
}
|
||||
|
||||
int fb_printLog(int prio, const char *tag, const char *fmt, ...) {
|
||||
char logBuffer[LOG_BUFFER_SIZE];
|
||||
|
||||
va_list va_args;
|
||||
va_start(va_args, fmt);
|
||||
int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args);
|
||||
va_end(va_args);
|
||||
if (gLogHandler != NULL) {
|
||||
gLogHandler(prio, tag, logBuffer);
|
||||
}
|
||||
__android_log_write(prio, tag, logBuffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
void logPrintByDelims(int priority, const char* tag, const char* delims,
|
||||
const char* msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[32768];
|
||||
char* context;
|
||||
char* tok;
|
||||
|
||||
va_start(ap, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
tok = strtok_r(buf, delims, &context);
|
||||
|
||||
if (!tok) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
__android_log_write(priority, tag, tok);
|
||||
} while ((tok = strtok_r(NULL, delims, &context)));
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
// Implementations of the basic android logging functions for non-android platforms.
|
||||
|
||||
static char logTagChar(int prio) {
|
||||
switch (prio) {
|
||||
default:
|
||||
case ANDROID_LOG_UNKNOWN:
|
||||
case ANDROID_LOG_DEFAULT:
|
||||
case ANDROID_LOG_SILENT:
|
||||
return ' ';
|
||||
case ANDROID_LOG_VERBOSE:
|
||||
return 'V';
|
||||
case ANDROID_LOG_DEBUG:
|
||||
return 'D';
|
||||
case ANDROID_LOG_INFO:
|
||||
return 'I';
|
||||
case ANDROID_LOG_WARN:
|
||||
return 'W';
|
||||
case ANDROID_LOG_ERROR:
|
||||
return 'E';
|
||||
case ANDROID_LOG_FATAL:
|
||||
return 'F';
|
||||
}
|
||||
}
|
||||
|
||||
int __android_log_write(int prio, const char *tag, const char *text) {
|
||||
return fprintf(stderr, "[%c/%.16s] %s\n", logTagChar(prio), tag, text);
|
||||
}
|
||||
|
||||
int __android_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
int res = fprintf(stderr, "[%c/%.16s] ", logTagChar(prio), tag);
|
||||
res += vfprintf(stderr, "%s\n", ap);
|
||||
|
||||
va_end(ap);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif
|
120
lib/fb/lyra/lyra.cpp
Normal file
120
lib/fb/lyra/lyra.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include <fb/lyra.h>
|
||||
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <unwind.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace facebook {
|
||||
namespace lyra {
|
||||
|
||||
namespace {
|
||||
|
||||
class IosFlagsSaver {
|
||||
ios_base& ios_;
|
||||
ios_base::fmtflags flags_;
|
||||
|
||||
public:
|
||||
IosFlagsSaver(ios_base& ios)
|
||||
: ios_(ios),
|
||||
flags_(ios.flags())
|
||||
{}
|
||||
|
||||
~IosFlagsSaver() {
|
||||
ios_.flags(flags_);
|
||||
}
|
||||
};
|
||||
|
||||
struct BacktraceState {
|
||||
size_t skip;
|
||||
vector<InstructionPointer>& stackTrace;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
|
||||
BacktraceState* state = reinterpret_cast<BacktraceState*>(arg);
|
||||
auto absoluteProgramCounter =
|
||||
reinterpret_cast<InstructionPointer>(_Unwind_GetIP(context));
|
||||
|
||||
if (state->skip > 0) {
|
||||
--state->skip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
if (state->stackTrace.size() == state->stackTrace.capacity()) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
state->stackTrace.push_back(absoluteProgramCounter);
|
||||
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
void captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {
|
||||
// Beware of a bug on some platforms, which makes the trace loop until the
|
||||
// buffer is full when it reaches a noexcept function. It seems to be fixed in
|
||||
// newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
|
||||
// TODO(t10738439): Investigate workaround for the stack trace bug
|
||||
BacktraceState state = {skip, stackTrace};
|
||||
_Unwind_Backtrace(unwindCallback, &state);
|
||||
}
|
||||
}
|
||||
|
||||
void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {
|
||||
stackTrace.clear();
|
||||
captureBacktrace(skip + 1, stackTrace);
|
||||
}
|
||||
|
||||
// TODO(t10737622): Improve on-device symbolification
|
||||
void getStackTraceSymbols(vector<StackTraceElement>& symbols,
|
||||
const vector<InstructionPointer>& trace) {
|
||||
symbols.clear();
|
||||
symbols.reserve(trace.size());
|
||||
|
||||
for (size_t i = 0; i < trace.size(); ++i) {
|
||||
Dl_info info;
|
||||
if (dladdr(trace[i], &info)) {
|
||||
symbols.emplace_back(trace[i], info.dli_fbase, info.dli_saddr,
|
||||
info.dli_fname ? info.dli_fname : "",
|
||||
info.dli_sname ? info.dli_sname : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& out, const StackTraceElement& elm) {
|
||||
IosFlagsSaver flags{out};
|
||||
|
||||
// TODO(t10748683): Add build id to the output
|
||||
out << "{dso=" << elm.libraryName() << " offset=" << hex
|
||||
<< showbase << elm.libraryOffset();
|
||||
|
||||
if (!elm.functionName().empty()) {
|
||||
out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
|
||||
}
|
||||
|
||||
out << " build-id=" << hex << setw(8) << 0
|
||||
<< "}";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// TODO(t10737667): The implement a tool that parse the stack trace and
|
||||
// symbolicate it
|
||||
ostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {
|
||||
IosFlagsSaver flags{out};
|
||||
|
||||
auto i = 0;
|
||||
out << "Backtrace:\n";
|
||||
for (auto& elm : trace) {
|
||||
out << " #" << dec << setfill('0') << setw(2) << i++ << " " << elm << '\n';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
31
lib/fb/onload.cpp
Normal file
31
lib/fb/onload.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#ifndef DISABLE_CPUCAP
|
||||
#include <fb/CpuCapabilities.h>
|
||||
#endif
|
||||
#include <fb/fbjni.h>
|
||||
|
||||
using namespace facebook::jni;
|
||||
|
||||
void initialize_xplatinit();
|
||||
void initialize_fbjni();
|
||||
|
||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
return facebook::jni::initialize(vm, [] {
|
||||
initialize_fbjni();
|
||||
#ifndef DISABLE_XPLAT
|
||||
initialize_xplatinit();
|
||||
#endif
|
||||
#ifndef DISABLE_CPUCAP
|
||||
initialize_cpucapabilities();
|
||||
#endif
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user