Remove fbjni remnants

Summary: D42207782 moved Yoga to use vanilla JNI instead of fbjni, but there are some remnants left. One is a source copy being used to build lib/fb. The others are some targets/definitions left in Buck logic. This removes those, to prevent confusion.

Reviewed By: christophpurrer

Differential Revision: D42247773

fbshipit-source-id: ef9d831957948a183c39aac782ce869011e74fea
This commit is contained in:
Nick Gerleman
2022-12-28 01:21:52 -08:00
committed by Facebook GitHub Bot
parent 08eaae7223
commit 8035456330
66 changed files with 1 additions and 8343 deletions

View File

@@ -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.
load("@fbsource//tools/build_defs:platform_defs.bzl", "ANDROID", "APPLE", "CXX", "FBCODE", "WINDOWS")
load("//tools/build_defs/oss:yoga_defs.bzl", "JNI_TARGET", "YOGA_ROOTS", "subdir_glob", "yoga_cxx_library", "yoga_prebuilt_cxx_library")
yoga_prebuilt_cxx_library(
name = "ndklog",
exported_platform_linker_flags = [
(
"^android.*",
["-llog"],
),
],
header_only = True,
platforms = (ANDROID, APPLE, CXX, FBCODE, WINDOWS),
visibility = YOGA_ROOTS,
)
yoga_cxx_library(
name = "fbjni",
srcs = glob(["src/main/cpp/**/*.cpp"]),
header_namespace = "",
exported_headers = subdir_glob([("src/main/cpp/include", "**/*.h")]),
compiler_flags = [
"-DLOG_TAG=\"libfb\"",
"-DDISABLE_CPUCAP",
"-DDISABLE_XPLAT",
"-DHAVE_POSIX_CLOCKS",
"-fno-omit-frame-pointer",
"-fexceptions",
"-frtti",
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wno-unused-variable",
"-std=c++11",
],
platforms = (CXX, ANDROID),
soname = "libfbjni.$(ext)",
visibility = ["PUBLIC"],
deps = [
":ndklog",
JNI_TARGET,
],
)

View File

@@ -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.
*/
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
ndk {
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
externalNativeBuild {
cmake {
arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static'
}
}
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
dependencies {
implementation 'com.facebook.soloader:soloader:0.10.4'
implementation 'com.google.code.findbugs:jsr305:3.0.2'
implementation project(':yoga:proguard-annotations')
}
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.libfb">
</manifest>

View File

@@ -1,32 +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.
#
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fno-omit-frame-pointer
-fexceptions
-Wall
-std=c++11
-DDISABLE_CPUCAP
-DDISABLE_XPLAT)
file(GLOB fb_SRC
*.cpp
jni/*.cpp
jni/detail/*.cpp
lyra/*.cpp)
add_library(fb SHARED
${fb_SRC})
target_include_directories(fb PRIVATE
include)
target_link_libraries(fb android log)

View File

@@ -1,15 +0,0 @@
PROJECT_NAME = "Facebook Android Support"
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
RECURSIVE = YES
EXCLUDE = tests
EXCLUDE_PATTERNS = *.cpp
GENERATE_HTML = YES
GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
HIDE_UNDOC_MEMBERS = YES
HIDE_SCOPE_NAMES = YES
HIDE_FRIEND_COMPOUNDS = YES
HIDE_UNDOC_CLASSES = YES
SHOW_INCLUDE_FILES = NO
#ENABLED_SECTIONS = INTERNAL

View File

@@ -1,18 +0,0 @@
PROJECT_NAME = "Facebook JNI"
PROJECT_BRIEF = "Helper library to provide safe and convenient access to JNI with very low overhead"
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
RECURSIVE = YES
EXCLUDE = tests Asserts.h Countable.h GlobalReference.h LocalReference.h LocalString.h Registration.h WeakReference.h jni_helpers.h Environment.h
EXCLUDE_PATTERNS = *-inl.h *.cpp
GENERATE_HTML = YES
GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
HIDE_UNDOC_MEMBERS = YES
HIDE_SCOPE_NAMES = YES
HIDE_FRIEND_COMPOUNDS = YES
HIDE_UNDOC_CLASSES = YES
SHOW_INCLUDE_FILES = NO
PREDEFINED = LOG_TAG=fbjni
EXAMPLE_PATH = samples
#ENABLED_SECTIONS = INTERNAL

View File

@@ -1,42 +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 <fbjni/fbjni.h>
namespace facebook {
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
// class provides much more user-friendly access.
class JByteBuffer : public JavaClass<JByteBuffer, JBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
static local_ref<JByteBuffer> allocateDirect(jint size);
uint8_t* getDirectBytes() const {
return static_cast<uint8_t*>(getDirectAddress());
}
size_t getDirectSize() const {
return getDirectCapacity();
}
};
}}

View File

@@ -1,32 +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 <fbjni/fbjni.h>
#include <fbjni/File.h>
namespace facebook {
namespace jni {
class AContext : public JavaClass<AContext> {
public:
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
// Define a method that calls into the represented Java class
local_ref<JFile::javaobject> getCacheDir() {
static const auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
return method(self());
}
local_ref<JFile::javaobject> getFilesDir() {
static const auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
return method(self());
}
};
}
}

View File

@@ -1,27 +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 <fbjni/fbjni.h>
namespace facebook {
namespace jni {
class JFile : public JavaClass<JFile> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
// Define a method that calls into the represented Java class
std::string getAbsolutePath() {
static const auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
return method(self())->toStdString();
}
};
}
}

View File

@@ -1,56 +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 <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);
}
};
}
}

View File

@@ -1,42 +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 <fbjni/fbjni.h>
#include <functional>
namespace facebook {
namespace jni {
struct JRunnable : public JavaClass<JRunnable> {
static auto constexpr kJavaDescriptor = "Ljava/lang/Runnable;";
};
struct JNativeRunnable : public HybridClass<JNativeRunnable, JRunnable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/NativeRunnable;";
JNativeRunnable(std::function<void()>&& runnable) : runnable_(std::move(runnable)) {}
static void OnLoad() {
registerHybrid({
makeNativeMethod("run", JNativeRunnable::run),
});
}
void run() {
runnable_();
}
private:
std::function<void()> runnable_;
};
} // namespace jni
} // namespace facebook

View File

@@ -1,22 +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 <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;
};
}}

View File

@@ -1,74 +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"
namespace facebook {
namespace jni {
namespace detail {
template <typename T, typename jprim>
struct JPrimitive : JavaClass<T> {
using typename JavaClass<T>::javaobject;
using JavaClass<T>::javaClassStatic;
static local_ref<javaobject> valueOf(jprim val) {
static const auto cls = javaClassStatic();
static const auto method =
cls->template getStaticMethod<javaobject(jprim)>("valueOf");
return method(cls, val);
}
jprim value() const {
static const auto method =
javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);
return method(this->self());
}
};
} // namespace detail
#define DEFINE_BOXED_PRIMITIVE(LITTLE, BIG) \
struct J ## BIG : detail::JPrimitive<J ## BIG, j ## LITTLE> { \
static auto constexpr kJavaDescriptor = "Ljava/lang/" #BIG ";"; \
static auto constexpr kValueMethod = #LITTLE "Value"; \
j ## LITTLE LITTLE ## Value() const { \
return value(); \
} \
}; \
inline local_ref<jobject> autobox(j ## LITTLE val) { \
return J ## BIG::valueOf(val); \
}
DEFINE_BOXED_PRIMITIVE(boolean, Boolean)
DEFINE_BOXED_PRIMITIVE(byte, Byte)
DEFINE_BOXED_PRIMITIVE(char, Character)
DEFINE_BOXED_PRIMITIVE(short, Short)
DEFINE_BOXED_PRIMITIVE(int, Integer)
DEFINE_BOXED_PRIMITIVE(long, Long)
DEFINE_BOXED_PRIMITIVE(float, Float)
DEFINE_BOXED_PRIMITIVE(double, Double)
#undef DEFINE_BOXED_PRIMITIVE
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) {
return make_local(val);
}
}}

View File

@@ -1,90 +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 Common.h
*
* Defining the stuff that don't deserve headers of their own...
*/
#pragma once
#include <functional>
#include <jni.h>
#ifdef FBJNI_DEBUG_REFS
# ifdef __ANDROID__
# include <android/log.h>
# else
# include <cstdio>
# endif
#endif
// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as
// a C++ exception.
#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \
::facebook::jni::throwPendingJniExceptionAsCppException()
// If the condition is true, throws a JniException object, which wraps the pending JNI Java
// exception if any. If no pending exception is found, throws a JniException object that wraps a
// RuntimeException throwable. 
#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \
::facebook::jni::throwCppExceptionIf(CONDITION)
/// @cond INTERNAL
namespace facebook {
namespace jni {
void throwPendingJniExceptionAsCppException();
void throwCppExceptionIf(bool condition);
[[noreturn]] void throwNewJavaException(jthrowable);
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* msg);
template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);
/**
* This needs to be called at library load time, typically in your JNI_OnLoad method.
*
* The intended use is to return the result of initialize() directly
* from JNI_OnLoad and to do nothing else there. Library specific
* initialization code should go in the function passed to initialize
* (which can be, and probably should be, a C++ lambda). This approach
* provides correct error handling and translation errors during
* initialization into Java exceptions when appropriate.
*
* Failure to call this will cause your code to crash in a remarkably
* unhelpful way (typically a segfault) while trying to handle an exception
* which occurs later.
*/
jint initialize(JavaVM*, std::function<void()>&&) noexcept;
namespace internal {
// Define to get extremely verbose logging of references and to enable reference stats
#ifdef FBJNI_DEBUG_REFS
template<typename... Args>
inline void dbglog(const char* msg, Args... args) {
# ifdef __ANDROID__
__android_log_print(ANDROID_LOG_VERBOSE, "fbjni_dbg", msg, args...);
# else
std::fprintf(stderr, msg, args...);
# endif
}
#else
template<typename... Args>
inline void dbglog(const char*, Args...) {
}
#endif
}}}
/// @endcond

View File

@@ -1,677 +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.h>
#include <type_traits>
#include <stdlib.h>
#include "Common.h"
#include "Exceptions.h"
#include "Meta.h"
#include "MetaConvert.h"
namespace facebook {
namespace jni {
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
return Environment::current()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
}
inline local_ref<JClass> JObject::getClass() const noexcept {
return adopt_local(Environment::current()->GetObjectClass(self()));
}
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
return Environment::current()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
}
template<typename T>
inline T JObject::getFieldValue(JField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
field.set(self(), value);
}
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 {
static const auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
return method(self())->toStdString();
}
// Class is here instead of CoreClasses.h because we need
// alias_ref to be complete.
class MonitorLock {
public:
inline MonitorLock() noexcept;
inline MonitorLock(alias_ref<JObject> object) noexcept;
inline ~MonitorLock() noexcept;
inline MonitorLock(MonitorLock&& other) noexcept;
inline MonitorLock& operator=(MonitorLock&& other) noexcept;
inline MonitorLock(const MonitorLock&) = delete;
inline MonitorLock& operator=(const MonitorLock&) = delete;
private:
inline void reset() noexcept;
alias_ref<JObject> owned_;
};
MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
: owned_(object) {
Environment::current()->MonitorEnter(object.get());
}
void MonitorLock::reset() noexcept {
if (owned_) {
Environment::current()->MonitorExit(owned_.get());
if (Environment::current()->ExceptionCheck()) {
abort(); // Lock mismatch
}
owned_ = nullptr;
}
}
MonitorLock::~MonitorLock() noexcept {
reset();
}
MonitorLock::MonitorLock(MonitorLock&& other) noexcept
: owned_(other.owned_)
{
other.owned_ = nullptr;
}
MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
reset();
owned_ = other.owned_;
other.owned_ = nullptr;
return *this;
}
inline MonitorLock JObject::lock() const noexcept {
return MonitorLock(this_);
}
inline jobject JObject::self() const noexcept {
return this_;
}
inline void swap(JObject& a, JObject& b) noexcept {
using std::swap;
swap(a.this_, b.this_);
}
// JavaClass ///////////////////////////////////////////////////////////////////////////////////////
namespace detail {
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args) {
static auto cls = JC::javaClassStatic();
static const auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
return cls->newObject(constructor, args...);
}
}
template <typename T, typename B, typename J>
auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
return static_cast<javaobject>(JObject::self());
}
// jclass //////////////////////////////////////////////////////////////////////////////////////////
namespace detail {
// This is not a real type. It is used so people won't accidentally
// use a void* to initialize a NativeMethod.
struct NativeMethodWrapper;
}
struct NativeMethod {
const char* name;
std::string descriptor;
detail::NativeMethodWrapper* wrapper;
};
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
return adopt_local(Environment::current()->GetSuperclass(self()));
}
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
const auto env = Environment::current();
JNINativeMethod jnimethods[methods.size()];
size_t i = 0;
for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {
// The JNI struct members are unnecessarily non-const.
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);
}
auto result = env->RegisterNatives(self(), jnimethods, methods.size());
FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
}
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
const auto env = Environment::current();
// 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;
}
template<typename F>
inline JConstructor<F> JClass::getConstructor() const {
return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());
}
template<typename F>
inline JConstructor<F> JClass::getConstructor(const char* descriptor) const {
constexpr auto constructor_method_name = "<init>";
return getMethod<F>(constructor_method_name, descriptor);
}
template<typename F>
inline JMethod<F> JClass::getMethod(const char* name) const {
return getMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JMethod<F> JClass::getMethod(
const char* name,
const char* descriptor) const {
const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JMethod<F>{method};
}
template<typename F>
inline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {
return getStaticMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JStaticMethod<F> JClass::getStaticMethod(
const char* name,
const char* descriptor) const {
const auto env = Environment::current();
const auto method = env->GetStaticMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JStaticMethod<F>{method};
}
template<typename F>
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(const char* name) const {
return getNonvirtualMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());
}
template<typename F>
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
const char* name,
const char* descriptor) const {
const auto env = Environment::current();
const auto method = env->GetMethodID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);
return JNonvirtualMethod<F>{method};
}
template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>>
JClass::getField(const char* name) const {
return getField<T>(name, jtype_traits<T>::descriptor().c_str());
}
template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
const char* name,
const char* descriptor) const {
const auto env = Environment::current();
auto field = env->GetFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JField<T>{field};
}
template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name) const {
return getStaticField<T>(name, jtype_traits<T>::descriptor().c_str());
}
template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name,
const char* descriptor) const {
const auto env = Environment::current();
auto field = env->GetStaticFieldID(self(), name, descriptor);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);
return JStaticField<T>{field};
}
template<typename T>
inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
field.set(self(), value);
}
template<typename T, typename>
inline void JClass::setStaticFieldValue(JStaticField<T> field, alias_ref<T> value) noexcept {
setStaticFieldValue(field, value.get());
}
template<typename R, typename... Args>
inline local_ref<R> JClass::newObject(
JConstructor<R(Args...)> constructor,
Args... args) const {
const auto env = Environment::current();
auto object = env->NewObject(self(), constructor.getId(),
detail::callToJni(
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);
return adopt_local(static_cast<R>(object));
}
inline jclass JClass::self() const noexcept {
return static_cast<jclass>(JObject::self());
}
inline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {
findClassLocal(name)->registerNatives(methods);
}
// jstring /////////////////////////////////////////////////////////////////////////////////////////
inline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {
return make_jstring(modifiedUtf8.c_str());
}
namespace detail {
// convert to std::string from jstring
template <>
struct Convert<std::string> {
typedef jstring jniType;
static std::string fromJni(jniType t) {
return wrap_alias(t)->toStdString();
}
static jniType toJniRet(const std::string& t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const std::string& t) {
return make_jstring(t);
}
};
// convert return from const char*
template <>
struct Convert<const char*> {
typedef jstring jniType;
// no automatic synthesis of const char*. (It can't be freed.)
static jniType toJniRet(const char* t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const char* t) {
return make_jstring(t);
}
};
}
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
namespace detail {
inline size_t JArray::size() const noexcept {
const auto env = Environment::current();
return env->GetArrayLength(self());
}
}
namespace detail {
template<typename Target>
inline ElementProxy<Target>::ElementProxy(
Target* target,
size_t idx)
: target_{target}, idx_{idx} {}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
target_->setElement(idx_, o);
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const ElementProxy<Target>& o) {
auto src = o.target_->getElement(o.idx_);
target_->setElement(idx_, src.get());
return *this;
}
template<typename Target>
inline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {
return target_->getElement(idx_);
}
template<typename Target>
inline ElementProxy<Target>::ElementProxy::operator local_ref<T> () {
return target_->getElement(idx_);
}
}
template <typename T>
std::string JArrayClass<T>::get_instantiated_java_descriptor() {
return "[" + jtype_traits<T>::descriptor();
};
template <typename T>
std::string JArrayClass<T>::get_instantiated_base_name() {
return get_instantiated_java_descriptor();
};
template<typename T>
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
static const auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
const auto env = Environment::current();
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
return adopt_local(static_cast<javaobject>(rawArray));
}
template<typename T>
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
const auto env = Environment::current();
env->SetObjectArrayElement(this->self(), idx, value);
}
template<typename T>
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
const auto env = Environment::current();
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
return adopt_local(static_cast<T>(rawElement));
}
template<typename T>
inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {
return detail::ElementProxy<JArrayClass<T>>(this, index);
}
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 /////////////////////////////////////////////////////////////////////////////////////////
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)
-> std::unique_ptr<T[]> {
auto buf = std::unique_ptr<T[]>{new T[length]};
getRegion(start, length, buf.get());
return buf;
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
return jtype_traits<JArrayType>::descriptor();
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
return JPrimitiveArray::get_instantiated_java_descriptor();
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinCritical()
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
}
template <typename T>
class PinnedArrayAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
(void) start;
(void) length;
*elements = array->getElements(isCopy);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
(void) start;
(void) size;
array->releaseElements(elements, mode);
}
};
template <typename T>
class PinnedCriticalAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
(void)start;
(void)length;
const auto env = Environment::current();
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
(void)start;
(void)size;
const auto env = Environment::current();
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
}
};
template <typename T>
class PinnedRegionAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
auto buf = array->getRegion(start, length);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
*elements = buf.release();
*size = length;
*isCopy = true;
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
std::unique_ptr<T[]> holder;
if (mode == 0 || mode == JNI_ABORT) {
holder.reset(elements);
}
if (mode == 0 || mode == JNI_COMMIT) {
array->setRegion(start, size, elements);
}
}
};
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
template<typename T, typename Alloc>
PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
*this = std::move(o);
}
template<typename T, typename Alloc>
PinnedPrimitiveArray<T, Alloc>&
PinnedPrimitiveArray<T, Alloc>::operator=(PinnedPrimitiveArray&& o) {
if (array_) {
release();
}
array_ = std::move(o.array_);
elements_ = o.elements_;
isCopy_ = o.isCopy_;
size_ = o.size_;
start_ = o.start_;
o.clear();
return *this;
}
template<typename T, typename Alloc>
T* PinnedPrimitiveArray<T, Alloc>::get() {
return elements_;
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::release() {
releaseImpl(0);
clear();
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::commit() {
releaseImpl(JNI_COMMIT);
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::abort() {
releaseImpl(JNI_ABORT);
clear();
}
template <typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);
Alloc::release(array_, elements_, start_, size_, mode);
}
template<typename T, typename Alloc>
inline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {
array_ = nullptr;
elements_ = nullptr;
isCopy_ = false;
start_ = 0;
size_ = 0;
}
template<typename T, typename Alloc>
inline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {
FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);
return elements_[index];
}
template<typename T, typename Alloc>
inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
return isCopy_ == JNI_TRUE;
}
template<typename T, typename Alloc>
inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
return size_;
}
template<typename T, typename Alloc>
inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
if (elements_) {
release();
}
}
template<typename T, typename Alloc>
inline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(alias_ref<typename jtype_traits<T>::array_type> array, jint start, jint length) {
array_ = array;
start_ = start;
Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);
}
template<typename T, typename Base, typename JType>
inline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {
static auto cls = findClassStatic(jtype_traits<typename T::javaobject>::base_name().c_str());
return cls;
}
template<typename T, typename Base, typename JType>
inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());
return findClassLocal(className.c_str());
}
}}

