Upgrade fbjni
Summary: Upgrades Yoga’s copy of *fbjni* to the latest version. This will enable us - to move from `finalize()` to `PhantomReference` to deallocate native memory, with the potential of making GC more efficient. - to remove the internal dependency to *libfb,* allowing apps without an own dependency to ship less code Reviewed By: passy Differential Revision: D16220924 fbshipit-source-id: e8233fe2b5403946ff51f43cb6def558ded52fda
This commit is contained in:
committed by
Facebook Github Bot
parent
be305b5d0f
commit
59d680f4e9
@@ -4,7 +4,7 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/fbjni.h>
|
#include <fbjni/fbjni.h>
|
||||||
#include <yoga/YGNode.h>
|
#include <yoga/YGNode.h>
|
||||||
#include <yoga/Yoga.h>
|
#include <yoga/Yoga.h>
|
||||||
#include <yoga/Yoga-internal.h>
|
#include <yoga/Yoga-internal.h>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/fbjni.h>
|
#include <fbjni/fbjni.h>
|
||||||
#include <yoga/YGValue.h>
|
#include <yoga/YGValue.h>
|
||||||
#include <yoga/Yoga.h>
|
#include <yoga/Yoga.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@@ -32,6 +32,7 @@ yoga_cxx_library(
|
|||||||
"-Wall",
|
"-Wall",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Wno-unused-parameter",
|
"-Wno-unused-parameter",
|
||||||
|
"-Wno-unused-variable",
|
||||||
"-std=c++11",
|
"-std=c++11",
|
||||||
],
|
],
|
||||||
platforms = (ANDROID,),
|
platforms = (ANDROID,),
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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
|
|
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
/** @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
|
|
@@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -1,271 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -1,115 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#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
|
|
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#include <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>
|
|
@@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,206 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "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)) {
|
|
||||||
(void)method0;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R, typename... Args>
|
|
||||||
template <R (*func)(Args...)>
|
|
||||||
JNI_ENTRY_POINT R CriticalMethod<R (*)(Args...)>::call(
|
|
||||||
alias_ref<jclass>,
|
|
||||||
Args... args) noexcept {
|
|
||||||
static_assert(
|
|
||||||
IsJniPrimitive<R>() || std::is_void<R>(),
|
|
||||||
"Critical Native Methods may only return primitive JNI types, or void.");
|
|
||||||
static_assert(
|
|
||||||
AreJniPrimitives<Args...>(),
|
|
||||||
"Critical Native Methods may only use primitive JNI types as parameters");
|
|
||||||
|
|
||||||
return func(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R, typename... Args>
|
|
||||||
template <R (*func)(Args...)>
|
|
||||||
inline std::string CriticalMethod<R (*)(Args...)>::desc() {
|
|
||||||
return makeDescriptor(call<func>);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
@@ -1,346 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
@@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
|
|
||||||
struct noncopyable {
|
|
||||||
noncopyable(const noncopyable&) = delete;
|
|
||||||
noncopyable& operator=(const noncopyable&) = delete;
|
|
||||||
protected:
|
|
||||||
noncopyable() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
|
|
||||||
struct nonmovable {
|
|
||||||
nonmovable(nonmovable&&) = delete;
|
|
||||||
nonmovable& operator=(nonmovable&&) = delete;
|
|
||||||
protected:
|
|
||||||
nonmovable() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@@ -6,26 +6,37 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
#include <fbjni/fbjni.h>
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
|
||||||
#include "References-forward.h"
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
|
class JBuffer : public JavaClass<JBuffer> {
|
||||||
|
public:
|
||||||
|
static constexpr const char* kJavaDescriptor = "Ljava/nio/Buffer;";
|
||||||
|
|
||||||
|
void rewind() const;
|
||||||
|
bool isDirect() const;
|
||||||
|
void* getDirectAddress() const;
|
||||||
|
size_t getDirectCapacity() const;
|
||||||
|
};
|
||||||
|
|
||||||
// JNI's NIO support has some awkward preconditions and error reporting. This
|
// JNI's NIO support has some awkward preconditions and error reporting. This
|
||||||
// class provides much more user-friendly access.
|
// class provides much more user-friendly access.
|
||||||
class FBEXPORT JByteBuffer : public JavaClass<JByteBuffer> {
|
class JByteBuffer : public JavaClass<JByteBuffer, JBuffer> {
|
||||||
public:
|
public:
|
||||||
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
|
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
|
||||||
|
|
||||||
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
|
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
|
||||||
|
static local_ref<JByteBuffer> allocateDirect(jint size);
|
||||||
|
|
||||||
bool isDirect() const;
|
uint8_t* getDirectBytes() const {
|
||||||
|
return static_cast<uint8_t*>(getDirectAddress());
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t* getDirectBytes() const;
|
size_t getDirectSize() const {
|
||||||
size_t getDirectSize() const;
|
return getDirectCapacity();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}}
|
}}
|
@@ -6,8 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
#include <fbjni/fbjni.h>
|
||||||
#include "File.h"
|
#include <fbjni/File.h>
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
@@ -18,12 +18,12 @@ class AContext : public JavaClass<AContext> {
|
|||||||
|
|
||||||
// Define a method that calls into the represented Java class
|
// Define a method that calls into the represented Java class
|
||||||
local_ref<JFile::javaobject> getCacheDir() {
|
local_ref<JFile::javaobject> getCacheDir() {
|
||||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
|
static const auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
|
||||||
return method(self());
|
return method(self());
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ref<JFile::javaobject> getFilesDir() {
|
local_ref<JFile::javaobject> getFilesDir() {
|
||||||
static auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
|
static const auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
|
||||||
return method(self());
|
return method(self());
|
||||||
}
|
}
|
||||||
};
|
};
|
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
#include <fbjni/fbjni.h>
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
@@ -17,7 +17,7 @@ class JFile : public JavaClass<JFile> {
|
|||||||
|
|
||||||
// Define a method that calls into the represented Java class
|
// Define a method that calls into the represented Java class
|
||||||
std::string getAbsolutePath() {
|
std::string getAbsolutePath() {
|
||||||
static auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
|
static const auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
|
||||||
return method(self())->toStdString();
|
return method(self())->toStdString();
|
||||||
}
|
}
|
||||||
|
|
56
lib/fb/src/main/cpp/include/fbjni/JThread.h
Normal file
56
lib/fb/src/main/cpp/include/fbjni/JThread.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fbjni/fbjni.h>
|
||||||
|
#include <fbjni/NativeRunnable.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
class JThread : public JavaClass<JThread> {
|
||||||
|
public:
|
||||||
|
static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;";
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
static const auto method = javaClassStatic()->getMethod<void()>("start");
|
||||||
|
method(self());
|
||||||
|
}
|
||||||
|
|
||||||
|
void join() {
|
||||||
|
static const 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
static local_ref<JThread> create(std::function<void()>&& runnable, std::string&& name) {
|
||||||
|
auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));
|
||||||
|
return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable), make_jstring(std::move(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static local_ref<JThread> getCurrent() {
|
||||||
|
static const auto method = javaClassStatic()->getStaticMethod<local_ref<JThread>()>("currentThread");
|
||||||
|
return method(javaClassStatic());
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPriority() {
|
||||||
|
static const auto method = getClass()->getMethod<jint()>("getPriority");
|
||||||
|
return method(self());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPriority(int priority) {
|
||||||
|
static const auto method = getClass()->getMethod<void(int)>("setPriority");
|
||||||
|
method(self(), priority);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -6,9 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
#include <fbjni/fbjni.h>
|
||||||
#include "Hybrid.h"
|
|
||||||
#include "Registration.h"
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
22
lib/fb/src/main/cpp/include/fbjni/ReadableByteChannel.h
Normal file
22
lib/fb/src/main/cpp/include/fbjni/ReadableByteChannel.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fbjni/fbjni.h>
|
||||||
|
#include <fbjni/ByteBuffer.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
class JReadableByteChannel : public JavaClass<JReadableByteChannel> {
|
||||||
|
public:
|
||||||
|
static constexpr const char* kJavaDescriptor = "Ljava/nio/channels/ReadableByteChannel;";
|
||||||
|
|
||||||
|
int read(alias_ref<JByteBuffer> dest) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}
|
@@ -17,13 +17,13 @@ struct JPrimitive : JavaClass<T> {
|
|||||||
using typename JavaClass<T>::javaobject;
|
using typename JavaClass<T>::javaobject;
|
||||||
using JavaClass<T>::javaClassStatic;
|
using JavaClass<T>::javaClassStatic;
|
||||||
static local_ref<javaobject> valueOf(jprim val) {
|
static local_ref<javaobject> valueOf(jprim val) {
|
||||||
static auto cls = javaClassStatic();
|
static const auto cls = javaClassStatic();
|
||||||
static auto method =
|
static const auto method =
|
||||||
cls->template getStaticMethod<javaobject(jprim)>("valueOf");
|
cls->template getStaticMethod<javaobject(jprim)>("valueOf");
|
||||||
return method(cls, val);
|
return method(cls, val);
|
||||||
}
|
}
|
||||||
jprim value() const {
|
jprim value() const {
|
||||||
static auto method =
|
static const auto method =
|
||||||
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
|
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
|
||||||
return method(this->self());
|
return method(this->self());
|
||||||
}
|
}
|
||||||
@@ -55,9 +55,20 @@ DEFINE_BOXED_PRIMITIVE(double, Double)
|
|||||||
|
|
||||||
#undef DEFINE_BOXED_PRIMITIVE
|
#undef DEFINE_BOXED_PRIMITIVE
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
(std::is_same<T, long long>::value || std::is_same<T, int64_t>::value) && !std::is_same<T, jlong>::value,
|
||||||
|
local_ref<jobject>
|
||||||
|
>::type autobox(T val) {
|
||||||
|
return JLong::valueOf(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct JVoid : public jni::JavaClass<JVoid> {
|
||||||
|
static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
|
||||||
|
};
|
||||||
|
|
||||||
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
|
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
|
||||||
return make_local(val);
|
return make_local(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
@@ -15,9 +15,6 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
#include <fb/Environment.h>
|
|
||||||
|
|
||||||
#ifdef FBJNI_DEBUG_REFS
|
#ifdef FBJNI_DEBUG_REFS
|
||||||
# ifdef __ANDROID__
|
# ifdef __ANDROID__
|
||||||
# include <android/log.h>
|
# include <android/log.h>
|
||||||
@@ -42,11 +39,11 @@
|
|||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
FBEXPORT void throwPendingJniExceptionAsCppException();
|
void throwPendingJniExceptionAsCppException();
|
||||||
FBEXPORT void throwCppExceptionIf(bool condition);
|
void throwCppExceptionIf(bool condition);
|
||||||
|
|
||||||
[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);
|
[[noreturn]] void throwNewJavaException(jthrowable);
|
||||||
[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);
|
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* msg);
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
|
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
|
||||||
|
|
||||||
@@ -65,20 +62,10 @@ template<typename... Args>
|
|||||||
* unhelpful way (typically a segfault) while trying to handle an exception
|
* unhelpful way (typically a segfault) while trying to handle an exception
|
||||||
* which occurs later.
|
* which occurs later.
|
||||||
*/
|
*/
|
||||||
FBEXPORT jint initialize(JavaVM*, std::function<void()>&&) noexcept;
|
jint initialize(JavaVM*, std::function<void()>&&) noexcept;
|
||||||
|
|
||||||
namespace internal {
|
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
|
// Define to get extremely verbose logging of references and to enable reference stats
|
||||||
#ifdef FBJNI_DEBUG_REFS
|
#ifdef FBJNI_DEBUG_REFS
|
||||||
template<typename... Args>
|
template<typename... Args>
|
@@ -21,15 +21,15 @@ namespace jni {
|
|||||||
// jobject /////////////////////////////////////////////////////////////////////////////////////////
|
// jobject /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
|
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
|
||||||
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
|
return Environment::current()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline local_ref<JClass> JObject::getClass() const noexcept {
|
inline local_ref<JClass> JObject::getClass() const noexcept {
|
||||||
return adopt_local(internal::getEnv()->GetObjectClass(self()));
|
return adopt_local(Environment::current()->GetObjectClass(self()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
|
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
|
||||||
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
|
return Environment::current()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -47,8 +47,13 @@ inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
|
|||||||
field.set(self(), value);
|
field.set(self(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename>
|
||||||
|
inline void JObject::setFieldValue(JField<T> field, alias_ref<T> value) noexcept {
|
||||||
|
setFieldValue(field, value.get());
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string JObject::toString() const {
|
inline std::string JObject::toString() const {
|
||||||
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
|
static const auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
|
||||||
|
|
||||||
return method(self())->toStdString();
|
return method(self())->toStdString();
|
||||||
}
|
}
|
||||||
@@ -77,13 +82,13 @@ MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
|
|||||||
|
|
||||||
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
|
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
|
||||||
: owned_(object) {
|
: owned_(object) {
|
||||||
internal::getEnv()->MonitorEnter(object.get());
|
Environment::current()->MonitorEnter(object.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MonitorLock::reset() noexcept {
|
void MonitorLock::reset() noexcept {
|
||||||
if (owned_) {
|
if (owned_) {
|
||||||
internal::getEnv()->MonitorExit(owned_.get());
|
Environment::current()->MonitorExit(owned_.get());
|
||||||
if (internal::getEnv()->ExceptionCheck()) {
|
if (Environment::current()->ExceptionCheck()) {
|
||||||
abort(); // Lock mismatch
|
abort(); // Lock mismatch
|
||||||
}
|
}
|
||||||
owned_ = nullptr;
|
owned_ = nullptr;
|
||||||
@@ -126,7 +131,7 @@ namespace detail {
|
|||||||
template<typename JC, typename... Args>
|
template<typename JC, typename... Args>
|
||||||
static local_ref<JC> newInstance(Args... args) {
|
static local_ref<JC> newInstance(Args... args) {
|
||||||
static auto cls = JC::javaClassStatic();
|
static auto cls = JC::javaClassStatic();
|
||||||
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
|
static const auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
|
||||||
return cls->newObject(constructor, args...);
|
return cls->newObject(constructor, args...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,17 +159,18 @@ struct NativeMethod {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
|
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
|
||||||
return adopt_local(internal::getEnv()->GetSuperclass(self()));
|
return adopt_local(Environment::current()->GetSuperclass(self()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
|
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
|
|
||||||
JNINativeMethod jnimethods[methods.size()];
|
JNINativeMethod jnimethods[methods.size()];
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
|
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
|
||||||
jnimethods[i].name = it->name;
|
// The JNI struct members are unnecessarily non-const.
|
||||||
jnimethods[i].signature = it->descriptor.c_str();
|
jnimethods[i].name = const_cast<char*>(it->name);
|
||||||
|
jnimethods[i].signature = const_cast<char*>(it->descriptor.c_str());
|
||||||
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
|
jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,8 +179,13 @@ inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
|
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
const auto result = env->IsAssignableFrom(self(), other.get());
|
// Ths method has behavior compatible with the
|
||||||
|
// java.lang.Class#isAssignableFrom method. The order of the
|
||||||
|
// arguments to the JNI IsAssignableFrom C function is "opposite"
|
||||||
|
// from what some might expect, which makes this code look a little
|
||||||
|
// odd, but it is correct.
|
||||||
|
const auto result = env->IsAssignableFrom(other.get(), self());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +209,7 @@ template<typename F>
|
|||||||
inline JMethod<F> JClass::getMethod(
|
inline JMethod<F> JClass::getMethod(
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* descriptor) const {
|
const char* descriptor) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||||
return JMethod<F>{method};
|
return JMethod<F>{method};
|
||||||
@@ -213,7 +224,7 @@ template<typename F>
|
|||||||
inline JStaticMethod<F> JClass::getStaticMethod(
|
inline JStaticMethod<F> JClass::getStaticMethod(
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* descriptor) const {
|
const char* descriptor) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
const auto method = env->GetStaticMethodID(self(), name, descriptor);
|
const auto method = env->GetStaticMethodID(self(), name, descriptor);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||||
return JStaticMethod<F>{method};
|
return JStaticMethod<F>{method};
|
||||||
@@ -228,7 +239,7 @@ template<typename F>
|
|||||||
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
|
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* descriptor) const {
|
const char* descriptor) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
const auto method = env->GetMethodID(self(), name, descriptor);
|
const auto method = env->GetMethodID(self(), name, descriptor);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
|
||||||
return JNonvirtualMethod<F>{method};
|
return JNonvirtualMethod<F>{method};
|
||||||
@@ -244,7 +255,7 @@ template<typename T>
|
|||||||
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
|
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* descriptor) const {
|
const char* descriptor) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto field = env->GetFieldID(self(), name, descriptor);
|
auto field = env->GetFieldID(self(), name, descriptor);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||||
return JField<T>{field};
|
return JField<T>{field};
|
||||||
@@ -260,7 +271,7 @@ template<typename T>
|
|||||||
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
|
||||||
const char* name,
|
const char* name,
|
||||||
const char* descriptor) const {
|
const char* descriptor) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto field = env->GetStaticFieldID(self(), name, descriptor);
|
auto field = env->GetStaticFieldID(self(), name, descriptor);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||||
return JStaticField<T>{field};
|
return JStaticField<T>{field};
|
||||||
@@ -281,11 +292,16 @@ inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept
|
|||||||
field.set(self(), value);
|
field.set(self(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T, typename>
|
||||||
|
inline void JClass::setStaticFieldValue(JStaticField<T> field, alias_ref<T> value) noexcept {
|
||||||
|
setStaticFieldValue(field, value.get());
|
||||||
|
}
|
||||||
|
|
||||||
template<typename R, typename... Args>
|
template<typename R, typename... Args>
|
||||||
inline local_ref<R> JClass::newObject(
|
inline local_ref<R> JClass::newObject(
|
||||||
JConstructor<R(Args...)> constructor,
|
JConstructor<R(Args...)> constructor,
|
||||||
Args... args) const {
|
Args... args) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto object = env->NewObject(self(), constructor.getId(),
|
auto object = env->NewObject(self(), constructor.getId(),
|
||||||
detail::callToJni(
|
detail::callToJni(
|
||||||
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
|
||||||
@@ -338,18 +354,11 @@ struct Convert<const char*> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 //////////////////////////////////////////////////////////////////////////////////////
|
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline size_t JArray::size() const noexcept {
|
inline size_t JArray::size() const noexcept {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
return env->GetArrayLength(self());
|
return env->GetArrayLength(self());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,8 +418,8 @@ std::string JArrayClass<T>::get_instantiated_base_name() {
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
|
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
|
||||||
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
|
static const auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
|
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
|
||||||
return adopt_local(static_cast<javaobject>(rawArray));
|
return adopt_local(static_cast<javaobject>(rawArray));
|
||||||
@@ -418,13 +427,13 @@ auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
|
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
env->SetObjectArrayElement(this->self(), idx, value);
|
env->SetObjectArrayElement(this->self(), idx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
|
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
|
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
|
||||||
return adopt_local(static_cast<T>(rawElement));
|
return adopt_local(static_cast<T>(rawElement));
|
||||||
}
|
}
|
||||||
@@ -434,12 +443,16 @@ inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t in
|
|||||||
return detail::ElementProxy<JArrayClass<T>>(this, index);
|
return detail::ElementProxy<JArrayClass<T>>(this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
|
||||||
|
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
|
||||||
|
}
|
||||||
|
|
||||||
// jarray /////////////////////////////////////////////////////////////////////////////////////////
|
// jarray /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename JArrayType>
|
template <typename JArrayType>
|
||||||
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
|
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
|
||||||
-> std::unique_ptr<T[]> {
|
-> std::unique_ptr<T[]> {
|
||||||
using T = typename jtype_traits<JArrayType>::entry_type;
|
|
||||||
auto buf = std::unique_ptr<T[]>{new T[length]};
|
auto buf = std::unique_ptr<T[]>{new T[length]};
|
||||||
getRegion(start, length, buf.get());
|
getRegion(start, length, buf.get());
|
||||||
return buf;
|
return buf;
|
||||||
@@ -510,7 +523,7 @@ class PinnedCriticalAlloc {
|
|||||||
jboolean* isCopy) {
|
jboolean* isCopy) {
|
||||||
(void)start;
|
(void)start;
|
||||||
(void)length;
|
(void)length;
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
|
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
|
||||||
*size = array->size();
|
*size = array->size();
|
||||||
@@ -523,7 +536,7 @@ class PinnedCriticalAlloc {
|
|||||||
jint mode) {
|
jint mode) {
|
||||||
(void)start;
|
(void)start;
|
||||||
(void)size;
|
(void)size;
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
|
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
|
||||||
}
|
}
|
||||||
};
|
};
|
@@ -20,14 +20,19 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
class JClass;
|
class JClass;
|
||||||
class JObject;
|
class JObject;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/// Lookup a class by name. This should only be used internally.
|
||||||
|
jclass findClass(JNIEnv* env, const char* name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// Lookup a class by name. Note this functions returns an alias_ref that
|
/// Lookup a class by name. Note this functions returns an alias_ref that
|
||||||
/// points to a leaked global reference. This is appropriate for classes
|
/// 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
|
/// that are never unloaded (which is any class in an Android app and most
|
||||||
@@ -37,7 +42,7 @@ class JObject;
|
|||||||
/// in a "static auto" variable, or a static global.
|
/// in a "static auto" variable, or a static global.
|
||||||
///
|
///
|
||||||
/// @return Returns a leaked global reference to the class
|
/// @return Returns a leaked global reference to the class
|
||||||
FBEXPORT alias_ref<JClass> findClassStatic(const char* name);
|
alias_ref<JClass> findClassStatic(const char* name);
|
||||||
|
|
||||||
/// Lookup a class by name. Note this functions returns a local reference,
|
/// Lookup a class by name. Note this functions returns a local reference,
|
||||||
/// which means that it must not be stored in a static variable.
|
/// which means that it must not be stored in a static variable.
|
||||||
@@ -46,12 +51,12 @@ FBEXPORT alias_ref<JClass> findClassStatic(const char* name);
|
|||||||
/// (like caching method ids).
|
/// (like caching method ids).
|
||||||
///
|
///
|
||||||
/// @return Returns a global reference to the class
|
/// @return Returns a global reference to the class
|
||||||
FBEXPORT local_ref<JClass> findClassLocal(const char* name);
|
local_ref<JClass> findClassLocal(const char* name);
|
||||||
|
|
||||||
/// Check to see if two references refer to the same object. Comparison with nullptr
|
/// 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
|
/// returns true if and only if compared to another nullptr. A weak reference that
|
||||||
/// refers to a reclaimed object count as nullptr.
|
/// refers to a reclaimed object count as nullptr.
|
||||||
FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
|
bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
|
||||||
|
|
||||||
// Together, these classes allow convenient use of any class with the fbjni
|
// Together, these classes allow convenient use of any class with the fbjni
|
||||||
// helpers. To use:
|
// helpers. To use:
|
||||||
@@ -70,7 +75,7 @@ FBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexc
|
|||||||
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
|
||||||
//
|
//
|
||||||
// void foo() {
|
// void foo() {
|
||||||
// static auto method = javaClassStatic()->getMethod<void()>("foo");
|
// static const auto method = javaClassStatic()->getMethod<void()>("foo");
|
||||||
// method(self());
|
// method(self());
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -94,7 +99,7 @@ static local_ref<JC> newInstance(Args... args);
|
|||||||
|
|
||||||
class MonitorLock;
|
class MonitorLock;
|
||||||
|
|
||||||
class FBEXPORT JObject : detail::JObjectBase {
|
class JObject : detail::JObjectBase {
|
||||||
public:
|
public:
|
||||||
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
|
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
|
||||||
|
|
||||||
@@ -115,10 +120,12 @@ public:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
|
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
|
||||||
|
|
||||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
/// Set the value of field. Any Java type is accepted.
|
||||||
/// and raw reference types.
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void setFieldValue(JField<T> field, T value) noexcept;
|
void setFieldValue(JField<T> field, T value) noexcept;
|
||||||
|
template<typename T,
|
||||||
|
typename = typename std::enable_if<IsPlainJniReference<T>(), T>::type>
|
||||||
|
void setFieldValue(JField<T> field, alias_ref<T> value) noexcept;
|
||||||
|
|
||||||
/// Convenience method to create a std::string representing the object
|
/// Convenience method to create a std::string representing the object
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
@@ -190,7 +197,7 @@ struct JTypeFor<T, Base, void> {
|
|||||||
// jthrowable) to be used as javaobject. This should only be necessary for
|
// jthrowable) to be used as javaobject. This should only be necessary for
|
||||||
// built-in jni types and not user-defined ones.
|
// built-in jni types and not user-defined ones.
|
||||||
template <typename T, typename Base = JObject, typename JType = void>
|
template <typename T, typename Base = JObject, typename JType = void>
|
||||||
class FBEXPORT JavaClass : public Base {
|
class JavaClass : public Base {
|
||||||
using JObjType = typename detail::JTypeFor<T, Base, JType>;
|
using JObjType = typename detail::JTypeFor<T, Base, JType>;
|
||||||
public:
|
public:
|
||||||
using _javaobject = typename JObjType::_javaobject;
|
using _javaobject = typename JObjType::_javaobject;
|
||||||
@@ -218,7 +225,7 @@ protected:
|
|||||||
/// Wrapper to provide functionality to jclass references
|
/// Wrapper to provide functionality to jclass references
|
||||||
struct NativeMethod;
|
struct NativeMethod;
|
||||||
|
|
||||||
class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {
|
class JClass : public JavaClass<JClass, JObject, jclass> {
|
||||||
public:
|
public:
|
||||||
/// Java type descriptor
|
/// Java type descriptor
|
||||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
|
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
|
||||||
@@ -295,10 +302,12 @@ class FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;
|
local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;
|
||||||
|
|
||||||
/// Set the value of field. Any Java type is accepted, including the primitive types
|
/// Set the value of field. Any Java type is accepted.
|
||||||
/// and raw reference types.
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void setStaticFieldValue(JStaticField<T> field, T value) noexcept;
|
void setStaticFieldValue(JStaticField<T> field, T value) noexcept;
|
||||||
|
template<typename T,
|
||||||
|
typename = typename std::enable_if<IsPlainJniReference<T>(), T>::type>
|
||||||
|
void setStaticFieldValue(JStaticField<T> field, alias_ref<T> value) noexcept;
|
||||||
|
|
||||||
/// Allocates a new object and invokes the specified constructor
|
/// Allocates a new object and invokes the specified constructor
|
||||||
template<typename R, typename... Args>
|
template<typename R, typename... Args>
|
||||||
@@ -330,27 +339,23 @@ private:
|
|||||||
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
|
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
|
||||||
|
|
||||||
/// Wrapper to provide functionality to jstring references
|
/// Wrapper to provide functionality to jstring references
|
||||||
class FBEXPORT JString : public JavaClass<JString, JObject, jstring> {
|
class JString : public JavaClass<JString, JObject, jstring> {
|
||||||
public:
|
public:
|
||||||
/// Java type descriptor
|
/// Java type descriptor
|
||||||
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
|
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
|
||||||
|
|
||||||
/// Convenience method to convert a jstring object to a std::string
|
/// Convenience method to convert a jstring object to a std::string
|
||||||
std::string toStdString() const;
|
std::string toStdString() const;
|
||||||
|
|
||||||
|
/// Convenience method to convert a jstring object to a std::u16string
|
||||||
|
std::u16string toU16String() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a
|
/// Convenience functions to convert a const char*, std::string, or std::u16string
|
||||||
/// jstring
|
/// into a @ref local_ref to a jstring.
|
||||||
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);
|
local_ref<JString> make_jstring(const char* modifiedUtf8);
|
||||||
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
|
local_ref<JString> make_jstring(const std::string& modifiedUtf8);
|
||||||
|
local_ref<JString> make_jstring(const std::u16string& utf16);
|
||||||
/// 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 {
|
namespace detail {
|
||||||
template<typename Target>
|
template<typename Target>
|
||||||
@@ -378,7 +383,7 @@ class ElementProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {
|
class JArray : public JavaClass<JArray, JObject, jarray> {
|
||||||
public:
|
public:
|
||||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
// 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
|
// signature). Use a more derived type instead (like JArrayInt or
|
||||||
@@ -390,7 +395,7 @@ class FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {
|
|||||||
// This is used so that the JArrayClass<T> javaobject extends jni's
|
// This is used so that the JArrayClass<T> javaobject extends jni's
|
||||||
// jobjectArray. This class should not be used directly. A general Object[]
|
// jobjectArray. This class should not be used directly. A general Object[]
|
||||||
// should use JArrayClass<jobject>.
|
// should use JArrayClass<jobject>.
|
||||||
class FBEXPORT JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
|
class JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
|
||||||
// This cannot be used in a scope that derives a descriptor (like in a method
|
// This cannot be used in a scope that derives a descriptor (like in a method
|
||||||
// signature).
|
// signature).
|
||||||
static constexpr const char* kJavaDescriptor = nullptr;
|
static constexpr const char* kJavaDescriptor = nullptr;
|
||||||
@@ -440,9 +445,7 @@ template <typename T>
|
|||||||
using jtypeArray = typename JArrayClass<T>::javaobject;
|
using jtypeArray = typename JArrayClass<T>::javaobject;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
|
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref);
|
||||||
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Target>
|
template<typename Target>
|
||||||
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
|
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
|
||||||
@@ -460,7 +463,7 @@ template <typename T> class PinnedCriticalAlloc;
|
|||||||
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
|
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
|
||||||
/// the elements of the array.
|
/// the elements of the array.
|
||||||
template <typename JArrayType>
|
template <typename JArrayType>
|
||||||
class FBEXPORT JPrimitiveArray :
|
class JPrimitiveArray :
|
||||||
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
|
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
|
||||||
static_assert(is_jni_primitive_array<JArrayType>(), "");
|
static_assert(is_jni_primitive_array<JArrayType>(), "");
|
||||||
public:
|
public:
|
||||||
@@ -500,14 +503,14 @@ private:
|
|||||||
void releaseElements(T* elements, jint mode);
|
void releaseElements(T* elements, jint mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
FBEXPORT local_ref<jbooleanArray> make_boolean_array(jsize size);
|
local_ref<jbooleanArray> make_boolean_array(jsize size);
|
||||||
FBEXPORT local_ref<jbyteArray> make_byte_array(jsize size);
|
local_ref<jbyteArray> make_byte_array(jsize size);
|
||||||
FBEXPORT local_ref<jcharArray> make_char_array(jsize size);
|
local_ref<jcharArray> make_char_array(jsize size);
|
||||||
FBEXPORT local_ref<jshortArray> make_short_array(jsize size);
|
local_ref<jshortArray> make_short_array(jsize size);
|
||||||
FBEXPORT local_ref<jintArray> make_int_array(jsize size);
|
local_ref<jintArray> make_int_array(jsize size);
|
||||||
FBEXPORT local_ref<jlongArray> make_long_array(jsize size);
|
local_ref<jlongArray> make_long_array(jsize size);
|
||||||
FBEXPORT local_ref<jfloatArray> make_float_array(jsize size);
|
local_ref<jfloatArray> make_float_array(jsize size);
|
||||||
FBEXPORT local_ref<jdoubleArray> make_double_array(jsize size);
|
local_ref<jdoubleArray> make_double_array(jsize size);
|
||||||
|
|
||||||
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
|
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
|
||||||
using JArrayByte = JPrimitiveArray<jbyteArray>;
|
using JArrayByte = JPrimitiveArray<jbyteArray>;
|
||||||
@@ -569,6 +572,29 @@ class PinnedPrimitiveArray {
|
|||||||
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
|
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct JStackTraceElement : JavaClass<JStackTraceElement> {
|
||||||
|
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
|
||||||
|
|
||||||
|
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
|
||||||
|
|
||||||
|
std::string getClassName() const;
|
||||||
|
std::string getMethodName() const;
|
||||||
|
std::string getFileName() const;
|
||||||
|
int getLineNumber() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Wrapper to provide functionality to jthrowable references
|
||||||
|
class JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
|
||||||
|
public:
|
||||||
|
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
|
||||||
|
|
||||||
|
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
|
||||||
|
|
||||||
|
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
|
||||||
|
local_ref<JStackTrace> getStackTrace();
|
||||||
|
void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);
|
||||||
|
};
|
||||||
|
|
||||||
#pragma push_macro("PlainJniRefMap")
|
#pragma push_macro("PlainJniRefMap")
|
||||||
#undef PlainJniRefMap
|
#undef PlainJniRefMap
|
||||||
#define PlainJniRefMap(rtype, jtype) \
|
#define PlainJniRefMap(rtype, jtype) \
|
@@ -9,24 +9,69 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
// Keeps a thread-local reference to the current thread's JNIEnv.
|
// Keeps a thread-local reference to the current thread's JNIEnv.
|
||||||
struct Environment {
|
struct Environment {
|
||||||
// May be null if this thread isn't attached to the JVM
|
// Throws a std::runtime_error if this thread isn't attached to the JVM
|
||||||
FBEXPORT static JNIEnv* current();
|
// TODO(T6594868) Benchmark against raw JNI access
|
||||||
|
static JNIEnv* current();
|
||||||
static void initialize(JavaVM* vm);
|
static void initialize(JavaVM* vm);
|
||||||
|
|
||||||
// There are subtle issues with calling the next functions directly. It is
|
// There are subtle issues with calling the next functions directly. It is
|
||||||
// much better to always use a ThreadScope to manage attaching/detaching for
|
// much better to always use a ThreadScope to manage attaching/detaching for
|
||||||
// you.
|
// you.
|
||||||
FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached();
|
static JNIEnv* ensureCurrentThreadIsAttached();
|
||||||
FBEXPORT static void detachCurrentThread();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// This will return null the thread isn't attached to the VM, or if
|
||||||
|
// fbjni has never been initialized with a VM at all. You probably
|
||||||
|
// shouldn't be using this.
|
||||||
|
JNIEnv* currentOrNull();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there's thread-local data, it's a pointer to one of these. The
|
||||||
|
* instance is a member of JniEnvCacher or ThreadScope, and lives on
|
||||||
|
* the stack.
|
||||||
|
*/
|
||||||
|
struct TLData {
|
||||||
|
// This is modified only by JniEnvCacher, and is guaranteed to be
|
||||||
|
// valid if set, and refer to an env which originated from a JNI
|
||||||
|
// call into C++.
|
||||||
|
JNIEnv* env;
|
||||||
|
// This is modified only by ThreadScope, and is set only if an
|
||||||
|
// instance of ThreadScope which attached is on the stack.
|
||||||
|
bool attached;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAII object which manages a cached JNIEnv* value. A Value is only
|
||||||
|
* cached if it is guaranteed safe, which means when C++ is called
|
||||||
|
* from a registered fbjni function.
|
||||||
|
*/
|
||||||
|
class JniEnvCacher {
|
||||||
|
public:
|
||||||
|
JniEnvCacher(JNIEnv* env);
|
||||||
|
JniEnvCacher(JniEnvCacher&) = delete;
|
||||||
|
JniEnvCacher(JniEnvCacher&&) = default;
|
||||||
|
JniEnvCacher& operator=(JniEnvCacher&) = delete;
|
||||||
|
JniEnvCacher& operator=(JniEnvCacher&&) = delete;
|
||||||
|
~JniEnvCacher();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// If this flag is set, then, this object needs to clear the cache.
|
||||||
|
bool thisCached_;
|
||||||
|
|
||||||
|
// The thread local pointer may point here.
|
||||||
|
detail::TLData data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it
|
* 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
|
* exits will cause a crash, as will calling Detach an extra time, and this guard class helps
|
||||||
@@ -48,8 +93,11 @@ struct Environment {
|
|||||||
* class or instance to the new thread; this bypasses the need for the class loader.
|
* 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)
|
* (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.
|
* If you need access to the application's classes, you can use ThreadScope::WithClassLoader.
|
||||||
|
* - If fbjni has never been initialized, there will be no JavaVM object to attach with.
|
||||||
|
* In that case, a std::runtime_error will be thrown. This is only likely to happen in a
|
||||||
|
* standalone C++ application, or if Environment::initialize is not used.
|
||||||
*/
|
*/
|
||||||
class FBEXPORT ThreadScope {
|
class ThreadScope {
|
||||||
public:
|
public:
|
||||||
ThreadScope();
|
ThreadScope();
|
||||||
ThreadScope(ThreadScope&) = delete;
|
ThreadScope(ThreadScope&) = delete;
|
||||||
@@ -67,8 +115,14 @@ class FBEXPORT ThreadScope {
|
|||||||
static void WithClassLoader(std::function<void()>&& runnable);
|
static void WithClassLoader(std::function<void()>&& runnable);
|
||||||
|
|
||||||
static void OnLoad();
|
static void OnLoad();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool attachedWithThisScope_;
|
// If this flag is set, then this object needs to detach.
|
||||||
|
bool thisAttached_;
|
||||||
|
|
||||||
|
// The thread local pointer may point here.
|
||||||
|
detail::TLData data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -23,12 +23,15 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "References.h"
|
#include "References.h"
|
||||||
#include "CoreClasses.h"
|
#include "CoreClasses.h"
|
||||||
|
|
||||||
|
#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)
|
||||||
|
// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.
|
||||||
|
#define FBJNI_NO_EXCEPTION_PTR
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
@@ -57,10 +60,10 @@ class JCppException : public JavaClass<JCppException, JThrowable> {
|
|||||||
*
|
*
|
||||||
* Note: the what() method of this class is not thread-safe (t6900503).
|
* Note: the what() method of this class is not thread-safe (t6900503).
|
||||||
*/
|
*/
|
||||||
class FBEXPORT JniException : public std::exception {
|
class JniException : public std::exception {
|
||||||
public:
|
public:
|
||||||
JniException();
|
JniException();
|
||||||
~JniException();
|
~JniException() override;
|
||||||
|
|
||||||
explicit JniException(alias_ref<jthrowable> throwable);
|
explicit JniException(alias_ref<jthrowable> throwable);
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ class FBEXPORT JniException : public std::exception {
|
|||||||
|
|
||||||
local_ref<JThrowable> getThrowable() const noexcept;
|
local_ref<JThrowable> getThrowable() const noexcept;
|
||||||
|
|
||||||
virtual const char* what() const noexcept;
|
const char* what() const noexcept override;
|
||||||
|
|
||||||
void setJavaException() const noexcept;
|
void setJavaException() const noexcept;
|
||||||
|
|
||||||
@@ -105,11 +108,23 @@ template<typename... Args>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
|
// 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.
|
// be thrown, it aborts the program.
|
||||||
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept;
|
void translatePendingCppExceptionToJavaException();
|
||||||
|
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The stack returned may include build ids. It may be beneficial to
|
||||||
|
* call lyra::setLibraryIdentifierFunction before calling this if
|
||||||
|
* build ids are desirable.
|
||||||
|
*/
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppBackTrace();
|
||||||
|
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
|
||||||
|
|
||||||
// For convenience, some exception names in java.lang are available here.
|
// For convenience, some exception names in java.lang are available here.
|
||||||
|
|
||||||
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
|
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
|
||||||
|
|
||||||
}}
|
}}
|
@@ -9,9 +9,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <fb/assert.h>
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
#include "CoreClasses.h"
|
#include "CoreClasses.h"
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
@@ -24,13 +21,45 @@ public:
|
|||||||
virtual ~BaseHybridClass() {}
|
virtual ~BaseHybridClass() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FBEXPORT HybridData : public JavaClass<HybridData> {
|
struct HybridData : public JavaClass<HybridData> {
|
||||||
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
|
||||||
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
|
|
||||||
BaseHybridClass* getNativePointer();
|
|
||||||
static local_ref<HybridData> create();
|
static local_ref<HybridData> create();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HybridDestructor : public JavaClass<HybridDestructor> {
|
||||||
|
public:
|
||||||
|
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
|
||||||
|
|
||||||
|
detail::BaseHybridClass* getNativePointer();
|
||||||
|
|
||||||
|
void setNativePointer(std::unique_ptr<detail::BaseHybridClass> new_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
detail::BaseHybridClass* getNativePointer(T t) {
|
||||||
|
return getHolder(t)->getNativePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
|
||||||
|
getHolder(t)->setNativePointer(std::move(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
local_ref<HybridDestructor> getHolder(T t) {
|
||||||
|
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
|
||||||
|
return t->getFieldValue(holderField);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JavaClass for HybridClassBase
|
||||||
|
struct HybridClassBase : public JavaClass<HybridClassBase> {
|
||||||
|
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;";
|
||||||
|
|
||||||
|
static bool isHybridClassBase(alias_ref<jclass> jclass) {
|
||||||
|
return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Base, typename Enabled = void>
|
template <typename Base, typename Enabled = void>
|
||||||
struct HybridTraits {
|
struct HybridTraits {
|
||||||
// This static assert should actually always fail if we don't use one of the
|
// This static assert should actually always fail if we don't use one of the
|
||||||
@@ -65,7 +94,7 @@ struct HybridTraits<
|
|||||||
|
|
||||||
// convert to HybridClass* from jhybridobject
|
// convert to HybridClass* from jhybridobject
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct FBEXPORT Convert<
|
struct Convert<
|
||||||
T, typename std::enable_if<
|
T, typename std::enable_if<
|
||||||
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
|
||||||
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
|
||||||
@@ -90,7 +119,7 @@ struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Base = detail::BaseHybridClass>
|
template <typename T, typename Base = detail::BaseHybridClass>
|
||||||
class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
class HybridClass : public detail::HybridTraits<Base>::CxxBase {
|
||||||
public:
|
public:
|
||||||
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
|
||||||
// At this point, T is incomplete, and so we cannot access
|
// At this point, T is incomplete, and so we cannot access
|
||||||
@@ -107,6 +136,7 @@ public:
|
|||||||
T* cthis();
|
T* cthis();
|
||||||
|
|
||||||
friend class HybridClass;
|
friend class HybridClass;
|
||||||
|
friend T;
|
||||||
};
|
};
|
||||||
|
|
||||||
using jhybridobject = typename JavaPart::javaobject;
|
using jhybridobject = typename JavaPart::javaobject;
|
||||||
@@ -136,7 +166,7 @@ protected:
|
|||||||
|
|
||||||
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
|
||||||
auto hybridData = detail::HybridData::create();
|
auto hybridData = detail::HybridData::create();
|
||||||
hybridData->setNativePointer(std::move(cxxPart));
|
setNativePointer(hybridData, std::move(cxxPart));
|
||||||
return hybridData;
|
return hybridData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +175,11 @@ protected:
|
|||||||
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
|
||||||
|
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Factory method for creating a hybrid object where the arguments
|
// Factory method for creating a hybrid object where the arguments
|
||||||
// are used to initialize the C++ part directly without passing them
|
// are used to initialize the C++ part directly without passing them
|
||||||
@@ -158,8 +193,20 @@ public:
|
|||||||
// C++ object fails, or any JNI methods throw.
|
// C++ object fails, or any JNI methods throw.
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
|
||||||
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
|
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
|
||||||
return JavaPart::newInstance(hybridData);
|
auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
|
||||||
|
local_ref<JavaPart> result;
|
||||||
|
if (isHybrid) {
|
||||||
|
result = JavaPart::newInstance();
|
||||||
|
setNativePointer(result, std::move(cxxPart));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto hybridData = makeHybridData(std::move(cxxPart));
|
||||||
|
result = JavaPart::newInstance(hybridData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO? Create reusable interface for Allocatable classes and use it to
|
// TODO? Create reusable interface for Allocatable classes and use it to
|
||||||
@@ -194,17 +241,23 @@ public:
|
|||||||
|
|
||||||
template <typename T, typename B>
|
template <typename T, typename B>
|
||||||
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
inline T* HybridClass<T, B>::JavaPart::cthis() {
|
||||||
|
detail::BaseHybridClass* result = 0;
|
||||||
|
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
|
||||||
|
if (isHybrid) {
|
||||||
|
result = getNativePointer(this);
|
||||||
|
} else {
|
||||||
static auto field =
|
static auto field =
|
||||||
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
|
||||||
auto hybridData = this->getFieldValue(field);
|
auto hybridData = this->getFieldValue(field);
|
||||||
if (!hybridData) {
|
if (!hybridData) {
|
||||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = getNativePointer(hybridData);
|
||||||
|
}
|
||||||
|
|
||||||
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
|
||||||
T* value = static_cast<T*>(hybridData->getNativePointer());
|
return static_cast<T*>(result);
|
||||||
// This would require some serious programmer error.
|
|
||||||
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename B>
|
template <typename T, typename B>
|
@@ -32,7 +32,7 @@ struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
|
|||||||
value_type next() {
|
value_type next() {
|
||||||
static auto elementField =
|
static auto elementField =
|
||||||
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
|
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
|
||||||
return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField));
|
return dynamic_ref_cast<JniType<E>>(JavaBase_::getFieldValue(elementField));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset(value_type& v) {
|
static void reset(value_type& v) {
|
39
lib/fb/src/main/cpp/include/fbjni/detail/JWeakReference.h
Normal file
39
lib/fb/src/main/cpp/include/fbjni/detail/JWeakReference.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreClasses.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.
|
||||||
|
* A WeakGlobalRef can yield a strong reference even after the object has been
|
||||||
|
* finalized. See comment in the djinni library.
|
||||||
|
* https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp
|
||||||
|
*/
|
||||||
|
template<typename T = jobject>
|
||||||
|
class JWeakReference : public JavaClass<JWeakReference<T>> {
|
||||||
|
|
||||||
|
typedef JavaClass<JWeakReference<T>> JavaBase_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;";
|
||||||
|
|
||||||
|
static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {
|
||||||
|
return JavaBase_::newInstance(static_ref_cast<jobject>(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref<T> get() const {
|
||||||
|
static const auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>("get");
|
||||||
|
return static_ref_cast<T>(method(JavaBase_::self()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
67
lib/fb/src/main/cpp/include/fbjni/detail/Log.h
Normal file
67
lib/fb/src/main/cpp/include/fbjni/detail/Log.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
/** @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 jni {
|
||||||
|
namespace log_ {
|
||||||
|
// the weird name of this namespace is to avoid a conflict with the
|
||||||
|
// function named log.
|
||||||
|
|
||||||
|
inline void loge(const char* tag, const char* msg) noexcept {
|
||||||
|
__android_log_write(ANDROID_LOG_ERROR, tag, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
inline void loge(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, tag, msg, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void logf(const char* tag, const char* msg) noexcept {
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL, tag, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
inline void logf(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||||
|
__android_log_print(ANDROID_LOG_FATAL, tag, msg, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
[[noreturn]]
|
||||||
|
inline void logassert(const char* tag, const char* msg, ARGS... args) noexcept {
|
||||||
|
__android_log_assert(0, tag, msg, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LOG_TAG
|
||||||
|
# define FBJNI_LOGE(...) ::facebook::jni::log_::loge(LOG_TAG, __VA_ARGS__)
|
||||||
|
# define FBJNI_LOGF(...) ::facebook::jni::log_::logf(LOG_TAG, __VA_ARGS__)
|
||||||
|
# define FBJNI_ASSERT(cond) do { if (!(cond)) ::facebook::jni::log_::logassert(LOG_TAG, "%s", #cond); } while(0)
|
||||||
|
#else
|
||||||
|
# define FBJNI_LOGE(...) ::facebook::jni::log_::loge("log", __VA_ARGS__)
|
||||||
|
# define FBJNI_LOGF(...) ::facebook::jni::log_::logf("log", __VA_ARGS__)
|
||||||
|
# define FBJNI_ASSERT(cond) do { if (!(cond)) ::facebook::jni::log_::logassert("log", "%s", #cond); } while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}}}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
# define FBJNI_LOGE(...) ((void)0)
|
||||||
|
# define FBJNI_LOGF(...) (abort())
|
||||||
|
# define FBJNI_ASSERT(cond) ((void)0)
|
||||||
|
#endif
|
@@ -14,10 +14,6 @@
|
|||||||
#include "References.h"
|
#include "References.h"
|
||||||
#include "Boxed.h"
|
#include "Boxed.h"
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
#include <sys/system_properties.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
@@ -63,31 +59,10 @@ local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
|
|||||||
return arr;
|
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>
|
template<typename... Args>
|
||||||
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
|
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) const {
|
||||||
const auto env = Environment::current();
|
const auto env = Environment::current();
|
||||||
env->CallVoidMethod(
|
env->CallVoidMethod(
|
||||||
self.get(),
|
self.get(),
|
||||||
@@ -98,10 +73,10 @@ inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args...
|
|||||||
|
|
||||||
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
|
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
|
||||||
#undef DEFINE_PRIMITIVE_CALL
|
#undef DEFINE_PRIMITIVE_CALL
|
||||||
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \
|
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
|
||||||
template<typename... Args> \
|
template<typename... Args> \
|
||||||
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \
|
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) const { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
auto result = env->Call ## METHOD ## Method( \
|
auto result = env->Call ## METHOD ## Method( \
|
||||||
self.get(), \
|
self.get(), \
|
||||||
getId(), \
|
getId(), \
|
||||||
@@ -110,14 +85,14 @@ inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args...
|
|||||||
return result; \
|
return result; \
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean)
|
DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
|
||||||
DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte)
|
DEFINE_PRIMITIVE_CALL(jbyte, Byte)
|
||||||
DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter)
|
DEFINE_PRIMITIVE_CALL(jchar, Char)
|
||||||
DEFINE_PRIMITIVE_CALL(jshort, Short, JShort)
|
DEFINE_PRIMITIVE_CALL(jshort, Short)
|
||||||
DEFINE_PRIMITIVE_CALL(jint, Int, JInteger)
|
DEFINE_PRIMITIVE_CALL(jint, Int)
|
||||||
DEFINE_PRIMITIVE_CALL(jlong, Long, JLong)
|
DEFINE_PRIMITIVE_CALL(jlong, Long)
|
||||||
DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat)
|
DEFINE_PRIMITIVE_CALL(jfloat, Float)
|
||||||
DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble)
|
DEFINE_PRIMITIVE_CALL(jdouble, Double)
|
||||||
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
|
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
|
||||||
|
|
||||||
/// JMethod specialization for references that wraps the return value in a @ref local_ref
|
/// JMethod specialization for references that wraps the return value in a @ref local_ref
|
||||||
@@ -132,13 +107,13 @@ class JMethod<R(Args...)> : public JMethodBase {
|
|||||||
JMethod(const JMethod& other) noexcept = default;
|
JMethod(const JMethod& other) noexcept = default;
|
||||||
|
|
||||||
/// Invoke a method and return a local reference wrapping the result
|
/// Invoke a method and return a local reference wrapping the result
|
||||||
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args);
|
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args) const;
|
||||||
|
|
||||||
friend class JClass;
|
friend class JClass;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename R, typename... Args>
|
template<typename R, typename... Args>
|
||||||
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) -> local_ref<JniRet> {
|
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) const -> local_ref<JniRet> {
|
||||||
const auto env = Environment::current();
|
const auto env = Environment::current();
|
||||||
auto result = env->CallObjectMethod(
|
auto result = env->CallObjectMethod(
|
||||||
self.get(),
|
self.get(),
|
||||||
@@ -149,8 +124,8 @@ inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... arg
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
|
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
env->CallStaticVoidMethod(
|
env->CallStaticVoidMethod(
|
||||||
cls.get(),
|
cls.get(),
|
||||||
getId(),
|
getId(),
|
||||||
@@ -162,8 +137,8 @@ inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args
|
|||||||
#undef DEFINE_PRIMITIVE_STATIC_CALL
|
#undef DEFINE_PRIMITIVE_STATIC_CALL
|
||||||
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
|
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
|
||||||
template<typename... Args> \
|
template<typename... Args> \
|
||||||
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \
|
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
auto result = env->CallStatic ## METHOD ## Method( \
|
auto result = env->CallStatic ## METHOD ## Method( \
|
||||||
cls.get(), \
|
cls.get(), \
|
||||||
getId(), \
|
getId(), \
|
||||||
@@ -194,8 +169,8 @@ class JStaticMethod<R(Args...)> : public JMethodBase {
|
|||||||
JStaticMethod(const JStaticMethod& other) noexcept = default;
|
JStaticMethod(const JStaticMethod& other) noexcept = default;
|
||||||
|
|
||||||
/// Invoke a method and return a local reference wrapping the result
|
/// Invoke a method and return a local reference wrapping the result
|
||||||
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) {
|
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto result = env->CallStaticObjectMethod(
|
auto result = env->CallStaticObjectMethod(
|
||||||
cls.get(),
|
cls.get(),
|
||||||
getId(),
|
getId(),
|
||||||
@@ -209,8 +184,8 @@ class JStaticMethod<R(Args...)> : public JMethodBase {
|
|||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
inline void
|
inline void
|
||||||
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) {
|
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
env->CallNonvirtualVoidMethod(
|
env->CallNonvirtualVoidMethod(
|
||||||
self.get(),
|
self.get(),
|
||||||
cls.get(),
|
cls.get(),
|
||||||
@@ -224,8 +199,8 @@ JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<
|
|||||||
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
|
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
|
||||||
template<typename... Args> \
|
template<typename... Args> \
|
||||||
inline TYPE \
|
inline TYPE \
|
||||||
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \
|
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
auto result = env->CallNonvirtual ## METHOD ## Method( \
|
auto result = env->CallNonvirtual ## METHOD ## Method( \
|
||||||
self.get(), \
|
self.get(), \
|
||||||
cls.get(), \
|
cls.get(), \
|
||||||
@@ -256,8 +231,8 @@ class JNonvirtualMethod<R(Args...)> : public JMethodBase {
|
|||||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
|
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
|
||||||
|
|
||||||
/// Invoke a method and return a local reference wrapping the result
|
/// 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){
|
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto result = env->CallNonvirtualObjectMethod(
|
auto result = env->CallNonvirtualObjectMethod(
|
||||||
self.get(),
|
self.get(),
|
||||||
cls.get(),
|
cls.get(),
|
||||||
@@ -306,13 +281,13 @@ inline jfieldID JField<T>::getId() const noexcept {
|
|||||||
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||||
template<> \
|
template<> \
|
||||||
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
|
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
return env->Get ## METHOD ## Field(object, field_id_); \
|
return env->Get ## METHOD ## Field(object, field_id_); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
|
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
env->Set ## METHOD ## Field(object, field_id_, value); \
|
env->Set ## METHOD ## Field(object, field_id_, value); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,12 +303,12 @@ DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T JField<T>::get(jobject object) const noexcept {
|
inline T JField<T>::get(jobject object) const noexcept {
|
||||||
return static_cast<T>(internal::getEnv()->GetObjectField(object, field_id_));
|
return static_cast<T>(Environment::current()->GetObjectField(object, field_id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void JField<T>::set(jobject object, T value) noexcept {
|
inline void JField<T>::set(jobject object, T value) noexcept {
|
||||||
internal::getEnv()->SetObjectField(object, field_id_, static_cast<jobject>(value));
|
Environment::current()->SetObjectField(object, field_id_, static_cast<jobject>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
|
// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -358,13 +333,13 @@ inline jfieldID JStaticField<T>::getId() const noexcept {
|
|||||||
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
|
||||||
template<> \
|
template<> \
|
||||||
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
|
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
|
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
|
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
|
||||||
const auto env = internal::getEnv(); \
|
const auto env = Environment::current(); \
|
||||||
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
|
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,13 +355,13 @@ DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T JStaticField<T>::get(jclass jcls) const noexcept {
|
inline T JStaticField<T>::get(jclass jcls) const noexcept {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
|
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
|
inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
|
||||||
internal::getEnv()->SetStaticObjectField(jcls, field_id_, value);
|
Environment::current()->SetStaticObjectField(jcls, field_id_, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@@ -80,7 +80,7 @@ class JMethod<TYPE(Args...)> : public JMethodBase {
|
|||||||
JMethod() noexcept {}; \
|
JMethod() noexcept {}; \
|
||||||
JMethod(const JMethod& other) noexcept = default; \
|
JMethod(const JMethod& other) noexcept = default; \
|
||||||
\
|
\
|
||||||
TYPE operator()(alias_ref<jobject> self, Args... args); \
|
TYPE operator()(alias_ref<jobject> self, Args... args) const; \
|
||||||
\
|
\
|
||||||
friend class JClass; \
|
friend class JClass; \
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
|
|||||||
JStaticMethod() noexcept {}; \
|
JStaticMethod() noexcept {}; \
|
||||||
JStaticMethod(const JStaticMethod& other) noexcept = default; \
|
JStaticMethod(const JStaticMethod& other) noexcept = default; \
|
||||||
\
|
\
|
||||||
TYPE operator()(alias_ref<jclass> cls, Args... args); \
|
TYPE operator()(alias_ref<jclass> cls, Args... args) const; \
|
||||||
\
|
\
|
||||||
friend class JClass; \
|
friend class JClass; \
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
|
|||||||
JNonvirtualMethod() noexcept {}; \
|
JNonvirtualMethod() noexcept {}; \
|
||||||
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
|
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
|
||||||
\
|
\
|
||||||
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args); \
|
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const; \
|
||||||
\
|
\
|
||||||
friend class JClass; \
|
friend class JClass; \
|
||||||
}
|
}
|
@@ -66,6 +66,25 @@ struct Convert<bool> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sometimes (64-bit Android) jlong is "long long", but int64_t is "long".
|
||||||
|
// Allow int64_t to work as jlong.
|
||||||
|
template<typename T>
|
||||||
|
struct Convert<T,
|
||||||
|
typename std::enable_if<
|
||||||
|
(std::is_same<T, long long>::value || std::is_same<T, int64_t>::value) && !std::is_same<T, jlong>::value
|
||||||
|
>::type> {
|
||||||
|
typedef jlong jniType;
|
||||||
|
static T fromJni(jniType t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static jniType toJniRet(T t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static jniType toCall(T t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// convert to alias_ref<T> from T
|
// convert to alias_ref<T> from T
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Convert<alias_ref<T>> {
|
struct Convert<alias_ref<T>> {
|
||||||
@@ -99,7 +118,21 @@ template <typename T>
|
|||||||
struct Convert<global_ref<T>> {
|
struct Convert<global_ref<T>> {
|
||||||
typedef JniType<T> jniType;
|
typedef JniType<T> jniType;
|
||||||
// No automatic synthesis of global_ref
|
// No automatic synthesis of global_ref
|
||||||
static jniType toJniRet(global_ref<jniType> t) {
|
static jniType toJniRet(global_ref<jniType>&& t) {
|
||||||
|
// If this gets called, ownership the global_ref was passed in here. (It's
|
||||||
|
// probably a copy of a persistent global_ref made when a function was
|
||||||
|
// declared to return a global_ref, but it could moved out or otherwise not
|
||||||
|
// referenced elsewhere. Doesn't matter.) Either way, the only safe way
|
||||||
|
// to return it is to make a local_ref, release it, and return the
|
||||||
|
// underlying local jobject.
|
||||||
|
auto ret = make_local(t);
|
||||||
|
return ret.release();
|
||||||
|
}
|
||||||
|
static jniType toJniRet(const global_ref<jniType>& t) {
|
||||||
|
// If this gets called, the function was declared to return const&. We
|
||||||
|
// have a ref to a global_ref whose lifetime will exceed this call, so we
|
||||||
|
// can just get the underlying jobject and return it to java without
|
||||||
|
// needing to make a local_ref.
|
||||||
return t.get();
|
return t.get();
|
||||||
}
|
}
|
||||||
static jniType toCall(global_ref<jniType> t) {
|
static jniType toCall(global_ref<jniType> t) {
|
@@ -10,6 +10,8 @@
|
|||||||
#include <new>
|
#include <new>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "Environment.h"
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
@@ -18,7 +20,8 @@ namespace internal {
|
|||||||
|
|
||||||
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
|
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
|
||||||
struct ReferenceStats {
|
struct ReferenceStats {
|
||||||
std::atomic_uint locals_deleted, globals_deleted, weaks_deleted;
|
std::atomic_uint locals_created, globals_created, weaks_created,
|
||||||
|
locals_deleted, globals_deleted, weaks_deleted;
|
||||||
|
|
||||||
void reset() noexcept;
|
void reset() noexcept;
|
||||||
};
|
};
|
||||||
@@ -32,7 +35,10 @@ extern ReferenceStats g_reference_stats;
|
|||||||
|
|
||||||
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
|
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
|
||||||
internal::dbglog("Local new: %p", original);
|
internal::dbglog("Local new: %p", original);
|
||||||
auto ref = internal::getEnv()->NewLocalRef(original);
|
#ifdef FBJNI_DEBUG_REFS
|
||||||
|
++internal::g_reference_stats.locals_created;
|
||||||
|
#endif
|
||||||
|
auto ref = Environment::current()->NewLocalRef(original);
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -45,15 +51,12 @@ inline void LocalReferenceAllocator::deleteReference(jobject reference) const no
|
|||||||
++internal::g_reference_stats.locals_deleted;
|
++internal::g_reference_stats.locals_deleted;
|
||||||
#endif
|
#endif
|
||||||
assert(verifyReference(reference));
|
assert(verifyReference(reference));
|
||||||
internal::getEnv()->DeleteLocalRef(reference);
|
Environment::current()->DeleteLocalRef(reference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
return isObjectRefType(reference, JNILocalRefType);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +64,10 @@ inline bool LocalReferenceAllocator::verifyReference(jobject reference) const no
|
|||||||
|
|
||||||
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
|
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
|
||||||
internal::dbglog("Global new: %p", original);
|
internal::dbglog("Global new: %p", original);
|
||||||
auto ref = internal::getEnv()->NewGlobalRef(original);
|
#ifdef FBJNI_DEBUG_REFS
|
||||||
|
++internal::g_reference_stats.globals_created;
|
||||||
|
#endif
|
||||||
|
auto ref = Environment::current()->NewGlobalRef(original);
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -74,15 +80,12 @@ inline void GlobalReferenceAllocator::deleteReference(jobject reference) const n
|
|||||||
++internal::g_reference_stats.globals_deleted;
|
++internal::g_reference_stats.globals_deleted;
|
||||||
#endif
|
#endif
|
||||||
assert(verifyReference(reference));
|
assert(verifyReference(reference));
|
||||||
internal::getEnv()->DeleteGlobalRef(reference);
|
Environment::current()->DeleteGlobalRef(reference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
return isObjectRefType(reference, JNIGlobalRefType);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -90,7 +93,10 @@ inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const n
|
|||||||
|
|
||||||
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
|
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
|
||||||
internal::dbglog("Weak global new: %p", original);
|
internal::dbglog("Weak global new: %p", original);
|
||||||
auto ref = internal::getEnv()->NewWeakGlobalRef(original);
|
#ifdef FBJNI_DEBUG_REFS
|
||||||
|
++internal::g_reference_stats.weaks_created;
|
||||||
|
#endif
|
||||||
|
auto ref = Environment::current()->NewWeakGlobalRef(original);
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -103,15 +109,12 @@ inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) con
|
|||||||
++internal::g_reference_stats.weaks_deleted;
|
++internal::g_reference_stats.weaks_deleted;
|
||||||
#endif
|
#endif
|
||||||
assert(verifyReference(reference));
|
assert(verifyReference(reference));
|
||||||
internal::getEnv()->DeleteWeakGlobalRef(reference);
|
Environment::current()->DeleteWeakGlobalRef(reference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
|
||||||
if (!reference || !internal::doesGetObjectRefTypeWork()) {
|
return isObjectRefType(reference, JNIWeakGlobalRefType);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
@@ -13,14 +13,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
namespace facebook { namespace jni {
|
namespace facebook { namespace jni {
|
||||||
|
|
||||||
/// Allocator that handles local references
|
/// Allocator that handles local references
|
||||||
class FBEXPORT LocalReferenceAllocator {
|
class LocalReferenceAllocator {
|
||||||
public:
|
public:
|
||||||
jobject newReference(jobject original) const;
|
jobject newReference(jobject original) const;
|
||||||
void deleteReference(jobject reference) const noexcept;
|
void deleteReference(jobject reference) const noexcept;
|
||||||
@@ -28,7 +26,7 @@ class FBEXPORT LocalReferenceAllocator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Allocator that handles global references
|
/// Allocator that handles global references
|
||||||
class FBEXPORT GlobalReferenceAllocator {
|
class GlobalReferenceAllocator {
|
||||||
public:
|
public:
|
||||||
jobject newReference(jobject original) const;
|
jobject newReference(jobject original) const;
|
||||||
void deleteReference(jobject reference) const noexcept;
|
void deleteReference(jobject reference) const noexcept;
|
||||||
@@ -36,23 +34,20 @@ class FBEXPORT GlobalReferenceAllocator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Allocator that handles weak global references
|
/// Allocator that handles weak global references
|
||||||
class FBEXPORT WeakGlobalReferenceAllocator {
|
class WeakGlobalReferenceAllocator {
|
||||||
public:
|
public:
|
||||||
jobject newReference(jobject original) const;
|
jobject newReference(jobject original) const;
|
||||||
void deleteReference(jobject reference) const noexcept;
|
void deleteReference(jobject reference) const noexcept;
|
||||||
bool verifyReference(jobject reference) const noexcept;
|
bool verifyReference(jobject reference) const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @cond INTERNAL
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true iff env->GetObjectRefType is expected to work properly.
|
* @return Helper based on GetObjectRefType. Since this isn't defined
|
||||||
|
* on all versions of Java or Android, if the type can't be
|
||||||
|
* determined, this returns true. If reference is nullptr, returns
|
||||||
|
* true.
|
||||||
*/
|
*/
|
||||||
FBEXPORT bool doesGetObjectRefTypeWork();
|
bool isObjectRefType(jobject reference, jobjectRefType refType);
|
||||||
|
|
||||||
}
|
|
||||||
/// @endcond
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
@@ -173,6 +173,29 @@ operator!=(const T1& a, const T2& b) {
|
|||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
inline enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator==(const T1& a, std::nullptr_t) {
|
||||||
|
return getPlainJniReference(a) == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
inline enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator==(std::nullptr_t, const T1& a) {
|
||||||
|
return nullptr == getPlainJniReference(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
inline enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator!=(const T1& a, std::nullptr_t) {
|
||||||
|
return !(a == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
inline enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator!=(std::nullptr_t, const T1& a) {
|
||||||
|
return !(nullptr == getPlainJniReference(a));
|
||||||
|
}
|
||||||
|
|
||||||
// base_owned_ref ///////////////////////////////////////////////////////////////////////
|
// base_owned_ref ///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -181,10 +204,11 @@ inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
|
|||||||
: base_owned_ref(nullptr)
|
: base_owned_ref(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <typename T, typename Alloc>
|
template<typename T, typename Alloc>
|
||||||
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t array) noexcept
|
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
|
||||||
: base_owned_ref(static_cast<javaobject>(nullptr)) {
|
: base_owned_ref(static_cast<javaobject>(nullptr))
|
||||||
(void)array;
|
{
|
||||||
|
(void)t;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Alloc>
|
template<typename T, typename Alloc>
|
||||||
@@ -484,22 +508,25 @@ template<typename T, typename RefType>
|
|||||||
auto dynamic_ref_cast(const RefType& ref) ->
|
auto dynamic_ref_cast(const RefType& ref) ->
|
||||||
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
|
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
|
||||||
{
|
{
|
||||||
if (! ref) {
|
if (!ref) {
|
||||||
return decltype(static_ref_cast<T>(ref))();
|
return decltype(static_ref_cast<T>(ref))();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string target_class_name{jtype_traits<T>::base_name()};
|
static alias_ref<jclass> target_class = findClassStatic(jtype_traits<T>::base_name().c_str());
|
||||||
|
if (!target_class) {
|
||||||
|
throwNewJavaException("java/lang/ClassCastException",
|
||||||
|
"Could not find class %s.",
|
||||||
|
jtype_traits<T>::base_name().c_str());
|
||||||
|
|
||||||
// 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();
|
local_ref<jclass> source_class = ref->getClass();
|
||||||
|
|
||||||
if ( ! source_class->isAssignableFrom(target_class)) {
|
if (!target_class->isAssignableFrom(source_class)) {
|
||||||
throwNewJavaException("java/lang/ClassCastException",
|
throwNewJavaException("java/lang/ClassCastException",
|
||||||
"Tried to cast from %s to %s.",
|
"Tried to cast from %s to %s.",
|
||||||
source_class->toString().c_str(),
|
source_class->toString().c_str(),
|
||||||
target_class_name.c_str());
|
jtype_traits<T>::base_name().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_ref_cast<T>(ref);
|
return static_ref_cast<T>(ref);
|
@@ -4,6 +4,7 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file References.h
|
/** @file References.h
|
||||||
*
|
*
|
||||||
* Functionality similar to smart pointers, but for references into the VM. Four main reference
|
* Functionality similar to smart pointers, but for references into the VM. Four main reference
|
||||||
@@ -73,8 +74,6 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
#include "ReferenceAllocators.h"
|
#include "ReferenceAllocators.h"
|
||||||
#include "TypeTraits.h"
|
#include "TypeTraits.h"
|
||||||
#include "References-forward.h"
|
#include "References-forward.h"
|
||||||
@@ -250,6 +249,26 @@ template<typename T1, typename T2>
|
|||||||
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
|
||||||
operator!=(const T1& a, const T2& b);
|
operator!=(const T1& a, const T2& b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare references against nullptr
|
||||||
|
*/
|
||||||
|
template<typename T1>
|
||||||
|
enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator==(const T1& a, std::nullptr_t);
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator==(std::nullptr_t, const T1& a);
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator!=(const T1& a, std::nullptr_t);
|
||||||
|
|
||||||
|
template<typename T1>
|
||||||
|
enable_if_t<IsNonWeakReference<T1>(), bool>
|
||||||
|
operator!=(std::nullptr_t, const T1& a);
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename Alloc>
|
template<typename T, typename Alloc>
|
||||||
class base_owned_ref {
|
class base_owned_ref {
|
||||||
public:
|
public:
|
||||||
@@ -336,7 +355,7 @@ class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
|
|||||||
: base_owned_ref<T, Allocator>{} {}
|
: base_owned_ref<T, Allocator>{} {}
|
||||||
|
|
||||||
/// Create a null reference
|
/// Create a null reference
|
||||||
explicit weak_ref(std::nullptr_t) noexcept
|
/* implicit */ weak_ref(std::nullptr_t) noexcept
|
||||||
: base_owned_ref<T, Allocator>{nullptr} {}
|
: base_owned_ref<T, Allocator>{nullptr} {}
|
||||||
|
|
||||||
/// Copy constructor (note creates a new reference)
|
/// Copy constructor (note creates a new reference)
|
||||||
@@ -405,7 +424,7 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
|
|||||||
: base_owned_ref<T, Alloc>{} {}
|
: base_owned_ref<T, Alloc>{} {}
|
||||||
|
|
||||||
/// Create a null reference
|
/// Create a null reference
|
||||||
explicit basic_strong_ref(std::nullptr_t) noexcept
|
/* implicit */ basic_strong_ref(std::nullptr_t) noexcept
|
||||||
: base_owned_ref<T, Alloc>{nullptr} {}
|
: base_owned_ref<T, Alloc>{nullptr} {}
|
||||||
|
|
||||||
/// Copy constructor (note creates a new reference)
|
/// Copy constructor (note creates a new reference)
|
||||||
@@ -492,7 +511,7 @@ class alias_ref {
|
|||||||
alias_ref() noexcept;
|
alias_ref() noexcept;
|
||||||
|
|
||||||
/// Create a null reference
|
/// Create a null reference
|
||||||
alias_ref(std::nullptr_t) noexcept;
|
/* implicit */ alias_ref(std::nullptr_t) noexcept;
|
||||||
|
|
||||||
/// Copy constructor
|
/// Copy constructor
|
||||||
alias_ref(const alias_ref& other) noexcept;
|
alias_ref(const alias_ref& other) noexcept;
|
||||||
@@ -553,7 +572,7 @@ class alias_ref {
|
|||||||
* This is useful when you have a call which is initiated from C++-land, and therefore
|
* 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.
|
* doesn't automatically get a local JNI frame managed for you by the JNI framework.
|
||||||
*/
|
*/
|
||||||
class FBEXPORT JniLocalScope {
|
class JniLocalScope {
|
||||||
public:
|
public:
|
||||||
JniLocalScope(JNIEnv* p_env, jint capacity);
|
JniLocalScope(JNIEnv* p_env, jint capacity);
|
||||||
~JniLocalScope();
|
~JniLocalScope();
|
166
lib/fb/src/main/cpp/include/fbjni/detail/Registration-inl.h
Normal file
166
lib/fb/src/main/cpp/include/fbjni/detail/Registration-inl.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "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
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
struct CreateDefault {
|
||||||
|
static R create() {
|
||||||
|
return R{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CreateDefault<void> {
|
||||||
|
static void create() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
using Converter = Convert<typename std::decay<R>::type>;
|
||||||
|
|
||||||
|
template <typename F, F func, typename R, typename... Args>
|
||||||
|
struct WrapForVoidReturn {
|
||||||
|
static typename Converter<R>::jniType call(Args&&... args) {
|
||||||
|
return Converter<R>::toJniRet(func(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F, F func, typename... Args>
|
||||||
|
struct WrapForVoidReturn<F, func, void, Args...> {
|
||||||
|
static void call(Args&&... args) {
|
||||||
|
func(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// registration wrapper for legacy JNI-style functions
|
||||||
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
|
struct BareJniWrapper {
|
||||||
|
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
|
||||||
|
detail::JniEnvCacher jec(env);
|
||||||
|
try {
|
||||||
|
return (*func)(env, static_cast<JniType<C>>(obj), args...);
|
||||||
|
} catch (...) {
|
||||||
|
translatePendingCppExceptionToJavaException();
|
||||||
|
return CreateDefault<R>::create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// registration wrappers for functions, with autoconversion of arguments.
|
||||||
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
|
struct FunctionWrapper {
|
||||||
|
using jniRet = typename Converter<R>::jniType;
|
||||||
|
JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||||
|
detail::JniEnvCacher jec(env);
|
||||||
|
try {
|
||||||
|
return WrapForVoidReturn<F, func, R, JniType<C>, Args...>::call(
|
||||||
|
static_cast<JniType<C>>(obj), Converter<Args>::fromJni(args)...);
|
||||||
|
} catch (...) {
|
||||||
|
translatePendingCppExceptionToJavaException();
|
||||||
|
return CreateDefault<jniRet>::create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// registration wrappers for non-static methods, with autoconvertion of arguments.
|
||||||
|
template<typename M, M method, typename C, typename R, typename... Args>
|
||||||
|
struct MethodWrapper {
|
||||||
|
using jhybrid = typename C::jhybridobject;
|
||||||
|
static R dispatch(alias_ref<jhybrid> ref, Args&&... args) {
|
||||||
|
try {
|
||||||
|
// 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*>(ref->cthis());
|
||||||
|
return (cobj->*method)(std::forward<Args>(args)...);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
C::mapException(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNI_ENTRY_POINT static typename Converter<R>::jniType call(
|
||||||
|
JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {
|
||||||
|
return FunctionWrapper<R(*)(alias_ref<jhybrid>, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
|
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
|
||||||
|
// This intentionally erases the real type; JNI will do it anyway
|
||||||
|
return reinterpret_cast<NativeMethodWrapper*>(&(BareJniWrapper<F, func, C, R, Args...>::call));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
|
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {
|
||||||
|
// This intentionally erases the real type; JNI will do it anyway
|
||||||
|
return reinterpret_cast<NativeMethodWrapper*>(&(FunctionWrapper<F, func, C, R, Args...>::call));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename M, M method, typename C, typename R, typename... Args>
|
||||||
|
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {
|
||||||
|
(void)method0;
|
||||||
|
// This intentionally erases the real type; JNI will do it anyway
|
||||||
|
return reinterpret_cast<NativeMethodWrapper*>(&(MethodWrapper<M, method, C, R, Args...>::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();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
template<R(*func)(Args...)>
|
||||||
|
JNI_ENTRY_POINT R CriticalMethod<R(*)(Args...)>::call(alias_ref<jclass>, Args... args) noexcept {
|
||||||
|
static_assert(
|
||||||
|
IsJniPrimitive<R>() || std::is_void<R>(),
|
||||||
|
"Critical Native Methods may only return primitive JNI types, or void.");
|
||||||
|
static_assert(
|
||||||
|
AreJniPrimitives<Args...>(),
|
||||||
|
"Critical Native Methods may only use primitive JNI types as parameters");
|
||||||
|
|
||||||
|
return func(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
template<R(*func)(Args...)>
|
||||||
|
inline std::string CriticalMethod<R(*)(Args...)>::desc() {
|
||||||
|
return makeDescriptor(call<func>);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
@@ -17,28 +17,14 @@ namespace detail {
|
|||||||
// This uses the real JNI function as a non-type template parameter to
|
// This uses the real JNI function as a non-type template parameter to
|
||||||
// cause a (static member) function to exist with the same signature,
|
// cause a (static member) function to exist with the same signature,
|
||||||
// but with try/catch exception translation.
|
// 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>
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
|
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
|
||||||
|
|
||||||
// Automatically wrap object argument, and don't take env explicitly.
|
// 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>
|
template<typename F, F func, typename C, typename R, typename... Args>
|
||||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... 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,
|
// 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>
|
template<typename M, M method, typename C, typename R, typename... Args>
|
||||||
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
|
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
|
||||||
|
|
||||||
@@ -57,15 +43,15 @@ std::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));
|
|||||||
template<typename R, typename C, typename... Args>
|
template<typename R, typename C, typename... Args>
|
||||||
std::string makeDescriptor(R (C::*method0)(Args... args));
|
std::string makeDescriptor(R (C::*method0)(Args... args));
|
||||||
|
|
||||||
template <typename F>
|
template<typename F>
|
||||||
struct CriticalMethod;
|
struct CriticalMethod;
|
||||||
|
|
||||||
template <typename R, typename... Args>
|
template<typename R, typename ...Args>
|
||||||
struct CriticalMethod<R (*)(Args...)> {
|
struct CriticalMethod<R(*)(Args...)> {
|
||||||
template <R (*func)(Args...)>
|
template<R(*func)(Args...)>
|
||||||
static R call(alias_ref<jclass>, Args... args) noexcept;
|
static R call(alias_ref<jclass>, Args... args) noexcept;
|
||||||
|
|
||||||
template <R (*func)(Args...)>
|
template<R(*func)(Args...)>
|
||||||
inline static std::string desc();
|
inline static std::string desc();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,6 +77,7 @@ struct CriticalMethod<R (*)(Args...)> {
|
|||||||
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
|
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
|
||||||
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
// FAST CALLS / CRITICAL CALLS
|
// FAST CALLS / CRITICAL CALLS
|
||||||
// Android up to and including v7 supports "fast calls" by prefixing the method
|
// Android up to and including v7 supports "fast calls" by prefixing the method
|
||||||
// signature with an exclamation mark.
|
// signature with an exclamation mark.
|
@@ -86,6 +86,7 @@ constexpr bool AreJniPrimitives() {
|
|||||||
return are_jni_primitives<Ts...>::value;
|
return are_jni_primitives<Ts...>::value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Metafunction to determine whether a type is a JNI array of primitives or not
|
/// Metafunction to determine whether a type is a JNI array of primitives or not
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_jni_primitive_array :
|
struct is_jni_primitive_array :
|
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
@@ -42,28 +40,18 @@ std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
|
|||||||
// - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
// - 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
|
// - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8
|
||||||
|
|
||||||
class FBEXPORT LocalString {
|
// JString to UTF16 extractor using RAII idiom. Note that the
|
||||||
public:
|
// ctor/dtor use GetStringCritical/ReleaseStringCritical, so this
|
||||||
// Assumes UTF8 encoding and make a required convertion to modified UTF-8 when the string
|
// class is subject to the restrictions imposed by those functions.
|
||||||
// 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 {
|
class JStringUtf16Extractor {
|
||||||
public:
|
public:
|
||||||
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
|
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
|
||||||
: env_(env)
|
: env_(env)
|
||||||
, javaString_(javaString)
|
, javaString_(javaString)
|
||||||
|
, length_(0)
|
||||||
, utf16String_(nullptr) {
|
, utf16String_(nullptr) {
|
||||||
if (env_ && javaString_) {
|
if (env_ && javaString_) {
|
||||||
|
length_ = env_->GetStringLength(javaString_);
|
||||||
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
|
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,18 +62,20 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator const jchar* () const {
|
const jsize length() const {
|
||||||
|
return length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jchar* chars() const {
|
||||||
return utf16String_;
|
return utf16String_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JNIEnv* env_;
|
JNIEnv* env_;
|
||||||
jstring javaString_;
|
jstring javaString_;
|
||||||
|
jsize length_;
|
||||||
const jchar* utf16String_;
|
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);
|
|
||||||
|
|
||||||
} }
|
|
22
lib/fb/src/main/cpp/include/fbjni/fbjni.h
Normal file
22
lib/fb/src/main/cpp/include/fbjni/fbjni.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <fbjni/detail/Environment.h>
|
||||||
|
#include <fbjni/detail/Log.h>
|
||||||
|
#include <fbjni/detail/Common.h>
|
||||||
|
#include <fbjni/detail/Exceptions.h>
|
||||||
|
#include <fbjni/detail/ReferenceAllocators.h>
|
||||||
|
#include <fbjni/detail/References.h>
|
||||||
|
#include <fbjni/detail/Meta.h>
|
||||||
|
#include <fbjni/detail/CoreClasses.h>
|
||||||
|
#include <fbjni/detail/Iterator.h>
|
||||||
|
#include <fbjni/detail/Hybrid.h>
|
||||||
|
#include <fbjni/detail/Registration.h>
|
||||||
|
#include <fbjni/detail/JWeakReference.h>
|
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#include <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);
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
@@ -1,88 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <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_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}}
|
|
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <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>>;
|
|
||||||
|
|
||||||
} }
|
|
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <jni.h>
|
|
||||||
#include <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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <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;
|
|
||||||
};
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
@@ -1,136 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#include <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
|
|
||||||
|
|
@@ -7,12 +7,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <fb/visibility.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace lyra {
|
namespace lyra {
|
||||||
|
|
||||||
@@ -20,17 +17,21 @@ constexpr size_t kDefaultLimit = 64;
|
|||||||
|
|
||||||
using InstructionPointer = const void*;
|
using InstructionPointer = const void*;
|
||||||
|
|
||||||
class FBEXPORT StackTraceElement {
|
class StackTraceElement {
|
||||||
public:
|
public:
|
||||||
StackTraceElement(InstructionPointer absoluteProgramCounter,
|
StackTraceElement(InstructionPointer absoluteProgramCounter,
|
||||||
InstructionPointer libraryBase,
|
InstructionPointer libraryBase,
|
||||||
InstructionPointer functionAddress, std::string libraryName,
|
InstructionPointer functionAddress,
|
||||||
|
std::string libraryName,
|
||||||
std::string functionName)
|
std::string functionName)
|
||||||
: absoluteProgramCounter_{absoluteProgramCounter},
|
: absoluteProgramCounter_{absoluteProgramCounter},
|
||||||
libraryBase_{libraryBase},
|
libraryBase_{libraryBase},
|
||||||
functionAddress_{functionAddress},
|
functionAddress_{functionAddress},
|
||||||
libraryName_{std::move(libraryName)},
|
libraryName_{std::move(libraryName)},
|
||||||
functionName_{std::move(functionName)} {}
|
functionName_{std::move(functionName)},
|
||||||
|
hasBuildId_{false},
|
||||||
|
buildId_{}
|
||||||
|
{}
|
||||||
|
|
||||||
InstructionPointer libraryBase() const noexcept { return libraryBase_; }
|
InstructionPointer libraryBase() const noexcept { return libraryBase_; }
|
||||||
|
|
||||||
@@ -67,14 +68,28 @@ class FBEXPORT StackTraceElement {
|
|||||||
return absoluteabsoluteProgramCounter - absoluteSymbol;
|
return absoluteabsoluteProgramCounter - absoluteSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string buildId() const;
|
||||||
private:
|
private:
|
||||||
const InstructionPointer absoluteProgramCounter_;
|
const InstructionPointer absoluteProgramCounter_;
|
||||||
const InstructionPointer libraryBase_;
|
const InstructionPointer libraryBase_;
|
||||||
const InstructionPointer functionAddress_;
|
const InstructionPointer functionAddress_;
|
||||||
const std::string libraryName_;
|
const std::string libraryName_;
|
||||||
const std::string functionName_;
|
const std::string functionName_;
|
||||||
|
|
||||||
|
mutable bool hasBuildId_;
|
||||||
|
mutable std::string buildId_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a library identifier function is set, it is passed a libraryName
|
||||||
|
* for the frame, and returns a library build id string, which will be
|
||||||
|
* included in the logged stack trace. The most common use for this
|
||||||
|
* will be correlating stack traces with breakpad identifiers.
|
||||||
|
*/
|
||||||
|
typedef std::string (*LibraryIdentifierFunctionType)(const std::string&);
|
||||||
|
|
||||||
|
void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the vector with the current stack trace
|
* Populate the vector with the current stack trace
|
||||||
*
|
*
|
||||||
@@ -91,8 +106,7 @@ class FBEXPORT StackTraceElement {
|
|||||||
*
|
*
|
||||||
* @param skip The number of frames to skip before capturing the trace
|
* @param skip The number of frames to skip before capturing the trace
|
||||||
*/
|
*/
|
||||||
FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,
|
void getStackTrace(std::vector<InstructionPointer>& stackTrace, size_t skip = 0);
|
||||||
size_t skip = 0);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a vector and populates it with the current stack trace
|
* Creates a vector and populates it with the current stack trace
|
||||||
@@ -108,7 +122,7 @@ FBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,
|
|||||||
*
|
*
|
||||||
* @limit The maximum number of frames captured
|
* @limit The maximum number of frames captured
|
||||||
*/
|
*/
|
||||||
FBEXPORT inline std::vector<InstructionPointer> getStackTrace(
|
inline std::vector<InstructionPointer> getStackTrace(
|
||||||
size_t skip = 0,
|
size_t skip = 0,
|
||||||
size_t limit = kDefaultLimit) {
|
size_t limit = kDefaultLimit) {
|
||||||
auto stackTrace = std::vector<InstructionPointer>{};
|
auto stackTrace = std::vector<InstructionPointer>{};
|
||||||
@@ -125,7 +139,7 @@ FBEXPORT inline std::vector<InstructionPointer> getStackTrace(
|
|||||||
*
|
*
|
||||||
* @param stackTrace The input stack trace
|
* @param stackTrace The input stack trace
|
||||||
*/
|
*/
|
||||||
FBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
|
void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
|
||||||
const std::vector<InstructionPointer>& trace);
|
const std::vector<InstructionPointer>& trace);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +147,7 @@ FBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
|
|||||||
*
|
*
|
||||||
* @param stackTrace The input stack trace
|
* @param stackTrace The input stack trace
|
||||||
*/
|
*/
|
||||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||||
const std::vector<InstructionPointer>& trace) {
|
const std::vector<InstructionPointer>& trace) {
|
||||||
auto symbols = std::vector<StackTraceElement>{};
|
auto symbols = std::vector<StackTraceElement>{};
|
||||||
getStackTraceSymbols(symbols, trace);
|
getStackTraceSymbols(symbols, trace);
|
||||||
@@ -152,7 +166,7 @@ FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
|||||||
*
|
*
|
||||||
* @param limit The maximum number of frames captured
|
* @param limit The maximum number of frames captured
|
||||||
*/
|
*/
|
||||||
FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
inline std::vector<StackTraceElement> getStackTraceSymbols(
|
||||||
size_t skip = 0,
|
size_t skip = 0,
|
||||||
size_t limit = kDefaultLimit) {
|
size_t limit = kDefaultLimit) {
|
||||||
return getStackTraceSymbols(getStackTrace(skip + 1, limit));
|
return getStackTraceSymbols(getStackTrace(skip + 1, limit));
|
||||||
@@ -161,12 +175,21 @@ FBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(
|
|||||||
/**
|
/**
|
||||||
* Formatting a stack trace element
|
* Formatting a stack trace element
|
||||||
*/
|
*/
|
||||||
FBEXPORT std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
|
std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formatting a stack trace
|
* Formatting a stack trace
|
||||||
*/
|
*/
|
||||||
FBEXPORT std::ostream& operator<<(std::ostream& out,
|
std::ostream& operator<<(std::ostream& out,
|
||||||
const std::vector<StackTraceElement>& trace);
|
const std::vector<StackTraceElement>& trace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log stack trace
|
||||||
|
*
|
||||||
|
* Makes it possible to log a trace without using a temporary stream when the
|
||||||
|
* underlying log API is not stream based.
|
||||||
|
*/
|
||||||
|
void logStackTrace(const std::vector<StackTraceElement>& trace);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
84
lib/fb/src/main/cpp/include/lyra/lyra_exceptions.h
Normal file
84
lib/fb/src/main/cpp/include/lyra/lyra_exceptions.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <lyra/lyra.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace lyra {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
struct ExceptionTraceHolder {
|
||||||
|
ExceptionTraceHolder();
|
||||||
|
// Need some virtual function to make this a polymorphic type.
|
||||||
|
virtual ~ExceptionTraceHolder();
|
||||||
|
ExceptionTraceHolder(const ExceptionTraceHolder&) = delete;
|
||||||
|
ExceptionTraceHolder(ExceptionTraceHolder&&) = default;
|
||||||
|
|
||||||
|
std::vector<InstructionPointer> stackTrace_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename E, bool hasTraceHolder>
|
||||||
|
struct Holder : E, ExceptionTraceHolder {
|
||||||
|
Holder(E&& e) : E{std::forward<E>(e)}, ExceptionTraceHolder{} {}
|
||||||
|
};
|
||||||
|
template <typename E>
|
||||||
|
struct Holder<E, true> : E {
|
||||||
|
Holder(E&& e) : E{std::forward<E>(e)} {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the stack trace of an exception
|
||||||
|
*/
|
||||||
|
const std::vector<InstructionPointer>& getExceptionTrace(std::exception_ptr ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an exception and store the stack trace. This works like
|
||||||
|
* std::throw_with_nested in that it will actually throw a type that is
|
||||||
|
* publicly derived from both E and detail::ExceptionTraceHolder.
|
||||||
|
*/
|
||||||
|
template <class E>
|
||||||
|
[[noreturn]] void fbthrow(E&& exception) {
|
||||||
|
throw detail::Holder<E, std::is_base_of<detail::ExceptionTraceHolder, E>::value>{std::forward<E>(exception)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that a terminate handler that logs traces is installed.
|
||||||
|
* setLibraryIdentifierFunction should be called first if the stack
|
||||||
|
* trace should log build ids for libraries.
|
||||||
|
*/
|
||||||
|
void ensureRegisteredTerminateHandler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to convert an exception to a string
|
||||||
|
*/
|
||||||
|
std::string toString(std::exception_ptr exceptionPointer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lyra's cxa_throw will delegate to the original cxa throw. That pointer must
|
||||||
|
* be set before lyra::cxa_throw is called.
|
||||||
|
*
|
||||||
|
* One example use would be to statically compile against something that overrides __cxa_throw.
|
||||||
|
* That would look something like:
|
||||||
|
*
|
||||||
|
* [[noreturn]] void __cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void*)) {
|
||||||
|
* static auto initializer = lyra::original_cxa_throw = lookupOriginalCxaThrow();
|
||||||
|
* lyra::cxa_throw(obj, type, destructor);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
[[gnu::noreturn]] extern void (*original_cxa_throw)(void*, const std::type_info*, void (*) (void*));
|
||||||
|
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *));
|
||||||
|
|
||||||
|
void enableCxaThrowHookBacktraces(bool enable);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -4,21 +4,51 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/fbjni/ByteBuffer.h>
|
#include <fbjni/ByteBuffer.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <fb/fbjni/References.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
namespace {
|
void JBuffer::rewind() const {
|
||||||
local_ref<JByteBuffer> createEmpty() {
|
static auto meth = javaClassStatic()->getMethod<alias_ref<JBuffer>()>("rewind");
|
||||||
static auto cls = JByteBuffer::javaClassStatic();
|
meth(self());
|
||||||
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
|
|
||||||
return meth(cls, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* JBuffer::getDirectAddress() const {
|
||||||
|
if (!self()) {
|
||||||
|
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||||
|
}
|
||||||
|
void* addr = Environment::current()->GetDirectBufferAddress(self());
|
||||||
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
|
if (!addr) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
isDirect() ?
|
||||||
|
"Attempt to get direct bytes of non-direct buffer." :
|
||||||
|
"Error getting direct bytes of buffer.");
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JBuffer::getDirectCapacity() const {
|
||||||
|
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 buffer." :
|
||||||
|
"Error getting direct size of buffer.");
|
||||||
|
}
|
||||||
|
return static_cast<size_t>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JBuffer::isDirect() const {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
|
||||||
|
return meth(self());
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
||||||
@@ -26,7 +56,7 @@ local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
|||||||
// dalvik returns an invalid result and Android's art aborts if size == 0.
|
// 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.
|
// Workaround this by using a slow path through Java in that case.
|
||||||
if (!size) {
|
if (!size) {
|
||||||
return createEmpty();
|
return allocateDirect(0);
|
||||||
}
|
}
|
||||||
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
|
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
@@ -36,39 +66,10 @@ local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* JByteBuffer::getDirectBytes() const {
|
local_ref<JByteBuffer> JByteBuffer::allocateDirect(jint size) {
|
||||||
if (!self()) {
|
static auto cls = JByteBuffer::javaClassStatic();
|
||||||
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
|
||||||
}
|
return meth(cls, size);
|
||||||
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() const {
|
|
||||||
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() const {
|
|
||||||
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
|
|
||||||
return meth(self());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
@@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@@ -1,129 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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));
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
@@ -1,282 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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());
|
|
||||||
// std::terminate will print the message of the pending exception e
|
|
||||||
std::terminate();
|
|
||||||
} catch (...) {
|
|
||||||
FBLOGE("unexpected exception in translatePendingCppExceptionToJavaException");
|
|
||||||
std::terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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_);
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
@@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include "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),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
@@ -4,16 +4,15 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <jni/Countable.h>
|
#include <fbjni/fbjni.h>
|
||||||
#include <fb/Environment.h>
|
#include <fbjni/NativeRunnable.h>
|
||||||
#include <fb/fbjni.h>
|
|
||||||
#include <fb/fbjni/NativeRunnable.h>
|
|
||||||
|
|
||||||
using namespace facebook::jni;
|
using namespace facebook::jni;
|
||||||
|
|
||||||
void initialize_fbjni() {
|
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
CountableOnLoad(Environment::current());
|
return facebook::jni::initialize(vm, [] {
|
||||||
HybridDataOnLoad();
|
HybridDataOnLoad();
|
||||||
JNativeRunnable::OnLoad();
|
JNativeRunnable::OnLoad();
|
||||||
ThreadScope::OnLoad();
|
ThreadScope::OnLoad();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
21
lib/fb/src/main/cpp/jni/ReadableByteChannel.cpp
Normal file
21
lib/fb/src/main/cpp/jni/ReadableByteChannel.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <fbjni/ReadableByteChannel.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
int JReadableByteChannel::read(alias_ref<JByteBuffer> dest) const {
|
||||||
|
if (!self()) {
|
||||||
|
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||||
|
}
|
||||||
|
static auto method = javaClassStatic()->getMethod<jint(alias_ref<JByteBuffer>)>("read");
|
||||||
|
return method(self(), dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
@@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
||||||
|
|
291
lib/fb/src/main/cpp/jni/detail/Environment.cpp
Normal file
291
lib/fb/src/main/cpp/jni/detail/Environment.cpp
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <fbjni/fbjni.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
JavaVM* g_vm = nullptr;
|
||||||
|
|
||||||
|
struct EnvironmentInitializer {
|
||||||
|
EnvironmentInitializer(JavaVM* vm) {
|
||||||
|
FBJNI_ASSERT(!g_vm);
|
||||||
|
FBJNI_ASSERT(vm);
|
||||||
|
g_vm = vm;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int getEnv(JNIEnv** env) {
|
||||||
|
FBJNI_ASSERT(g_vm);
|
||||||
|
// g_vm->GetEnv() might not clear the env* in failure cases.
|
||||||
|
*env = nullptr;
|
||||||
|
jint ret = g_vm->GetEnv((void**)env, JNI_VERSION_1_6);
|
||||||
|
// Other possibilites are that JNI_VERSION_1_6 is invalid, or some
|
||||||
|
// unknown return was received.
|
||||||
|
FBJNI_ASSERT(ret == JNI_OK || ret == JNI_EDETACHED);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some jni.h define the first arg to AttachCurrentThread as void**,
|
||||||
|
// and some as JNIEnv**. This hack allows both to work.
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
struct AttachTraits;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AttachTraits<jint(JavaVM::*)(JNIEnv**, void*)> {
|
||||||
|
using EnvType = JNIEnv*;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AttachTraits<jint(JavaVM::*)(void**, void*)> {
|
||||||
|
using EnvType = void*;
|
||||||
|
};
|
||||||
|
|
||||||
|
JNIEnv* attachCurrentThread() {
|
||||||
|
JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};
|
||||||
|
using AttachEnvType =
|
||||||
|
typename AttachTraits<decltype(&JavaVM::AttachCurrentThread)>::EnvType;
|
||||||
|
AttachEnvType env;
|
||||||
|
auto result = g_vm->AttachCurrentThread(&env, &args);
|
||||||
|
FBJNI_ASSERT(result == JNI_OK);
|
||||||
|
return reinterpret_cast<JNIEnv*>(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
void Environment::initialize(JavaVM* vm) {
|
||||||
|
static EnvironmentInitializer init(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
pthread_key_t makeKey() {
|
||||||
|
pthread_key_t key;
|
||||||
|
int ret = pthread_key_create(&key, nullptr);
|
||||||
|
if (ret != 0) {
|
||||||
|
FBJNI_LOGF("pthread_key_create failed: %d", ret);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_key_t getTLKey() {
|
||||||
|
static pthread_key_t key = makeKey();
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline detail::TLData* getTLData(pthread_key_t key) {
|
||||||
|
return reinterpret_cast<detail::TLData*>(pthread_getspecific(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setTLData(pthread_key_t key, detail::TLData* data) {
|
||||||
|
int ret = pthread_setspecific(key, data);
|
||||||
|
if (ret != 0) {
|
||||||
|
(void) ret;
|
||||||
|
FBJNI_LOGF("pthread_setspecific failed: %d", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This returns non-nullptr iff the env was cached from java. So it
|
||||||
|
// can return nullptr for a thread which has been registered.
|
||||||
|
inline JNIEnv* cachedOrNull() {
|
||||||
|
detail::TLData* pdata = getTLData(getTLKey());
|
||||||
|
return (pdata ? pdata->env : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// This will return a cached env if there is one, or get one from JNI
|
||||||
|
// if the thread has already been attached some other way. If it
|
||||||
|
// returns nullptr, then the thread has never been registered, or the
|
||||||
|
// VM has never been set up for fbjni.
|
||||||
|
|
||||||
|
JNIEnv* currentOrNull() {
|
||||||
|
if (!g_vm) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::TLData* pdata = getTLData(getTLKey());
|
||||||
|
if (pdata && pdata->env) {
|
||||||
|
return pdata->env;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
if (getEnv(&env) != JNI_OK) {
|
||||||
|
// If there's a ThreadScope on the stack, we should have gotten a
|
||||||
|
// JNIEnv and not ended up here.
|
||||||
|
FBJNI_ASSERT(!pdata || !pdata->attached);
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To understand JniEnvCacher and ThreadScope, it is helpful to
|
||||||
|
// realize that if a flagged JniEnvCacher is on the stack, then a
|
||||||
|
// flagged ThreadScope cannot be after it. If a flagged ThreadCacher
|
||||||
|
// is on the stack, then a JniEnvCacher *can* be after it. So,
|
||||||
|
// ThreadScope's setup and teardown can both assume they are the
|
||||||
|
// first/last interesting objects, but this is not true of
|
||||||
|
// JniEnvCacher.
|
||||||
|
|
||||||
|
JniEnvCacher::JniEnvCacher(JNIEnv* env)
|
||||||
|
: thisCached_(false)
|
||||||
|
{
|
||||||
|
FBJNI_ASSERT(env);
|
||||||
|
|
||||||
|
pthread_key_t key = getTLKey();
|
||||||
|
detail::TLData* pdata = getTLData(key);
|
||||||
|
if (pdata && pdata->env) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata) {
|
||||||
|
pdata = &data_;
|
||||||
|
setTLData(key, pdata);
|
||||||
|
pdata->attached = false;
|
||||||
|
} else {
|
||||||
|
FBJNI_ASSERT(!pdata->env);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->env = env;
|
||||||
|
|
||||||
|
thisCached_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JniEnvCacher::~JniEnvCacher() {
|
||||||
|
if (!thisCached_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_key_t key = getTLKey();
|
||||||
|
TLData* pdata = getTLData(key);
|
||||||
|
FBJNI_ASSERT(pdata);
|
||||||
|
FBJNI_ASSERT(pdata->env != nullptr);
|
||||||
|
pdata->env = nullptr;
|
||||||
|
if (!pdata->attached) {
|
||||||
|
setTLData(key, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadScope::ThreadScope()
|
||||||
|
: thisAttached_(false)
|
||||||
|
{
|
||||||
|
if (g_vm == nullptr) {
|
||||||
|
throw std::runtime_error("fbjni is uninitialized; no thread can be attached.");
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
|
||||||
|
// Check if the thread is attached somehow.
|
||||||
|
auto result = getEnv(&env);
|
||||||
|
if (result == JNI_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, it appears there's no thread attached and no env is
|
||||||
|
// cached, or we would have returned already. So there better not
|
||||||
|
// be TLData.
|
||||||
|
|
||||||
|
pthread_key_t key = getTLKey();
|
||||||
|
detail::TLData* pdata = getTLData(key);
|
||||||
|
FBJNI_ASSERT(pdata == nullptr);
|
||||||
|
setTLData(key, &data_);
|
||||||
|
|
||||||
|
attachCurrentThread();
|
||||||
|
|
||||||
|
data_.env = nullptr;
|
||||||
|
data_.attached = true;
|
||||||
|
|
||||||
|
thisAttached_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadScope::~ThreadScope() {
|
||||||
|
if (!thisAttached_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_key_t key = getTLKey();
|
||||||
|
detail::TLData* pdata = getTLData(key);
|
||||||
|
FBJNI_ASSERT(pdata);
|
||||||
|
FBJNI_ASSERT(pdata->env == nullptr);
|
||||||
|
FBJNI_ASSERT(pdata->attached);
|
||||||
|
FBJNI_ASSERT(g_vm);
|
||||||
|
g_vm->DetachCurrentThread();
|
||||||
|
setTLData(key, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
JNIEnv* Environment::current() {
|
||||||
|
FBJNI_ASSERT(g_vm);
|
||||||
|
JNIEnv* env = detail::currentOrNull();
|
||||||
|
if (env == nullptr) {
|
||||||
|
throw std::runtime_error("Unable to retrieve jni environment. Is the thread attached?");
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
JNIEnv* Environment::ensureCurrentThreadIsAttached() {
|
||||||
|
FBJNI_ASSERT(g_vm);
|
||||||
|
JNIEnv* env = detail::currentOrNull();
|
||||||
|
if (env == nullptr) {
|
||||||
|
env = attachCurrentThread();
|
||||||
|
FBJNI_ASSERT(env);
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
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 const 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 */
|
||||||
|
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) {
|
||||||
|
if (cachedOrNull() == nullptr) {
|
||||||
|
ThreadScope ts;
|
||||||
|
JThreadScopeSupport::runStdFunction(std::move(runnable));
|
||||||
|
} else {
|
||||||
|
runnable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} }
|
390
lib/fb/src/main/cpp/jni/detail/Exceptions.cpp
Normal file
390
lib/fb/src/main/cpp/jni/detail/Exceptions.cpp
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <fbjni/detail/CoreClasses.h>
|
||||||
|
#include <fbjni/detail/Log.h>
|
||||||
|
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
#include <lyra/lyra.h>
|
||||||
|
#include <lyra/lyra_exceptions.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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) {
|
||||||
|
FBJNI_LOGF("Failed to set Java exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions that throw C++ exceptions
|
||||||
|
|
||||||
|
// TODO(T6618159) Inject the c++ stack into the exception's stack trace. One
|
||||||
|
// issue: when a java exception is created, it captures the full java stack
|
||||||
|
// across jni boundaries. lyra will only capture the c++ stack to the jni
|
||||||
|
// boundary. So, as we pass the java exception up to c++, we need to capture
|
||||||
|
// the c++ stack and then insert it into the correct place in the java stack
|
||||||
|
// trace. Then, as the exception propagates across the boundaries, we will
|
||||||
|
// slowly fill in the c++ parts of the trace.
|
||||||
|
void throwPendingJniExceptionAsCppException() {
|
||||||
|
JNIEnv* env = Environment::current();
|
||||||
|
if (env->ExceptionCheck() == JNI_FALSE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto throwable = env->ExceptionOccurred();
|
||||||
|
if (!throwable) {
|
||||||
|
throw std::runtime_error("Unable to get pending JNI exception.");
|
||||||
|
}
|
||||||
|
env->ExceptionClear();
|
||||||
|
|
||||||
|
throw JniException(adopt_local(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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<javaobject(alias_ref<javaobject>)>("initCause");
|
||||||
|
return meth(self(), cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto JThrowable::getStackTrace() -> local_ref<JStackTrace> {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<JStackTrace::javaobject()>("getStackTrace");
|
||||||
|
return meth(self());
|
||||||
|
}
|
||||||
|
|
||||||
|
void JThrowable::setStackTrace(alias_ref<JStackTrace> stack) {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<void(alias_ref<JStackTrace>)>("setStackTrace");
|
||||||
|
return meth(self(), stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto JStackTraceElement::create(
|
||||||
|
const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)
|
||||||
|
-> local_ref<javaobject> {
|
||||||
|
return newInstance(declaringClass, methodName, file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JStackTraceElement::getClassName() const {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getClassName");
|
||||||
|
return meth(self())->toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JStackTraceElement::getMethodName() const {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getMethodName");
|
||||||
|
return meth(self())->toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JStackTraceElement::getFileName() const {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>("getFileName");
|
||||||
|
return meth(self())->toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int JStackTraceElement::getLineNumber() const {
|
||||||
|
static auto meth = javaClassStatic()->getMethod<jint()>("getLineNumber");
|
||||||
|
return meth(self());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate C++ to Java Exception
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// For each exception in the chain of the exception_ptr argument, func
|
||||||
|
// will be called with that exception (in reverse order, i.e. innermost first).
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
void denest(const std::function<void(std::exception_ptr)>& func, std::exception_ptr ptr) {
|
||||||
|
FBJNI_ASSERT(ptr);
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
} catch (const std::nested_exception& e) {
|
||||||
|
denest(func, e.nested_ptr());
|
||||||
|
} catch (...) {
|
||||||
|
// ignored.
|
||||||
|
}
|
||||||
|
func(ptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
local_ref<JStackTraceElement> createJStackTraceElement(const lyra::StackTraceElement& cpp) {
|
||||||
|
return JStackTraceElement::create(
|
||||||
|
"|lyra|{" + cpp.libraryName() + "}", cpp.functionName(), cpp.buildId(), cpp.libraryOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCppStacktraceToJavaException(alias_ref<JThrowable> java, std::exception_ptr cpp) {
|
||||||
|
auto cppStack = lyra::getStackTraceSymbols(
|
||||||
|
(cpp == nullptr) ?
|
||||||
|
lyra::getStackTrace()
|
||||||
|
: lyra::getExceptionTrace(cpp));
|
||||||
|
|
||||||
|
auto javaStack = java->getStackTrace();
|
||||||
|
auto newStack = JThrowable::JStackTrace::newArray(javaStack->size() + cppStack.size());
|
||||||
|
size_t i = 0;
|
||||||
|
for (size_t j = 0; j < cppStack.size(); j++, i++) {
|
||||||
|
(*newStack)[i] = createJStackTraceElement(cppStack[j]);
|
||||||
|
}
|
||||||
|
for (size_t j = 0; j < javaStack->size(); j++, i++) {
|
||||||
|
(*newStack)[i] = (*javaStack)[j];
|
||||||
|
}
|
||||||
|
java->setStackTrace(newStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref<JThrowable> convertCppExceptionToJavaException(std::exception_ptr ptr) {
|
||||||
|
FBJNI_ASSERT(ptr);
|
||||||
|
local_ref<JThrowable> current;
|
||||||
|
bool addCppStack = true;
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
addCppStack = false;
|
||||||
|
} 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 (addCppStack) {
|
||||||
|
addCppStacktraceToJavaException(current, ptr);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppBackTrace() {
|
||||||
|
return getJavaExceptionForCppBackTrace(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg) {
|
||||||
|
local_ref<JThrowable> current =
|
||||||
|
msg ? JUnknownCppException::create(msg) : JUnknownCppException::create();
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
addCppStacktraceToJavaException(current, nullptr);
|
||||||
|
#endif
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr) {
|
||||||
|
FBJNI_ASSERT(ptr);
|
||||||
|
local_ref<JThrowable> previous;
|
||||||
|
auto func = [&previous] (std::exception_ptr ptr) {
|
||||||
|
auto current = convertCppExceptionToJavaException(ptr);
|
||||||
|
if (previous) {
|
||||||
|
current->initCause(previous);
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
};
|
||||||
|
denest(func, ptr);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void translatePendingCppExceptionToJavaException() {
|
||||||
|
try {
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
auto exc = getJavaExceptionForCppException(std::current_exception());
|
||||||
|
#else
|
||||||
|
auto exc = JUnknownCppException::create();
|
||||||
|
#endif
|
||||||
|
setJavaExceptionAndAbortOnFailure(exc);
|
||||||
|
} catch (...) {
|
||||||
|
#ifndef FBJNI_NO_EXCEPTION_PTR
|
||||||
|
FBJNI_LOGE(
|
||||||
|
"Unexpected error in translatePendingCppExceptionToJavaException(): %s",
|
||||||
|
lyra::toString(std::current_exception()).c_str());
|
||||||
|
#else
|
||||||
|
FBJNI_LOGE(
|
||||||
|
"Unexpected error in translatePendingCppExceptionToJavaException()");
|
||||||
|
#endif
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
try {
|
||||||
|
ThreadScope ts;
|
||||||
|
throwable_.reset();
|
||||||
|
} catch (...) {
|
||||||
|
FBJNI_LOGE("Exception in ~JniException()");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ref<JThrowable> JniException::getThrowable() const noexcept {
|
||||||
|
return make_local(throwable_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 6900503: consider making this thread-safe.
|
||||||
|
void JniException::populateWhat() const noexcept {
|
||||||
|
try {
|
||||||
|
ThreadScope ts;
|
||||||
|
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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
32
lib/fb/src/main/cpp/jni/detail/Hybrid.cpp
Normal file
32
lib/fb/src/main/cpp/jni/detail/Hybrid.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <fbjni/fbjni.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
local_ref<HybridData> HybridData::create() {
|
||||||
|
return newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void deleteNative(alias_ref<jclass>, jlong ptr) {
|
||||||
|
delete reinterpret_cast<detail::BaseHybridClass*>(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HybridDataOnLoad() {
|
||||||
|
registerNatives("com/facebook/jni/HybridData$Destructor", {
|
||||||
|
makeNativeMethod("deleteNative", deleteNative),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
79
lib/fb/src/main/cpp/jni/detail/References.cpp
Normal file
79
lib/fb/src/main/cpp/jni/detail/References.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <fbjni/detail/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 {
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
|
int32_t getAndroidApiLevel() {
|
||||||
|
// This is called from the static local initializer in
|
||||||
|
// isObjectRefType(), and creating fbjni references can call
|
||||||
|
// isObjectRefType(). So, to avoid recursively entering the block
|
||||||
|
// where the static is initialized (which is undefined behavior), we
|
||||||
|
// avoid using standard fbjni references here.
|
||||||
|
|
||||||
|
JNIEnv* env = Environment::current();
|
||||||
|
jclass cls = detail::findClass(env, "android/os/Build$VERSION");
|
||||||
|
jfieldID field = env->GetStaticFieldID(
|
||||||
|
cls, "SDK_INT", jtype_traits<jint>::descriptor().c_str());
|
||||||
|
if (!field) {
|
||||||
|
env->DeleteLocalRef(cls);
|
||||||
|
}
|
||||||
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
|
||||||
|
int32_t ret = env->GetStaticIntField(cls, field);
|
||||||
|
env->DeleteLocalRef(cls);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesGetObjectRefTypeWork() {
|
||||||
|
auto level = getAndroidApiLevel();
|
||||||
|
return level >= 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool doesGetObjectRefTypeWork() {
|
||||||
|
auto jni_version = Environment::current()->GetVersion();
|
||||||
|
return jni_version >= JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool isObjectRefType(jobject reference, jobjectRefType refType) {
|
||||||
|
// null-check first so that we short-circuit during (safe) global
|
||||||
|
// constructors, where we won't have an Environment::current() yet
|
||||||
|
if (!reference) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getObjectRefTypeWorks = doesGetObjectRefTypeWork();
|
||||||
|
|
||||||
|
return !getObjectRefTypeWorks ||
|
||||||
|
Environment::current()->GetObjectRefType(reference) == refType;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jni
|
||||||
|
} // namespace facebook
|
@@ -4,11 +4,9 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <jni/LocalString.h>
|
#include <fbjni/detail/utf8.h>
|
||||||
#include <fb/Environment.h>
|
|
||||||
#include <fb/assert.h>
|
|
||||||
|
|
||||||
#include <vector>
|
#include <fbjni/detail/Log.h>
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
@@ -22,7 +20,9 @@ const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
|
|||||||
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
|
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
|
||||||
|
|
||||||
inline void encode3ByteUTF8(char32_t code, uint8_t* out) {
|
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");
|
if ((code & 0xffff0000) != 0) {
|
||||||
|
FBJNI_LOGF("3 byte utf-8 encodings only valid for up to 16 bits");
|
||||||
|
}
|
||||||
|
|
||||||
out[0] = 0xE0 | (code >> 12);
|
out[0] = 0xE0 | (code >> 12);
|
||||||
out[1] = 0x80 | ((code >> 6) & 0x3F);
|
out[1] = 0x80 | ((code >> 6) & 0x3F);
|
||||||
@@ -36,7 +36,9 @@ inline char32_t decode3ByteUTF8(const uint8_t* in) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) {
|
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");
|
if ((code & 0xfff80000) != 0) {
|
||||||
|
FBJNI_LOGF("4 byte utf-8 encodings only valid for up to 21 bits");
|
||||||
|
}
|
||||||
|
|
||||||
out[offset] = (char) (0xF0 | (code >> 18));
|
out[offset] = (char) (0xF0 | (code >> 18));
|
||||||
out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
|
out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
|
||||||
@@ -100,9 +102,13 @@ void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size
|
|||||||
{
|
{
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
for (size_t i = 0; i < len; ) {
|
for (size_t i = 0; i < len; ) {
|
||||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
if (j >= modifiedBufLen) {
|
||||||
|
FBJNI_LOGF("output buffer is too short");
|
||||||
|
}
|
||||||
if (utf8[i] == 0) {
|
if (utf8[i] == 0) {
|
||||||
FBASSERTMSGF(j + 1 < modifiedBufLen, "output buffer is too short");
|
if (j + 1 >= modifiedBufLen) {
|
||||||
|
FBJNI_LOGF("output buffer is too short");
|
||||||
|
}
|
||||||
modified[j] = 0xc0;
|
modified[j] = 0xc0;
|
||||||
modified[j + 1] = 0x80;
|
modified[j + 1] = 0x80;
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -142,14 +148,18 @@ void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encode each as a 3 byte surrogate value
|
// encode each as a 3 byte surrogate value
|
||||||
FBASSERTMSGF(j + 5 < modifiedBufLen, "output buffer is too short");
|
if (j + 5 >= modifiedBufLen) {
|
||||||
|
FBJNI_LOGF("output buffer is too short");
|
||||||
|
}
|
||||||
encode3ByteUTF8(first, modified + j);
|
encode3ByteUTF8(first, modified + j);
|
||||||
encode3ByteUTF8(second, modified + j + 3);
|
encode3ByteUTF8(second, modified + j + 3);
|
||||||
i += 4;
|
i += 4;
|
||||||
j += 6;
|
j += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
FBASSERTMSGF(j < modifiedBufLen, "output buffer is too short");
|
if (j >= modifiedBufLen) {
|
||||||
|
FBJNI_LOGF("output buffer is too short");
|
||||||
|
}
|
||||||
modified[j++] = '\0';
|
modified[j++] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,46 +274,5 @@ std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noex
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} }
|
|
@@ -4,36 +4,35 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/fbjni.h>
|
#include <fbjni/fbjni.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <jni/LocalString.h>
|
|
||||||
#include <fb/log.h>
|
#include <fbjni/detail/utf8.h>
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
namespace jni {
|
namespace jni {
|
||||||
|
|
||||||
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
|
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
|
||||||
static std::once_flag flag{};
|
|
||||||
// TODO (t7832883): DTRT when we have exception pointers
|
// TODO (t7832883): DTRT when we have exception pointers
|
||||||
static auto error_msg = std::string{"Failed to initialize fbjni"};
|
static auto error_msg = std::string{"Failed to initialize fbjni"};
|
||||||
static auto error_occured = false;
|
static bool error_occured = [vm] {
|
||||||
|
bool retVal = false;
|
||||||
std::call_once(flag, [vm] {
|
|
||||||
try {
|
try {
|
||||||
Environment::initialize(vm);
|
Environment::initialize(vm);
|
||||||
} catch (std::exception& ex) {
|
} catch (std::exception& ex) {
|
||||||
error_occured = true;
|
retVal = true;
|
||||||
try {
|
try {
|
||||||
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
|
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
// Ignore, we already have a fall back message
|
// Ignore, we already have a fall back message
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
error_occured = true;
|
retVal = true;
|
||||||
}
|
}
|
||||||
});
|
return retVal;
|
||||||
|
}();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (error_occured) {
|
if (error_occured) {
|
||||||
@@ -42,7 +41,7 @@ jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
|
|||||||
|
|
||||||
init_fn();
|
init_fn();
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
FBLOGE("error %s", e.what());
|
FBJNI_LOGE("error %s", e.what());
|
||||||
translatePendingCppExceptionToJavaException();
|
translatePendingCppExceptionToJavaException();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
translatePendingCppExceptionToJavaException();
|
translatePendingCppExceptionToJavaException();
|
||||||
@@ -52,43 +51,54 @@ jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
|
|||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias_ref<JClass> findClassStatic(const char* name) {
|
namespace detail {
|
||||||
const auto env = internal::getEnv();
|
|
||||||
|
jclass findClass(JNIEnv* env, const char* name) {
|
||||||
if (!env) {
|
if (!env) {
|
||||||
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
||||||
}
|
}
|
||||||
auto cls = env->FindClass(name);
|
jclass cls = env->FindClass(name);
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
||||||
auto leaking_ref = (jclass)env->NewGlobalRef(cls);
|
return cls;
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
|
}
|
||||||
return wrap_alias(leaking_ref);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ref<JClass> findClassLocal(const char* name) {
|
local_ref<JClass> findClassLocal(const char* name) {
|
||||||
const auto env = internal::getEnv();
|
return adopt_local(detail::findClass(detail::currentOrNull(), name));
|
||||||
if (!env) {
|
}
|
||||||
throw std::runtime_error("Unable to retrieve JNIEnv*.");
|
|
||||||
}
|
alias_ref<JClass> findClassStatic(const char* name) {
|
||||||
auto cls = env->FindClass(name);
|
JNIEnv* env = detail::currentOrNull();
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
|
auto cls = adopt_local(detail::findClass(env, name));
|
||||||
return adopt_local(cls);
|
auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
|
||||||
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
|
||||||
|
return wrap_alias(leaking_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// jstring /////////////////////////////////////////////////////////////////////////////////////////
|
// jstring /////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
std::string JString::toStdString() const {
|
std::string JString::toStdString() const {
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
auto utf16String = JStringUtf16Extractor(env, self());
|
auto utf16String = JStringUtf16Extractor(env, self());
|
||||||
auto length = env->GetStringLength(self());
|
return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
|
||||||
return detail::utf16toUTF8(utf16String, length);
|
}
|
||||||
|
|
||||||
|
std::u16string JString::toU16String() const {
|
||||||
|
const auto env = Environment::current();
|
||||||
|
auto utf16String = JStringUtf16Extractor(env, self());
|
||||||
|
if (!utf16String.chars() || utf16String.length() == 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return std::u16string(reinterpret_cast<const char16_t*>(utf16String.chars()), utf16String.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ref<JString> make_jstring(const char* utf8) {
|
local_ref<JString> make_jstring(const char* utf8) {
|
||||||
if (!utf8) {
|
if (!utf8) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto env = internal::getEnv();
|
const auto env = Environment::current();
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
|
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
|
||||||
jstring result;
|
jstring result;
|
||||||
@@ -111,6 +121,18 @@ local_ref<JString> make_jstring(const char* utf8) {
|
|||||||
return adopt_local(result);
|
return adopt_local(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local_ref<JString> make_jstring(const std::u16string& utf16) {
|
||||||
|
if (utf16.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto env = Environment::current();
|
||||||
|
static_assert(
|
||||||
|
sizeof(jchar) == sizeof(std::u16string::value_type),
|
||||||
|
"Expecting jchar to be the same size as std::u16string::CharT");
|
||||||
|
jstring result = env->NewString(reinterpret_cast<const jchar*>(utf16.c_str()), utf16.size());
|
||||||
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
|
||||||
|
return adopt_local(result);
|
||||||
|
}
|
||||||
|
|
||||||
// JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////
|
// JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -119,50 +141,44 @@ local_ref<JString> make_jstring(const char* utf8) {
|
|||||||
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
|
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
FBEXPORT \
|
|
||||||
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
|
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
|
||||||
auto env = internal::getEnv(); \
|
auto env = Environment::current(); \
|
||||||
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
|
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||||
return res; \
|
return res; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
FBEXPORT \
|
|
||||||
void JPrimitiveArray<TYPE ## Array>::releaseElements( \
|
void JPrimitiveArray<TYPE ## Array>::releaseElements( \
|
||||||
TYPE* elements, jint mode) { \
|
TYPE* elements, jint mode) { \
|
||||||
auto env = internal::getEnv(); \
|
auto env = Environment::current(); \
|
||||||
env->Release ## NAME ## ArrayElements(self(), elements, mode); \
|
env->Release ## NAME ## ArrayElements(self(), elements, mode); \
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
FBEXPORT \
|
|
||||||
void JPrimitiveArray<TYPE ## Array>::getRegion( \
|
void JPrimitiveArray<TYPE ## Array>::getRegion( \
|
||||||
jsize start, jsize length, TYPE* buf) { \
|
jsize start, jsize length, TYPE* buf) { \
|
||||||
auto env = internal::getEnv(); \
|
auto env = Environment::current(); \
|
||||||
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
|
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
FBEXPORT \
|
|
||||||
void JPrimitiveArray<TYPE ## Array>::setRegion( \
|
void JPrimitiveArray<TYPE ## Array>::setRegion( \
|
||||||
jsize start, jsize length, const TYPE* elements) { \
|
jsize start, jsize length, const TYPE* elements) { \
|
||||||
auto env = internal::getEnv(); \
|
auto env = Environment::current(); \
|
||||||
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
|
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
|
||||||
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
FBEXPORT \
|
|
||||||
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
|
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
|
||||||
auto array = internal::getEnv()->New ## NAME ## Array(size); \
|
auto array = Environment::current()->New ## NAME ## Array(size); \
|
||||||
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
|
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
|
||||||
return adopt_local(array); \
|
return adopt_local(array); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
template<> \
|
template<> \
|
||||||
FBEXPORT \
|
|
||||||
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
|
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
|
||||||
return make_ ## SMALLNAME ## _array(count); \
|
return make_ ## SMALLNAME ## _array(count); \
|
||||||
} \
|
} \
|
||||||
@@ -178,13 +194,37 @@ DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
|
|||||||
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
|
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
|
||||||
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
|
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
detail::BaseHybridClass* HybridDestructor::getNativePointer() {
|
||||||
|
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||||
|
auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
|
||||||
|
if (!value) {
|
||||||
|
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HybridDestructor::setNativePointer(
|
||||||
|
std::unique_ptr<detail::BaseHybridClass> new_value) {
|
||||||
|
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
|
||||||
|
auto old_value = std::unique_ptr<detail::BaseHybridClass>(
|
||||||
|
reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField)));
|
||||||
|
if (new_value && old_value) {
|
||||||
|
FBJNI_LOGF("Attempt to set C++ native pointer twice");
|
||||||
|
}
|
||||||
|
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Internal debug /////////////////////////////////////////////////////////////////////////////////
|
// Internal debug /////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
FBEXPORT ReferenceStats g_reference_stats;
|
ReferenceStats g_reference_stats;
|
||||||
|
|
||||||
FBEXPORT void facebook::jni::internal::ReferenceStats::reset() noexcept {
|
void facebook::jni::internal::ReferenceStats::reset() noexcept {
|
||||||
locals_deleted = globals_deleted = weaks_deleted = 0;
|
locals_deleted = globals_deleted = weaks_deleted = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,194 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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
|
|
@@ -1,97 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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
|
|
106
lib/fb/src/main/cpp/lyra/cxa_throw.cpp
Normal file
106
lib/fb/src/main/cpp/lyra/cxa_throw.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <atomic>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#include <unwind.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <lyra/lyra_exceptions.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace lyra {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::atomic<bool> enableBacktraces{true};
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableCxaThrowHookBacktraces(bool enable) {
|
||||||
|
enableBacktraces.store(enable, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::noreturn]] void (*original_cxa_throw)(void*, const std::type_info*, void (*) (void *));
|
||||||
|
|
||||||
|
#if defined(_LIBCPP_VERSION)
|
||||||
|
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *)) {
|
||||||
|
// lyra doesn't have support yet for libc++.
|
||||||
|
original_cxa_throw(obj, type, destructor);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
using namespace detail;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const auto traceHolderType =
|
||||||
|
static_cast<const abi::__class_type_info*>(&typeid(ExceptionTraceHolder));
|
||||||
|
|
||||||
|
// lyra's __cxa_throw attaches stack trace information to thrown exceptions. It basically does:
|
||||||
|
// 1. capture stack trace
|
||||||
|
// 2. construct a new type_info struct that:
|
||||||
|
// a. holds the ExceptionTraceHolder
|
||||||
|
// b. supports upcasting to lyra::ExceptionTraceHolder* (by just returning the holder member)
|
||||||
|
// c. acts like the original exception type_info otherwise
|
||||||
|
// 3. call original __cxa_throw() with original exception pointer, the
|
||||||
|
// HijackedExceptionTypeInfo, and HijackedExceptionTypeInfo::destructor
|
||||||
|
// (which will both delete the constructed type info and call the original
|
||||||
|
// destructor).
|
||||||
|
struct HijackedExceptionTypeInfo : public abi::__class_type_info {
|
||||||
|
HijackedExceptionTypeInfo(void* obj, const std::type_info* base, void(*destructor)(void*))
|
||||||
|
: abi::__class_type_info{base->name()}, base_{base}, orig_dest_{destructor} {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __is_pointer_p() const override {
|
||||||
|
return base_->__is_pointer_p();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __is_function_p() const override {
|
||||||
|
return base_->__is_function_p();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __do_catch(const type_info *__thr_type, void **__thr_obj, unsigned __outer) const override {
|
||||||
|
return base_->__do_catch(__thr_type, __thr_obj, __outer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __do_upcast(const abi::__class_type_info *__target, void **__obj_ptr) const override {
|
||||||
|
if (__target == traceHolderType) {
|
||||||
|
*__obj_ptr = (void*)&stack_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return base_->__do_upcast(__target, __obj_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destructor(void* obj) {
|
||||||
|
auto exc_ptr = reinterpret_cast<std::exception_ptr*>(&obj);
|
||||||
|
auto info = reinterpret_cast<const::std::type_info*>(exc_ptr->__cxa_exception_type());
|
||||||
|
auto mutable_info = static_cast<HijackedExceptionTypeInfo*>(const_cast<std::type_info*>(info));
|
||||||
|
mutable_info->orig_dest_(obj);
|
||||||
|
delete mutable_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::type_info* base_;
|
||||||
|
void (*orig_dest_)(void*);
|
||||||
|
ExceptionTraceHolder stack_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
[[noreturn]] void cxa_throw(void* obj, const std::type_info* type, void (*destructor) (void *)) {
|
||||||
|
if (enableBacktraces.load(std::memory_order_relaxed)) {
|
||||||
|
if (!type->__do_upcast(traceHolderType, &obj)) {
|
||||||
|
type = new HijackedExceptionTypeInfo(obj, type, destructor);
|
||||||
|
destructor = HijackedExceptionTypeInfo::destructor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
original_cxa_throw(obj, type, destructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // libc++
|
||||||
|
|
||||||
|
} // namespace lyra
|
||||||
|
} // namespace facebook
|
@@ -4,15 +4,20 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/lyra.h>
|
#include <lyra/lyra.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
|
#include <ostream>
|
||||||
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <unwind.h>
|
#include <unwind.h>
|
||||||
|
|
||||||
|
#include <fbjni/detail/Log.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace facebook {
|
namespace facebook {
|
||||||
@@ -67,6 +72,27 @@ void captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {
|
|||||||
BacktraceState state = {skip, stackTrace};
|
BacktraceState state = {skip, stackTrace};
|
||||||
_Unwind_Backtrace(unwindCallback, &state);
|
_Unwind_Backtrace(unwindCallback, &state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is a pointer to a function
|
||||||
|
std::atomic<LibraryIdentifierFunctionType> gLibraryIdentifierFunction{nullptr};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func) {
|
||||||
|
gLibraryIdentifierFunction.store(func, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string StackTraceElement::buildId() const {
|
||||||
|
if (!hasBuildId_) {
|
||||||
|
auto getBuildId = gLibraryIdentifierFunction.load(std::memory_order_relaxed);
|
||||||
|
if (getBuildId) {
|
||||||
|
buildId_ = getBuildId(libraryName());
|
||||||
|
} else {
|
||||||
|
buildId_ = "<unimplemented>";
|
||||||
|
}
|
||||||
|
hasBuildId_ = true;
|
||||||
|
}
|
||||||
|
return buildId_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {
|
void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {
|
||||||
@@ -93,7 +119,6 @@ void getStackTraceSymbols(vector<StackTraceElement>& symbols,
|
|||||||
ostream& operator<<(ostream& out, const StackTraceElement& elm) {
|
ostream& operator<<(ostream& out, const StackTraceElement& elm) {
|
||||||
IosFlagsSaver flags{out};
|
IosFlagsSaver flags{out};
|
||||||
|
|
||||||
// TODO(t10748683): Add build id to the output
|
|
||||||
out << "{dso=" << elm.libraryName() << " offset=" << hex
|
out << "{dso=" << elm.libraryName() << " offset=" << hex
|
||||||
<< showbase << elm.libraryOffset();
|
<< showbase << elm.libraryOffset();
|
||||||
|
|
||||||
@@ -101,7 +126,7 @@ ostream& operator<<(ostream& out, const StackTraceElement& elm) {
|
|||||||
out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
|
out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
out << " build-id=" << hex << setw(8) << 0
|
out << " build-id=" << hex << setw(8) << elm.buildId()
|
||||||
<< "}";
|
<< "}";
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@@ -120,5 +145,28 @@ ostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void logStackTrace(const vector<StackTraceElement>& trace) {
|
||||||
|
auto i = 0;
|
||||||
|
FBJNI_LOGE("Backtrace:");
|
||||||
|
for (auto& elm : trace) {
|
||||||
|
if (!elm.functionName().empty()) {
|
||||||
|
FBJNI_LOGE(" #%02d |lyra|{dso=%s offset=%#x func=%s+%#x build-id=%s}",
|
||||||
|
i++,
|
||||||
|
elm.libraryName().c_str(),
|
||||||
|
elm.libraryOffset(),
|
||||||
|
elm.functionName().c_str(),
|
||||||
|
elm.functionOffset(),
|
||||||
|
elm.buildId().c_str());
|
||||||
|
} else {
|
||||||
|
FBJNI_LOGE(" #%02d |lyra|{dso=%s offset=%#x build-id=%s}",
|
||||||
|
i++,
|
||||||
|
elm.libraryName().c_str(),
|
||||||
|
elm.libraryOffset(),
|
||||||
|
elm.buildId().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
lib/fb/src/main/cpp/lyra/lyra_breakpad.cpp
Normal file
22
lib/fb/src/main/cpp/lyra/lyra_breakpad.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <lyra/lyra.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace lyra {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This can be overridden by an implementation capable of looking up
|
||||||
|
* the breakpad id for logging purposes.
|
||||||
|
*/
|
||||||
|
__attribute__((weak))
|
||||||
|
std::string getBreakpadId(const std::string& library) {
|
||||||
|
return "<unimplemented>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
88
lib/fb/src/main/cpp/lyra/lyra_exceptions.cpp
Normal file
88
lib/fb/src/main/cpp/lyra/lyra_exceptions.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
#include <lyra/lyra_exceptions.h>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <sstream>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
#include <fbjni/detail/Log.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace lyra {
|
||||||
|
|
||||||
|
using namespace detail;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::terminate_handler gTerminateHandler;
|
||||||
|
|
||||||
|
const ExceptionTraceHolder* getExceptionTraceHolder(std::exception_ptr ptr) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
} catch (const ExceptionTraceHolder& holder) {
|
||||||
|
return &holder;
|
||||||
|
} catch (...) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void logExceptionAndAbort() {
|
||||||
|
if (auto ptr = std::current_exception()) {
|
||||||
|
FBJNI_LOGE("Uncaught exception: %s", toString(ptr).c_str());
|
||||||
|
auto trace = getExceptionTraceHolder(ptr);
|
||||||
|
if (trace) {
|
||||||
|
logStackTrace(getStackTraceSymbols(trace->stackTrace_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gTerminateHandler) {
|
||||||
|
gTerminateHandler();
|
||||||
|
} else {
|
||||||
|
FBJNI_LOGF("Uncaught exception and no gTerminateHandler set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<InstructionPointer> emptyTrace;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ExceptionTraceHolder::~ExceptionTraceHolder() {}
|
||||||
|
|
||||||
|
detail::ExceptionTraceHolder::ExceptionTraceHolder() {
|
||||||
|
// TODO(cjhopman): This should be done more safely (i.e. use preallocated space, etc.).
|
||||||
|
stackTrace_.reserve(128);
|
||||||
|
getStackTrace(stackTrace_, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ensureRegisteredTerminateHandler() {
|
||||||
|
static auto initializer = (gTerminateHandler = std::set_terminate(logExceptionAndAbort));
|
||||||
|
(void)initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<InstructionPointer>& getExceptionTrace(std::exception_ptr ptr) {
|
||||||
|
auto holder = getExceptionTraceHolder(ptr);
|
||||||
|
return holder ? holder->stackTrace_ : emptyTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(std::exception_ptr ptr) {
|
||||||
|
if (!ptr) {
|
||||||
|
return "No exception";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << typeid(e).name() << ": " << e.what();
|
||||||
|
return ss.str();
|
||||||
|
} catch (...) {
|
||||||
|
return "Unknown exception";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the LICENSE
|
|
||||||
* file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
#include <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
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
||||||
|
|
||||||
package com.facebook.jni;
|
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Java Object that has native memory allocated corresponding to this instance.
|
|
||||||
*
|
|
||||||
* NB: THREAD SAFETY (this comment also exists at Countable.cpp)
|
|
||||||
*
|
|
||||||
* {@link #dispose} 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.
|
|
||||||
*/
|
|
||||||
@DoNotStrip
|
|
||||||
public class Countable {
|
|
||||||
|
|
||||||
// Private C++ instance
|
|
||||||
@DoNotStrip
|
|
||||||
private long mInstance = 0;
|
|
||||||
|
|
||||||
public native void dispose();
|
|
||||||
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
dispose();
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +1,12 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class CppException extends RuntimeException {
|
public class CppException extends RuntimeException {
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class CppSystemErrorException extends CppException {
|
public class CppSystemErrorException extends CppException {
|
||||||
|
138
lib/fb/src/main/java/com/facebook/jni/DestructorThread.java
Normal file
138
lib/fb/src/main/java/com/facebook/jni/DestructorThread.java
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.facebook.jni;
|
||||||
|
|
||||||
|
import java.lang.ref.PhantomReference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread which invokes the "destruct" routine for objects after they have been garbage collected.
|
||||||
|
*
|
||||||
|
* <p>An object which needs to be destructed should create a static subclass of {@link Destructor}.
|
||||||
|
* Once the referent object is garbage collected, the DestructorThread will callback to the {@link
|
||||||
|
* Destructor#destruct()} method.
|
||||||
|
*
|
||||||
|
* <p>The underlying thread in DestructorThread starts when the first Destructor is constructed and
|
||||||
|
* then runs indefinitely.
|
||||||
|
*/
|
||||||
|
public class DestructorThread {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* N.B The Destructor <b>SHOULD NOT</b> refer back to its referent object either explicitly or
|
||||||
|
* implicitly (for example, as a non-static inner class). This will create a reference cycle where
|
||||||
|
* the referent object will never be garbage collected.
|
||||||
|
*/
|
||||||
|
public abstract static class Destructor extends PhantomReference<Object> {
|
||||||
|
|
||||||
|
private Destructor next;
|
||||||
|
private Destructor previous;
|
||||||
|
|
||||||
|
public Destructor(Object referent) {
|
||||||
|
super(referent, sReferenceQueue);
|
||||||
|
sDestructorStack.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Destructor() {
|
||||||
|
super(null, sReferenceQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Callback which is invoked when the original object has been garbage collected. */
|
||||||
|
protected abstract void destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A list to keep all active Destructors in memory confined to the Destructor thread. */
|
||||||
|
private static final DestructorList sDestructorList;
|
||||||
|
/** A thread safe stack where new Destructors are placed before being add to sDestructorList. */
|
||||||
|
private static final DestructorStack sDestructorStack;
|
||||||
|
|
||||||
|
private static final ReferenceQueue sReferenceQueue;
|
||||||
|
private static final Thread sThread;
|
||||||
|
|
||||||
|
static {
|
||||||
|
sDestructorStack = new DestructorStack();
|
||||||
|
sReferenceQueue = new ReferenceQueue();
|
||||||
|
sDestructorList = new DestructorList();
|
||||||
|
sThread =
|
||||||
|
new Thread("HybridData DestructorThread") {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Destructor current = (Destructor) sReferenceQueue.remove();
|
||||||
|
current.destruct();
|
||||||
|
|
||||||
|
// If current is in the sDestructorStack,
|
||||||
|
// transfer all the Destructors in the stack to the list.
|
||||||
|
if (current.previous == null) {
|
||||||
|
sDestructorStack.transferAllToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
DestructorList.drop(current);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Continue. This thread should never be terminated.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Terminus extends Destructor {
|
||||||
|
@Override
|
||||||
|
protected void destruct() {
|
||||||
|
throw new IllegalStateException("Cannot destroy Terminus Destructor.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This is a thread safe, lock-free Treiber-like Stack of Destructors. */
|
||||||
|
private static class DestructorStack {
|
||||||
|
private final AtomicReference<Destructor> mHead = new AtomicReference<>();
|
||||||
|
|
||||||
|
public void push(Destructor newHead) {
|
||||||
|
Destructor oldHead;
|
||||||
|
do {
|
||||||
|
oldHead = mHead.get();
|
||||||
|
newHead.next = oldHead;
|
||||||
|
} while (!mHead.compareAndSet(oldHead, newHead));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transferAllToList() {
|
||||||
|
Destructor current = mHead.getAndSet(null);
|
||||||
|
while (current != null) {
|
||||||
|
Destructor next = current.next;
|
||||||
|
sDestructorList.enqueue(current);
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A doubly-linked list of Destructors. */
|
||||||
|
private static class DestructorList {
|
||||||
|
private final Destructor mHead;
|
||||||
|
|
||||||
|
public DestructorList() {
|
||||||
|
mHead = new Terminus();
|
||||||
|
mHead.next = new Terminus();
|
||||||
|
mHead.next.previous = mHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enqueue(Destructor current) {
|
||||||
|
current.next = mHead.next;
|
||||||
|
mHead.next = current;
|
||||||
|
|
||||||
|
current.next.previous = current;
|
||||||
|
current.previous = mHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drop(Destructor current) {
|
||||||
|
current.next.previous = current.previous;
|
||||||
|
current.previous.next = current.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,9 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
package com.facebook.jni;
|
||||||
|
|
||||||
#define FBEXPORT __attribute__((visibility("default")))
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public abstract class HybridClassBase extends HybridData {}
|
@@ -1,44 +1,77 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This object holds a native C++ member for hybrid Java/C++ objects.
|
* This object holds a native C++ member for hybrid Java/C++ objects.
|
||||||
*
|
*
|
||||||
* NB: THREAD SAFETY
|
* <p>NB: THREAD SAFETY
|
||||||
*
|
*
|
||||||
* {@link #dispose} deletes the corresponding native object on whatever thread
|
* <p>{@link #resetNative} deletes the corresponding native object synchronously on whatever thread
|
||||||
* the method is called on. In the common case when this is called by
|
* the method is called on. Otherwise, deletion will occur on the {@link DestructorThread} thread.
|
||||||
* HybridData#finalize(), this will be called on the system finalizer
|
|
||||||
* thread. If you manually call resetNative() on the Java object, the C++
|
|
||||||
* object will be deleted synchronously on that thread.
|
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class HybridData {
|
public class HybridData {
|
||||||
|
|
||||||
// Private C++ instance
|
static {
|
||||||
@DoNotStrip
|
SoLoader.loadLibrary("fbjni");
|
||||||
private long mNativePointer = 0;
|
}
|
||||||
|
|
||||||
|
@DoNotStrip private Destructor mDestructor = new Destructor(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To explicitly delete the instance, call resetNative(). If the C++
|
* To explicitly delete the instance, call resetNative(). If the C++ instance is referenced after
|
||||||
* instance is referenced after this is called, a NullPointerException will
|
* this is called, a NullPointerException will be thrown. resetNative() may be called multiple
|
||||||
* be thrown. resetNative() may be called multiple times safely. Because
|
* times safely. Because the {@link DestructorThread} also calls resetNative, the instance will
|
||||||
* {@link #finalize} calls resetNative, the instance will not leak if this is
|
* not leak if this is not called, but timing of deletion and the thread the C++ dtor is called on
|
||||||
* not called, but timing of deletion and the thread the C++ dtor is called
|
* will be at the whim of the Java GC. If you want to control the thread and timing of the
|
||||||
* on will be at the whim of the Java GC. If you want to control the thread
|
* destructor, you should call resetNative() explicitly.
|
||||||
* and timing of the destructor, you should call resetNative() explicitly.
|
|
||||||
*/
|
*/
|
||||||
public native void resetNative();
|
public synchronized void resetNative() {
|
||||||
|
mDestructor.destruct();
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
resetNative();
|
|
||||||
super.finalize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* N.B. Thread safety. If you call isValid from a different thread than {@link #resetNative()}
|
||||||
|
* then be sure to do so while synchronizing on the hybrid. For example:
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* synchronized(hybrid) {
|
||||||
|
* if (hybrid.isValid) {
|
||||||
|
* // Do stuff.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </code></pre>
|
||||||
|
*/
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return mNativePointer != 0;
|
return mDestructor.mNativePointer != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Destructor extends DestructorThread.Destructor {
|
||||||
|
|
||||||
|
// Private C++ instance
|
||||||
|
@DoNotStrip private long mNativePointer;
|
||||||
|
|
||||||
|
Destructor(Object referent) {
|
||||||
|
super(referent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void destruct() {
|
||||||
|
// When invoked from the DestructorThread instead of resetNative,
|
||||||
|
// the DestructorThread has exclusive ownership of the HybridData
|
||||||
|
// so synchronization is not necessary.
|
||||||
|
deleteNative(mNativePointer);
|
||||||
|
mNativePointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static native void deleteNative(long pointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,31 +1,26 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
import java.util.Iterator;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To iterate over an Iterator from C++ requires two calls per entry: hasNext()
|
* To iterate over an Iterator from C++ requires two calls per entry: hasNext() and next(). This
|
||||||
* and next(). This helper reduces it to one call and one field get per entry.
|
* helper reduces it to one call and one field get per entry. It does not use a generic argument,
|
||||||
* It does not use a generic argument, since in C++, the types will be erased,
|
* since in C++, the types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
|
||||||
* anyway. This is *not* a {@link java.util.Iterator}.
|
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class IteratorHelper {
|
public class IteratorHelper {
|
||||||
private final Iterator mIterator;
|
private final Iterator mIterator;
|
||||||
|
|
||||||
// This is private, but accessed via JNI.
|
// This is private, but accessed via JNI.
|
||||||
@DoNotStrip
|
@DoNotStrip private @Nullable Object mElement;
|
||||||
private @Nullable Object mElement;
|
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public IteratorHelper(Iterator iterator) {
|
public IteratorHelper(Iterator iterator) {
|
||||||
@@ -38,8 +33,8 @@ public class IteratorHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the helper to the next entry in the map, if any. Returns true iff
|
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
|
||||||
* there is an entry to read.
|
* read.
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
boolean hasNext() {
|
boolean hasNext() {
|
||||||
|
@@ -1,24 +1,21 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To iterate over a Map from C++ requires four calls per entry: hasNext(),
|
* To iterate over a Map from C++ requires four calls per entry: hasNext(), next(), getKey(),
|
||||||
* next(), getKey(), getValue(). This helper reduces it to one call and two
|
* getValue(). This helper reduces it to one call and two field gets per entry. It does not use a
|
||||||
* field gets per entry. It does not use a generic argument, since in C++, the
|
* generic argument, since in C++, the types will be erased, anyway. This is *not* a {@link
|
||||||
* types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
|
* java.util.Iterator}.
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class MapIteratorHelper {
|
public class MapIteratorHelper {
|
||||||
@@ -32,8 +29,8 @@ public class MapIteratorHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the helper to the next entry in the map, if any. Returns true iff
|
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
|
||||||
* there is an entry to read.
|
* read.
|
||||||
*/
|
*/
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
boolean hasNext() {
|
boolean hasNext() {
|
||||||
|
@@ -1,18 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.jni.HybridData;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
|
||||||
|
|
||||||
/**
|
/** A Runnable that has a native run implementation. */
|
||||||
* A Runnable that has a native run implementation.
|
|
||||||
*/
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class NativeRunnable implements Runnable {
|
public class NativeRunnable implements Runnable {
|
||||||
|
|
||||||
|
@@ -1,11 +1,20 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class ThreadScopeSupport {
|
public class ThreadScopeSupport {
|
||||||
|
static {
|
||||||
|
SoLoader.loadLibrary("fbjni");
|
||||||
|
}
|
||||||
|
|
||||||
// This is just used for ThreadScope::withClassLoader to have a java function
|
// This is just used for ThreadScope::withClassLoader to have a java function
|
||||||
// in the stack so that jni has access to the correct classloader.
|
// in the stack so that jni has access to the correct classloader.
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
/*
|
/**
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
*
|
*
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* LICENSE file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.jni;
|
package com.facebook.jni;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.jni.annotations.DoNotStrip;
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class UnknownCppException extends CppException {
|
public class UnknownCppException extends CppException {
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.facebook.jni.annotations;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.CLASS;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this annotation to a class, method, or field to instruct Proguard to not strip it out.
|
||||||
|
*
|
||||||
|
* This is useful for methods called via reflection that could appear as unused to Proguard.
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
|
||||||
|
@Retention(CLASS)
|
||||||
|
public @interface DoNotStrip {
|
||||||
|
}
|
@@ -4,7 +4,7 @@
|
|||||||
* This source code is licensed under the MIT license found in the LICENSE
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
* file in the root directory of this source tree.
|
* file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
#include <fb/fbjni.h>
|
#include <fbjni/fbjni.h>
|
||||||
#include <yoga/testutil/testutil.h>
|
#include <yoga/testutil/testutil.h>
|
||||||
|
|
||||||
using namespace facebook;
|
using namespace facebook;
|
||||||
|
Reference in New Issue
Block a user