提交 dc916917 编写于 作者: I Ivan Kobzarev

Initial content commit: com/facebook/fbjni library copy to use as a

submodule in pytorch/pytorch; Pytorch LICENSE
上级 3ad1421e
*.iml
.gradle
local.properties
gradle*
.idea
build
.externalNativeBuild
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.facebook.fbjni">
</manifest>
cmake_minimum_required(VERSION 3.6.0)
project(fbjni CXX)
file(GLOB fbjni_SOURCES
cxx/fbjni/*.cpp
cxx/fbjni/detail/*.cpp
cxx/lyra/*.cpp
)
add_library(fbjni SHARED
${fbjni_SOURCES}
)
target_compile_options(fbjni PRIVATE
-fno-omit-frame-pointer
-fexceptions
-O3
-Wall
-std=c++11
-frtti
-ffunction-sections
-DDISABLE_CPUCAP
-DDISABLE_XPLAT
-DNDEBUG
)
target_include_directories(fbjni PUBLIC
cxx
)
target_link_libraries(fbjni
android
log
)
BSD 3-Clause License
From PyTorch:
Copyright (c) 2016- Facebook, Inc (Adam Paszke)
Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
Copyright (c) 2011-2013 NYU (Clement Farabet)
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
From Caffe2:
Copyright (c) 2016-present, Facebook Inc. All rights reserved.
All contributions by Facebook:
Copyright (c) 2016 Facebook Inc.
All contributions by Google:
Copyright (c) 2015 Google Inc.
All rights reserved.
All contributions by Yangqing Jia:
Copyright (c) 2015 Yangqing Jia
All rights reserved.
All contributions from Caffe:
Copyright(c) 2013, 2014, 2015, the respective contributors
All rights reserved.
All other contributions:
Copyright(c) 2015, 2016 the respective contributors
All rights reserved.
Caffe2 uses a copyright model similar to Caffe: each contributor holds
copyright over their contributions to Caffe2. The project versioning records
all such contribution and copyright details. If a contributor wants to further
mark their specific copyright on a particular contribution, they should
indicate their copyright solely in the commit message of the change when it is
committed.
Copyright (c) 2019, Ivan Kobzarev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories America
and IDIAP Research Institute nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
buildscript {
repositories {
google()
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
sourceSets {
main {
manifest.srcFile './ApplicationManifest.xml'
java {
srcDir 'java'
}
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
compileOnly 'com.google.code.findbugs:jsr305:3.0.1'
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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 {
namespace {
local_ref<JByteBuffer> createEmpty() {
static auto cls = JByteBuffer::javaClassStatic();
static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>("allocateDirect");
return meth(cls, 0);
}
}
void JBuffer::rewind() const {
static auto meth = javaClassStatic()->getMethod<alias_ref<JBuffer>()>("rewind");
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 createEmpty();
}
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!res) {
throw std::runtime_error("Direct byte buffers are unsupported.");
}
return res;
}
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);
}
uint8_t* JByteBuffer::getDirectBytes() const {
if (!self()) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
void* bytes = Environment::current()->GetDirectBufferAddress(self());
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!bytes) {
throw std::runtime_error(
isDirect() ?
"Attempt to get direct bytes of non-direct byte buffer." :
"Error getting direct bytes of byte buffer.");
}
return static_cast<uint8_t*>(bytes);
}
size_t JByteBuffer::getDirectSize() const {
if (!self()) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
int size = Environment::current()->GetDirectBufferCapacity(self());
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (size < 0) {
throw std::runtime_error(
isDirect() ?
"Attempt to get direct size of non-direct byte buffer." :
"Error getting direct size of byte buffer.");
}
return static_cast<size_t>(size);
}
bool JByteBuffer::isDirect() const {
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
return meth(self());
}
local_ref<JByteOrder> JByteOrder::nativeOrder() {
static auto meth = JByteOrder::javaClassStatic()->getStaticMethod<
local_ref<JByteOrder>()>
("nativeOrder");
return meth(JByteOrder::javaClassStatic());
}
local_ref<JByteBuffer> JByteBuffer::order(alias_ref<JByteOrder> order) {
static auto meth = JByteBuffer::javaClassStatic()->getMethod<
local_ref<JByteBuffer>(alias_ref<JByteOrder>)>
("order");
return meth(self(), order);
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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 JByteOrder : public facebook::jni::JavaClass<JByteOrder> {
public:
constexpr static const char* kJavaDescriptor = "Ljava/nio/ByteOrder;";
static facebook::jni::local_ref<JByteOrder> nativeOrder();
};
class JBuffer : public JavaClass<JBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/Buffer;";
void rewind() 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);
bool isDirect() const;
uint8_t* getDirectBytes() const;
size_t getDirectSize() const;
local_ref<JByteBuffer> order(alias_ref<JByteOrder>);
};
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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());
}
};
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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();
}
};
}
}
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
namespace compactdisk_jni { namespace experimental {
template <typename K, typename V>
class JMapEntry : public facebook::jni::JavaClass<JMapEntry<K, V>> {
public:
static constexpr auto kJavaDescriptor = "Ljava/util/Map$Entry;";
facebook::jni::local_ref<typename K::javaobject> getKey() const {
static auto getKeyMethod =
JMapEntry<K, V>::javaClassStatic()->template getMethod<typename K::javaobject()>("getKey");
return getKeyMethod(this->self());
}
facebook::jni::local_ref<typename V::javaobject> getValue() const {
static auto getValueMethod =
JMapEntry<K, V>::javaClassStatic()->template getMethod<typename V::javaobject()>("getValue");
return getValueMethod(this->self());
}
};
}}
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <fb/fbjni.h>
#include <compactdisk-current-jni/JMapEntry.h>
namespace compactdisk_jni { namespace experimental {
template <typename K, typename V>
class JSimpleMapEntry : public facebook::jni::JavaClass<JSimpleMapEntry<K, V>, JMapEntry<K, V>> {
public:
static constexpr auto kJavaDescriptor = "Ljava/util/AbstractMap$SimpleEntry;";
static facebook::jni::local_ref<JSimpleMapEntry> create(
facebook::jni::alias_ref<K> key,
facebook::jni::alias_ref<V> value);
};
// -------------------- Template Implementation --------------------
template <typename K, typename V>
facebook::jni::local_ref<JSimpleMapEntry<K, V>> JSimpleMapEntry<K, V>::create(
facebook::jni::alias_ref<K> key,
facebook::jni::alias_ref<V> value) {
static auto jClass = JSimpleMapEntry<K, V>::javaClassStatic();
static auto jConstructor = jClass->template getConstructor<
typename JSimpleMapEntry<K, V>::javaobject(jobject, jobject)>();
return jClass->newObject(
jConstructor,
facebook::jni::static_ref_cast<jobject>(key).get(),
facebook::jni::static_ref_cast<jobject>(value).get());
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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);
}
};
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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();
});
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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);
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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;
};
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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
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);
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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);
}
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 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[]> {
using T = typename jtype_traits<JArrayType>::entry_type;
auto buf = std::unique_ptr<T[]>{new T[length]};
getRegion(start, length, buf.get());
return buf;
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
return jtype_traits<JArrayType>::descriptor();
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
return JPrimitiveArray::get_instantiated_java_descriptor();
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinCritical()
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
}
template <typename T>
class PinnedArrayAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
(void) start;
(void) length;
*elements = array->getElements(isCopy);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
(void) start;
(void) size;
array->releaseElements(elements, mode);
}
};
template <typename T>
class PinnedCriticalAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
const auto env = 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) {
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());
}
}}
此差异已折叠。
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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();
}
}
} }
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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_;
};
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#include "CoreClasses.h"
#include "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_);
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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();
explicit JniException(alias_ref<jthrowable> throwable);
JniException(JniException &&rhs);
JniException(const JniException &other);
local_ref<JThrowable> getThrowable() const noexcept;
virtual const char* what() const noexcept;
void setJavaException() const noexcept;
private:
global_ref<JThrowable> throwable_;
mutable std::string what_;
mutable bool isMessageExtracted_;
const static std::string kExceptionMessageFailure_;
void populateWhat() const noexcept;
};
// Exception throwing & translating functions //////////////////////////////////////////////////////
// Functions that throw C++ exceptions
static const int kMaxExceptionMessageBufferSize = 512;
// These methods are the preferred way to throw a Java exception from
// a C++ function. They create and throw a C++ exception which wraps
// a Java exception, so the C++ flow is interrupted. Then, when
// translatePendingCppExceptionToJavaException is called at the
// topmost level of the native stack, the wrapped Java exception is
// thrown to the java caller.
template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {
int msgSize = snprintf(nullptr, 0, fmt, args...);
char *msg = (char*) alloca(msgSize + 1);
snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);
throwNewJavaException(throwableName, msg);
}
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
// be thrown, it aborts the program.
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";
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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),
});
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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) {}
};
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();
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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();
}
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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"
/*
* Copyright (c) 2004-present, Facebook, Inc.
*
* 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()));
}
};
}
}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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;
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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...>();
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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 its 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"
/*
* Copyright (c) 2004-present, Facebook, Inc.
*
* 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;
}
};
// 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...)>> {
};
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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);
}
}}
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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"
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* 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
此差异已折叠。
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*
*/
#include "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() {
auto cls = findClassLocal("android/os/Build$VERSION");
auto fld = cls->getStaticField<int32_t>("SDK_INT");
if (fld) {
return cls->getStaticFieldValue(fld);
}
return 0;
}
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
}
bool isObjectRefType(jobject reference, jobjectRefType refType) {
// TODO Rather than setting it true, use doesGetObjectRefTypeWork(). But it
// causes sample app to freeze
static bool getObjectRefTypeWorks = true;
return
!reference ||
!getObjectRefTypeWorks ||
Environment::current()->GetObjectRefType(reference) == refType;
}
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* Copyright (c) 2004-present, Facebook, Inc.
*
* 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 {}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册