View File

@@ -1,625 +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
/** @file CoreClasses.h
*
* In CoreClasses.h wrappers for the core classes (jobject, jclass, and jstring) is defined
* to provide access to corresponding JNI functions + some conveniance.
*/
#include "References-forward.h"
#include "Meta-forward.h"
#include "TypeTraits.h"
#include <memory>
#include <jni.h>
namespace facebook {
namespace jni {
class JClass;
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
/// points to a leaked global reference. This is appropriate for classes
/// that are never unloaded (which is any class in an Android app and most
/// Java programs).
///
/// The most common use case for this is storing the result
/// in a "static auto" variable, or a static global.
///
/// @return Returns a leaked global reference to the class
alias_ref<JClass> findClassStatic(const char* name);
/// Lookup a class by name. Note this functions returns a local reference,
/// which means that it must not be stored in a static variable.
///
/// The most common use case for this is one-time initialization
/// (like caching method ids).
///
/// @return Returns a global reference to the class
local_ref<JClass> findClassLocal(const char* name);
/// Check to see if two references refer to the same object. Comparison with nullptr
/// returns true if and only if compared to another nullptr. A weak reference that
/// refers to a reclaimed object count as nullptr.
bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
// Together, these classes allow convenient use of any class with the fbjni
// helpers. To use:
//
// struct MyClass : public JavaClass<MyClass> {
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
// };
//
// Then, an alias_ref<MyClass::javaobject> will be backed by an instance of
// MyClass. JavaClass provides a convenient way to add functionality to these
// smart references.
//
// For example:
//
// struct MyClass : public JavaClass<MyClass> {
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
//
// void foo() {
// static const auto method = javaClassStatic()->getMethod<void()>("foo");
// method(self());
// }
//
// static local_ref<javaobject> create(int i) {
// return newInstance(i);
// }
// };
//
// auto obj = MyClass::create(10);
// obj->foo();
//
// While users of a JavaClass-type can lookup methods and fields through the
// underlying JClass, those calls can only be checked at runtime. It is recommended
// that the JavaClass-type instead explicitly expose it's methods as in the example
// above.
namespace detail {
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args);
}
class MonitorLock;
class JObject : detail::JObjectBase {
public:
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }
static constexpr const char* get_instantiated_base_name() { return nullptr; }
/// Get a @ref local_ref of the object's class
local_ref<JClass> getClass() const noexcept;
/// Checks if the object is an instance of a class
bool isInstanceOf(alias_ref<JClass> cls) const noexcept;
/// Get the primitive value of a field
template<typename T>
T getFieldValue(JField<T> field) const noexcept;
/// Get and wrap the value of a field in a @ref local_ref
template<typename T>
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
/// Set the value of field. Any Java type is accepted.
template<typename T>
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
std::string toString() const;
// Take this object's monitor lock
MonitorLock lock() const noexcept;
typedef _jobject _javaobject;
typedef _javaobject* javaobject;
protected:
jobject self() const noexcept;
private:
friend void swap(JObject& a, JObject& b) noexcept;
template<typename>
friend struct detail::ReprAccess;
template<typename, typename, typename>
friend class JavaClass;
template <typename, typename>
friend class JObjectWrapper;
};
// This is only to maintain backwards compatibility with things that are
// already providing a specialization of JObjectWrapper. Any such instances
// should be updated to use a JavaClass.
template<>
class JObjectWrapper<jobject> : public JObject {
};
namespace detail {
template <typename, typename Base, typename JType>
struct JTypeFor {
static_assert(
std::is_base_of<
std::remove_pointer<jobject>::type,
typename std::remove_pointer<JType>::type
>::value, "");
using _javaobject = typename std::remove_pointer<JType>::type;
using javaobject = JType;
};
template <typename T, typename Base>
struct JTypeFor<T, Base, void> {
// JNI pattern for jobject assignable pointer
struct _javaobject : Base::_javaobject {
// This allows us to map back to the defining type (in ReprType, for
// example).
typedef T JniRefRepr;
};
using javaobject = _javaobject*;
};
}
// JavaClass provides a method to inform fbjni about user-defined Java types.
// Given a class:
// struct Foo : JavaClass<Foo> {
// static constexpr auto kJavaDescriptor = "Lcom/example/package/Foo;";
// };
// fbjni can determine the java type/method signatures for Foo::javaobject and
// smart refs (like alias_ref<Foo::javaobject>) will hold an instance of Foo
// and provide access to it through the -> and * operators.
//
// The "Base" template argument can be used to specify the JavaClass superclass
// of this type (for instance, JString's Base is JObject).
//
// The "JType" template argument is used to provide a jni type (like jstring,
// jthrowable) to be used as javaobject. This should only be necessary for
// built-in jni types and not user-defined ones.
template <typename T, typename Base = JObject, typename JType = void>
class JavaClass : public Base {
using JObjType = typename detail::JTypeFor<T, Base, JType>;
public:
using _javaobject = typename JObjType::_javaobject;
using javaobject = typename JObjType::javaobject;
using JavaBase = JavaClass;
static alias_ref<JClass> javaClassStatic();
static local_ref<JClass> javaClassLocal();
protected:
/// Allocates a new object and invokes the specified constructor
/// Like JClass's getConstructor, this function can only check at runtime if
/// the class actually has a constructor that accepts the corresponding types.
/// While a JavaClass-type can expose this function directly, it is recommended
/// to instead to use this to explicitly only expose those constructors that
/// the Java class actually has (i.e. with static create() functions).
template<typename... Args>
static local_ref<T> newInstance(Args... args) {
return detail::newInstance<T>(args...);
}
javaobject self() const noexcept;
};
/// Wrapper to provide functionality to jclass references
struct NativeMethod;
class JClass : public JavaClass<JClass, JObject, jclass> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
/// Get a @local_ref to the super class of this class
local_ref<JClass> getSuperclass() const noexcept;
/// Register native methods for the class. Usage looks like this:
///
/// classRef->registerNatives({
/// makeNativeMethod("nativeMethodWithAutomaticDescriptor",
/// methodWithAutomaticDescriptor),
/// makeNativeMethod("nativeMethodWithExplicitDescriptor",
/// "(Lcom/facebook/example/MyClass;)V",
/// methodWithExplicitDescriptor),
/// makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED("criticalNativeMethodWithAutomaticDescriptor",
/// criticalNativeMethodWithAutomaticDescriptor),
/// makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED("criticalNativeMethodWithExplicitDescriptor",
/// "(IIF)Z",
/// criticalNativeMethodWithExplicitDescriptor),
/// });
///
/// By default, C++ exceptions raised will be converted to Java exceptions.
/// To avoid this and get the "standard" JNI behavior of a crash when a C++
/// exception is crashing out of the JNI method, declare the method noexcept.
/// This does NOT apply to critical native methods, where exceptions causes
/// a crash.
void registerNatives(std::initializer_list<NativeMethod> methods);
/// Check to see if the class is assignable from another class
/// @pre cls != nullptr
bool isAssignableFrom(alias_ref<JClass> cls) const noexcept;
/// Convenience method to lookup the constructor with descriptor as specified by the
/// type arguments
template<typename F>
JConstructor<F> getConstructor() const;
/// Convenience method to lookup the constructor with specified descriptor
template<typename F>
JConstructor<F> getConstructor(const char* descriptor) const;
/// Look up the method with given name and descriptor as specified with the type arguments
template<typename F>
JMethod<F> getMethod(const char* name) const;
/// Look up the method with given name and descriptor
template<typename F>
JMethod<F> getMethod(const char* name, const char* descriptor) const;
/// Lookup the field with the given name and deduced descriptor
template<typename T>
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name) const;
/// Lookup the field with the given name and descriptor
template<typename T>
JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name, const char* descriptor) const;
/// Lookup the static field with the given name and deduced descriptor
template<typename T>
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(const char* name) const;
/// Lookup the static field with the given name and descriptor
template<typename T>
JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(
const char* name,
const char* descriptor) const;
/// Get the primitive value of a static field
template<typename T>
T getStaticFieldValue(JStaticField<T> field) const noexcept;
/// Get and wrap the value of a field in a @ref local_ref
template<typename T>
local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;
/// Set the value of field. Any Java type is accepted.
template<typename T>
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
template<typename R, typename... Args>
local_ref<R> newObject(JConstructor<R(Args...)> constructor, Args... args) const;
/// Look up the static method with given name and descriptor as specified with the type arguments
template<typename F>
JStaticMethod<F> getStaticMethod(const char* name) const;
/// Look up the static method with given name and descriptor
template<typename F>
JStaticMethod<F> getStaticMethod(const char* name, const char* descriptor) const;
/// Look up the non virtual method with given name and descriptor as specified with the
/// type arguments
template<typename F>
JNonvirtualMethod<F> getNonvirtualMethod(const char* name) const;
/// Look up the non virtual method with given name and descriptor
template<typename F>
JNonvirtualMethod<F> getNonvirtualMethod(const char* name, const char* descriptor) const;
private:
jclass self() const noexcept;
};
// Convenience method to register methods on a class without holding
// onto the class object.
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
/// Wrapper to provide functionality to jstring references
class JString : public JavaClass<JString, JObject, jstring> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
/// Convenience method to convert a jstring object to a std::string
std::string toStdString() const;
/// Convenience method to convert a jstring object to a std::u16string
std::u16string toU16String() const;
};
/// Convenience functions to convert a const char*, std::string, or std::u16string
/// into a @ref local_ref to a jstring.
local_ref<JString> make_jstring(const char* modifiedUtf8);
local_ref<JString> make_jstring(const std::string& modifiedUtf8);
local_ref<JString> make_jstring(const std::u16string& utf16);
namespace detail {
template<typename Target>
class ElementProxy {
private:
Target* target_;
size_t idx_;
public:
using T = typename Target::javaentry;
ElementProxy(Target* target, size_t idx);
ElementProxy& operator=(const T& o);
ElementProxy& operator=(alias_ref<T>& o);
ElementProxy& operator=(alias_ref<T>&& o);
ElementProxy& operator=(const ElementProxy& o);
operator const local_ref<T> () const;
operator local_ref<T> ();
};
}
namespace detail {
class JArray : public JavaClass<JArray, JObject, jarray> {
public:
// This cannot be used in a scope that derives a descriptor (like in a method
// signature). Use a more derived type instead (like JArrayInt or
// JArrayClass<T>).
static constexpr const char* kJavaDescriptor = nullptr;
size_t size() const noexcept;
};
// This is used so that the JArrayClass<T> javaobject extends jni's
// jobjectArray. This class should not be used directly. A general Object[]
// should use JArrayClass<jobject>.
class JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
// This cannot be used in a scope that derives a descriptor (like in a method
// signature).
static constexpr const char* kJavaDescriptor = nullptr;
};
}
template<typename T>
class JArrayClass : public JavaClass<JArrayClass<T>, detail::JTypeArray> {
public:
static_assert(is_plain_jni_reference<T>(), "");
// javaentry is the jni type of an entry in the array (i.e. jint).
using javaentry = T;
// javaobject is the jni type of the array.
using javaobject = typename JavaClass<JArrayClass<T>, detail::JTypeArray>::javaobject;
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor();
static std::string get_instantiated_base_name();
/// Allocate a new array from Java heap, for passing as a JNI parameter or return value.
/// NOTE: if using as a return value, you want to call release() instead of get() on the
/// smart pointer.
static local_ref<javaobject> newArray(size_t count);
/// Assign an object to the array.
/// Typically you will use the shorthand (*ref)[idx]=value;
void setElement(size_t idx, const T& value);
/// Read an object from the array.
/// Typically you will use the shorthand
/// T value = (*ref)[idx];
/// If you use auto, you'll get an ElementProxy, which may need to be cast.
local_ref<T> getElement(size_t idx);
/// EXPERIMENTAL SUBSCRIPT SUPPORT
/// This implementation of [] returns a proxy object which then has a bunch of specializations
/// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can
/// make code look like it is dealing with a T rather than an obvious proxy. In particular, the
/// proxy in this iteration does not read a value and therefore does not create a LocalRef
/// until one of these other operators is used. There are certainly holes that you may find
/// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand,
/// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests
/// for some examples.
detail::ElementProxy<JArrayClass> operator[](size_t idx);
};
template <typename T>
using jtypeArray = typename JArrayClass<T>::javaobject;
template<typename T>
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref);
template<typename Target>
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
return static_cast<local_ref<typename Target::javaentry>>(elementProxy);
}
template <typename T, typename PinAlloc>
class PinnedPrimitiveArray;
template <typename T> class PinnedArrayAlloc;
template <typename T> class PinnedRegionAlloc;
template <typename T> class PinnedCriticalAlloc;
/// Wrapper to provide functionality to jarray references.
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
/// the elements of the array.
template <typename JArrayType>
class JPrimitiveArray :
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
static_assert(is_jni_primitive_array<JArrayType>(), "");
public:
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor();
static std::string get_instantiated_base_name();
using T = typename jtype_traits<JArrayType>::entry_type;
static local_ref<JArrayType> newArray(size_t count);
void getRegion(jsize start, jsize length, T* buf);
std::unique_ptr<T[]> getRegion(jsize start, jsize length);
void setRegion(jsize start, jsize length, const T* buf);
/// Returns a view of the underlying array. This will either be a "pinned"
/// version of the array (in which case changes to one immediately affect the
/// other) or a copy of the array (in which cases changes to the view will take
/// affect when destroyed or on calls to release()/commit()).
PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> pin();
/// Returns a view of part of the underlying array. A pinned region is always
/// backed by a copy of the region.
PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> pinRegion(jsize start, jsize length);
/// Returns a view of the underlying array like pin(). However, while the pin
/// is held, the code is considered within a "critical region". In a critical
/// region, native code must not call JNI functions or make any calls that may
/// block on other Java threads. These restrictions make it more likely that
/// the view will be "pinned" rather than copied (for example, the VM may
/// suspend garbage collection within a critical region).
PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> pinCritical();
private:
friend class PinnedArrayAlloc<T>;
T* getElements(jboolean* isCopy);
void releaseElements(T* elements, jint mode);
};
local_ref<jbooleanArray> make_boolean_array(jsize size);
local_ref<jbyteArray> make_byte_array(jsize size);
local_ref<jcharArray> make_char_array(jsize size);
local_ref<jshortArray> make_short_array(jsize size);
local_ref<jintArray> make_int_array(jsize size);
local_ref<jlongArray> make_long_array(jsize size);
local_ref<jfloatArray> make_float_array(jsize size);
local_ref<jdoubleArray> make_double_array(jsize size);
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
using JArrayByte = JPrimitiveArray<jbyteArray>;
using JArrayChar = JPrimitiveArray<jcharArray>;
using JArrayShort = JPrimitiveArray<jshortArray>;
using JArrayInt = JPrimitiveArray<jintArray>;
using JArrayLong = JPrimitiveArray<jlongArray>;
using JArrayFloat = JPrimitiveArray<jfloatArray>;
using JArrayDouble = JPrimitiveArray<jdoubleArray>;
/// RAII class for pinned primitive arrays
/// This currently only supports read/write access to existing java arrays. You can't create a
/// primitive array this way yet. This class also pins the entire array into memory during the
/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the
/// release() or abort() functions. During a long-running block of code, you
/// should unpin the array as soon as you're done with it, to avoid holding up
/// the Java garbage collector.
template <typename T, typename PinAlloc>
class PinnedPrimitiveArray {
public:
static_assert(is_jni_primitive<T>::value,
"PinnedPrimitiveArray requires primitive jni type.");
using ArrayType = typename jtype_traits<T>::array_type;
PinnedPrimitiveArray(PinnedPrimitiveArray&&);
PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;
~PinnedPrimitiveArray() noexcept;
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&);
PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;
T* get();
void release();
/// Unpins the array. If the array is a copy, pending changes are discarded.
void abort();
/// If the array is a copy, copies pending changes to the underlying java array.
void commit();
bool isCopy() const noexcept;
const T& operator[](size_t index) const;
T& operator[](size_t index);
size_t size() const noexcept;
private:
alias_ref<ArrayType> array_;
size_t start_;
T* elements_;
jboolean isCopy_;
size_t size_;
void allocate(alias_ref<ArrayType>, jint start, jint length);
void releaseImpl(jint mode);
void clear() noexcept;
PinnedPrimitiveArray(alias_ref<ArrayType>, jint start, jint length);
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
};
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")
#undef PlainJniRefMap
#define PlainJniRefMap(rtype, jtype) \
namespace detail { \
template<> \
struct RefReprType<jtype> { \
using type = rtype; \
}; \
}
PlainJniRefMap(JArrayBoolean, jbooleanArray);
PlainJniRefMap(JArrayByte, jbyteArray);
PlainJniRefMap(JArrayChar, jcharArray);
PlainJniRefMap(JArrayShort, jshortArray);
PlainJniRefMap(JArrayInt, jintArray);
PlainJniRefMap(JArrayLong, jlongArray);
PlainJniRefMap(JArrayFloat, jfloatArray);
PlainJniRefMap(JArrayDouble, jdoubleArray);
PlainJniRefMap(JObject, jobject);
PlainJniRefMap(JClass, jclass);
PlainJniRefMap(JString, jstring);
PlainJniRefMap(JThrowable, jthrowable);
#pragma pop_macro("PlainJniRefMap")
}}
#include "CoreClasses-inl.h"

View File

@@ -1,128 +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 <functional>
#include <string>
#include <jni.h>
namespace facebook {
namespace jni {
// Keeps a thread-local reference to the current thread's JNIEnv.
struct Environment {
// Throws a std::runtime_error if this thread isn't attached to the JVM
// TODO(T6594868) Benchmark against raw JNI access
static JNIEnv* current();
static void initialize(JavaVM* vm);
// There are subtle issues with calling the next functions directly. It is
// much better to always use a ThreadScope to manage attaching/detaching for
// you.
static JNIEnv* ensureCurrentThreadIsAttached();
};
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
* exits will cause a crash, as will calling Detach an extra time, and this guard class helps
* keep that straight. In addition, it remembers whether it performed the attach or not, so it
* is safe to nest it with itself or with non-fbjni code that manages the attachment correctly.
*
* Potential concerns:
* - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the
* app is not busy.
* - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that
* there is no Java code on the current stack or you run the risk of a crash like:
* ERROR: detaching thread with interp frames (count=18)
* (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)
* ThreadScope won't do a detach if the thread was already attached before the guard is
* instantiated, but there's probably some usage that could trip this up.
* - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language
* classes, not any of our application's classes. This will be different behavior than threads
* that were initiated on the Java side. A workaround is to pass a global reference for a
* class or instance to the new thread; this bypasses the need for the class loader.
* (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
* If you need access to the application's classes, you can use ThreadScope::WithClassLoader.
* - 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 ThreadScope {
public:
ThreadScope();
ThreadScope(ThreadScope&) = delete;
ThreadScope(ThreadScope&&) = default;
ThreadScope& operator=(ThreadScope&) = delete;
ThreadScope& operator=(ThreadScope&&) = delete;
~ThreadScope();
/**
* This runs the closure in a scope with fbjni's classloader. This should be
* the same classloader as the rest of the application and thus anything
* running in the closure will have access to the same classes as in a normal
* java-create thread.
*/
static void WithClassLoader(std::function<void()>&& runnable);
static void OnLoad();
private:
// If this flag is set, then this object needs to detach.
bool thisAttached_;
// The thread local pointer may point here.
detail::TLData data_;
};
}
}

View File

@@ -1,130 +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 Exceptions.h
*
* After invoking a JNI function that can throw a Java exception, the macro
* @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref FACEBOOK_JNI_THROW_EXCEPTION_IF()
* should be invoked.
*
* IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!
* To use these methods you MUST call initExceptionHelpers() when your library is loaded.
*/
#pragma once
#include <alloca.h>
#include <stdexcept>
#include <string>
#include <jni.h>
#include "Common.h"
#include "References.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 jni {
class JThrowable;
class JCppException : public JavaClass<JCppException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;";
static local_ref<JCppException> create(const char* str) {
return newInstance(make_jstring(str));
}
static local_ref<JCppException> create(const std::exception& ex) {
return newInstance(make_jstring(ex.what()));
}
};
// JniException ////////////////////////////////////////////////////////////////////////////////////
/**
* This class wraps a Java exception into a C++ exception; if the exception is routed back
* to the Java side, it can be unwrapped and just look like a pure Java interaction. The class
* is resilient to errors while creating the exception, falling back to some pre-allocated
* exceptions if a new one cannot be allocated or populated.
*
* Note: the what() method of this class is not thread-safe (t6900503).
*/
class JniException : public std::exception {
public:
JniException();
~JniException() override;
explicit JniException(alias_ref<jthrowable> throwable);
JniException(JniException &&rhs);
JniException(const JniException &other);
local_ref<JThrowable> getThrowable() const noexcept;
const char* what() const noexcept override;
void setJavaException() const noexcept;
private:
global_ref<JThrowable> throwable_;
mutable std::string what_;
mutable bool isMessageExtracted_;
const static std::string kExceptionMessageFailure_;
void populateWhat() const noexcept;
};
// Exception throwing & translating functions //////////////////////////////////////////////////////
// Functions that throw C++ exceptions
static const int kMaxExceptionMessageBufferSize = 512;
// These methods are the preferred way to throw a Java exception from
// a C++ function. They create and throw a C++ exception which wraps
// a Java exception, so the C++ flow is interrupted. Then, when
// translatePendingCppExceptionToJavaException is called at the
// topmost level of the native stack, the wrapped Java exception is
// thrown to the java caller.
template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {
int msgSize = snprintf(nullptr, 0, fmt, args...);
char *msg = (char*) alloca(msgSize + 1);
snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
throwNewJavaException(throwableName, msg);
}
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
// be thrown, it aborts the program.
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.
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";
}}

View File

@@ -1,285 +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 "CoreClasses.h"
namespace facebook {
namespace jni {
namespace detail {
class BaseHybridClass {
public:
virtual ~BaseHybridClass() {}
};
struct HybridData : public JavaClass<HybridData> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
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>
struct HybridTraits {
// This static assert should actually always fail if we don't use one of the
// specializations below.
static_assert(
std::is_base_of<JObject, Base>::value ||
std::is_base_of<BaseHybridClass, Base>::value,
"The base of a HybridClass must be either another HybridClass or derived from JObject.");
};
template <>
struct HybridTraits<BaseHybridClass> {
using CxxBase = BaseHybridClass;
using JavaBase = JObject;
};
template <typename Base>
struct HybridTraits<
Base,
typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {
using CxxBase = Base;
using JavaBase = typename Base::JavaPart;
};
template <typename Base>
struct HybridTraits<
Base,
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
using CxxBase = BaseHybridClass;
using JavaBase = Base;
};
// convert to HybridClass* from jhybridobject
template <typename T>
struct Convert<
T, typename std::enable_if<
std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {
typedef typename std::remove_pointer<T>::type::jhybridobject jniType;
static T fromJni(jniType t) {
if (t == nullptr) {
return nullptr;
}
return wrap_alias(t)->cthis();
}
// There is no automatic return conversion for objects.
};
template<typename T>
struct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {
static_assert(std::is_same<T, void>::value,
"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. "
"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.");
using Repr = T;
};
}
template <typename T, typename Base = detail::BaseHybridClass>
class HybridClass : public detail::HybridTraits<Base>::CxxBase {
public:
struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {
// At this point, T is incomplete, and so we cannot access
// T::kJavaDescriptor directly. jtype_traits support this escape hatch for
// such a case.
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor();
static std::string get_instantiated_base_name();
using HybridType = T;
// This will reach into the java object and extract the C++ instance from
// the mHybridData and return it.
T* cthis();
friend class HybridClass;
friend T;
};
using jhybridobject = typename JavaPart::javaobject;
using javaobject = typename JavaPart::javaobject;
typedef detail::HybridData::javaobject jhybriddata;
static alias_ref<JClass> javaClassStatic() {
return JavaPart::javaClassStatic();
}
static local_ref<JClass> javaClassLocal() {
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
return findClassLocal(className.c_str());
}
protected:
typedef HybridClass HybridBase;
// This ensures that a C++ hybrid part cannot be created on its own
// by default. If a hybrid wants to enable this, it can provide its
// own public ctor, or change the accessibility of this to public.
using detail::HybridTraits<Base>::CxxBase::CxxBase;
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
javaClassStatic()->registerNatives(methods);
}
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
auto hybridData = detail::HybridData::create();
setNativePointer(hybridData, std::move(cxxPart));
return hybridData;
}
template <typename... Args>
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
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:
// Factory method for creating a hybrid object where the arguments
// are used to initialize the C++ part directly without passing them
// through java. This method requires the Java part to have a ctor
// which takes a HybridData, and for the C++ part to have a ctor
// compatible with the arguments passed here. For safety, the ctor
// can be private, and the hybrid declared a friend of its base, so
// the hybrid can only be created from here.
//
// Exception behavior: This can throw an exception if creating the
// C++ object fails, or any JNI methods throw.
template <typename... Args>
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
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
// strengthen type-checking (and possibly provide a default
// implementation of allocate().)
template <typename... Args>
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
static auto allocateMethod =
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
return allocateMethod(javaClassStatic(), hybridData.get());
}
// Factory method for creating a hybrid object where the arguments
// are passed to the java ctor.
template <typename... Args>
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
return JavaPart::newInstance(std::move(args)...);
}
// If a hybrid class throws an exception which derives from
// std::exception, it will be passed to mapException on the hybrid
// class, or nearest ancestor. This allows boilerplate exception
// translation code (for example, calling throwNewJavaException on a
// particular java class) to be hoisted to a common function. If
// mapException returns, then the std::exception will be translated
// to Java.
static void mapException(const std::exception& ex) {
(void)ex;
}
};
template <typename T, typename B>
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 =
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
auto hybridData = this->getFieldValue(field);
if (!hybridData) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
result = getNativePointer(hybridData);
}
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
return static_cast<T*>(result);
};
template <typename T, typename B>
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
return T::kJavaDescriptor;
}
template <typename T, typename B>
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
auto name = get_instantiated_java_descriptor();
return name.substr(1, name.size() - 2);
}
// Given a *_ref object which refers to a hybrid class, this will reach inside
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
// the appropriate type, and return it.
template <typename T>
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
return jthis->cthis();
}
void HybridDataOnLoad();
}
}

View File

@@ -1,196 +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 {
namespace jni {
namespace detail {
template <typename E>
struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;";
typedef local_ref<E> value_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::forward_iterator_tag iterator_category;
typedef JavaClass<IteratorHelper<E>> JavaBase_;
bool hasNext() const {
static auto hasNextMethod =
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
return hasNextMethod(JavaBase_::self());
}
value_type next() {
static auto elementField =
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
return dynamic_ref_cast<JniType<E>>(JavaBase_::getFieldValue(elementField));
}
static void reset(value_type& v) {
v.reset();
}
};
template <typename K, typename V>
struct MapIteratorHelper : public JavaClass<MapIteratorHelper<K,V>> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;";
typedef std::pair<local_ref<K>, local_ref<V>> value_type;
typedef JavaClass<MapIteratorHelper<K,V>> JavaBase_;
bool hasNext() const {
static auto hasNextMethod =
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
return hasNextMethod(JavaBase_::self());
}
value_type next() {
static auto keyField = JavaBase_::javaClassStatic()->template getField<jobject>("mKey");
static auto valueField = JavaBase_::javaClassStatic()->template getField<jobject>("mValue");
return std::make_pair(dynamic_ref_cast<K>(JavaBase_::getFieldValue(keyField)),
dynamic_ref_cast<V>(JavaBase_::getFieldValue(valueField)));
}
static void reset(value_type& v) {
v.first.reset();
v.second.reset();
}
};
template <typename T>
class Iterator {
public:
typedef typename T::value_type value_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::input_iterator_tag iterator_category;
// begin ctor
Iterator(global_ref<typename T::javaobject>&& helper)
: helper_(std::move(helper))
, i_(-1) {
++(*this);
}
// end ctor
Iterator()
: i_(-1) {}
bool operator==(const Iterator& it) const { return i_ == it.i_; }
bool operator!=(const Iterator& it) const { return !(*this == it); }
const value_type& operator*() const { assert(i_ != -1); return entry_; }
const value_type* operator->() const { assert(i_ != -1); return &entry_; }
Iterator& operator++() { // preincrement
bool hasNext = helper_->hasNext();
if (hasNext) {
++i_;
entry_ = helper_->next();
} else {
i_ = -1;
helper_->reset(entry_);
}
return *this;
}
Iterator operator++(int) { // postincrement
Iterator ret;
ret.i_ = i_;
ret.entry_ = std::move(entry_);
++(*this);
return ret;
}
global_ref<typename T::javaobject> helper_;
// set to -1 at end
std::ptrdiff_t i_;
value_type entry_;
};
}
template <typename E>
struct JIterator<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
};
template <typename E>
typename JIterator<E>::Iterator JIterator<E>::begin() const {
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
typename JIterator<E>::javaobject)>();
return Iterator(
make_global(
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename E>
typename JIterator<E>::Iterator JIterator<E>::end() const {
return Iterator();
}
template <typename E>
struct JIterable<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
};
template <typename E>
typename JIterable<E>::Iterator JIterable<E>::begin() const {
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
typename JIterable<E>::javaobject)>();
return Iterator(
make_global(
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename E>
typename JIterable<E>::Iterator JIterable<E>::end() const {
return Iterator();
}
template <typename E>
size_t JCollection<E>::size() const {
static auto sizeMethod =
JCollection<E>::javaClassStatic()->template getMethod<jint()>("size");
return sizeMethod(this->self());
}
template <typename K, typename V>
struct JMap<K,V>::Iterator : public detail::Iterator<detail::MapIteratorHelper<K,V>> {
using detail::Iterator<detail::MapIteratorHelper<K,V>>::Iterator;
};
template <typename K, typename V>
size_t JMap<K,V>::size() const {
static auto sizeMethod =
JMap<K,V>::javaClassStatic()->template getMethod<jint()>("size");
return sizeMethod(this->self());
}
template <typename K, typename V>
typename JMap<K,V>::Iterator JMap<K,V>::begin() const {
static auto ctor = detail::MapIteratorHelper<K,V>::javaClassStatic()->
template getConstructor<typename detail::MapIteratorHelper<K,V>::javaobject(
typename JMap<K,V>::javaobject)>();
return Iterator(
make_global(
detail::MapIteratorHelper<K,V>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename K, typename V>
typename JMap<K,V>::Iterator JMap<K,V>::end() const {
return Iterator();
}
}
}

View File

@@ -1,143 +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"
namespace facebook {
namespace jni {
/**
* JavaClass which represents a reference to a java.util.Iterator instance. It
* provides begin()/end() methods to provide C++-style iteration over the
* underlying collection. The class has a template parameter for the element
* type, which defaults to jobject. For example:
*
* alias_ref<JIterator<jstring>::javaobject> my_iter = ...;
*
* In the simplest case, it can be used just as alias_ref<JIterator<>::javaobject>,
* for example in a method declaration.
*/
template <typename E = jobject>
struct JIterator : JavaClass<JIterator<E>> {
constexpr static auto kJavaDescriptor = "Ljava/util/Iterator;";
struct Iterator;
/**
* To iterate:
*
* for (const auto& element : *jiter) { ... }
*
* The JIterator iterator value_type is local_ref<E>, containing a reference
* to an element instance.
*
* If the Iterator returns objects whch are not convertible to the given
* element type, iteration will throw a java ClassCastException.
*
* For example, to convert an iterator over a collection of java strings to
* an std::vector of std::strings:
*
* std::vector<std::string> vs;
* for (const auto& elem : *jiter) {
* vs.push_back(elem->toStdString());
* }
*
* Or if you prefer using std algorithms:
*
* std::vector<std::string> vs;
* std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs),
* [](const local_ref<jstring>& elem) { return elem->toStdString(); });
*
* The iterator is a InputIterator.
*/
Iterator begin() const;
Iterator end() const;
};
/**
* Similar to JIterator, except this represents any object which implements the
* java.lang.Iterable interface. It will create the Java Iterator as a part of
* begin().
*/
template <typename E = jobject>
struct JIterable : JavaClass<JIterable<E>> {
constexpr static auto kJavaDescriptor = "Ljava/lang/Iterable;";
struct Iterator;
Iterator begin() const;
Iterator end() const;
};
/**
* JavaClass types which represent Collection, List, and Set are also provided.
* These preserve the Java class heirarchy.
*/
template <typename E = jobject>
struct JCollection : JavaClass<JCollection<E>, JIterable<E>> {
constexpr static auto kJavaDescriptor = "Ljava/util/Collection;";
/**
* Returns the number of elements in the collection.
*/
size_t size() const;
};
template <typename E = jobject>
struct JList : JavaClass<JList<E>, JCollection<E>> {
constexpr static auto kJavaDescriptor = "Ljava/util/List;";
};
template <typename E = jobject>
struct JSet : JavaClass<JSet<E>, JCollection<E>> {
constexpr static auto kJavaDescriptor = "Ljava/util/Set;";
};
/**
* JavaClass which represents a reference to a java.util.Map instance. It adds
* wrappers around Java methods, including begin()/end() methods to provide
* C++-style iteration over the Java Map. The class has template parameters
* for the key and value types, which default to jobject. For example:
*
* alias_ref<JMap<jstring, MyJClass::javaobject>::javaobject> my_map = ...;
*
* In the simplest case, it can be used just as alias_ref<JMap<>::javaobject>,
* for example in a method declaration.
*/
template <typename K = jobject, typename V = jobject>
struct JMap : JavaClass<JMap<K,V>> {
constexpr static auto kJavaDescriptor = "Ljava/util/Map;";
struct Iterator;
/**
* Returns the number of pairs in the map.
*/
size_t size() const;
/**
* To iterate over the Map:
*
* for (const auto& entry : *jmap) { ... }
*
* The JMap iterator value_type is std::pair<local_ref<K>, local_ref<V>>
* containing references to key and value instances.
*
* If the Map contains objects whch are not convertible to the given key and
* value types, iteration will throw a java ClassCastException.
*
* The iterator is a InputIterator.
*/
Iterator begin() const;
Iterator end() const;
};
}
}
#include "Iterator-inl.h"

View File

@@ -1,39 +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"
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()));
}
};
}
}

View File

@@ -1,67 +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 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

View File

@@ -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
namespace facebook {
namespace jni {
template<typename F>
class JMethod;
template<typename F>
class JStaticMethod;
template<typename F>
class JNonvirtualMethod;
template<typename F>
struct JConstructor;
template<typename F>
class JField;
template<typename F>
class JStaticField;
/// Type traits for Java types (currently providing Java type descriptors)
template<typename T>
struct jtype_traits;
/// Type traits for Java methods (currently providing Java type descriptors)
template<typename F>
struct jmethod_traits;
}}

View File

@@ -1,405 +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 "Common.h"
#include "Exceptions.h"
#include "MetaConvert.h"
#include "References.h"
#include "Boxed.h"
namespace facebook {
namespace jni {
// JMethod /////////////////////////////////////////////////////////////////////////////////////////
inline JMethodBase::JMethodBase(jmethodID method_id) noexcept
: method_id_{method_id}
{}
inline JMethodBase::operator bool() const noexcept {
return method_id_ != nullptr;
}
inline jmethodID JMethodBase::getId() const noexcept {
return method_id_;
}
namespace {
template <int idx, typename... Args>
struct ArgsArraySetter;
template <int idx, typename Arg, typename... Args>
struct ArgsArraySetter<idx, Arg, Args...> {
static void set(alias_ref<JArrayClass<jobject>::javaobject> array, Arg arg0, Args... args) {
// TODO(xxxxxxxx): Use Convert<Args>... to do conversions like the fast path.
(*array)[idx] = autobox(arg0);
ArgsArraySetter<idx + 1, Args...>::set(array, args...);
}
};
template <int idx>
struct ArgsArraySetter<idx> {
static void set(alias_ref<JArrayClass<jobject>::javaobject> array) {
(void)array;
}
};
template <typename... Args>
local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
auto arr = JArrayClass<jobject>::newArray(sizeof...(args));
ArgsArraySetter<0, Args...>::set(arr, args...);
return arr;
}
}
template<typename... Args>
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) const {
const auto env = Environment::current();
env->CallVoidMethod(
self.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
#pragma push_macro("DEFINE_PRIMITIVE_CALL")
#undef DEFINE_PRIMITIVE_CALL
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) const { \
const auto env = Environment::current(); \
auto result = env->Call ## METHOD ## Method( \
self.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
DEFINE_PRIMITIVE_CALL(jbyte, Byte)
DEFINE_PRIMITIVE_CALL(jchar, Char)
DEFINE_PRIMITIVE_CALL(jshort, Short)
DEFINE_PRIMITIVE_CALL(jint, Int)
DEFINE_PRIMITIVE_CALL(jlong, Long)
DEFINE_PRIMITIVE_CALL(jfloat, Float)
DEFINE_PRIMITIVE_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
/// JMethod specialization for references that wraps the return value in a @ref local_ref
template<typename R, typename... Args>
class JMethod<R(Args...)> : public JMethodBase {
public:
// TODO: static_assert is jobject-derived or local_ref jobject
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
static_assert(IsPlainJniReference<JniRet>(), "JniRet must be a JNI reference");
using JMethodBase::JMethodBase;
JMethod() noexcept {};
JMethod(const JMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args) const;
friend class JClass;
};
template<typename R, typename... Args>
inline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) const -> local_ref<JniRet> {
const auto env = Environment::current();
auto result = env->CallObjectMethod(
self.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<JniRet>(result));
}
template<typename... Args>
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const {
const auto env = Environment::current();
env->CallStaticVoidMethod(
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_CALL")
#undef DEFINE_PRIMITIVE_STATIC_CALL
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) const { \
const auto env = Environment::current(); \
auto result = env->CallStatic ## METHOD ## Method( \
cls.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean)
DEFINE_PRIMITIVE_STATIC_CALL(jbyte, Byte)
DEFINE_PRIMITIVE_STATIC_CALL(jchar, Char)
DEFINE_PRIMITIVE_STATIC_CALL(jshort, Short)
DEFINE_PRIMITIVE_STATIC_CALL(jint, Int)
DEFINE_PRIMITIVE_STATIC_CALL(jlong, Long)
DEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float)
DEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_CALL")
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
template<typename R, typename... Args>
class JStaticMethod<R(Args...)> : public JMethodBase {
public:
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
using JMethodBase::JMethodBase;
JStaticMethod() noexcept {};
JStaticMethod(const JStaticMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) const {
const auto env = Environment::current();
auto result = env->CallStaticObjectMethod(
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<JniRet>(result));
}
friend class JClass;
};
template<typename... Args>
inline void
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
const auto env = Environment::current();
env->CallNonvirtualVoidMethod(
self.get(),
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_CALL
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE \
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const { \
const auto env = Environment::current(); \
auto result = env->CallNonvirtual ## METHOD ## Method( \
self.get(), \
cls.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jbyte, Byte)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jchar, Char)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jshort, Short)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jint, Int)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jlong, Long)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref
template<typename R, typename... Args>
class JNonvirtualMethod<R(Args...)> : public JMethodBase {
public:
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
using JMethodBase::JMethodBase;
JNonvirtualMethod() noexcept {};
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const {
const auto env = Environment::current();
auto result = env->CallNonvirtualObjectMethod(
self.get(),
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<JniRet>(result));
}
friend class JClass;
};
template <typename... Args>
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {
static auto invoke = findClassStatic("java/lang/reflect/Method")
->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>("invoke");
// TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.
auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method");
auto argsArray = makeArgsArray(args...);
// No need to check for exceptions since invoke is itself a JMethod that will do that for us.
return invoke(reflected, self.get(), argsArray.get());
}
// JField<T> ///////////////////////////////////////////////////////////////////////////////////////
template<typename T>
inline JField<T>::JField(jfieldID field) noexcept
: field_id_{field}
{}
template<typename T>
inline JField<T>::operator bool() const noexcept {
return field_id_ != nullptr;
}
template<typename T>
inline jfieldID JField<T>::getId() const noexcept {
return field_id_;
}
#pragma push_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
#undef DEFINE_FIELD_PRIMITIVE_GET_SET
#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
template<> \
inline TYPE JField<TYPE>::get(jobject object) const noexcept { \
const auto env = Environment::current(); \
return env->Get ## METHOD ## Field(object, field_id_); \
} \
\
template<> \
inline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \
const auto env = Environment::current(); \
env->Set ## METHOD ## Field(object, field_id_, value); \
}
DEFINE_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
DEFINE_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
DEFINE_FIELD_PRIMITIVE_GET_SET(jchar, Char)
DEFINE_FIELD_PRIMITIVE_GET_SET(jshort, Short)
DEFINE_FIELD_PRIMITIVE_GET_SET(jint, Int)
DEFINE_FIELD_PRIMITIVE_GET_SET(jlong, Long)
DEFINE_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
#pragma pop_macro("DEFINE_FIELD_PRIMITIVE_GET_SET")
template<typename T>
inline T JField<T>::get(jobject object) const noexcept {
return static_cast<T>(Environment::current()->GetObjectField(object, field_id_));
}
template<typename T>
inline void JField<T>::set(jobject object, T value) noexcept {
Environment::current()->SetObjectField(object, field_id_, static_cast<jobject>(value));
}
// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////
template<typename T>
inline JStaticField<T>::JStaticField(jfieldID field) noexcept
: field_id_{field}
{}
template<typename T>
inline JStaticField<T>::operator bool() const noexcept {
return field_id_ != nullptr;
}
template<typename T>
inline jfieldID JStaticField<T>::getId() const noexcept {
return field_id_;
}
#pragma push_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
#undef DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET
#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \
template<> \
inline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept { \
const auto env = Environment::current(); \
return env->GetStatic ## METHOD ## Field(jcls, field_id_); \
} \
\
template<> \
inline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept { \
const auto env = Environment::current(); \
env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \
}
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jchar, Char)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jshort, Short)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jint, Int)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jlong, Long)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jfloat, Float)
DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)
#pragma pop_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET")
template<typename T>
inline T JStaticField<T>::get(jclass jcls) const noexcept {
const auto env = Environment::current();
return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));
}
template<typename T>
inline void JStaticField<T>::set(jclass jcls, T value) noexcept {
Environment::current()->SetStaticObjectField(jcls, field_id_, value);
}
// jmethod_traits //////////////////////////////////////////////////////////////////////////////////
// TODO(T6608405) Adapt this to implement a register natives method that requires no descriptor
namespace internal {
template<typename Head>
inline std::string JavaDescriptor() {
return jtype_traits<Head>::descriptor();
}
template<typename Head, typename Elem, typename... Tail>
inline std::string JavaDescriptor() {
return JavaDescriptor<Head>() + JavaDescriptor<Elem, Tail...>();
}
template<typename R, typename Arg1, typename... Args>
inline std::string JMethodDescriptor() {
return "(" + JavaDescriptor<Arg1, Args...>() + ")" + JavaDescriptor<R>();
}
template<typename R>
inline std::string JMethodDescriptor() {
return "()" + JavaDescriptor<R>();
}
} // internal
template<typename R, typename... Args>
inline std::string jmethod_traits<R(Args...)>::descriptor() {
return internal::JMethodDescriptor<R, Args...>();
}
template<typename R, typename... Args>
inline std::string jmethod_traits<R(Args...)>::constructor_descriptor() {
return internal::JMethodDescriptor<void, Args...>();
}
}}

View File

@@ -1,337 +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 meta.h
*
* Provides wrappers for meta data such as methods and fields.
*/
#pragma once
#include <type_traits>
#include <string>
#include <jni.h>
#include "References-forward.h"
#ifdef __ANDROID__
# include <android/log.h>
# define XLOG_TAG "fb-jni"
# define XLOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, XLOG_TAG, __VA_ARGS__)
# define XLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, XLOG_TAG, __VA_ARGS__)
# define XLOGI(...) __android_log_print(ANDROID_LOG_INFO, XLOG_TAG, __VA_ARGS__)
# define XLOGW(...) __android_log_print(ANDROID_LOG_WARN, XLOG_TAG, __VA_ARGS__)
# define XLOGE(...) __android_log_print(ANDROID_LOG_ERROR, XLOG_TAG, __VA_ARGS__)
# define XLOGWTF(...) __android_log_print(ANDROID_LOG_FATAL, XLOG_TAG, __VA_ARGS__)
#endif
namespace facebook {
namespace jni {
// This will get the reflected Java Method from the method_id, get it's invoke
// method, and call the method via that. This shouldn't ever be needed, but
// Android 6.0 crashes when calling a method on a java.lang.Proxy via jni.
template <typename... Args>
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args);
class JObject;
/// Wrapper of a jmethodID. Provides a common base for JMethod specializations
class JMethodBase {
public:
/// Verify that the method is valid
explicit operator bool() const noexcept;
/// Access the wrapped id
jmethodID getId() const noexcept;
protected:
/// Create a wrapper of a method id
explicit JMethodBase(jmethodID method_id = nullptr) noexcept;
private:
jmethodID method_id_;
};
/// Representation of a jmethodID
template<typename F>
class JMethod;
/// @cond INTERNAL
#pragma push_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
#undef DEFINE_PRIMITIVE_METHOD_CLASS
// Defining JMethod specializations based on return value
#define DEFINE_PRIMITIVE_METHOD_CLASS(TYPE) \
template<typename... Args> \
class JMethod<TYPE(Args...)> : public JMethodBase { \
public: \
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
"TYPE must be primitive or void"); \
\
using JMethodBase::JMethodBase; \
JMethod() noexcept {}; \
JMethod(const JMethod& other) noexcept = default; \
\
TYPE operator()(alias_ref<jobject> self, Args... args) const; \
\
friend class JClass; \
}
DEFINE_PRIMITIVE_METHOD_CLASS(void);
DEFINE_PRIMITIVE_METHOD_CLASS(jboolean);
DEFINE_PRIMITIVE_METHOD_CLASS(jbyte);
DEFINE_PRIMITIVE_METHOD_CLASS(jchar);
DEFINE_PRIMITIVE_METHOD_CLASS(jshort);
DEFINE_PRIMITIVE_METHOD_CLASS(jint);
DEFINE_PRIMITIVE_METHOD_CLASS(jlong);
DEFINE_PRIMITIVE_METHOD_CLASS(jfloat);
DEFINE_PRIMITIVE_METHOD_CLASS(jdouble);
#pragma pop_macro("DEFINE_PRIMITIVE_METHOD_CLASS")
/// @endcond
/// Convenience type representing constructors
/// These should only be used with JClass::getConstructor and JClass::newObject.
template<typename F>
struct JConstructor : private JMethod<F> {
using JMethod<F>::JMethod;
private:
JConstructor(const JMethod<F>& other) : JMethod<F>(other.getId()) {}
friend class JClass;
};
/// Representation of a jStaticMethodID
template<typename F>
class JStaticMethod;
/// @cond INTERNAL
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
#undef DEFINE_PRIMITIVE_STATIC_METHOD_CLASS
// Defining JStaticMethod specializations based on return value
#define DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(TYPE) \
template<typename... Args> \
class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
"T must be a JNI primitive or void"); \
\
public: \
using JMethodBase::JMethodBase; \
JStaticMethod() noexcept {}; \
JStaticMethod(const JStaticMethod& other) noexcept = default; \
\
TYPE operator()(alias_ref<jclass> cls, Args... args) const; \
\
friend class JClass; \
}
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jboolean);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jbyte);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jchar);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jshort);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jint);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jlong);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jfloat);
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble);
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS")
/// @endcond
/// Representation of a jNonvirtualMethodID
template<typename F>
class JNonvirtualMethod;
/// @cond INTERNAL
#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
#undef DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS
// Defining JNonvirtualMethod specializations based on return value
#define DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(TYPE) \
template<typename... Args> \
class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(), \
"T must be a JNI primitive or void"); \
\
public: \
using JMethodBase::JMethodBase; \
JNonvirtualMethod() noexcept {}; \
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
\
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) const; \
\
friend class JClass; \
}
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jboolean);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jbyte);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jchar);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jshort);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jint);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jlong);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jfloat);
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble);
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS")
/// @endcond
/**
* JField represents typed fields and simplifies their access. Note that object types return
* raw pointers which generally should promptly get a wrap_local treatment.
*/
template<typename T>
class JField {
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
public:
/// Wraps an existing field id
explicit JField(jfieldID field = nullptr) noexcept;
/// Verify that the id is valid
explicit operator bool() const noexcept;
/// Access the wrapped id
jfieldID getId() const noexcept;
private:
jfieldID field_id_;
/// Get field value
/// @pre object != nullptr
T get(jobject object) const noexcept;
/// Set field value
/// @pre object != nullptr
void set(jobject object, T value) noexcept;
friend class JObject;
};
/**
* JStaticField represents typed fields and simplifies their access. Note that object types
* return raw pointers which generally should promptly get a wrap_local treatment.
*/
template<typename T>
class JStaticField {
static_assert(IsJniScalar<T>(), "T must be a JNI scalar");
public:
/// Wraps an existing field id
explicit JStaticField(jfieldID field = nullptr) noexcept;
/// Verify that the id is valid
explicit operator bool() const noexcept;
/// Access the wrapped id
jfieldID getId() const noexcept;
private:
jfieldID field_id_;
/// Get field value
/// @pre object != nullptr
T get(jclass jcls) const noexcept;
/// Set field value
/// @pre object != nullptr
void set(jclass jcls, T value) noexcept;
friend class JClass;
friend class JObject;
};
/// Template magic to provide @ref jmethod_traits
template<typename R, typename... Args>
struct jmethod_traits<R(Args...)> {
static std::string descriptor();
static std::string constructor_descriptor();
};
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
template<typename T>
struct jtype_traits {
private:
using Repr = ReprType<T>;
public:
// The jni type signature (described at
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
static std::string descriptor() {
std::string descriptor;
if (Repr::kJavaDescriptor == nullptr) {
descriptor = Repr::get_instantiated_java_descriptor();
} else {
descriptor = Repr::kJavaDescriptor;
}
return descriptor;
}
// The signature used for class lookups. See
// http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName().
static std::string base_name() {
if (Repr::kJavaDescriptor != nullptr) {
std::string base_name = Repr::kJavaDescriptor;
return base_name.substr(1, base_name.size() - 2);
}
return Repr::get_instantiated_base_name();
}
};
#pragma push_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
#undef DEFINE_FIELD_AND_ARRAY_TRAIT
#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC) \
template<> \
struct jtype_traits<TYPE> { \
static std::string descriptor() { return std::string{#DSC}; } \
static std::string base_name() { return descriptor(); } \
using array_type = TYPE ## Array; \
}; \
template<> \
struct jtype_traits<TYPE ## Array> { \
static std::string descriptor() { return std::string{"[" #DSC}; } \
static std::string base_name() { return descriptor(); } \
using entry_type = TYPE; \
};
// There is no voidArray, handle that without the macro.
template<>
struct jtype_traits<void> {
static std::string descriptor() { return std::string{"V"}; };
};
DEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z)
DEFINE_FIELD_AND_ARRAY_TRAIT(jbyte, B)
DEFINE_FIELD_AND_ARRAY_TRAIT(jchar, C)
DEFINE_FIELD_AND_ARRAY_TRAIT(jshort, S)
DEFINE_FIELD_AND_ARRAY_TRAIT(jint, I)
DEFINE_FIELD_AND_ARRAY_TRAIT(jlong, J)
DEFINE_FIELD_AND_ARRAY_TRAIT(jfloat, F)
DEFINE_FIELD_AND_ARRAY_TRAIT(jdouble, D)
#pragma pop_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
template <typename T>
struct jmethod_traits_from_cxx;
}}
#include "Meta-inl.h"

View File

@@ -1,159 +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 "Common.h"
#include "References.h"
namespace facebook {
namespace jni {
namespace detail {
// In order to avoid potentially filling the jni locals table,
// temporary objects (right now, this is just jstrings) need to be
// released. This is done by returning a holder which autoconverts to
// jstring.
template <typename T>
inline T callToJni(T&& t) {
return t;
}
template <typename T>
inline JniType<T> callToJni(local_ref<T>&& sref) {
return sref.get();
}
// Normally, pass through types unmolested.
template <typename T, typename Enabled = void>
struct Convert {
typedef T jniType;
static jniType fromJni(jniType t) {
return t;
}
static jniType toJniRet(jniType t) {
return t;
}
static jniType toCall(jniType t) {
return t;
}
};
// This is needed for return conversion
template <>
struct Convert<void> {
typedef void jniType;
};
// jboolean is an unsigned char, not a bool. Allow it to work either way.
template<>
struct Convert<bool> {
typedef jboolean jniType;
static bool fromJni(jniType t) {
return t;
}
static jniType toJniRet(bool t) {
return t;
}
static jniType toCall(bool t) {
return t;
}
};
// 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
template <typename T>
struct Convert<alias_ref<T>> {
typedef JniType<T> jniType;
static alias_ref<jniType> fromJni(jniType t) {
return wrap_alias(t);
}
static jniType toJniRet(alias_ref<jniType> t) {
return t.get();
}
static jniType toCall(alias_ref<jniType> t) {
return t.get();
}
};
// convert return from local_ref<T>
template <typename T>
struct Convert<local_ref<T>> {
typedef JniType<T> jniType;
// No automatic synthesis of local_ref
static jniType toJniRet(local_ref<jniType> t) {
return t.release();
}
static jniType toCall(local_ref<jniType> t) {
return t.get();
}
};
// convert return from global_ref<T>
template <typename T>
struct Convert<global_ref<T>> {
typedef JniType<T> jniType;
// No automatic synthesis of global_ref
static jniType toJniRet(global_ref<jniType>&& t) {
// 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();
}
static jniType toCall(global_ref<jniType> t) {
return t.get();
}
};
template <typename T> struct jni_sig_from_cxx_t;
template <typename R, typename... Args>
struct jni_sig_from_cxx_t<R(Args...)> {
using JniRet = typename Convert<typename std::decay<R>::type>::jniType;
using JniSig = JniRet(typename Convert<typename std::decay<Args>::type>::jniType...);
};
template <typename T>
using jni_sig_from_cxx = typename jni_sig_from_cxx_t<T>::JniSig;
} // namespace detail
template <typename R, typename... Args>
struct jmethod_traits_from_cxx<R(Args...)> : jmethod_traits<detail::jni_sig_from_cxx<R(Args...)>> {
};
}}

View File

@@ -1,120 +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 <cassert>
#include <new>
#include <atomic>
#include "Environment.h"
namespace facebook {
namespace jni {
/// @cond INTERNAL
namespace internal {
// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)
struct ReferenceStats {
std::atomic_uint locals_created, globals_created, weaks_created,
locals_deleted, globals_deleted, weaks_deleted;
void reset() noexcept;
};
extern ReferenceStats g_reference_stats;
}
/// @endcond
// LocalReferenceAllocator /////////////////////////////////////////////////////////////////////////
inline jobject LocalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Local new: %p", original);
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.locals_created;
#endif
auto ref = Environment::current()->NewLocalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref;
}
inline void LocalReferenceAllocator::deleteReference(jobject reference) const noexcept {
internal::dbglog("Local release: %p", reference);
if (reference) {
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.locals_deleted;
#endif
assert(verifyReference(reference));
Environment::current()->DeleteLocalRef(reference);
}
}
inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {
return isObjectRefType(reference, JNILocalRefType);
}
// GlobalReferenceAllocator ////////////////////////////////////////////////////////////////////////
inline jobject GlobalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Global new: %p", original);
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.globals_created;
#endif
auto ref = Environment::current()->NewGlobalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref;
}
inline void GlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
internal::dbglog("Global release: %p", reference);
if (reference) {
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.globals_deleted;
#endif
assert(verifyReference(reference));
Environment::current()->DeleteGlobalRef(reference);
}
}
inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
return isObjectRefType(reference, JNIGlobalRefType);
}
// WeakGlobalReferenceAllocator ////////////////////////////////////////////////////////////////////
inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {
internal::dbglog("Weak global new: %p", original);
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.weaks_created;
#endif
auto ref = Environment::current()->NewWeakGlobalRef(original);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return ref;
}
inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {
internal::dbglog("Weak Global release: %p", reference);
if (reference) {
#ifdef FBJNI_DEBUG_REFS
++internal::g_reference_stats.weaks_deleted;
#endif
assert(verifyReference(reference));
Environment::current()->DeleteWeakGlobalRef(reference);
}
}
inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {
return isObjectRefType(reference, JNIWeakGlobalRefType);
}
}}

View File

@@ -1,54 +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 ReferenceAllocators.h
*
* Reference allocators are used to create and delete various classes of JNI references (local,
* global, and weak global).
*/
#pragma once
#include "Common.h"
namespace facebook { namespace jni {
/// Allocator that handles local references
class LocalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/// Allocator that handles global references
class GlobalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/// Allocator that handles weak global references
class WeakGlobalReferenceAllocator {
public:
jobject newReference(jobject original) const;
void deleteReference(jobject reference) const noexcept;
bool verifyReference(jobject reference) const noexcept;
};
/**
* @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.
*/
bool isObjectRefType(jobject reference, jobjectRefType refType);
}}
#include "ReferenceAllocators-inl.h"

View File

@@ -1,64 +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 "ReferenceAllocators.h"
namespace facebook {
namespace jni {
template<typename T, typename Enable = void>
class JObjectWrapper;
namespace detail {
struct JObjectBase {
jobject get() const noexcept;
void set(jobject reference) noexcept;
jobject this_;
};
// RefReprType maps a type to the representation used by fbjni smart references.
template <typename T, typename Enable = void>
struct RefReprType;
template <typename T>
struct JavaObjectType;
template <typename T>
struct ReprAccess;
}
// Given T, either a jobject-like type or a JavaClass-derived type, ReprType<T>
// is the corresponding JavaClass-derived type and JniType<T> is the
// jobject-like type.
template <typename T>
using ReprType = typename detail::RefReprType<T>::type;
template <typename T>
using JniType = typename detail::JavaObjectType<T>::type;
template<typename T, typename Alloc>
class base_owned_ref;
template<typename T, typename Alloc>
class basic_strong_ref;
template<typename T>
class weak_ref;
template<typename T>
class alias_ref;
/// A smart unique reference owning a local JNI reference
template<typename T>
using local_ref = basic_strong_ref<T, LocalReferenceAllocator>;
/// A smart unique reference owning a global JNI reference
template<typename T>
using global_ref = basic_strong_ref<T, GlobalReferenceAllocator>;
}} // namespace facebook::jni

View File

@@ -1,535 +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 <new>
#include "CoreClasses.h"
namespace facebook {
namespace jni {
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
return ref;
}
template<typename T>
inline JniType<T> getPlainJniReference(alias_ref<T> ref) {
return ref.get();
}
template<typename T, typename A>
inline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {
return ref.get();
}
namespace detail {
template <typename Repr>
struct ReprAccess {
using javaobject = JniType<Repr>;
static void set(Repr& repr, javaobject obj) noexcept {
repr.JObjectBase::set(obj);
}
static javaobject get(const Repr& repr) {
return static_cast<javaobject>(repr.JObject::get());
}
};
namespace {
template <typename Repr>
void StaticAssertValidRepr() noexcept {
static_assert(std::is_base_of<JObject, Repr>::value,
"A smart ref representation must be derived from JObject.");
static_assert(IsPlainJniReference<JniType<Repr>>(), "T must be a JNI reference");
static_assert(sizeof(Repr) == sizeof(JObjectBase), "");
static_assert(alignof(Repr) == alignof(JObjectBase), "");
}
}
template <typename Repr>
ReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {
StaticAssertValidRepr<Repr>();
set(obj);
}
template <typename Repr>
void ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {
new (&storage_) Repr;
ReprAccess<Repr>::set(get(), obj);
}
template <typename Repr>
Repr& ReprStorage<Repr>::get() noexcept {
return *reinterpret_cast<Repr*>(&storage_);
}
template <typename Repr>
const Repr& ReprStorage<Repr>::get() const noexcept {
return *reinterpret_cast<const Repr*>(&storage_);
}
template <typename Repr>
JniType<Repr> ReprStorage<Repr>::jobj() const noexcept {
ReprAccess<Repr>::get(get());
return ReprAccess<Repr>::get(get());
}
template <typename Repr>
void ReprStorage<Repr>::swap(ReprStorage& other) noexcept {
StaticAssertValidRepr<Repr>();
using std::swap;
swap(get(), other.get());
}
inline void JObjectBase::set(jobject reference) noexcept {
this_ = reference;
}
inline jobject JObjectBase::get() const noexcept {
return this_;
}
template<typename T, typename Alloc>
enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T& reference) {
auto old_reference = getPlainJniReference(reference);
if (!old_reference) {
return nullptr;
}
auto ref = Alloc{}.newReference(old_reference);
if (!ref) {
// Note that we end up here if we pass a weak ref that refers to a collected object.
// Thus, it's hard to come up with a reason why this function should be used with
// weak references.
throw std::bad_alloc{};
}
return static_cast<plain_jni_reference_t<T>>(ref);
}
} // namespace detail
template<typename T>
inline local_ref<T> adopt_local(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return local_ref<T>{ref};
}
template<typename T>
inline global_ref<T> adopt_global(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return global_ref<T>{ref};
}
template<typename T>
inline weak_ref<T> adopt_weak_global(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return weak_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {
return alias_ref<T>(ref);
}
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
template<typename T>
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T& ref) {
return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));
}
template<typename T>
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
make_global(const T& ref) {
return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));
}
template<typename T>
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T& ref) {
return adopt_weak_global(detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));
}
template<typename T1, typename T2>
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator==(const T1& a, const T2& b) {
return isSameObject(getPlainJniReference(a), getPlainJniReference(b));
}
template<typename T1, typename T2>
inline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator!=(const T1& a, const T2& b) {
return !(a == b);
}
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 ///////////////////////////////////////////////////////////////////////
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
: base_owned_ref(nullptr)
{}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
: base_owned_ref(static_cast<javaobject>(nullptr))
{
(void)t;
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
{}
template<typename T, typename Alloc>
template<typename U>
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
{
static_assert(std::is_convertible<JniType<U>, javaobject>::value, "");
}
template<typename T, typename Alloc>
inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
javaobject reference) noexcept
: storage_(reference) {
assert(Alloc{}.verifyReference(reference));
internal::dbglog("New wrapped ref=%p this=%p", get(), this);
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(
base_owned_ref<T, Alloc>&& other) noexcept
: storage_(other.get()) {
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
internal::dbglog("New move to ref=%p this=%p", get(), this);
// JObject is a simple type and does not support move semantics so we explicitly
// clear other
other.set(nullptr);
}
template<typename T, typename Alloc>
template<typename U>
base_owned_ref<T, Alloc>::base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept
: storage_(other.get()) {
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
internal::dbglog("New move to ref=%p this=%p", get(), this);
// JObject is a simple type and does not support move semantics so we explicitly
// clear other
other.set(nullptr);
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {
reset();
internal::dbglog("Ref destruct ref=%p this=%p", get(), this);
}
template<typename T, typename Alloc>
inline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {
auto value = get();
internal::dbglog("Ref release ref=%p this=%p", value, this);
set(nullptr);
return value;
}
template<typename T, typename Alloc>
inline void base_owned_ref<T,Alloc>::reset() noexcept {
reset(nullptr);
}
template<typename T, typename Alloc>
inline void base_owned_ref<T,Alloc>::reset(javaobject reference) noexcept {
if (get()) {
assert(Alloc{}.verifyReference(reference));
Alloc{}.deleteReference(get());
}
set(reference);
}
template<typename T, typename Alloc>
inline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {
return storage_.jobj();
}
template<typename T, typename Alloc>
inline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {
storage_.set(ref);
}
// weak_ref ///////////////////////////////////////////////////////////////////////
template<typename T>
inline weak_ref<T>& weak_ref<T>::operator=(
const weak_ref& other) {
auto otherCopy = other;
swap(*this, otherCopy);
return *this;
}
template<typename T>
inline weak_ref<T>& weak_ref<T>::operator=(
weak_ref<T>&& other) noexcept {
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
get(), this, other.get(), &other);
reset(other.release());
return *this;
}
template<typename T>
local_ref<T> weak_ref<T>::lockLocal() const {
return adopt_local(
static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));
}
template<typename T>
global_ref<T> weak_ref<T>::lockGlobal() const {
return adopt_global(
static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));
}
template<typename T>
inline void swap(
weak_ref<T>& a,
weak_ref<T>& b) noexcept {
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
a.get(), &a, b.get(), &b);
a.storage_.swap(b.storage_);
}
// basic_strong_ref ////////////////////////////////////////////////////////////////////////////
template<typename T, typename Alloc>
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
const basic_strong_ref& other) {
auto otherCopy = other;
swap(*this, otherCopy);
return *this;
}
template<typename T, typename Alloc>
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
basic_strong_ref<T, Alloc>&& other) noexcept {
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
get(), this, other.get(), &other);
reset(other.release());
return *this;
}
template<typename T, typename Alloc>
inline alias_ref<T> basic_strong_ref<T, Alloc>::releaseAlias() noexcept {
return wrap_alias(release());
}
template<typename T, typename Alloc>
inline basic_strong_ref<T, Alloc>::operator bool() const noexcept {
return get() != nullptr;
}
template<typename T, typename Alloc>
inline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {
return &storage_.get();
}
template<typename T, typename Alloc>
inline auto basic_strong_ref<T, Alloc>::operator->() const noexcept -> const Repr* {
return &storage_.get();
}
template<typename T, typename Alloc>
inline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {
return storage_.get();
}
template<typename T, typename Alloc>
inline auto basic_strong_ref<T, Alloc>::operator*() const noexcept -> const Repr& {
return storage_.get();
}
template<typename T, typename Alloc>
inline void swap(
basic_strong_ref<T, Alloc>& a,
basic_strong_ref<T, Alloc>& b) noexcept {
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
a.get(), &a, b.get(), &b);
using std::swap;
a.storage_.swap(b.storage_);
}
// alias_ref //////////////////////////////////////////////////////////////////////////////
template<typename T>
inline alias_ref<T>::alias_ref() noexcept
: storage_{nullptr}
{}
template<typename T>
inline alias_ref<T>::alias_ref(std::nullptr_t) noexcept
: storage_{nullptr}
{}
template<typename T>
inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
: storage_{other.get()}
{}
template<typename T>
inline alias_ref<T>::alias_ref(javaobject ref) noexcept
: storage_(ref) {
assert(
LocalReferenceAllocator{}.verifyReference(ref) ||
GlobalReferenceAllocator{}.verifyReference(ref));
}
template<typename T>
template<typename TOther, typename /* for SFINAE */>
inline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept
: storage_{other.get()}
{}
template<typename T>
template<typename TOther, typename AOther, typename /* for SFINAE */>
inline alias_ref<T>::alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept
: storage_{other.get()}
{}
template<typename T>
inline alias_ref<T>& alias_ref<T>::operator=(alias_ref other) noexcept {
swap(*this, other);
return *this;
}
template<typename T>
inline alias_ref<T>::operator bool() const noexcept {
return get() != nullptr;
}
template<typename T>
inline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {
return storage_.jobj();
}
template<typename T>
inline auto alias_ref<T>::operator->() noexcept -> Repr* {
return &(**this);
}
template<typename T>
inline auto alias_ref<T>::operator->() const noexcept -> const Repr* {
return &(**this);
}
template<typename T>
inline auto alias_ref<T>::operator*() noexcept -> Repr& {
return storage_.get();
}
template<typename T>
inline auto alias_ref<T>::operator*() const noexcept -> const Repr& {
return storage_.get();
}
template<typename T>
inline void alias_ref<T>::set(javaobject ref) noexcept {
storage_.set(ref);
}
template<typename T>
inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
a.storage_.swap(b.storage_);
}
// Could reduce code duplication by using a pointer-to-function
// template argument. I'm not sure whether that would make the code
// more maintainable (DRY), or less (too clever/confusing.).
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
static_ref_cast(const local_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return make_local(p);
}
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
static_ref_cast(const global_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return make_global(p);
}
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
static_ref_cast(const alias_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return wrap_alias(p);
}
template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
{
if (!ref) {
return decltype(static_ref_cast<T>(ref))();
}
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());
}
local_ref<jclass> source_class = ref->getClass();
if (!target_class->isAssignableFrom(source_class)) {
throwNewJavaException("java/lang/ClassCastException",
"Tried to cast from %s to %s.",
source_class->toString().c_str(),
jtype_traits<T>::base_name().c_str());
}
return static_ref_cast<T>(ref);
}
}}

View File

@@ -1,603 +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 References.h
*
* Functionality similar to smart pointers, but for references into the VM. Four main reference
* types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic
* templates that and refer to objects in the jobject hierarchy. The type of the referred objects
* are specified using the template parameter. All reference types except alias_ref own their
* underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context
* of std smart pointers, these references behave like unique_ptr, and have basically the same
* interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying
* JNI reference (like the parameters passed directly to JNI functions), is released. The alias
* references provides no ownership and is a simple wrapper for plain JNI references.
*
* All but the weak references provides access to the underlying object using dereferencing, and a
* get() method. It is also possible to convert these references to booleans to test for nullity.
* To access the underlying object of a weak reference, the reference must either be released, or
* the weak reference can be used to create a local or global reference.
*
* An owning reference is created either by moving the reference from an existing owned reference,
* by copying an existing owned reference (which creates a new underlying reference), by using the
* default constructor which initialize the reference to nullptr, or by using a helper function. The
* helper function exist in two flavors: make_XXX or adopt_XXX.
*
* Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the
* plain JNI reference so be sure that no one else owns the reference when you adopt it, and make
* sure that you know what kind of reference it is.
*
* New owned references can be created from existing plain JNI references, alias references, local
* references, and global references (i.e. non-weak references) using the make_local, make_global,
* and make_weak functions.
*
* Alias references can be implicitly initialized using global, local and plain JNI references using
* the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather
* create a separate reference that we do own, leaving the passed-in reference to its fate.
*
* Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart
* reference of the same type. In the case of copy assignment a new reference is created. Alias
* reference can also be assigned new values, but since they are simple wrappers of plain JNI
* references there is no move semantics involved.
*
* Alias references are special in that they do not own the object and can therefore safely be
* converted to and from its corresponding plain JNI reference. They are useful as parameters of
* functions that do not affect the lifetime of a reference. Usage can be compared with using plain
* JNI pointers as parameters where a function does not take ownership of the underlying object.
*
* The local, global, and alias references makes it possible to access methods in the underlying
* objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are
* supported (see example below). The wrappers also supports inheritance so a wrapper can inherit
* from another wrapper to gain access to its functionality. As an example the jstring wrapper
* inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for
* example call the toString() method using the jclass wrapper, or any other class that inherits
* from the jobject wrapper.
*
* Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if
* you have a jobject that refers to a Java String you will need to cast it to jstring to get the
* jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one
* stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast
* wisely.
*
* @include WrapperSample.cpp
*/
#pragma once
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <jni.h>
#include "ReferenceAllocators.h"
#include "TypeTraits.h"
#include "References-forward.h"
namespace facebook {
namespace jni {
/// Convenience function to wrap an existing local reference
template<typename T>
local_ref<T> adopt_local(T ref) noexcept;
/// Convenience function to wrap an existing global reference
template<typename T>
global_ref<T> adopt_global(T ref) noexcept;
/// Convenience function to wrap an existing weak reference
template<typename T>
weak_ref<T> adopt_weak_global(T ref) noexcept;
/// Swaps two owning references of the same type
template<typename T>
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
/// Swaps two owning references of the same type
template<typename T, typename Alloc>
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
/**
* Retrieve the plain reference from a plain reference.
*/
template<typename T>
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
/**
* Retrieve the plain reference from an alias reference.
*/
template<typename T>
JniType<T> getPlainJniReference(alias_ref<T> ref);
/**
* Retrieve the plain JNI reference from any reference owned reference.
*/
template<typename T, typename Alloc>
JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
class JObject;
class JClass;
namespace detail {
template <typename T, typename Enable = void>
struct HasJniRefRepr : std::false_type {};
template <typename T>
struct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {
using type = typename T::JniRefRepr;
};
template <typename T>
struct RefReprType<T*> {
using type = typename std::conditional<HasJniRefRepr<T>::value, typename HasJniRefRepr<T>::type, JObjectWrapper<T*>>::type;
static_assert(std::is_base_of<JObject, type>::value,
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct RefReprType<T, typename std::enable_if<std::is_base_of<JObject, T>::value, void>::type> {
using type = T;
static_assert(std::is_base_of<JObject, type>::value,
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct JavaObjectType {
using type = typename RefReprType<T>::type::javaobject;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct JavaObjectType<JObjectWrapper<T>> {
using type = T;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct JavaObjectType<T*> {
using type = T*;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename Repr>
struct ReprStorage {
explicit ReprStorage(JniType<Repr> obj) noexcept;
void set(JniType<Repr> obj) noexcept;
Repr& get() noexcept;
const Repr& get() const noexcept;
JniType<Repr> jobj() const noexcept;
void swap(ReprStorage& other) noexcept;
private:
ReprStorage() = delete;
ReprStorage(const ReprStorage&) = delete;
ReprStorage(ReprStorage&&) = delete;
ReprStorage& operator=(const ReprStorage&) = delete;
ReprStorage& operator=(ReprStorage&&) = delete;
using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
Storage storage_;
};
} // namespace detail
/**
* Create a new local reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned local reference (referring to null if the input does)
* @throws std::bad_alloc if the JNI reference could not be created
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T& r);
/**
* Create a new global reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned global reference (referring to null if the input does)
* @throws std::bad_alloc if the JNI reference could not be created
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
make_global(const T& r);
/**
* Create a new weak global reference from an existing reference
*
* @param ref a plain JNI, alias, or strong reference
* @return an owned weak global reference (referring to null if the input does)
* @throws std::bad_alloc if the returned reference is null
*/
template<typename T>
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T& r);
/**
* Compare two references to see if they refer to the same object
*/
template<typename T1, typename T2>
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator==(const T1& a, const T2& b);
/**
* Compare two references to see if they don't refer to the same object
*/
template<typename T1, typename T2>
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator!=(const T1& a, const T2& b);
/**
* 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>
class base_owned_ref {
public:
using javaobject = JniType<T>;
/**
* Release the ownership and set the reference to null. Thus no deleter is invoked.
* @return Returns the reference
*/
javaobject release() noexcept;
/**
* Reset the reference to refer to nullptr.
*/
void reset() noexcept;
protected:
using Repr = ReprType<T>;
detail::ReprStorage<Repr> storage_;
javaobject get() const noexcept;
void set(javaobject ref) noexcept;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit base_owned_ref(javaobject reference) noexcept;
/// Create a null reference
base_owned_ref() noexcept;
/// Create a null reference
explicit base_owned_ref(std::nullptr_t) noexcept;
/// Copy constructor (note creates a new reference)
base_owned_ref(const base_owned_ref& other);
template<typename U>
base_owned_ref(const base_owned_ref<U, Alloc>& other);
/// Transfers ownership of an underlying reference from one unique reference to another
base_owned_ref(base_owned_ref&& other) noexcept;
template<typename U>
base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;
/// The delete the underlying reference if applicable
~base_owned_ref() noexcept;
/// Assignment operator (note creates a new reference)
base_owned_ref& operator=(const base_owned_ref& other);
/// Assignment by moving a reference thus not creating a new reference
base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
void reset(javaobject reference) noexcept;
friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref);
template<typename U, typename UAlloc>
friend class base_owned_ref;
};
/**
* A smart reference that owns its underlying JNI reference. The class provides basic
* functionality to handle a reference but gives no access to it unless the reference is
* released, thus no longer owned. The API is stolen with pride from unique_ptr and the
* semantics should be basically the same. This class should not be used directly, instead use
* @ref weak_ref
*/
template<typename T>
class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
public:
using javaobject = JniType<T>;
using Allocator = WeakGlobalReferenceAllocator;
// This inherits non-default, non-copy, non-move ctors.
using base_owned_ref<T, Allocator>::base_owned_ref;
/// Create a null reference
weak_ref() noexcept
: base_owned_ref<T, Allocator>{} {}
/// Create a null reference
/* implicit */ weak_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Allocator>{nullptr} {}
/// Copy constructor (note creates a new reference)
weak_ref(const weak_ref& other)
: base_owned_ref<T, Allocator>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
weak_ref(const weak_ref<U>& other)
: base_owned_ref<T, Allocator>{other} {}
/// Transfers ownership of an underlying reference from one unique reference to another
weak_ref(weak_ref&& other) noexcept
: base_owned_ref<T, Allocator>{std::move(other)} {}
/// Assignment operator (note creates a new reference)
weak_ref& operator=(const weak_ref& other);
/// Assignment by moving a reference thus not creating a new reference
weak_ref& operator=(weak_ref&& rhs) noexcept;
// Creates an owned local reference to the referred object or to null if the object is reclaimed
local_ref<T> lockLocal() const;
// Creates an owned global reference to the referred object or to null if the object is reclaimed
global_ref<T> lockGlobal() const;
private:
// get/release/reset on weak_ref are not exposed to users.
using base_owned_ref<T, Allocator>::get;
using base_owned_ref<T, Allocator>::release;
using base_owned_ref<T, Allocator>::reset;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit weak_ref(javaobject reference) noexcept
: base_owned_ref<T, Allocator>{reference} {}
template<typename T2> friend class weak_ref;
friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;
friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
};
/**
* A class representing owned strong references to Java objects. This class
* should not be used directly, instead use @ref local_ref, or @ref global_ref.
*/
template<typename T, typename Alloc>
class basic_strong_ref : public base_owned_ref<T, Alloc> {
using typename base_owned_ref<T, Alloc>::Repr;
public:
using javaobject = JniType<T>;
using Allocator = Alloc;
// This inherits non-default, non-copy, non-move ctors.
using base_owned_ref<T, Alloc>::base_owned_ref;
using base_owned_ref<T, Alloc>::release;
using base_owned_ref<T, Alloc>::reset;
/// Create a null reference
basic_strong_ref() noexcept
: base_owned_ref<T, Alloc>{} {}
/// Create a null reference
/* implicit */ basic_strong_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Alloc>{nullptr} {}
/// Copy constructor (note creates a new reference)
basic_strong_ref(const basic_strong_ref& other)
: base_owned_ref<T, Alloc>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
: base_owned_ref<T, Alloc>{other} {}
/// Transfers ownership of an underlying reference from one unique reference to another
basic_strong_ref(basic_strong_ref&& other) noexcept
: base_owned_ref<T, Alloc>{std::move(other)} {}
/// Assignment operator (note creates a new reference)
basic_strong_ref& operator=(const basic_strong_ref& other);
/// Assignment by moving a reference thus not creating a new reference
basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
/// Get the plain JNI reference
using base_owned_ref<T, Allocator>::get;
/// Release the ownership of the reference and return the wrapped reference in an alias
alias_ref<T> releaseAlias() noexcept;
/// Checks if the reference points to a non-null object
explicit operator bool() const noexcept;
/// Access the functionality provided by the object wrappers
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const Repr* operator->() const noexcept;
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
Repr& operator*() noexcept;
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
/// before invoking)
const Repr& operator*() const noexcept;
private:
using base_owned_ref<T, Alloc>::storage_;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit basic_strong_ref(javaobject reference) noexcept
: base_owned_ref<T, Alloc>{reference} {}
friend local_ref<T> adopt_local<T>(T ref) noexcept;
friend global_ref<T> adopt_global<T>(T ref) noexcept;
friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
};
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
/// Swaps to alias reference of the same type
template<typename T>
void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
/**
* A non-owning variant of the smart references (a dumb reference). These references still provide
* access to the functionality of the @ref JObjectWrapper specializations including exception
* handling and ease of use. Use this representation when you don't want to claim ownership of the
* underlying reference (compare to using raw pointers instead of smart pointers.) For symmetry use
* @ref alias_ref instead of this class.
*/
template<typename T>
class alias_ref {
using Repr = ReprType<T>;
public:
using javaobject = JniType<T>;
/// Create a null reference
alias_ref() noexcept;
/// Create a null reference
/* implicit */ alias_ref(std::nullptr_t) noexcept;
/// Copy constructor
alias_ref(const alias_ref& other) noexcept;
/// Wrap an existing plain JNI reference
/* implicit */ alias_ref(javaobject ref) noexcept;
/// Wrap an existing smart reference of any type convertible to T
template<
typename TOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(alias_ref<TOther> other) noexcept;
/// Wrap an existing alias reference of a type convertible to T
template<
typename TOther,
typename AOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
/// Assignment operator
alias_ref& operator=(alias_ref other) noexcept;
/// Checks if the reference points to a non-null object
explicit operator bool() const noexcept;
/// Converts back to a plain JNI reference
javaobject get() const noexcept;
/// Access the functionality provided by the object wrappers
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const Repr* operator->() const noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
Repr& operator*() noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
const Repr& operator*() const noexcept;
private:
void set(javaobject ref) noexcept;
detail::ReprStorage<Repr> storage_;
friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
};
/**
* RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.
*
* This is useful when you have a call which is initiated from C++-land, and therefore
* doesn't automatically get a local JNI frame managed for you by the JNI framework.
*/
class JniLocalScope {
public:
JniLocalScope(JNIEnv* p_env, jint capacity);
~JniLocalScope();
private:
JNIEnv* env_;
bool hasFrame_;
};
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
static_ref_cast(const local_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
static_ref_cast(const global_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
static_ref_cast(const alias_ref<U>& ref) noexcept;
template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> ;
}}
#include "References-inl.h"

View File

@@ -1,166 +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
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>);
}
}
}}

View File

@@ -1,167 +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 "References.h"
namespace facebook {
namespace jni {
namespace detail {
// This uses the real JNI function as a non-type template parameter to
// cause a (static member) function to exist with the same signature,
// but with try/catch exception translation.
template<typename F, F func, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));
// Automatically wrap object argument, and don't take env explicitly.
template<typename F, F func, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... args));
// Extract C++ instance from object, and invoke given method on it,
template<typename M, M method, typename C, typename R, typename... Args>
NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));
// This uses deduction to figure out the descriptor name if the types
// are primitive or have JObjectWrapper specializations.
template<typename R, typename C, typename... Args>
std::string makeDescriptor(R (*func)(JNIEnv*, C, Args... args));
// This uses deduction to figure out the descriptor name if the types
// are primitive or have JObjectWrapper specializations.
template<typename R, typename C, typename... Args>
std::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));
// This uses deduction to figure out the descriptor name if the types
// are primitive or have JObjectWrapper specializations.
template<typename R, typename C, typename... Args>
std::string makeDescriptor(R (C::*method0)(Args... args));
template<typename F>
struct CriticalMethod;
template<typename R, typename ...Args>
struct CriticalMethod<R(*)(Args...)> {
template<R(*func)(Args...)>
static R call(alias_ref<jclass>, Args... args) noexcept;
template<R(*func)(Args...)>
inline static std::string desc();
};
}
// We have to use macros here, because the func needs to be used
// as both a decltype expression argument and as a non-type template
// parameter, since C++ provides no way for translateException
// to deduce the type of its non-type template parameter.
// The empty string in the macros below ensures that name
// is always a string literal (because that syntax is only
// valid when name is a string literal).
#define makeNativeMethod2(name, func) \
{ name "", ::facebook::jni::detail::makeDescriptor(&func), \
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
#define makeNativeMethod3(name, desc, func) \
{ name "", desc, \
::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }
// Variadic template hacks to get macros with different numbers of
// arguments. Usage instructions are in CoreClasses.h.
#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count
#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
// FAST CALLS / CRITICAL CALLS
// Android up to and including v7 supports "fast calls" by prefixing the method
// signature with an exclamation mark.
// Android v8+ supports fast calls by annotating methods:
// https://source.android.com/devices/tech/dalvik/improvements#faster-native-methods
//
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
//
// "Fast" calls are only on the order of a few dozen NANO-seconds faster than
// regular JNI calls. If your method does almost aaanything of consequence - if
// you loop, if you write to a log, if you call another method, if you even
// simply allocate or deallocate - then the method body will significantly
// outweigh the method overhead.
//
// The difference between a regular JNI method and a "FastJNI" method (as
// they're called inside the runtime) is that a FastJNI method doesn't mark the
// thread as executing native code, and by skipping that avoids the locking and
// thread state check overhead of interacting with the Garbage Collector.
//
// To understand why this is dangerous, you need to understand a bit about the
// GC. In order to perform its work the GC needs to have at least one (usually
// two in modern implementations) "stop the world" checkpoints where it can
// guarantee that all managed-code execution is paused. The VM performs these
// checkpoints at allocations, method boundaries, and each backward branch (ie
// anytime you loop). When the GC wants to run, it will signal to all managed
// threads that they should pause at the next checkpoint, and then it will wait
// for every thread in the system to transition from the "runnable" state into a
// "waiting" state. Once every thread has stopped, the GC thread can perform the
// work it needs to and then it will trigger the execution threads to resume.
//
// JNI methods fit neatly into the above paradigm: They're still methods, so
// they perform GC checkpoints at method entry and method exit. JNI methods also
// perform checkpoints at any JNI boundary crossing - ie, any time you call
// GetObjectField etc. Because access to managed objects from native code is
// tightly controlled, the VM is able to mark threads executing native methods
// into a special "native" state which the GC is able to ignore: It knows they
// can't touch managed objects (without hitting a checkpoint) so it doesn't care
// about them.
//
// JNI critical methods don't perform that "runnable" -> "native" thread state
// transition. Skipping that transition allows them to shave about 20ns off
// their total execution time, but it means that the GC has to wait for them to
// complete before it can move forward. If a critical method begins blocking,
// say on a long loop, or an I/O operation, or on perhaps a mutex, then the GC
// will also block, and because the GC is blocking the entire rest of the VM
// (which is waiting on the GC) will block. If the critical method is blocking
// on a mutex that's already held by the GC - for example, the VM's internal
// weak_globals_lock_ which guards modifications to the weak global reference
// table (and is required in order to create or free a weak_ref<>) - then you
// have a system-wide deadlock.
// prefixes a JNI method signature as android "fast call".
#if defined(__ANDROID__) && defined(FBJNI_WITH_FAST_CALLS)
#define FBJNI_PREFIX_FAST_CALL(desc) (std::string{"!"} + desc)
#else
#define FBJNI_PREFIX_FAST_CALL(desc) (desc)
#endif
#define makeCriticalNativeMethod3(name, desc, func) \
makeNativeMethod3( \
name, \
FBJNI_PREFIX_FAST_CALL(desc), \
::facebook::jni::detail::CriticalMethod<decltype(&func)>::call<&func>)
#define makeCriticalNativeMethod2(name, func) \
makeCriticalNativeMethod3( \
name, \
::facebook::jni::detail::CriticalMethod<decltype(&func)>::desc<&func>(), \
func)
#define makeCriticalNativeMethodN(a, b, c, count, ...) makeCriticalNativeMethod ## count
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// YOU ALMOST CERTAINLY DO NOT NEED THIS AND IT IS DANGEROUS.
// See above for an explanation.
#define makeCriticalNativeMethod_DO_NOT_USE_OR_YOU_WILL_BE_FIRED(...) makeCriticalNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)
}}
#include "Registration-inl.h"

View File

@@ -1,176 +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 <type_traits>
#include "References-forward.h"
namespace facebook {
namespace jni {
/// Generic std::enable_if helper
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
/// Generic std::is_convertible helper
template<typename From, typename To>
constexpr bool IsConvertible() {
return std::is_convertible<From, To>::value;
}
template<template<typename...> class TT, typename T>
struct is_instantiation_of : std::false_type {};
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : std::true_type {};
template<template<typename...> class TT, typename... Ts>
constexpr bool IsInstantiationOf() {
return is_instantiation_of<TT, Ts...>::value;
}
/// Metafunction to determine whether a type is a JNI reference or not
template<typename T>
struct is_plain_jni_reference :
std::integral_constant<bool,
std::is_pointer<T>::value &&
std::is_base_of<
typename std::remove_pointer<jobject>::type,
typename std::remove_pointer<T>::type>::value> {};
/// Helper to simplify use of is_plain_jni_reference
template<typename T>
constexpr bool IsPlainJniReference() {
return is_plain_jni_reference<T>::value;
}
/// Metafunction to determine whether a type is a primitive JNI type or not
template<typename T>
struct is_jni_primitive :
std::integral_constant<bool,
std::is_same<jboolean, T>::value ||
std::is_same<jbyte, T>::value ||
std::is_same<jchar, T>::value ||
std::is_same<jshort, T>::value ||
std::is_same<jint, T>::value ||
std::is_same<jlong, T>::value ||
std::is_same<jfloat, T>::value ||
std::is_same<jdouble, T>::value> {};
/// Helper to simplify use of is_jni_primitive
template<typename T>
constexpr bool IsJniPrimitive() {
return is_jni_primitive<T>::value;
}
/// Metafunction to determine whether a series of types are all primitive JNI types.
template<typename ...Ts>
struct are_jni_primitives;
template<typename T, typename ...Ts>
struct are_jni_primitives<T, Ts...> :
std::integral_constant<bool,
is_jni_primitive<T>::value && are_jni_primitives<Ts...>::value> {};
template<>
struct are_jni_primitives<> : std::integral_constant<bool, true> {};
/// Helper to simplify use of are_jni_primitives
template<typename ...Ts>
constexpr bool AreJniPrimitives() {
return are_jni_primitives<Ts...>::value;
}
/// Metafunction to determine whether a type is a JNI array of primitives or not
template <typename T>
struct is_jni_primitive_array :
std::integral_constant<bool,
std::is_same<jbooleanArray, T>::value ||
std::is_same<jbyteArray, T>::value ||
std::is_same<jcharArray, T>::value ||
std::is_same<jshortArray, T>::value ||
std::is_same<jintArray, T>::value ||
std::is_same<jlongArray, T>::value ||
std::is_same<jfloatArray, T>::value ||
std::is_same<jdoubleArray, T>::value> {};
/// Helper to simplify use of is_jni_primitive_array
template <typename T>
constexpr bool IsJniPrimitiveArray() {
return is_jni_primitive_array<T>::value;
}
/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type
template<typename T>
struct is_jni_scalar :
std::integral_constant<bool,
is_plain_jni_reference<T>::value ||
is_jni_primitive<T>::value> {};
/// Helper to simplify use of is_jni_scalar
template<typename T>
constexpr bool IsJniScalar() {
return is_jni_scalar<T>::value;
}
// Metafunction to determine if a type is a JNI type
template<typename T>
struct is_jni_type :
std::integral_constant<bool,
is_jni_scalar<T>::value ||
std::is_void<T>::value> {};
/// Helper to simplify use of is_jni_type
template<typename T>
constexpr bool IsJniType() {
return is_jni_type<T>::value;
}
template<typename T>
struct is_non_weak_reference :
std::integral_constant<bool,
IsPlainJniReference<T>() ||
IsInstantiationOf<basic_strong_ref, T>() ||
IsInstantiationOf<alias_ref, T>()> {};
template<typename T>
constexpr bool IsNonWeakReference() {
return is_non_weak_reference<T>::value;
}
template<typename T>
struct is_any_reference :
std::integral_constant<bool,
IsPlainJniReference<T>() ||
IsInstantiationOf<weak_ref, T>() ||
IsInstantiationOf<basic_strong_ref, T>() ||
IsInstantiationOf<alias_ref, T>()> {};
template<typename T>
constexpr bool IsAnyReference() {
return is_any_reference<T>::value;
}
template<typename T>
struct reference_traits {
using plain_jni_reference_t = JniType<T>;
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
};
template<template <typename...> class R, typename T, typename... A>
struct reference_traits<R<T, A...>> {
using plain_jni_reference_t = JniType<T>;
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
};
template<typename T>
using plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;
} // namespace jni
} // namespace facebook

View File

@@ -1,81 +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>
namespace facebook {
namespace jni {
namespace detail {
void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength);
size_t modifiedLength(const std::string& str);
size_t modifiedLength(const uint8_t* str, size_t* length);
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept;
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
}
// JNI represents strings encoded with modified version of UTF-8. The difference between UTF-8 and
// Modified UTF-8 is that the latter support only 1-byte, 2-byte, and 3-byte formats. Supplementary
// character (4 bytes in unicode) needs to be represented in the form of surrogate pairs. To create
// a Modified UTF-8 surrogate pair that Dalvik would understand we take 4-byte unicode character,
// encode it with UTF-16 which gives us two 2 byte chars (surrogate pair) and then we encode each
// pair as UTF-8. This result in 2 x 3 byte characters. To convert modified UTF-8 to standard
// UTF-8, this mus tbe reversed.
//
// The second difference is that Modified UTF-8 is encoding NUL byte in 2-byte format.
//
// In order to avoid complex error handling, only a minimum of validity checking is done to avoid
// crashing. If the input is invalid, the output may be invalid as well.
//
// Relevant links:
// - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
// - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8
// JString to UTF16 extractor using RAII idiom. Note that the
// ctor/dtor use GetStringCritical/ReleaseStringCritical, so this
// class is subject to the restrictions imposed by those functions.
class JStringUtf16Extractor {
public:
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
: env_(env)
, javaString_(javaString)
, length_(0)
, utf16String_(nullptr) {
if (env_ && javaString_) {
length_ = env_->GetStringLength(javaString_);
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
}
}
~JStringUtf16Extractor() {
if (utf16String_) {
env_->ReleaseStringCritical(javaString_, utf16String_);
}
}
const jsize length() const {
return length_;
}
const jchar* chars() const {
return utf16String_;
}
private:
JNIEnv* env_;
jstring javaString_;
jsize length_;
const jchar* utf16String_;
};
}
}

View File

@@ -1,22 +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 <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>

View File

@@ -1,195 +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 <iomanip>
#include <string>
#include <vector>
namespace facebook {
namespace lyra {
constexpr size_t kDefaultLimit = 64;
using InstructionPointer = const void*;
class StackTraceElement {
public:
StackTraceElement(InstructionPointer absoluteProgramCounter,
InstructionPointer libraryBase,
InstructionPointer functionAddress,
std::string libraryName,
std::string functionName)
: absoluteProgramCounter_{absoluteProgramCounter},
libraryBase_{libraryBase},
functionAddress_{functionAddress},
libraryName_{std::move(libraryName)},
functionName_{std::move(functionName)},
hasBuildId_{false},
buildId_{}
{}
InstructionPointer libraryBase() const noexcept { return libraryBase_; }
InstructionPointer functionAddress() const noexcept {
return functionAddress_;
}
InstructionPointer absoluteProgramCounter() const noexcept {
return absoluteProgramCounter_;
}
const std::string& libraryName() const noexcept { return libraryName_; }
const std::string& functionName() const noexcept { return functionName_; }
/**
* The offset of the program counter to the base of the library (i.e. the
* address that addr2line takes as input>
*/
std::ptrdiff_t libraryOffset() const noexcept {
auto absoluteLibrary = static_cast<const char*>(libraryBase_);
auto absoluteabsoluteProgramCounter =
static_cast<const char*>(absoluteProgramCounter_);
return absoluteabsoluteProgramCounter - absoluteLibrary;
}
/**
* The offset within the current function
*/
int functionOffset() const noexcept {
auto absoluteSymbol = static_cast<const char*>(functionAddress_);
auto absoluteabsoluteProgramCounter =
static_cast<const char*>(absoluteProgramCounter_);
return absoluteabsoluteProgramCounter - absoluteSymbol;
}
std::string buildId() const;
private:
const InstructionPointer absoluteProgramCounter_;
const InstructionPointer libraryBase_;
const InstructionPointer functionAddress_;
const std::string libraryName_;
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
*
* Note that this trace needs to be symbolicated to get the library offset even
* if it is to be symbolicated off-line.
*
* Beware of a bug on some platforms, which makes the trace loop until the
* buffer is full when it reaches a noexpr function. It seems to be fixed in
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
*
* @param stackTrace The vector that will receive the stack trace. Before
* filling the vector it will be cleared. The vector will never grow so the
* number of frames captured is limited by the capacity of it.
*
* @param skip The number of frames to skip before capturing the trace
*/
void getStackTrace(std::vector<InstructionPointer>& stackTrace, size_t skip = 0);
/**
* Creates a vector and populates it with the current stack trace
*
* Note that this trace needs to be symbolicated to get the library offset even
* if it is to be symbolicated off-line.
*
* Beware of a bug on some platforms, which makes the trace loop until the
* buffer is full when it reaches a noexpr function. It seems to be fixed in
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
*
* @param skip The number of frames to skip before capturing the trace
*
* @limit The maximum number of frames captured
*/
inline std::vector<InstructionPointer> getStackTrace(
size_t skip = 0,
size_t limit = kDefaultLimit) {
auto stackTrace = std::vector<InstructionPointer>{};
stackTrace.reserve(limit);
getStackTrace(stackTrace, skip + 1);
return stackTrace;
}
/**
* Symbolicates a stack trace into a given vector
*
* @param symbols The vector to receive the output. The vector is cleared and
* enough room to keep the frames are reserved.
*
* @param stackTrace The input stack trace
*/
void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,
const std::vector<InstructionPointer>& trace);
/**
* Symbolicates a stack trace into a new vector
*
* @param stackTrace The input stack trace
*/
inline std::vector<StackTraceElement> getStackTraceSymbols(
const std::vector<InstructionPointer>& trace) {
auto symbols = std::vector<StackTraceElement>{};
getStackTraceSymbols(symbols, trace);
return symbols;
}
/**
* Captures and symbolicates a stack trace
*
* Beware of a bug on some platforms, which makes the trace loop until the
* buffer is full when it reaches a noexpr function. It seems to be fixed in
* newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
*
* @param skip The number of frames before capturing the trace
*
* @param limit The maximum number of frames captured
*/
inline std::vector<StackTraceElement> getStackTraceSymbols(
size_t skip = 0,
size_t limit = kDefaultLimit) {
return getStackTraceSymbols(getStackTrace(skip + 1, limit));
}
/**
* Formatting a stack trace element
*/
std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
/**
* Formatting a stack trace
*/
std::ostream& operator<<(std::ostream& out,
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);
}
}

View File

@@ -1,84 +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 <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);
}
}

View File

@@ -1,75 +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 <fbjni/ByteBuffer.h>
#include <stdexcept>
namespace facebook {
namespace jni {
void JBuffer::rewind() const {
static auto meth = javaClassStatic()->getMethod<alias_ref<JBuffer>()>("rewind");
meth(self());
}
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) {
// env->NewDirectByteBuffer requires that size is positive. Android's
// dalvik returns an invalid result and Android's art aborts if size == 0.
// Workaround this by using a slow path through Java in that case.
if (!size) {
return allocateDirect(0);
}
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!res) {
throw std::runtime_error("Direct byte buffers are unsupported.");
}
return res;
}
local_ref<JByteBuffer> JByteBuffer::allocateDirect(jint size) {
static auto cls = JByteBuffer::javaClassStatic();
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
return meth(cls, size);
}
}}

View File

@@ -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.
*/
#include <fbjni/fbjni.h>
#include <fbjni/NativeRunnable.h>
using namespace facebook::jni;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return facebook::jni::initialize(vm, [] {
HybridDataOnLoad();
JNativeRunnable::OnLoad();
ThreadScope::OnLoad();
});
}

View File

@@ -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.
*/
#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);
}
}}

View File

@@ -1,291 +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 <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();
}
}
} }

View File

@@ -1,390 +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 <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_);
}
}}

View File

@@ -1,32 +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 <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),
});
}
}}

View File

@@ -1,79 +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 <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

View File

@@ -1,278 +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 <fbjni/detail/utf8.h>
#include <fbjni/detail/Log.h>
namespace facebook {
namespace jni {
namespace {
const uint16_t kUtf8OneByteBoundary = 0x80;
const uint16_t kUtf8TwoBytesBoundary = 0x800;
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
inline void encode3ByteUTF8(char32_t code, uint8_t* out) {
if ((code & 0xffff0000) != 0) {
FBJNI_LOGF("3 byte utf-8 encodings only valid for up to 16 bits");
}
out[0] = 0xE0 | (code >> 12);
out[1] = 0x80 | ((code >> 6) & 0x3F);
out[2] = 0x80 | (code & 0x3F);
}
inline char32_t decode3ByteUTF8(const uint8_t* in) {
return (((in[0] & 0x0f) << 12) |
((in[1] & 0x3f) << 6) |
( in[2] & 0x3f));
}
inline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) {
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 + 1] = (char) (0x80 | ((code >> 12) & 0x3F));
out[offset + 2] = (char) (0x80 | ((code >> 6) & 0x3F));
out[offset + 3] = (char) (0x80 | (code & 0x3F));
}
template <typename T>
inline bool isFourByteUTF8Encoding(const T* utf8) {
return ((*utf8 & 0xF8) == 0xF0);
}
}
namespace detail {
size_t modifiedLength(const std::string& str) {
// Scan for supplementary characters
size_t j = 0;
for (size_t i = 0; i < str.size(); ) {
if (str[i] == 0) {
i += 1;
j += 2;
} else if (i + 4 > str.size() ||
!isFourByteUTF8Encoding(&(str[i]))) {
// See the code in utf8ToModifiedUTF8 for what's happening here.
i += 1;
j += 1;
} else {
i += 4;
j += 6;
}
}
return j;
}
// returns modified utf8 length; *length is set to strlen(str)
size_t modifiedLength(const uint8_t* str, size_t* length) {
// NUL-terminated: Scan for length and supplementary characters
size_t i = 0;
size_t j = 0;
while (str[i] != 0) {
if (str[i + 1] == 0 ||
str[i + 2] == 0 ||
str[i + 3] == 0 ||
!isFourByteUTF8Encoding(&(str[i]))) {
i += 1;
j += 1;
} else {
i += 4;
j += 6;
}
}
*length = i;
return j;
}
void utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size_t modifiedBufLen)
{
size_t j = 0;
for (size_t i = 0; i < len; ) {
if (j >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
if (utf8[i] == 0) {
if (j + 1 >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
modified[j] = 0xc0;
modified[j + 1] = 0x80;
i += 1;
j += 2;
continue;
}
if (i + 4 > len ||
!isFourByteUTF8Encoding(utf8 + i)) {
// If the input is too short for this to be a four-byte
// encoding, or it isn't one for real, just copy it on through.
modified[j] = utf8[i];
i++;
j++;
continue;
}
// Convert 4 bytes of input to 2 * 3 bytes of output
char32_t code = (((utf8[i] & 0x07) << 18) |
((utf8[i + 1] & 0x3f) << 12) |
((utf8[i + 2] & 0x3f) << 6) |
( utf8[i + 3] & 0x3f));
char32_t first;
char32_t second;
if (code > 0x10ffff) {
// These could be valid utf-8, but cannot be represented as modified UTF-8, due to the 20-bit
// limit on that representation. Encode two replacement characters, so the expected output
// length lines up.
const char32_t kUnicodeReplacementChar = 0xfffd;
first = kUnicodeReplacementChar;
second = kUnicodeReplacementChar;
} else {
// split into surrogate pair
first = ((code - 0x010000) >> 10) | 0xd800;
second = ((code - 0x010000) & 0x3ff) | 0xdc00;
}
// encode each as a 3 byte surrogate value
if (j + 5 >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
encode3ByteUTF8(first, modified + j);
encode3ByteUTF8(second, modified + j + 3);
i += 4;
j += 6;
}
if (j >= modifiedBufLen) {
FBJNI_LOGF("output buffer is too short");
}
modified[j++] = '\0';
}
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept {
// Converting from modified utf8 to utf8 will always shrink, so this will always be sufficient
std::string utf8(len, 0);
size_t j = 0;
for (size_t i = 0; i < len; ) {
// surrogate pair: 1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
// encoded pair: 1110 1101 1010 xxxx 10xx xxxx 1110 1101 1011 xxxx 10xx xxxx
if (len >= i + 6 &&
modified[i] == 0xed &&
(modified[i + 1] & 0xf0) == 0xa0 &&
modified[i + 3] == 0xed &&
(modified[i + 4] & 0xf0) == 0xb0) {
// Valid surrogate pair
char32_t pair1 = decode3ByteUTF8(modified + i);
char32_t pair2 = decode3ByteUTF8(modified + i + 3);
char32_t ch = 0x10000 + (((pair1 & 0x3ff) << 10) |
( pair2 & 0x3ff));
encode4ByteUTF8(ch, utf8, j);
i += 6;
j += 4;
continue;
} else if (len >= i + 2 &&
modified[i] == 0xc0 &&
modified[i + 1] == 0x80) {
utf8[j] = 0;
i += 2;
j += 1;
continue;
}
// copy one byte. This might be a one, two, or three-byte encoding. It might be an invalid
// encoding of some sort, but garbage in garbage out is ok.
utf8[j] = (char) modified[i];
i++;
j++;
}
utf8.resize(j);
return utf8;
}
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
// UTF16 string
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
if (!utf16String || utf16StringLen == 0) {
return 0;
}
uint32_t utf8StringLen = 0;
auto utf16StringEnd = utf16String + utf16StringLen;
auto idx16 = utf16String;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
utf8StringLen++;
} else if (ch < kUtf8TwoBytesBoundary) {
utf8StringLen += 2;
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
utf8StringLen += 4;
idx16++;
} else {
utf8StringLen += 3;
}
}
return utf8StringLen;
}
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {
if (!utf16String || utf16StringLen <= 0) {
return "";
}
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
auto idx8 = utf8String.begin();
auto idx16 = utf16String;
auto utf16StringEnd = utf16String + utf16StringLen;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
*idx8++ = (ch & 0x7F);
} else if (ch < kUtf8TwoBytesBoundary) {
*idx8++ = 0b11000000 | (ch >> 6);
*idx8++ = 0b10000000 | (ch & 0x3F);
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
auto ch2 = *idx16++;
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
*idx8++ = 0b11110000 | (trunc_byte >> 2);
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
*idx8++ = 0b10000000 | (ch2 & 0x3F);
} else {
*idx8++ = 0b11100000 | (ch >> 12);
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
*idx8++ = 0b10000000 | (ch & 0x3F);
}
}
return utf8String;
}
}
}
}

View File

@@ -1,233 +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 <fbjni/fbjni.h>
#include <mutex>
#include <vector>
#include <fbjni/detail/utf8.h>
namespace facebook {
namespace jni {
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
// TODO (t7832883): DTRT when we have exception pointers
static auto error_msg = std::string{"Failed to initialize fbjni"};
static bool error_occured = [vm] {
bool retVal = false;
try {
Environment::initialize(vm);
} catch (std::exception& ex) {
retVal = true;
try {
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
} catch (...) {
// Ignore, we already have a fall back message
}
} catch (...) {
retVal = true;
}
return retVal;
}();
try {
if (error_occured) {
throw std::runtime_error(error_msg);
}
init_fn();
} catch (const std::exception& e) {
FBJNI_LOGE("error %s", e.what());
translatePendingCppExceptionToJavaException();
} catch (...) {
translatePendingCppExceptionToJavaException();
// So Java will handle the translated exception, fall through and
// return a good version number.
}
return JNI_VERSION_1_6;
}
namespace detail {
jclass findClass(JNIEnv* env, const char* name) {
if (!env) {
throw std::runtime_error("Unable to retrieve JNIEnv*.");
}
jclass cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
return cls;
}
}
local_ref<JClass> findClassLocal(const char* name) {
return adopt_local(detail::findClass(detail::currentOrNull(), name));
}
alias_ref<JClass> findClassStatic(const char* name) {
JNIEnv* env = detail::currentOrNull();
auto cls = adopt_local(detail::findClass(env, name));
auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
return wrap_alias(leaking_ref);
}
// jstring /////////////////////////////////////////////////////////////////////////////////////////
std::string JString::toStdString() const {
const auto env = Environment::current();
auto utf16String = JStringUtf16Extractor(env, self());
return detail::utf16toUTF8(utf16String.chars(), 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) {
if (!utf8) {
return {};
}
const auto env = Environment::current();
size_t len;
size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);
jstring result;
if (modlen == len) {
// The only difference between utf8 and modifiedUTF8 is in encoding 4-byte UTF8 chars
// and '\0' that is encoded on 2 bytes.
//
// Since modifiedUTF8-encoded string can be no shorter than it's UTF8 conterpart we
// know that if those two strings are of the same length we don't need to do any
// conversion -> no 4-byte chars nor '\0'.
result = env->NewStringUTF(utf8);
} else {
auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \0
detail::utf8ToModifiedUTF8(
reinterpret_cast<const uint8_t*>(utf8), len,
reinterpret_cast<uint8_t*>(modified.data()), modified.size());
result = env->NewStringUTF(modified.data());
}
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(result);
}
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 //////////////////////////////////////////////////////////////////////
#pragma push_macro("DEFINE_PRIMITIVE_METHODS")
#undef DEFINE_PRIMITIVE_METHODS
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
\
template<> \
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
auto env = Environment::current(); \
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return res; \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::releaseElements( \
TYPE* elements, jint mode) { \
auto env = Environment::current(); \
env->Release ## NAME ## ArrayElements(self(), elements, mode); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::getRegion( \
jsize start, jsize length, TYPE* buf) { \
auto env = Environment::current(); \
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::setRegion( \
jsize start, jsize length, const TYPE* elements) { \
auto env = Environment::current(); \
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
auto array = Environment::current()->New ## NAME ## Array(size); \
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
return adopt_local(array); \
} \
\
template<> \
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
return make_ ## SMALLNAME ## _array(count); \
} \
\
DEFINE_PRIMITIVE_METHODS(jboolean, Boolean, boolean)
DEFINE_PRIMITIVE_METHODS(jbyte, Byte, byte)
DEFINE_PRIMITIVE_METHODS(jchar, Char, char)
DEFINE_PRIMITIVE_METHODS(jshort, Short, short)
DEFINE_PRIMITIVE_METHODS(jint, Int, int)
DEFINE_PRIMITIVE_METHODS(jlong, Long, long)
DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
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 /////////////////////////////////////////////////////////////////////////////////
namespace internal {
ReferenceStats g_reference_stats;
void facebook::jni::internal::ReferenceStats::reset() noexcept {
locals_deleted = globals_deleted = weaks_deleted = 0;
}
}
}}

View File

@@ -1,106 +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 <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

View File

@@ -1,172 +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 <lyra/lyra.h>
#include <atomic>
#include <ios>
#include <ostream>
#include <iomanip>
#include <memory>
#include <vector>
#include <dlfcn.h>
#include <unwind.h>
#include <fbjni/detail/Log.h>
using namespace std;
namespace facebook {
namespace lyra {
namespace {
class IosFlagsSaver {
ios_base& ios_;
ios_base::fmtflags flags_;
public:
IosFlagsSaver(ios_base& ios)
: ios_(ios),
flags_(ios.flags())
{}
~IosFlagsSaver() {
ios_.flags(flags_);
}
};
struct BacktraceState {
size_t skip;
vector<InstructionPointer>& stackTrace;
};
_Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
BacktraceState* state = reinterpret_cast<BacktraceState*>(arg);
auto absoluteProgramCounter =
reinterpret_cast<InstructionPointer>(_Unwind_GetIP(context));
if (state->skip > 0) {
--state->skip;
return _URC_NO_REASON;
}
if (state->stackTrace.size() == state->stackTrace.capacity()) {
return _URC_END_OF_STACK;
}
state->stackTrace.push_back(absoluteProgramCounter);
return _URC_NO_REASON;
}
void captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {
// Beware of a bug on some platforms, which makes the trace loop until the
// buffer is full when it reaches a noexcept function. It seems to be fixed in
// newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
// TODO(t10738439): Investigate workaround for the stack trace bug
BacktraceState state = {skip, stackTrace};
_Unwind_Backtrace(unwindCallback, &state);
}
// 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) {
stackTrace.clear();
captureBacktrace(skip + 1, stackTrace);
}
// TODO(t10737622): Improve on-device symbolification
void getStackTraceSymbols(vector<StackTraceElement>& symbols,
const vector<InstructionPointer>& trace) {
symbols.clear();
symbols.reserve(trace.size());
for (size_t i = 0; i < trace.size(); ++i) {
Dl_info info;
if (dladdr(trace[i], &info)) {
symbols.emplace_back(trace[i], info.dli_fbase, info.dli_saddr,
info.dli_fname ? info.dli_fname : "",
info.dli_sname ? info.dli_sname : "");
}
}
}
ostream& operator<<(ostream& out, const StackTraceElement& elm) {
IosFlagsSaver flags{out};
out << "{dso=" << elm.libraryName() << " offset=" << hex
<< showbase << elm.libraryOffset();
if (!elm.functionName().empty()) {
out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
}
out << " build-id=" << hex << setw(8) << elm.buildId()
<< "}";
return out;
}
// TODO(t10737667): The implement a tool that parse the stack trace and
// symbolicate it
ostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {
IosFlagsSaver flags{out};
auto i = 0;
out << "Backtrace:\n";
for (auto& elm : trace) {
out << " #" << dec << setfill('0') << setw(2) << i++ << " " << elm << '\n';
}
return out;
}
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());
}
}
}
}
}

View File

@@ -1,22 +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 <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>";
}
}
}

View File

@@ -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.
*/
#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";
}
}
}
}

View File

@@ -1,20 +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.
load("//tools/build_defs/oss:yoga_defs.bzl", "JSR_305_TARGET", "PROGUARD_ANNOTATIONS_TARGET", "SOLOADER_TARGET", "yoga_java_library")
yoga_java_library(
name = "jni",
srcs = glob(["**/*.java"]),
proguard_config = "fbjni.pro",
visibility = [
"PUBLIC",
],
deps = [
PROGUARD_ANNOTATIONS_TARGET,
SOLOADER_TARGET,
JSR_305_TARGET,
],
)

View File

@@ -1,17 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppException extends RuntimeException {
@DoNotStrip
public CppException(String message) {
super(message);
}
}

View File

@@ -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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppSystemErrorException extends CppException {
int errorCode;
@DoNotStrip
public CppSystemErrorException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}

View File

@@ -1,138 +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.
*/
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;
}
}
}

View File

@@ -1,12 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public abstract class HybridClassBase extends HybridData {}

View File

@@ -1,77 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
/**
* This object holds a native C++ member for hybrid Java/C++ objects.
*
* <p>NB: THREAD SAFETY
*
* <p>{@link #resetNative} deletes the corresponding native object synchronously on whatever thread
* the method is called on. Otherwise, deletion will occur on the {@link DestructorThread} thread.
*/
@DoNotStrip
public class HybridData {
static {
SoLoader.loadLibrary("fbjni");
}
@DoNotStrip private Destructor mDestructor = new Destructor(this);
/**
* To explicitly delete the instance, call resetNative(). If the C++ instance is referenced after
* this is called, a NullPointerException will be thrown. resetNative() may be called multiple
* times safely. Because the {@link DestructorThread} also calls resetNative, the instance will
* not leak if this is not called, but timing of deletion and the thread the C++ dtor is called on
* will be at the whim of the Java GC. If you want to control the thread and timing of the
* destructor, you should call resetNative() explicitly.
*/
public synchronized void resetNative() {
mDestructor.destruct();
}
/**
* 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() {
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);
}
}

View File

@@ -1,49 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import javax.annotation.Nullable;
/**
* To iterate over an Iterator from C++ requires two calls per entry: hasNext() and next(). This
* helper reduces it to one call and one field get per entry. It does not use a generic argument,
* since in C++, the types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
*/
@DoNotStrip
public class IteratorHelper {
private final Iterator mIterator;
// This is private, but accessed via JNI.
@DoNotStrip private @Nullable Object mElement;
@DoNotStrip
public IteratorHelper(Iterator iterator) {
mIterator = iterator;
}
@DoNotStrip
public IteratorHelper(Iterable iterable) {
mIterator = iterable.iterator();
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {
if (mIterator.hasNext()) {
mElement = mIterator.next();
return true;
} else {
mElement = null;
return false;
}
}
}

View File

@@ -1,48 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.Nullable;
/**
* To iterate over a Map from C++ requires four calls per entry: hasNext(), next(), getKey(),
* getValue(). This helper reduces it to one call and two field gets per entry. It does not use a
* generic argument, since in C++, the types will be erased, anyway. This is *not* a {@link
* java.util.Iterator}.
*/
@DoNotStrip
public class MapIteratorHelper {
@DoNotStrip private final Iterator<Map.Entry> mIterator;
@DoNotStrip private @Nullable Object mKey;
@DoNotStrip private @Nullable Object mValue;
@DoNotStrip
public MapIteratorHelper(Map map) {
mIterator = map.entrySet().iterator();
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {
if (mIterator.hasNext()) {
Map.Entry entry = mIterator.next();
mKey = entry.getKey();
mValue = entry.getValue();
return true;
} else {
mKey = null;
mValue = null;
return false;
}
}
}

View File

@@ -1,22 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
/** A Runnable that has a native run implementation. */
@DoNotStrip
public class NativeRunnable implements Runnable {
private final HybridData mHybridData;
private NativeRunnable(HybridData hybridData) {
mHybridData = hybridData;
}
public native void run();
}

View File

@@ -1,26 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class ThreadScopeSupport {
static {
SoLoader.loadLibrary("fbjni");
}
// This is just used for ThreadScope::withClassLoader to have a java function
// in the stack so that jni has access to the correct classloader.
@DoNotStrip
private static void runStdFunction(long ptr) {
runStdFunctionImpl(ptr);
}
private static native void runStdFunctionImpl(long ptr);
}

View File

@@ -1,22 +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.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class UnknownCppException extends CppException {
@DoNotStrip
public UnknownCppException() {
super("Unknown");
}
@DoNotStrip
public UnknownCppException(String message) {
super(message);
}
}

View File

@@ -1,23 +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.
*/
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 {
}

View File

@@ -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.
#
# For common use cases for the hybrid pattern, keep symbols which may
# be referenced only from C++.
-keepclassmembers class * {
com.facebook.jni.HybridData *;
<init>(com.facebook.jni.HybridData);
}
-keepclasseswithmembers class * {
com.facebook.jni.HybridData *;
}