sync fbjni

Differential Revision: D2990551

fb-gh-sync-id: 587f2b5cd09295dc1829926c12e38f2880440a5e
shipit-source-id: 587f2b5cd09295dc1829926c12e38f2880440a5e
This commit is contained in:
Michał Gregorczyk 2016-03-02 07:23:48 -08:00 committed by Facebook Github Bot 1
parent 5df3eeb723
commit 5a6a94b05a
33 changed files with 2462 additions and 1157 deletions

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*

View File

@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \
LocalString.cpp \
OnLoad.cpp \
WeakReference.cpp \
fbjni/ByteBuffer.cpp \
fbjni/Exceptions.cpp \
fbjni/Hybrid.cpp \
fbjni/References.cpp

View File

@ -10,6 +10,7 @@ cxx_library(
'LocalString.cpp',
'OnLoad.cpp',
'WeakReference.cpp',
'fbjni/ByteBuffer.cpp',
'fbjni/Exceptions.cpp',
'fbjni/Hybrid.cpp',
'fbjni/References.cpp',

View File

@ -41,7 +41,7 @@ void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable
*
* This method deletes the corresponding native object on whatever thread the method is called
* on. In the common case when this is called by Countable#finalize(), this will be called on the
* system finalizer thread. If you manually call dispose on the Java object, the native object
* system finalizer thread. If you manually call dispose on the Java object, the native object
* will be deleted synchronously on that thread.
*/
void dispose(JNIEnv* env, jobject obj) {

View File

@ -27,7 +27,7 @@ struct LocalReferenceDeleter {
if (localReference != nullptr) {
Environment::current()->DeleteLocalRef(localReference);
}
}
}
};
template<class T>

View File

@ -18,6 +18,12 @@ namespace jni {
namespace {
const uint16_t kUtf8OneByteBoundary = 0x80;
const uint16_t kUtf8TwoBytesBoundary = 0x800;
const uint16_t kUtf16HighSubLowBoundary = 0xD800;
const uint16_t kUtf16HighSubHighBoundary = 0xDC00;
const uint16_t kUtf16LowSubHighBoundary = 0xE000;
inline void encode3ByteUTF8(char32_t code, uint8_t* out) {
FBASSERTMSGF((code & 0xffff0000) == 0, "3 byte utf-8 encodings only valid for up to 16 bits");
@ -194,6 +200,72 @@ std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) {
return utf8;
}
// Calculate how many bytes are needed to convert an UTF16 string into UTF8
// UTF16 string
size_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {
if (!utf16String || utf16StringLen == 0) {
return 0;
}
uint32_t utf8StringLen = 0;
auto utf16StringEnd = utf16String + utf16StringLen;
auto idx16 = utf16String;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
utf8StringLen++;
} else if (ch < kUtf8TwoBytesBoundary) {
utf8StringLen += 2;
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
utf8StringLen += 4;
idx16++;
} else {
utf8StringLen += 3;
}
}
return utf8StringLen;
}
std::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) {
if (!utf16String || utf16StringLen <= 0) {
return "";
}
std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\0');
auto idx8 = utf8String.begin();
auto idx16 = utf16String;
auto utf16StringEnd = utf16String + utf16StringLen;
while (idx16 < utf16StringEnd) {
auto ch = *idx16++;
if (ch < kUtf8OneByteBoundary) {
*idx8++ = (ch & 0x7F);
} else if (ch < kUtf8TwoBytesBoundary) {
*idx8++ = 0b11000000 | (ch >> 6);
*idx8++ = 0b10000000 | (ch & 0x3F);
} else if (
(ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&
(idx16 < utf16StringEnd) &&
(*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {
auto ch2 = *idx16++;
uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);
*idx8++ = 0b11110000 | (trunc_byte >> 2);
*idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);
*idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);
*idx8++ = 0b10000000 | (ch2 & 0x3F);
} else {
*idx8++ = 0b11100000 | (ch >> 12);
*idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);
*idx8++ = 0b10000000 | (ch & 0x3F);
}
}
return utf8String;
}
}
LocalString::LocalString(const std::string& str)
@ -232,11 +304,9 @@ LocalString::~LocalString() {
}
std::string fromJString(JNIEnv* env, jstring str) {
const char* modified = env->GetStringUTFChars(str, NULL);
jsize length = env->GetStringUTFLength(str);
std::string s = detail::modifiedUTF8ToUTF8(reinterpret_cast<const uint8_t*>(modified), length);
env->ReleaseStringUTFChars(str, modified);
return s;
auto utf16String = JStringUtf16Extractor(env, str);
auto length = env->GetStringLength(str);
return detail::utf16toUTF8(utf16String, length);
}
} }

View File

@ -20,6 +20,7 @@ void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, siz
size_t modifiedLength(const std::string& str);
size_t modifiedLength(const uint8_t* str, size_t* length);
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len);
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len);
}
@ -54,6 +55,34 @@ private:
jstring m_string;
};
// JString to UTF16 extractor using RAII idiom
class JStringUtf16Extractor {
public:
JStringUtf16Extractor(JNIEnv* env, jstring javaString)
: env_(env)
, javaString_(javaString)
, utf16String_(nullptr) {
if (env_ && javaString_) {
utf16String_ = env_->GetStringCritical(javaString_, nullptr);
}
}
~JStringUtf16Extractor() {
if (utf16String_) {
env_->ReleaseStringCritical(javaString_, utf16String_);
}
}
operator const jchar* () const {
return utf16String_;
}
private:
JNIEnv* env_;
jstring javaString_;
const jchar* utf16String_;
};
// The string from JNI is converted to standard UTF-8 if the string contains supplementary
// characters.
std::string fromJString(JNIEnv* env, jstring str);

View File

@ -16,36 +16,33 @@
namespace facebook {
namespace jni {
template<typename... Args>
static void log(Args... args) {
// TODO (7623232) Migrate to glog
#ifdef __ANDROID__
facebook::alog::loge("fbjni", args...);
#endif
}
jint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {
static std::once_flag flag{};
// TODO (t7832883): DTRT when we have exception pointers
static auto error_msg = std::string{"Failed to initialize fbjni"};
static auto error_occured = false;
jint initialize(JavaVM* vm, void(*init_fn)()) noexcept {
static std::once_flag init_flag;
static auto failed = false;
std::call_once(init_flag, [vm] {
std::call_once(flag, [vm] {
try {
Environment::initialize(vm);
internal::initExceptionHelpers();
} catch (std::exception& ex) {
log("Failed to initialize fbjni: %s", ex.what());
failed = true;
error_occured = true;
try {
error_msg = std::string{"Failed to initialize fbjni: "} + ex.what();
} catch (...) {
// Ignore, we already have a fall back message
}
} catch (...) {
log("Failed to initialize fbjni");
failed = true;
error_occured = true;
}
});
if (failed) {
return JNI_ERR;
}
try {
if (error_occured) {
throw std::runtime_error(error_msg);
}
init_fn();
} catch (...) {
translatePendingCppExceptionToJavaException();
@ -55,7 +52,7 @@ jint initialize(JavaVM* vm, void(*init_fn)()) noexcept {
return JNI_VERSION_1_6;
}
alias_ref<jclass> findClassStatic(const char* name) {
alias_ref<JClass> findClassStatic(const char* name) {
const auto env = internal::getEnv();
auto cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
@ -64,7 +61,7 @@ alias_ref<jclass> findClassStatic(const char* name) {
return wrap_alias(leaking_ref);
}
local_ref<jclass> findClassLocal(const char* name) {
local_ref<JClass> findClassLocal(const char* name) {
const auto env = internal::getEnv();
auto cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
@ -74,16 +71,14 @@ local_ref<jclass> findClassLocal(const char* name) {
// jstring /////////////////////////////////////////////////////////////////////////////////////////
std::string JObjectWrapper<jstring>::toStdString() const {
std::string JString::toStdString() const {
const auto env = internal::getEnv();
auto modified = env->GetStringUTFChars(self(), nullptr);
auto length = env->GetStringUTFLength(self());
auto string = detail::modifiedUTF8ToUTF8(reinterpret_cast<const uint8_t*>(modified), length);
env->ReleaseStringUTFChars(self(), modified);
return string;
auto utf16String = JStringUtf16Extractor(env, self());
auto length = env->GetStringLength(self());
return detail::utf16toUTF8(utf16String, length);
}
local_ref<jstring> make_jstring(const char* utf8) {
local_ref<JString> make_jstring(const char* utf8) {
if (!utf8) {
return {};
}
@ -111,96 +106,76 @@ local_ref<jstring> make_jstring(const char* utf8) {
}
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
// JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////
// TODO(T7847300): Allow array to be specified as constant so that JNI_ABORT can be passed
// on release, as opposed to 0, which results in unnecessary copying.
#pragma push_macro("DEFINE_PRIMITIVE_METHODS")
#undef DEFINE_PRIMITIVE_METHODS
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME) \
template<> \
TYPE* PinnedPrimitiveArray<TYPE>::get() { \
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \
const auto env = internal::getEnv(); \
elements_ = env->Get ## NAME ## ArrayElements( \
static_cast<TYPE ## Array>(array_.get()), &isCopy_); \
size_ = array_->size(); \
return elements_; \
} \
template<> \
void PinnedPrimitiveArray<TYPE>::release() { \
FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \
const auto env = internal::getEnv(); \
env->Release ## NAME ## ArrayElements( \
static_cast<TYPE ## Array>(array_.get()), elements_, 0); \
elements_ = nullptr; \
size_ = 0; \
}
#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \
\
template<> \
TYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) { \
auto env = internal::getEnv(); \
TYPE* res = env->Get ## NAME ## ArrayElements(self(), isCopy); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return res; \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::releaseElements( \
TYPE* elements, jint mode) { \
auto env = internal::getEnv(); \
env->Release ## NAME ## ArrayElements(self(), elements, mode); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::getRegion( \
jsize start, jsize length, TYPE* buf) { \
auto env = internal::getEnv(); \
env->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
template<> \
void JPrimitiveArray<TYPE ## Array>::setRegion( \
jsize start, jsize length, const TYPE* elements) { \
auto env = internal::getEnv(); \
env->Set ## NAME ## ArrayRegion(self(), start, length, elements); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
local_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) { \
auto array = internal::getEnv()->New ## NAME ## Array(size); \
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
return adopt_local(array); \
} \
\
template<> \
local_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
return make_ ## SMALLNAME ## _array(count); \
} \
\
DEFINE_PRIMITIVE_METHODS(jboolean, Boolean)
DEFINE_PRIMITIVE_METHODS(jbyte, Byte)
DEFINE_PRIMITIVE_METHODS(jchar, Char)
DEFINE_PRIMITIVE_METHODS(jshort, Short)
DEFINE_PRIMITIVE_METHODS(jint, Int)
DEFINE_PRIMITIVE_METHODS(jlong, Long)
DEFINE_PRIMITIVE_METHODS(jfloat, Float)
DEFINE_PRIMITIVE_METHODS(jdouble, Double)
DEFINE_PRIMITIVE_METHODS(jboolean, Boolean, boolean)
DEFINE_PRIMITIVE_METHODS(jbyte, Byte, byte)
DEFINE_PRIMITIVE_METHODS(jchar, Char, char)
DEFINE_PRIMITIVE_METHODS(jshort, Short, short)
DEFINE_PRIMITIVE_METHODS(jint, Int, int)
DEFINE_PRIMITIVE_METHODS(jlong, Long, long)
DEFINE_PRIMITIVE_METHODS(jfloat, Float, float)
DEFINE_PRIMITIVE_METHODS(jdouble, Double, double)
#pragma pop_macro("DEFINE_PRIMITIVE_METHODS")
#define DEFINE_PRIMITIVE_ARRAY_UTILS(TYPE, NAME) \
\
local_ref<j ## TYPE ## Array> make_ ## TYPE ## _array(jsize size) { \
auto array = internal::getEnv()->New ## NAME ## Array(size); \
FACEBOOK_JNI_THROW_EXCEPTION_IF(!array); \
return adopt_local(array); \
} \
\
local_ref<j ## TYPE ## Array> JArray ## NAME::newArray(size_t count) { \
return make_ ## TYPE ## _array(count); \
} \
\
j ## TYPE* JArray ## NAME::getRegion(jsize start, jsize length, j ## TYPE* buf) { \
internal::getEnv()->Get ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return buf; \
} \
\
std::unique_ptr<j ## TYPE[]> JArray ## NAME::getRegion(jsize start, jsize length) { \
auto buf = std::unique_ptr<j ## TYPE[]>{new j ## TYPE[length]}; \
internal::getEnv()->Get ## NAME ## ArrayRegion(self(), start, length, buf.get()); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return buf; \
} \
\
void JArray ## NAME::setRegion(jsize start, jsize length, const j ## TYPE* buf) { \
internal::getEnv()->Set ## NAME ## ArrayRegion(self(), start, length, buf); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
} \
\
PinnedPrimitiveArray<j ## TYPE> JArray ## NAME::pin() { \
return PinnedPrimitiveArray<j ## TYPE>{self()}; \
} \
DEFINE_PRIMITIVE_ARRAY_UTILS(boolean, Boolean)
DEFINE_PRIMITIVE_ARRAY_UTILS(byte, Byte)
DEFINE_PRIMITIVE_ARRAY_UTILS(char, Char)
DEFINE_PRIMITIVE_ARRAY_UTILS(short, Short)
DEFINE_PRIMITIVE_ARRAY_UTILS(int, Int)
DEFINE_PRIMITIVE_ARRAY_UTILS(long, Long)
DEFINE_PRIMITIVE_ARRAY_UTILS(float, Float)
DEFINE_PRIMITIVE_ARRAY_UTILS(double, Double)
// Internal debug /////////////////////////////////////////////////////////////////////////////////
namespace internal {
ReferenceStats g_reference_stats;
void facebook::jni::internal::ReferenceStats::reset() noexcept {
locals_deleted = globals_deleted = weaks_deleted = 0;
}
}
}}

View File

@ -19,5 +19,6 @@
#include "fbjni/References.h"
#include "fbjni/Meta.h"
#include "fbjni/CoreClasses.h"
#include "fbjni/Iterator.h"
#include "fbjni/Hybrid.h"
#include "fbjni/Registration.h"

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "ByteBuffer.h"
#include "References.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);
}
}
local_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {
// env->NewDirectByteBuffer requires that size is positive. Android's
// dalvik returns an invalid result and Android's art aborts if size == 0.
// Workaround this by using a slow path through Java in that case.
if (!size) {
return createEmpty();
}
auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!res) {
throw std::runtime_error("Direct byte buffers are unsupported.");
}
return res;
}
uint8_t* JByteBuffer::getDirectBytes() {
if (!self()) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
void* bytes = Environment::current()->GetDirectBufferAddress(self());
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!bytes) {
throw std::runtime_error(
isDirect() ?
"Attempt to get direct bytes of non-direct byte buffer." :
"Error getting direct bytes of byte buffer.");
}
return static_cast<uint8_t*>(bytes);
}
size_t JByteBuffer::getDirectSize() {
if (!self()) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
int size = Environment::current()->GetDirectBufferCapacity(self());
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (size < 0) {
throw std::runtime_error(
isDirect() ?
"Attempt to get direct size of non-direct byte buffer." :
"Error getting direct size of byte buffer.");
}
return static_cast<size_t>(size);
}
bool JByteBuffer::isDirect() {
static auto meth = javaClassStatic()->getMethod<jboolean()>("isDirect");
return meth(self());
}
}}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include "CoreClasses.h"
#include "References-forward.h"
namespace facebook {
namespace jni {
// JNI's NIO support has some awkward preconditions and error reporting. This
// class provides much more user-friendly access.
class JByteBuffer : public JavaClass<JByteBuffer> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;";
static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);
bool isDirect();
uint8_t* getDirectBytes();
size_t getDirectSize();
};
}}

View File

@ -14,10 +14,19 @@
#pragma once
#include <functional>
#include <jni.h>
#include <jni/Environment.h>
#include <jni/ALog.h>
#ifdef FBJNI_DEBUG_REFS
# ifdef __ANDROID__
# include <android/log.h>
# else
# include <cstdio>
# endif
#endif
/// @cond INTERNAL
@ -38,7 +47,7 @@ namespace jni {
* unhelpful way (typically a segfault) while trying to handle an exception
* which occurs later.
*/
jint initialize(JavaVM*, void(*)()) noexcept;
jint initialize(JavaVM*, std::function<void()>&&) noexcept;
namespace internal {
@ -53,14 +62,22 @@ inline JNIEnv* getEnv() noexcept {
}
// Define to get extremely verbose logging of references and to enable reference stats
#if defined(__ANDROID__) && defined(FBJNI_DEBUG_REFS)
#ifdef FBJNI_DEBUG_REFS
template<typename... Args>
inline void dbglog(Args... args) noexcept {
facebook::alog::logv("fbjni_ref", 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(Args...) noexcept {}
inline void dbglog(const char*, Args...) {
}
#endif
}}}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include "CoreClasses.h"
#include "File.h"
namespace facebook {
namespace jni {
class AContext : public JavaClass<AContext> {
public:
static constexpr const char* kJavaDescriptor = "Landroid/content/Context;";
// Define a method that calls into the represented Java class
local_ref<JFile::javaobject> getCacheDir() {
static auto method = getClass()->getMethod<JFile::javaobject()>("getCacheDir");
return method(self());
}
};
}
}

View File

@ -11,75 +11,134 @@
#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 {
inline bool isSameObject(alias_ref<jobject> lhs, alias_ref<jobject> rhs) noexcept {
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {
return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;
}
// jobject /////////////////////////////////////////////////////////////////////////////////////////
inline JObjectWrapper<jobject>::JObjectWrapper(jobject reference) noexcept
: this_{reference}
{}
inline JObjectWrapper<jobject>::JObjectWrapper(const JObjectWrapper<jobject>& other) noexcept
: this_{other.this_} {
internal::dbglog("wrapper copy from this=%p ref=%p other=%p", this, other.this_, &other);
}
inline local_ref<jclass> JObjectWrapper<jobject>::getClass() const noexcept {
inline local_ref<JClass> JObject::getClass() const noexcept {
return adopt_local(internal::getEnv()->GetObjectClass(self()));
}
inline bool JObjectWrapper<jobject>::isInstanceOf(alias_ref<jclass> cls) const noexcept {
inline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {
return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;
}
template<typename T>
inline T JObjectWrapper<jobject>::getFieldValue(JField<T> field) const noexcept {
inline T JObject::getFieldValue(JField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObjectWrapper<jobject>::getFieldValue(JField<T*> field) noexcept {
inline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObjectWrapper<jobject>::setFieldValue(JField<T> field, T value) noexcept {
inline void JObject::setFieldValue(JField<T> field, T value) noexcept {
field.set(self(), value);
}
inline std::string JObjectWrapper<jobject>::toString() const {
inline std::string JObject::toString() const {
static auto method = findClassLocal("java/lang/Object")->getMethod<jstring()>("toString");
return method(self())->toStdString();
}
inline void JObjectWrapper<jobject>::set(jobject reference) noexcept {
this_ = reference;
// Class is here instead of CoreClasses.h because we need
// alias_ref to be complete.
class MonitorLock {
public:
inline MonitorLock() noexcept;
inline MonitorLock(alias_ref<JObject> object) noexcept;
inline ~MonitorLock() noexcept;
inline MonitorLock(MonitorLock&& other) noexcept;
inline MonitorLock& operator=(MonitorLock&& other) noexcept;
inline MonitorLock(const MonitorLock&) = delete;
inline MonitorLock& operator=(const MonitorLock&) = delete;
private:
inline void reset() noexcept;
alias_ref<JObject> owned_;
};
MonitorLock::MonitorLock() noexcept : owned_(nullptr) {}
MonitorLock::MonitorLock(alias_ref<JObject> object) noexcept
: owned_(object) {
internal::getEnv()->MonitorEnter(object.get());
}
inline jobject JObjectWrapper<jobject>::get() const noexcept {
void MonitorLock::reset() noexcept {
if (owned_) {
internal::getEnv()->MonitorExit(owned_.get());
if (internal::getEnv()->ExceptionCheck()) {
abort(); // Lock mismatch
}
owned_ = nullptr;
}
}
MonitorLock::~MonitorLock() noexcept {
reset();
}
MonitorLock::MonitorLock(MonitorLock&& other) noexcept
: owned_(other.owned_)
{
other.owned_ = nullptr;
}
MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {
reset();
owned_ = other.owned_;
other.owned_ = nullptr;
return *this;
}
inline MonitorLock JObject::lock() const noexcept {
return MonitorLock(this_);
}
inline jobject JObject::self() const noexcept {
return this_;
}
inline jobject JObjectWrapper<jobject>::self() const noexcept {
return this_;
}
inline void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept {
inline void swap(JObject& a, JObject& b) noexcept {
using std::swap;
swap(a.this_, b.this_);
}
// JavaClass ///////////////////////////////////////////////////////////////////////////////////////
namespace detail {
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args) {
static auto cls = JC::javaClassStatic();
static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();
return cls->newObject(constructor, args...);
}
}
template <typename T, typename B, typename J>
auto JavaClass<T, B, J>::self() const noexcept -> javaobject {
return static_cast<javaobject>(JObject::self());
}
// jclass //////////////////////////////////////////////////////////////////////////////////////////
@ -89,7 +148,7 @@ namespace detail {
// use a void* to initialize a NativeMethod.
struct NativeMethodWrapper;
};
}
struct NativeMethod {
const char* name;
@ -97,11 +156,11 @@ struct NativeMethod {
detail::NativeMethodWrapper* wrapper;
};
inline local_ref<jclass> JObjectWrapper<jclass>::getSuperclass() const noexcept {
inline local_ref<JClass> JClass::getSuperclass() const noexcept {
return adopt_local(internal::getEnv()->GetSuperclass(self()));
}
inline void JObjectWrapper<jclass>::registerNatives(std::initializer_list<NativeMethod> methods) {
inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {
const auto env = internal::getEnv();
JNINativeMethod jnimethods[methods.size()];
@ -116,30 +175,30 @@ inline void JObjectWrapper<jclass>::registerNatives(std::initializer_list<Native
FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);
}
inline bool JObjectWrapper<jclass>::isAssignableFrom(alias_ref<jclass> other) const noexcept {
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
const auto env = internal::getEnv();
const auto result = env->IsAssignableFrom(self(), other.get());
return result;
}
template<typename F>
inline JConstructor<F> JObjectWrapper<jclass>::getConstructor() const {
return getConstructor<F>(jmethod_traits<F>::constructor_descriptor().c_str());
inline JConstructor<F> JClass::getConstructor() const {
return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());
}
template<typename F>
inline JConstructor<F> JObjectWrapper<jclass>::getConstructor(const char* descriptor) const {
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> JObjectWrapper<jclass>::getMethod(const char* name) const {
return getMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
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> JObjectWrapper<jclass>::getMethod(
inline JMethod<F> JClass::getMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
@ -149,12 +208,12 @@ inline JMethod<F> JObjectWrapper<jclass>::getMethod(
}
template<typename F>
inline JStaticMethod<F> JObjectWrapper<jclass>::getStaticMethod(const char* name) const {
return getStaticMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
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> JObjectWrapper<jclass>::getStaticMethod(
inline JStaticMethod<F> JClass::getStaticMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
@ -164,12 +223,12 @@ inline JStaticMethod<F> JObjectWrapper<jclass>::getStaticMethod(
}
template<typename F>
inline JNonvirtualMethod<F> JObjectWrapper<jclass>::getNonvirtualMethod(const char* name) const {
return getNonvirtualMethod<F>(name, jmethod_traits<F>::descriptor().c_str());
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> JObjectWrapper<jclass>::getNonvirtualMethod(
inline JNonvirtualMethod<F> JClass::getNonvirtualMethod(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
@ -180,12 +239,12 @@ inline JNonvirtualMethod<F> JObjectWrapper<jclass>::getNonvirtualMethod(
template<typename T>
inline JField<enable_if_t<IsJniScalar<T>(), T>>
JObjectWrapper<jclass>::getField(const char* name) const {
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>> JObjectWrapper<jclass>::getField(
inline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
@ -195,13 +254,13 @@ inline JField<enable_if_t<IsJniScalar<T>(), T>> JObjectWrapper<jclass>::getField
}
template<typename T>
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JObjectWrapper<jclass>::getStaticField(
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>> JObjectWrapper<jclass>::getStaticField(
inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(
const char* name,
const char* descriptor) const {
const auto env = internal::getEnv();
@ -211,32 +270,34 @@ inline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JObjectWrapper<jclass>::ge
}
template<typename T>
inline T JObjectWrapper<jclass>::getStaticFieldValue(JStaticField<T> field) const noexcept {
inline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {
return field.get(self());
}
template<typename T>
inline local_ref<T*> JObjectWrapper<jclass>::getStaticFieldValue(JStaticField<T*> field) noexcept {
inline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {
return adopt_local(field.get(self()));
}
template<typename T>
inline void JObjectWrapper<jclass>::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
inline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {
field.set(self(), value);
}
template<typename R, typename... Args>
inline local_ref<R> JObjectWrapper<jclass>::newObject(
inline local_ref<R> JClass::newObject(
JConstructor<R(Args...)> constructor,
Args... args) const {
const auto env = internal::getEnv();
auto object = env->NewObject(self(), constructor.getId(), args...);
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 JObjectWrapper<jclass>::self() const noexcept {
return static_cast<jclass>(this_);
inline jclass JClass::self() const noexcept {
return static_cast<jclass>(JObject::self());
}
inline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {
@ -246,197 +307,349 @@ inline void registerNatives(const char* name, std::initializer_list<NativeMethod
// jstring /////////////////////////////////////////////////////////////////////////////////////////
inline local_ref<jstring> make_jstring(const std::string& modifiedUtf8) {
inline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {
return make_jstring(modifiedUtf8.c_str());
}
inline jstring JObjectWrapper<jstring>::self() const noexcept {
return static_cast<jstring>(this_);
namespace detail {
// convert to std::string from jstring
template <>
struct Convert<std::string> {
typedef jstring jniType;
static std::string fromJni(jniType t) {
return wrap_alias(t)->toStdString();
}
static jniType toJniRet(const std::string& t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const std::string& t) {
return make_jstring(t);
}
};
// convert return from const char*
template <>
struct Convert<const char*> {
typedef jstring jniType;
// no automatic synthesis of const char*. (It can't be freed.)
static jniType toJniRet(const char* t) {
return make_jstring(t).release();
}
static local_ref<JString> toCall(const char* t) {
return make_jstring(t);
}
};
}
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
inline jthrowable JObjectWrapper<jthrowable>::self() const noexcept {
return static_cast<jthrowable>(this_);
}
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
template<typename T>
inline ElementProxy<T>::ElementProxy(
JObjectWrapper<_jtypeArray<T>*>* target,
namespace detail {
inline size_t JArray::size() const noexcept {
const auto env = internal::getEnv();
return env->GetArrayLength(self());
}
}
namespace detail {
template<typename Target>
inline ElementProxy<Target>::ElementProxy(
Target* target,
size_t idx)
: target_{target}, idx_{idx} {}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(const T& o) {
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {
target_->setElement(idx_, o);
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(alias_ref<T>& o) {
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(alias_ref<T>&& o) {
template<typename Target>
inline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {
target_->setElement(idx_, o.get());
return *this;
}
template<typename T>
inline ElementProxy<T>& ElementProxy<T>::operator=(const ElementProxy<T>& o) {
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 T>
inline ElementProxy<T>::ElementProxy::operator const local_ref<T> () const {
template<typename Target>
inline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {
return target_->getElement(idx_);
}
template<typename T>
inline ElementProxy<T>::ElementProxy::operator local_ref<T> () {
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>
local_ref<jtypeArray<T>> JObjectWrapper<jtypeArray<T>>::newArray(size_t size) {
auto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {
static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());
const auto env = internal::getEnv();
auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);
return adopt_local(static_cast<jtypeArray<T>>(rawArray));
return adopt_local(static_cast<javaobject>(rawArray));
}
template<typename T>
inline void JObjectWrapper<jtypeArray<T>>::setElement(size_t idx, const T& value) {
inline void JArrayClass<T>::setElement(size_t idx, const T& value) {
const auto env = internal::getEnv();
env->SetObjectArrayElement(static_cast<jobjectArray>(self()), idx, value);
env->SetObjectArrayElement(this->self(), idx, value);
}
template<typename T>
inline local_ref<T> JObjectWrapper<jtypeArray<T>>::getElement(size_t idx) {
inline local_ref<T> JArrayClass<T>::getElement(size_t idx) {
const auto env = internal::getEnv();
auto rawElement = env->GetObjectArrayElement(static_cast<jobjectArray>(self()), idx);
auto rawElement = env->GetObjectArrayElement(this->self(), idx);
return adopt_local(static_cast<T>(rawElement));
}
template<typename T>
inline size_t JObjectWrapper<jtypeArray<T>>::size() {
const auto env = internal::getEnv();
return env->GetArrayLength(static_cast<jobjectArray>(self()));
inline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {
return detail::ElementProxy<JArrayClass<T>>(this, index);
}
template<typename T>
inline ElementProxy<T> JObjectWrapper<jtypeArray<T>>::operator[](size_t index) {
return ElementProxy<T>(this, index);
}
template<typename T>
inline jtypeArray<T> JObjectWrapper<jtypeArray<T>>::self() const noexcept {
return static_cast<jtypeArray<T>>(this_);
}
// jarray /////////////////////////////////////////////////////////////////////////////////////////
inline size_t JObjectWrapper<jarray>::size() const noexcept {
const auto env = internal::getEnv();
return env->GetArrayLength(self());
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;
}
inline jarray JObjectWrapper<jarray>::self() const noexcept {
return static_cast<jarray>(this_);
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {
return jtype_traits<JArrayType>::descriptor();
}
template <typename JArrayType>
std::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {
return JPrimitiveArray::get_instantiated_java_descriptor();
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)
-> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};
}
template <typename JArrayType>
auto JPrimitiveArray<JArrayType>::pinCritical()
-> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {
return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};
}
template <typename T>
class PinnedArrayAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
(void) start;
(void) length;
*elements = array->getElements(isCopy);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
(void) start;
(void) size;
array->releaseElements(elements, mode);
}
};
template <typename T>
class PinnedCriticalAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
const auto env = internal::getEnv();
*elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);
*size = array->size();
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
const auto env = internal::getEnv();
env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);
}
};
template <typename T>
class PinnedRegionAlloc {
public:
static void allocate(
alias_ref<typename jtype_traits<T>::array_type> array,
jsize start,
jsize length,
T** elements,
size_t* size,
jboolean* isCopy) {
auto buf = array->getRegion(start, length);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);
*elements = buf.release();
*size = length;
*isCopy = true;
}
static void release(
alias_ref<typename jtype_traits<T>::array_type> array,
T* elements,
jint start,
jint size,
jint mode) {
std::unique_ptr<T[]> holder;
if (mode == 0 || mode == JNI_ABORT) {
holder.reset(elements);
}
if (mode == 0 || mode == JNI_COMMIT) {
array->setRegion(start, size, elements);
}
}
};
// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////
template<typename T>
inline PinnedPrimitiveArray<T>::PinnedPrimitiveArray(alias_ref<jarray> array) noexcept
: array_{array} {
get();
template<typename T, typename Alloc>
PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {
*this = std::move(o);
}
template<typename T>
PinnedPrimitiveArray<T>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) noexcept {
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_;
o.elements_ = nullptr;
o.isCopy_ = false;
o.size_ = 0;
}
template<typename T>
PinnedPrimitiveArray<T>&
PinnedPrimitiveArray<T>::operator=(PinnedPrimitiveArray&& o) noexcept {
array_ = std::move(o.array_);
elements_ = o.elements_;
isCopy_ = o.isCopy_;
size_ = o.size_;
o.elements_ = nullptr;
o.isCopy_ = false;
o.size_ = 0;
start_ = o.start_;
o.clear();
return *this;
}
template<typename T>
inline T& PinnedPrimitiveArray<T>::operator[](size_t index) {
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>
inline bool PinnedPrimitiveArray<T>::isCopy() const noexcept {
template<typename T, typename Alloc>
inline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {
return isCopy_ == JNI_TRUE;
}
template<typename T>
inline size_t PinnedPrimitiveArray<T>::size() const noexcept {
template<typename T, typename Alloc>
inline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {
return size_;
}
template<typename T>
inline PinnedPrimitiveArray<T>::~PinnedPrimitiveArray() noexcept {
template<typename T, typename Alloc>
inline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {
if (elements_) {
release();
}
}
#pragma push_macro("DECLARE_PRIMITIVE_METHODS")
#undef DECLARE_PRIMITIVE_METHODS
#define DECLARE_PRIMITIVE_METHODS(TYPE, NAME) \
template<> TYPE* PinnedPrimitiveArray<TYPE>::get(); \
template<> void PinnedPrimitiveArray<TYPE>::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_);
}
DECLARE_PRIMITIVE_METHODS(jboolean, Boolean)
DECLARE_PRIMITIVE_METHODS(jbyte, Byte)
DECLARE_PRIMITIVE_METHODS(jchar, Char)
DECLARE_PRIMITIVE_METHODS(jshort, Short)
DECLARE_PRIMITIVE_METHODS(jint, Int)
DECLARE_PRIMITIVE_METHODS(jlong, Long)
DECLARE_PRIMITIVE_METHODS(jfloat, Float)
DECLARE_PRIMITIVE_METHODS(jdouble, Double)
#pragma pop_macro("DECLARE_PRIMITIVE_METHODS")
template<typename T, typename Base>
inline alias_ref<jclass> JavaClass<T, Base>::javaClassStatic() {
static auto cls = findClassStatic(
std::string(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2).c_str());
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>
inline local_ref<jclass> JavaClass<T, Base>::javaClassLocal() {
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
template<typename T, typename Base, typename JType>
inline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {
std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());
return findClassLocal(className.c_str());
}

View File

@ -15,8 +15,9 @@
* to provide access to corresponding JNI functions + some conveniance.
*/
#include "Meta.h"
#include "References.h"
#include "References-forward.h"
#include "Meta-forward.h"
#include "TypeTraits.h"
#include <memory>
@ -25,6 +26,9 @@
namespace facebook {
namespace jni {
class JClass;
class JObject;
/// Lookup a class by name. Note this functions returns an alias_ref that
/// points to a leaked global reference. This is appropriate for classes
/// that are never unloaded (which is any class in an Android app and most
@ -34,7 +38,7 @@ namespace jni {
/// in a "static auto" variable, or a static global.
///
/// @return Returns a leaked global reference to the class
alias_ref<jclass> findClassStatic(const char* name);
alias_ref<JClass> findClassStatic(const char* name);
/// Lookup a class by name. Note this functions returns a local reference,
/// which means that it must not be stored in a static variable.
@ -43,34 +47,66 @@ alias_ref<jclass> findClassStatic(const char* name);
/// (like caching method ids).
///
/// @return Returns a global reference to the class
local_ref<jclass> findClassLocal(const char* name);
local_ref<JClass> findClassLocal(const char* name);
/// Check to see if two references refer to the same object. Comparison with nullptr
/// returns true if and only if compared to another nullptr. A weak reference that
/// refers to a reclaimed object count as nullptr.
bool isSameObject(alias_ref<jobject> lhs, alias_ref<jobject> rhs) noexcept;
bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;
// Together, these classes allow convenient use of any class with the fbjni
// helpers. To use:
//
// struct MyClass : public JavaClass<MyClass> {
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
// };
//
// Then, an alias_ref<MyClass::javaobject> will be backed by an instance of
// MyClass. JavaClass provides a convenient way to add functionality to these
// smart references.
//
// For example:
//
// struct MyClass : public JavaClass<MyClass> {
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
//
// void foo() {
// static auto method = javaClassStatic()->getMethod<void()>("foo");
// method(self());
// }
//
// static local_ref<javaobject> create(int i) {
// return newInstance(i);
// }
// };
//
// auto obj = MyClass::create(10);
// obj->foo();
//
// While users of a JavaClass-type can lookup methods and fields through the
// underlying JClass, those calls can only be checked at runtime. It is recommended
// that the JavaClass-type instead explicitly expose it's methods as in the example
// above.
/// Wrapper to provide functionality to jobject references
template<>
class JObjectWrapper<jobject> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Object;";
namespace detail {
template<typename JC, typename... Args>
static local_ref<JC> newInstance(Args... args);
}
class MonitorLock;
class JObject : detail::JObjectBase {
public:
static constexpr auto kJavaDescriptor = "Ljava/lang/Object;";
static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }
/// Wrap an existing JNI reference
JObjectWrapper(jobject reference = nullptr) noexcept;
// Copy constructor
JObjectWrapper(const JObjectWrapper& other) noexcept;
static constexpr const char* get_instantiated_base_name() { return nullptr; }
/// Get a @ref local_ref of the object's class
local_ref<jclass> getClass() const noexcept;
local_ref<JClass> getClass() const noexcept;
/// Checks if the object is an instance of a class
bool isInstanceOf(alias_ref<jclass> cls) const noexcept;
bool isInstanceOf(alias_ref<JClass> cls) const noexcept;
/// Get the primitive value of a field
template<typename T>
@ -78,7 +114,7 @@ class JObjectWrapper<jobject> {
/// Get and wrap the value of a field in a @ref local_ref
template<typename T>
local_ref<T*> getFieldValue(JField<T*> field) noexcept;
local_ref<T*> getFieldValue(JField<T*> field) const noexcept;
/// Set the value of field. Any Java type is accepted, including the primitive types
/// and raw reference types.
@ -88,41 +124,108 @@ class JObjectWrapper<jobject> {
/// Convenience method to create a std::string representing the object
std::string toString() const;
protected:
jobject this_;
// Take this object's monitor lock
MonitorLock lock() const noexcept;
private:
template<typename T, typename A>
friend class base_owned_ref;
typedef _jobject _javaobject;
typedef _javaobject* javaobject;
template<typename T>
friend class alias_ref;
friend void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept;
void set(jobject reference) noexcept;
jobject get() const noexcept;
protected:
jobject self() const noexcept;
private:
friend void swap(JObject& a, JObject& b) noexcept;
template<typename>
friend struct detail::ReprAccess;
template<typename, typename, typename>
friend class JavaClass;
template <typename, typename>
friend class JObjectWrapper;
};
using JObject = JObjectWrapper<jobject>;
// This is only to maintain backwards compatibility with things that are
// already providing a specialization of JObjectWrapper. Any such instances
// should be updated to use a JavaClass.
template<>
class JObjectWrapper<jobject> : public JObject {
};
void swap(JObjectWrapper<jobject>& a, JObjectWrapper<jobject>& b) noexcept;
namespace detail {
template <typename, typename Base, typename JType>
struct JTypeFor {
static_assert(
std::is_base_of<
std::remove_pointer<jobject>::type,
typename std::remove_pointer<JType>::type
>::value, "");
using _javaobject = typename std::remove_pointer<JType>::type;
using javaobject = JType;
};
template <typename T, typename Base>
struct JTypeFor<T, Base, void> {
// JNI pattern for jobject assignable pointer
struct _javaobject : Base::_javaobject {
// This allows us to map back to the defining type (in ReprType, for
// example).
typedef T JniRefRepr;
};
using javaobject = _javaobject*;
};
}
// JavaClass provides a method to inform fbjni about user-defined Java types.
// Given a class:
// struct Foo : JavaClass<Foo> {
// static constexpr auto kJavaDescriptor = "Lcom/example/package/Foo;";
// };
// fbjni can determine the java type/method signatures for Foo::javaobject and
// smart refs (like alias_ref<Foo::javaobject>) will hold an instance of Foo
// and provide access to it through the -> and * operators.
//
// The "Base" template argument can be used to specify the JavaClass superclass
// of this type (for instance, JString's Base is JObject).
//
// The "JType" template argument is used to provide a jni type (like jstring,
// jthrowable) to be used as javaobject. This should only be necessary for
// built-in jni types and not user-defined ones.
template <typename T, typename Base = JObject, typename JType = void>
class JavaClass : public Base {
using JObjType = typename detail::JTypeFor<T, Base, JType>;
public:
using _javaobject = typename JObjType::_javaobject;
using javaobject = typename JObjType::javaobject;
using JavaBase = JavaClass;
static alias_ref<JClass> javaClassStatic();
static local_ref<JClass> javaClassLocal();
protected:
/// Allocates a new object and invokes the specified constructor
/// Like JClass's getConstructor, this function can only check at runtime if
/// the class actually has a constructor that accepts the corresponding types.
/// While a JavaClass-type can expose this function directly, it is recommended
/// to instead to use this to explicitly only expose those constructors that
/// the Java class actually has (i.e. with static create() functions).
template<typename... Args>
static local_ref<T> newInstance(Args... args) {
return detail::newInstance<JavaClass>(args...);
}
javaobject self() const noexcept;
};
/// Wrapper to provide functionality to jclass references
struct NativeMethod;
template<>
class JObjectWrapper<jclass> : public JObjectWrapper<jobject> {
class JClass : public JavaClass<JClass, JObject, jclass> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;";
using JObjectWrapper<jobject>::JObjectWrapper;
/// Get a @local_ref to the super class of this class
local_ref<jclass> getSuperclass() const noexcept;
local_ref<JClass> getSuperclass() const noexcept;
/// Register native methods for the class. Usage looks like this:
///
@ -141,7 +244,7 @@ class JObjectWrapper<jclass> : public JObjectWrapper<jobject> {
/// Check to see if the class is assignable from another class
/// @pre cls != nullptr
bool isAssignableFrom(alias_ref<jclass> cls) const noexcept;
bool isAssignableFrom(alias_ref<JClass> cls) const noexcept;
/// Convenience method to lookup the constructor with descriptor as specified by the
/// type arguments
@ -212,95 +315,96 @@ class JObjectWrapper<jclass> : public JObjectWrapper<jobject> {
template<typename F>
JNonvirtualMethod<F> getNonvirtualMethod(const char* name, const char* descriptor) const;
private:
private:
jclass self() const noexcept;
};
using JClass = JObjectWrapper<jclass>;
// Convenience method to register methods on a class without holding
// onto the class object.
void registerNatives(const char* name, std::initializer_list<NativeMethod> methods);
/// Wrapper to provide functionality to jstring references
template<>
class JObjectWrapper<jstring> : public JObjectWrapper<jobject> {
class JString : public JavaClass<JString, JObject, jstring> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/String;";
using JObjectWrapper<jobject>::JObjectWrapper;
/// Convenience method to convert a jstring object to a std::string
std::string toStdString() const;
private:
jstring self() const noexcept;
};
/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a
/// jstring
local_ref<jstring> make_jstring(const char* modifiedUtf8);
local_ref<jstring> make_jstring(const std::string& modifiedUtf8);
using JString = JObjectWrapper<jstring>;
local_ref<JString> make_jstring(const char* modifiedUtf8);
local_ref<JString> make_jstring(const std::string& modifiedUtf8);
/// Wrapper to provide functionality to jthrowable references
template<>
class JObjectWrapper<jthrowable> : public JObjectWrapper<jobject> {
class JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
public:
/// Java type descriptor
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
using JObjectWrapper<jobject>::JObjectWrapper;
private:
jthrowable self() const noexcept;
};
/// @cond INTERNAL
template<class T> class _jtypeArray : public _jobjectArray {};
// @endcond
/// Wrapper to provide functionality for arrays of j-types
template<class T> using jtypeArray = _jtypeArray<T>*;
template<typename T>
namespace detail {
template<typename Target>
class ElementProxy {
private:
JObjectWrapper<_jtypeArray<T>*>* target_;
size_t idx_;
private:
Target* target_;
size_t idx_;
public:
ElementProxy(JObjectWrapper<_jtypeArray<T>*>* target, size_t idx);
public:
using T = typename Target::javaentry;
ElementProxy(Target* target, size_t idx);
ElementProxy<T>& operator=(const T& o);
ElementProxy& operator=(const T& o);
ElementProxy<T>& operator=(alias_ref<T>& o);
ElementProxy& operator=(alias_ref<T>& o);
ElementProxy<T>& operator=(alias_ref<T>&& o);
ElementProxy& operator=(alias_ref<T>&& o);
ElementProxy<T>& operator=(const ElementProxy<T>& o);
ElementProxy& operator=(const ElementProxy& o);
operator const local_ref<T> () const;
operator const local_ref<T> () const;
operator local_ref<T> ();
};
operator local_ref<T> ();
};
}
namespace detail {
class JArray : public JavaClass<JArray, JObject, jarray> {
public:
// This cannot be used in a scope that derives a descriptor (like in a method
// signature). Use a more derived type instead (like JArrayInt or
// JArrayClass<T>).
static constexpr const char* kJavaDescriptor = nullptr;
size_t size() const noexcept;
};
// This is used so that the JArrayClass<T> javaobject extends jni's
// jobjectArray. This class should not be used directly. A general Object[]
// should use JArrayClass<jobject>.
class JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {
// This cannot be used in a scope that derives a descriptor (like in a method
// signature).
static constexpr const char* kJavaDescriptor = nullptr;
};
}
template<typename T>
class JObjectWrapper<jtypeArray<T>> : public JObjectWrapper<jobject> {
class JArrayClass : public JavaClass<JArrayClass<T>, detail::JTypeArray> {
public:
static_assert(is_plain_jni_reference<T>(), "");
// javaentry is the jni type of an entry in the array (i.e. jint).
using javaentry = T;
// javaobject is the jni type of the array.
using javaobject = typename JavaClass<JArrayClass<T>, detail::JTypeArray>::javaobject;
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor() {
return "[" + jtype_traits<T>::descriptor();
};
using JObjectWrapper<jobject>::JObjectWrapper;
static std::string get_instantiated_java_descriptor();
static std::string get_instantiated_base_name();
/// Allocate a new array from Java heap, for passing as a JNI parameter or return value.
/// NOTE: if using as a return value, you want to call release() instead of get() on the
/// smart pointer.
static local_ref<jtypeArray<T>> newArray(size_t count);
static local_ref<javaobject> newArray(size_t count);
/// Assign an object to the array.
/// Typically you will use the shorthand (*ref)[idx]=value;
@ -312,9 +416,6 @@ class JObjectWrapper<jtypeArray<T>> : public JObjectWrapper<jobject> {
/// If you use auto, you'll get an ElementProxy, which may need to be cast.
local_ref<T> getElement(size_t idx);
/// Get the size of the array.
size_t size();
/// EXPERIMENTAL SUBSCRIPT SUPPORT
/// This implementation of [] returns a proxy object which then has a bunch of specializations
/// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can
@ -324,187 +425,167 @@ class JObjectWrapper<jtypeArray<T>> : public JObjectWrapper<jobject> {
/// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand,
/// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests
/// for some examples.
ElementProxy<T> operator[](size_t idx);
private:
jtypeArray<T> self() const noexcept;
detail::ElementProxy<JArrayClass> operator[](size_t idx);
};
template <class T>
using JArrayClass = JObjectWrapper<jtypeArray<T>>;
template <typename T>
using jtypeArray = typename JArrayClass<T>::javaobject;
template<typename T>
local_ref<jtypeArray<T>> adopt_local_array(jobjectArray ref) {
return adopt_local(static_cast<jtypeArray<T>>(ref));
local_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {
return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));
}
template<typename T>
local_ref<T> adopt_local(ElementProxy<T> elementProxy) {
return static_cast<local_ref<T>>(elementProxy);
template<typename Target>
local_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {
return static_cast<local_ref<typename Target::javaentry>>(elementProxy);
}
template <typename T, typename PinAlloc>
class PinnedPrimitiveArray;
template <typename T> class PinnedArrayAlloc;
template <typename T> class PinnedRegionAlloc;
template <typename T> class PinnedCriticalAlloc;
/// Wrapper to provide functionality to jarray references.
/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with
/// the elements of the array.
template<>
class JObjectWrapper<jarray> : public JObjectWrapper<jobject> {
template <typename JArrayType>
class JPrimitiveArray :
public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {
static_assert(is_jni_primitive_array<JArrayType>(), "");
public:
static constexpr const char* kJavaDescriptor = nullptr;
static std::string get_instantiated_java_descriptor();
static std::string get_instantiated_base_name();
using JObjectWrapper<jobject>::JObjectWrapper;
size_t size() const noexcept;
using T = typename jtype_traits<JArrayType>::entry_type;
private:
jarray self() const noexcept;
static local_ref<JArrayType> newArray(size_t count);
void getRegion(jsize start, jsize length, T* buf);
std::unique_ptr<T[]> getRegion(jsize start, jsize length);
void setRegion(jsize start, jsize length, const T* buf);
/// Returns a view of the underlying array. This will either be a "pinned"
/// version of the array (in which case changes to one immediately affect the
/// other) or a copy of the array (in which cases changes to the view will take
/// affect when destroyed or on calls to release()/commit()).
PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> pin();
/// Returns a view of part of the underlying array. A pinned region is always
/// backed by a copy of the region.
PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> pinRegion(jsize start, jsize length);
/// Returns a view of the underlying array like pin(). However, while the pin
/// is held, the code is considered within a "critical region". In a critical
/// region, native code must not call JNI functions or make any calls that may
/// block on other Java threads. These restrictions make it more likely that
/// the view will be "pinned" rather than copied (for example, the VM may
/// suspend garbage collection within a critical region).
PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> pinCritical();
private:
friend class PinnedArrayAlloc<T>;
T* getElements(jboolean* isCopy);
void releaseElements(T* elements, jint mode);
};
using JArray = JObjectWrapper<jarray>;
template <typename T>
class PinnedPrimitiveArray;
#pragma push_macro("DECLARE_PRIMITIVE_ARRAY_UTILS")
#undef DECLARE_PRIMITIVE_ARRAY_UTILS
#define DECLARE_PRIMITIVE_ARRAY_UTILS(TYPE, NAME, DESC) \
\
local_ref<j ## TYPE ## Array> make_ ## TYPE ## _array(jsize size); \
\
template<> class JObjectWrapper<j ## TYPE ## Array> : public JArray { \
public: \
static constexpr const char* kJavaDescriptor = "[" # DESC; \
\
using JArray::JArray; \
\
static local_ref<j ## TYPE ## Array> newArray(size_t count); \
\
j ## TYPE* getRegion(jsize start, jsize length, j ## TYPE* buf); \
std::unique_ptr<j ## TYPE[]> getRegion(jsize start, jsize length); \
void setRegion(jsize start, jsize length, const j ## TYPE* buf); \
PinnedPrimitiveArray<j ## TYPE> pin(); \
\
private: \
j ## TYPE ## Array self() const noexcept { \
return static_cast<j ## TYPE ## Array>(this_); \
} \
}; \
\
using JArray ## NAME = JObjectWrapper<j ## TYPE ## Array> \
DECLARE_PRIMITIVE_ARRAY_UTILS(boolean, Boolean, "Z");
DECLARE_PRIMITIVE_ARRAY_UTILS(byte, Byte, "B");
DECLARE_PRIMITIVE_ARRAY_UTILS(char, Char, "C");
DECLARE_PRIMITIVE_ARRAY_UTILS(short, Short, "S");
DECLARE_PRIMITIVE_ARRAY_UTILS(int, Int, "I");
DECLARE_PRIMITIVE_ARRAY_UTILS(long, Long, "J");
DECLARE_PRIMITIVE_ARRAY_UTILS(float, Float, "F");
DECLARE_PRIMITIVE_ARRAY_UTILS(double, Double, "D");
#pragma pop_macro("DECLARE_PRIMITIVE_ARRAY_UTILS")
local_ref<jbooleanArray> make_boolean_array(jsize size);
local_ref<jbyteArray> make_byte_array(jsize size);
local_ref<jcharArray> make_char_array(jsize size);
local_ref<jshortArray> make_short_array(jsize size);
local_ref<jintArray> make_int_array(jsize size);
local_ref<jlongArray> make_long_array(jsize size);
local_ref<jfloatArray> make_float_array(jsize size);
local_ref<jdoubleArray> make_double_array(jsize size);
using JArrayBoolean = JPrimitiveArray<jbooleanArray>;
using JArrayByte = JPrimitiveArray<jbyteArray>;
using JArrayChar = JPrimitiveArray<jcharArray>;
using JArrayShort = JPrimitiveArray<jshortArray>;
using JArrayInt = JPrimitiveArray<jintArray>;
using JArrayLong = JPrimitiveArray<jlongArray>;
using JArrayFloat = JPrimitiveArray<jfloatArray>;
using JArrayDouble = JPrimitiveArray<jdoubleArray>;
/// RAII class for pinned primitive arrays
/// This currently only supports read/write access to existing java arrays. You can't create a
/// primitive array this way yet. This class also pins the entire array into memory during the
/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the
/// release() function. During a long-running block of code, you should unpin the array as soon
/// as you're done with it, to avoid holding up the Java garbage collector.
template <typename T>
/// release() or abort() functions. During a long-running block of code, you
/// should unpin the array as soon as you're done with it, to avoid holding up
/// the Java garbage collector.
template <typename T, typename PinAlloc>
class PinnedPrimitiveArray {
public:
static_assert(is_jni_primitive<T>::value,
"PinnedPrimitiveArray requires primitive jni type.");
PinnedPrimitiveArray(PinnedPrimitiveArray&&) noexcept;
using ArrayType = typename jtype_traits<T>::array_type;
PinnedPrimitiveArray(PinnedPrimitiveArray&&);
PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;
~PinnedPrimitiveArray() noexcept;
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&) noexcept;
PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&);
PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;
T* get();
void release();
/// Unpins the array. If the array is a copy, pending changes are discarded.
void abort();
/// If the array is a copy, copies pending changes to the underlying java array.
void commit();
bool isCopy() const noexcept;
const T& operator[](size_t index) const;
T& operator[](size_t index);
bool isCopy() const noexcept;
size_t size() const noexcept;
private:
alias_ref<jarray> array_;
alias_ref<ArrayType> array_;
size_t start_;
T* elements_;
jboolean isCopy_;
size_t size_;
PinnedPrimitiveArray(alias_ref<jarray>) noexcept;
void allocate(alias_ref<ArrayType>, jint start, jint length);
void releaseImpl(jint mode);
void clear() noexcept;
friend class JObjectWrapper<jbooleanArray>;
friend class JObjectWrapper<jbyteArray>;
friend class JObjectWrapper<jcharArray>;
friend class JObjectWrapper<jshortArray>;
friend class JObjectWrapper<jintArray>;
friend class JObjectWrapper<jlongArray>;
friend class JObjectWrapper<jfloatArray>;
friend class JObjectWrapper<jdoubleArray>;
};
namespace detail {
class BaseJavaClass {
public:
typedef _jobject _javaobject;
typedef _javaobject* javaobject;
PinnedPrimitiveArray(alias_ref<ArrayType>, jint start, jint length);
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
};
#pragma push_macro("PlainJniRefMap")
#undef PlainJniRefMap
#define PlainJniRefMap(rtype, jtype) \
namespace detail { \
template<> \
struct RefReprType<jtype> { \
using type = rtype; \
}; \
}
// Together, these classes allow convenient use of any class with the fbjni
// helpers. To use:
//
// struct MyClass : public JavaClass<MyClass> {
// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;";
// };
//
// alias_ref<MyClass::javaobject> myClass = foo();
PlainJniRefMap(JArrayBoolean, jbooleanArray);
PlainJniRefMap(JArrayByte, jbyteArray);
PlainJniRefMap(JArrayChar, jcharArray);
PlainJniRefMap(JArrayShort, jshortArray);
PlainJniRefMap(JArrayInt, jintArray);
PlainJniRefMap(JArrayLong, jlongArray);
PlainJniRefMap(JArrayFloat, jfloatArray);
PlainJniRefMap(JArrayDouble, jdoubleArray);
PlainJniRefMap(JObject, jobject);
PlainJniRefMap(JClass, jclass);
PlainJniRefMap(JString, jstring);
PlainJniRefMap(JThrowable, jthrowable);
template <typename T, typename Base = detail::BaseJavaClass>
class JavaClass {
public:
// JNI pattern for jobject assignable pointer
struct _javaobject : public Base::_javaobject {
typedef T javaClass;
};
typedef _javaobject* javaobject;
static alias_ref<jclass> javaClassStatic();
static local_ref<jclass> javaClassLocal();
};
template <typename T>
class JObjectWrapper<T,
typename std::enable_if<
is_plain_jni_reference<T>::value &&
std::is_class<typename std::remove_pointer<T>::type::javaClass>::value
>::type>
: public JObjectWrapper<jobject> {
public:
static constexpr const char* kJavaDescriptor =
std::remove_pointer<T>::type::javaClass::kJavaDescriptor;
using JObjectWrapper<jobject>::JObjectWrapper;
template<typename U>
JObjectWrapper(const JObjectWrapper<U>& w)
: JObjectWrapper<jobject>(w) {
static_assert(std::is_convertible<U, T>::value,
"U must be convertible to T");
}
};
#pragma pop_macro("PlainJniRefMap")
}}
#include "CoreClasses-inl.h"
// This is here because code in Meta-inl.h uses alias_ref, which
// requires JObjectWrapper<jobject> to be concrete before it can work.
#include "Meta-inl.h"

View File

@ -7,9 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "Exceptions.h"
#include "CoreClasses.h"
#include <jni/ALog.h>
#include "jni/fbjni.h"
#include <fb/assert.h>

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include "CoreClasses.h"
namespace facebook {
namespace jni {
class JFile : public JavaClass<JFile> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/io/File;";
// Define a method that calls into the represented Java class
std::string getAbsolutePath() {
static auto method = getClass()->getMethod<jstring()>("getAbsolutePath");
return method(self())->toStdString();
}
};
}
}

View File

@ -7,20 +7,17 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include "Hybrid.h"
#include "jni/fbjni.h"
#include "Exceptions.h"
#include "Registration.h"
namespace facebook {
namespace jni {
namespace detail {
void setNativePointer(alias_ref<HybridData::javaobject> hybridData,
std::unique_ptr<BaseHybridClass> new_value) {
static auto pointerField = hybridData->getClass()->getField<jlong>("mNativePointer");
auto* old_value = reinterpret_cast<BaseHybridClass*>(hybridData->getFieldValue(pointerField));
void HybridData::setNativePointer(std::unique_ptr<BaseHybridClass> new_value) {
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
auto* old_value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
if (new_value) {
// Modify should only ever be called once with a non-null
// new_value. If this happens again it's a programmer error, so
@ -35,35 +32,28 @@ void setNativePointer(alias_ref<HybridData::javaobject> hybridData,
// ownership of it, to HybridData which is managed by the java GC. The
// finalizer on hybridData calls resetNative which will delete the object, if
// reseetNative has not already been called.
hybridData->setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
}
BaseHybridClass* getNativePointer(alias_ref<HybridData::javaobject> hybridData) {
static auto pointerField = hybridData->getClass()->getField<jlong>("mNativePointer");
auto* value = reinterpret_cast<BaseHybridClass*>(hybridData->getFieldValue(pointerField));
BaseHybridClass* HybridData::getNativePointer() {
static auto pointerField = getClass()->getField<jlong>("mNativePointer");
auto* value = reinterpret_cast<BaseHybridClass*>(getFieldValue(pointerField));
if (!value) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return value;
}
local_ref<HybridData::javaobject> getHybridData(alias_ref<jobject> jthis,
JField<HybridData::javaobject> field) {
auto hybridData = jthis->getFieldValue(field);
if (!hybridData) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
return hybridData;
local_ref<HybridData> HybridData::create() {
return newInstance();
}
}
namespace {
void resetNative(alias_ref<detail::HybridData::javaobject> jthis) {
detail::setNativePointer(jthis, nullptr);
void resetNative(alias_ref<detail::HybridData> jthis) {
jthis->setNativePointer(nullptr);
}
}
void HybridDataOnLoad() {

View File

@ -19,169 +19,108 @@ namespace jni {
namespace detail {
class BaseHybridClass : public BaseJavaClass {
class BaseHybridClass {
public:
virtual ~BaseHybridClass() {}
};
struct HybridData : public JavaClass<HybridData> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
BaseHybridClass* getNativePointer();
static local_ref<HybridData> create();
};
void setNativePointer(alias_ref<HybridData::javaobject> hybridData,
std::unique_ptr<BaseHybridClass> new_value);
BaseHybridClass* getNativePointer(alias_ref<HybridData::javaobject> hybridData);
local_ref<HybridData::javaobject> getHybridData(alias_ref<jobject> jthis,
JField<HybridData::javaobject> field);
// 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;
}
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.");
};
// This is needed for return conversion
template <>
struct Convert<void> {
typedef void jniType;
struct HybridTraits<BaseHybridClass> {
using CxxBase = BaseHybridClass;
using JavaBase = JObject;
};
// 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);
}
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;
};
// 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);
}
template <typename Base>
struct HybridTraits<
Base,
typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {
using CxxBase = BaseHybridClass;
using JavaBase = Base;
};
// 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
// convert to HybridClass* from jhybridobject
template <typename T>
struct Convert<alias_ref<T>> {
typedef 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();
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.
};
// convert return from local_ref<T>
template <typename T>
struct Convert<local_ref<T>> {
typedef 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();
}
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;
};
// convert return from global_ref<T>
template <typename T>
struct Convert<global_ref<T>> {
typedef T jniType;
// No automatic synthesis of global_ref
static jniType toJniRet(global_ref<jniType> t) {
return t.get();
}
static jniType toCall(global_ref<jniType> t) {
return t.get();
}
};
// 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. This is only relevant when the jniType is passed down, as
// in newObjectJavaArgs.
template <typename T>
inline T callToJni(T&& t) {
return t;
}
inline jstring callToJni(local_ref<jstring>&& sref) {
return sref.get();
}
struct jstring_holder {
local_ref<jstring> s_;
jstring_holder(const char* s) : s_(make_jstring(s)) {}
operator jstring() { return s_.get(); }
};
template <typename T, typename Enabled = void>
struct HybridRoot {};
template <typename T>
struct HybridRoot<T,
typename std::enable_if<!std::is_base_of<BaseHybridClass, T>::value>::type>
: public BaseHybridClass {};
}
template <typename T, typename Base = detail::BaseHybridClass>
class HybridClass : public Base
, public detail::HybridRoot<Base>
, public JavaClass<T, Base> {
class HybridClass : public detail::HybridTraits<Base>::CxxBase {
public:
typedef detail::HybridData::javaobject jhybriddata;
typedef typename JavaClass<T, Base>::javaobject jhybridobject;
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 JavaClass<T, Base>::javaClassStatic;
using JavaClass<T, Base>::javaClassLocal;
using JavaClass<T, Base>::javaobject;
typedef typename JavaClass<T, Base>::_javaobject _javaobject;
using HybridType = T;
// This will reach into the java object and extract the C++ instance from
// the mHybridData and return it.
T* cthis();
friend class HybridClass;
};
using jhybridobject = typename JavaPart::javaobject;
using javaobject = typename JavaPart::javaobject;
typedef detail::HybridData::javaobject jhybriddata;
static alias_ref<JClass> javaClassStatic() {
return JavaPart::javaClassStatic();
}
static local_ref<JClass> javaClassLocal() {
std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);
return findClassLocal(className.c_str());
}
protected:
typedef HybridClass HybridBase;
@ -189,21 +128,20 @@ protected:
// 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 Base::Base;
using detail::HybridTraits<Base>::CxxBase::CxxBase;
static void registerHybrid(std::initializer_list<NativeMethod> methods) {
javaClassStatic()->registerNatives(methods);
}
static local_ref<jhybriddata> makeHybridData(std::unique_ptr<T> cxxPart) {
static auto dataCtor = detail::HybridData::javaClassStatic()->getConstructor<jhybriddata()>();
auto hybridData = detail::HybridData::javaClassStatic()->newObject(dataCtor);
detail::setNativePointer(hybridData, std::move(cxxPart));
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
auto hybridData = detail::HybridData::create();
hybridData->setNativePointer(std::move(cxxPart));
return hybridData;
}
template <typename... Args>
static local_ref<jhybriddata> makeCxxInstance(Args&&... args) {
static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
}
@ -219,31 +157,27 @@ public:
// Exception behavior: This can throw an exception if creating the
// C++ object fails, or any JNI methods throw.
template <typename... Args>
static local_ref<jhybridobject> newObjectCxxArgs(Args&&... args) {
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
static auto ctor = javaClassStatic()->template getConstructor<jhybridobject(jhybriddata)>();
return javaClassStatic()->newObject(ctor, hybridData.get());
return JavaPart::newInstance(hybridData);
}
// TODO? Create reusable interface for Allocatable classes and use it to
// strengthen type-checking (and possibly provide a default
// implementation of allocate().)
template <typename... Args>
static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
static auto allocateMethod =
javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>("allocate");
return allocateMethod(javaClassStatic(), hybridData.get());
}
// Factory method for creating a hybrid object where the arguments
// are passed to the java ctor.
template <typename... Args>
static local_ref<jhybridobject> newObjectJavaArgs(Args&&... args) {
static auto ctor =
javaClassStatic()->template getConstructor<
jhybridobject(typename detail::Convert<typename std::decay<Args>::type>::jniType...)>();
// This can't use the same impl as Convert::toJniRet because that
// function sometimes creates and then releases local_refs, which
// could potentially cause the locals table to fill. Instead, we
// use two calls, one which can return a local_ref if needed, and
// a second which extracts its value. The lifetime of the
// local_ref is the expression, after which it is destroyed and
// the local_ref is cleaned up.
auto lref =
javaClassStatic()->newObject(
ctor, detail::callToJni(
detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
return lref;
static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {
return JavaPart::newInstance(std::move(args)...);
}
// If a hybrid class throws an exception which derives from
@ -256,19 +190,38 @@ public:
static void mapException(const std::exception& ex) {}
};
template <typename T, typename B>
inline T* HybridClass<T, B>::JavaPart::cthis() {
static auto field =
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
auto hybridData = this->getFieldValue(field);
if (!hybridData) {
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
}
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
T* value = static_cast<T*>(hybridData->getNativePointer());
// This would require some serious programmer error.
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
return value;
};
template <typename T, typename B>
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {
return T::kJavaDescriptor;
}
template <typename T, typename B>
/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {
auto name = get_instantiated_java_descriptor();
return name.substr(1, name.size() - 2);
}
// Given a *_ref object which refers to a hybrid class, this will reach inside
// of it, find the mHybridData, extract the C++ instance pointer, cast it to
// the appropriate type, and return it.
template <typename T>
inline typename std::remove_pointer<typename T::PlainJniType>::type::javaClass* cthis(T jthis) {
static auto dataField =
jthis->getClass()->template getField<detail::HybridData::javaobject>("mHybridData");
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
auto* value = static_cast<typename std::remove_pointer<typename T::PlainJniType>::type::javaClass*>(
detail::getNativePointer(detail::getHybridData(jthis, dataField)));
// This would require some serious programmer error.
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
return value;
inline auto cthis(T jthis) -> decltype(jthis->cthis()) {
return jthis->cthis();
}
void HybridDataOnLoad();

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
namespace facebook {
namespace jni {
namespace detail {
template <typename E>
struct IteratorHelper : public JavaClass<IteratorHelper<E>> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;";
typedef local_ref<E> value_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::forward_iterator_tag iterator_category;
typedef JavaClass<IteratorHelper<E>> JavaBase_;
bool hasNext() const {
static auto hasNextMethod =
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
return hasNextMethod(JavaBase_::self());
}
value_type next() {
static auto elementField =
JavaBase_::javaClassStatic()->template getField<jobject>("mElement");
return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField));
}
static void reset(value_type& v) {
v.reset();
}
};
template <typename K, typename V>
struct MapIteratorHelper : public JavaClass<MapIteratorHelper<K,V>> {
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;";
typedef std::pair<local_ref<K>, local_ref<V>> value_type;
typedef JavaClass<MapIteratorHelper<K,V>> JavaBase_;
bool hasNext() const {
static auto hasNextMethod =
JavaBase_::javaClassStatic()->template getMethod<jboolean()>("hasNext");
return hasNextMethod(JavaBase_::self());
}
value_type next() {
static auto keyField = JavaBase_::javaClassStatic()->template getField<jobject>("mKey");
static auto valueField = JavaBase_::javaClassStatic()->template getField<jobject>("mValue");
return std::make_pair(dynamic_ref_cast<K>(JavaBase_::getFieldValue(keyField)),
dynamic_ref_cast<V>(JavaBase_::getFieldValue(valueField)));
}
static void reset(value_type& v) {
v.first.reset();
v.second.reset();
}
};
template <typename T>
class Iterator {
public:
typedef typename T::value_type value_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::input_iterator_tag iterator_category;
// begin ctor
Iterator(global_ref<typename T::javaobject>&& helper)
: helper_(std::move(helper))
, i_(-1) {
++(*this);
}
// end ctor
Iterator()
: i_(-1) {}
bool operator==(const Iterator& it) const { return i_ == it.i_; }
bool operator!=(const Iterator& it) const { return !(*this == it); }
const value_type& operator*() const { assert(i_ != -1); return entry_; }
const value_type* operator->() const { assert(i_ != -1); return &entry_; }
Iterator& operator++() { // preincrement
bool hasNext = helper_->hasNext();
if (hasNext) {
++i_;
entry_ = helper_->next();
} else {
i_ = -1;
helper_->reset(entry_);
}
return *this;
}
Iterator operator++(int) { // postincrement
Iterator ret;
ret.i_ = i_;
ret.entry_ = std::move(entry_);
++(*this);
return ret;
}
global_ref<typename T::javaobject> helper_;
// set to -1 at end
std::ptrdiff_t i_;
value_type entry_;
};
}
template <typename E>
struct JIterator<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
};
template <typename E>
typename JIterator<E>::Iterator JIterator<E>::begin() const {
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
typename JIterator<E>::javaobject)>();
return Iterator(
make_global(
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename E>
typename JIterator<E>::Iterator JIterator<E>::end() const {
return Iterator();
}
template <typename E>
struct JIterable<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {
using detail::Iterator<detail::IteratorHelper<E>>::Iterator;
};
template <typename E>
typename JIterable<E>::Iterator JIterable<E>::begin() const {
static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->
template getConstructor<typename detail::IteratorHelper<E>::javaobject(
typename JIterable<E>::javaobject)>();
return Iterator(
make_global(
detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename E>
typename JIterable<E>::Iterator JIterable<E>::end() const {
return Iterator();
}
template <typename E>
size_t JCollection<E>::size() const {
static auto sizeMethod =
JCollection<E>::javaClassStatic()->template getMethod<jint()>("size");
return sizeMethod(this->self());
}
template <typename K, typename V>
struct JMap<K,V>::Iterator : public detail::Iterator<detail::MapIteratorHelper<K,V>> {
using detail::Iterator<detail::MapIteratorHelper<K,V>>::Iterator;
};
template <typename K, typename V>
size_t JMap<K,V>::size() const {
static auto sizeMethod =
JMap<K,V>::javaClassStatic()->template getMethod<jint()>("size");
return sizeMethod(this->self());
}
template <typename K, typename V>
typename JMap<K,V>::Iterator JMap<K,V>::begin() const {
static auto ctor = detail::MapIteratorHelper<K,V>::javaClassStatic()->
template getConstructor<typename detail::MapIteratorHelper<K,V>::javaobject(
typename JMap<K,V>::javaobject)>();
return Iterator(
make_global(
detail::MapIteratorHelper<K,V>::javaClassStatic()->newObject(ctor, this->self())));
}
template <typename K, typename V>
typename JMap<K,V>::Iterator JMap<K,V>::end() const {
return Iterator();
}
}
}

View File

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

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
namespace facebook {
namespace jni {
template<typename F>
class JMethod;
template<typename F>
class JStaticMethod;
template<typename F>
class JNonvirtualMethod;
template<typename F>
class JConstructor;
template<typename F>
class JField;
template<typename F>
class JStaticField;
/// Type traits for Java types (currently providing Java type descriptors)
template<typename T>
struct jtype_traits;
/// Type traits for Java methods (currently providing Java type descriptors)
template<typename F>
struct jmethod_traits;
}}

View File

@ -13,6 +13,8 @@
#include "Common.h"
#include "Exceptions.h"
#include "MetaConvert.h"
#include "References.h"
namespace facebook {
namespace jni {
@ -34,19 +36,25 @@ inline jmethodID JMethodBase::getId() const noexcept {
template<typename... Args>
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
const auto env = internal::getEnv();
env->CallVoidMethod(self.get(), getId(), args...);
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 auto env = internal::getEnv(); \
auto result = env->Call ## METHOD ## Method(self.get(), getId(), args...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->Call ## METHOD ## Method( \
self.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_CALL(jboolean, Boolean)
@ -59,30 +67,54 @@ DEFINE_PRIMITIVE_CALL(jfloat, Float)
DEFINE_PRIMITIVE_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_CALL")
template<typename T, typename... Args>
inline local_ref<T*> JMethod<T*(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
const auto env = internal::getEnv();
auto result = env->CallObjectMethod(self.get(), getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(result));
}
/// 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 auto env = internal::getEnv();
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));
}
friend class JClass;
};
template<typename... Args>
inline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
const auto env = internal::getEnv();
env->CallStaticVoidMethod(cls.get(), getId(), args...);
env->CallStaticVoidMethod(
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
}
#pragma push_macro("DEFINE_PRIMITIVE_STATIC_CALL")
#undef DEFINE_PRIMITIVE_STATIC_CALL
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->CallStatic ## METHOD ## Method(cls.get(), getId(), args...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->CallStatic ## METHOD ## Method( \
cls.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean)
@ -95,33 +127,57 @@ DEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float)
DEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_CALL")
template<typename T, typename... Args>
inline local_ref<T*> JStaticMethod<T*(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {
const auto env = internal::getEnv();
auto result = env->CallStaticObjectMethod(cls.get(), getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(result));
}
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
template<typename R, typename... Args>
class JStaticMethod<R(Args...)> : public JMethodBase {
public:
using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;
static_assert(IsPlainJniReference<JniRet>(), "T* must be a JNI reference");
using JMethodBase::JMethodBase;
JStaticMethod() noexcept {};
JStaticMethod(const JStaticMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) {
const auto env = internal::getEnv();
auto result = env->CallStaticObjectMethod(
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<JniRet>(result));
}
friend class JClass;
};
template<typename... Args>
inline void
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, jclass cls, Args... args) {
JNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) {
const auto env = internal::getEnv();
env->CallNonvirtualVoidMethod(self.get(), cls, getId(), args...);
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, jclass cls, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->CallNonvirtual ## METHOD ## Method(self.get(), cls, getId(), args...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \
template<typename... Args> \
inline TYPE \
JNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \
const auto env = internal::getEnv(); \
auto result = env->CallNonvirtual ## METHOD ## Method( \
self.get(), \
cls.get(), \
getId(), \
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \
FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \
return result; \
}
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean)
@ -134,77 +190,31 @@ DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float)
DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double)
#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL")
template<typename T, typename... Args>
inline local_ref<T*> JNonvirtualMethod<T*(Args...)>::operator()(
alias_ref<jobject> self,
jclass cls,
Args... args) {
const auto env = internal::getEnv();
auto result = env->CallNonvirtualObjectMethod(self.get(), cls, getId(), args...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<T*>(result));
}
/// 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;
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
/// The generic way to associate a descriptor to a type is to look it up in the
/// corresponding @ref JObjectWrapper specialization. This makes it easy to add
/// support for your user defined type.
template<typename T>
struct jtype_traits {
// The jni type signature (described at
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
static std::string descriptor() {
static const auto descriptor = JObjectWrapper<T>::kJavaDescriptor != nullptr ?
std::string{JObjectWrapper<T>::kJavaDescriptor} :
JObjectWrapper<T>::get_instantiated_java_descriptor();
return descriptor;
/// Invoke a method and return a local reference wrapping the result
local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args){
const auto env = internal::getEnv();
auto result = env->CallNonvirtualObjectMethod(
self.get(),
cls.get(),
getId(),
detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
return adopt_local(static_cast<JniRet>(result));
}
// 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 (JObjectWrapper<T>::kJavaDescriptor != nullptr) {
std::string base_name = JObjectWrapper<T>::kJavaDescriptor;
return base_name.substr(1, base_name.size() - 2);
}
return JObjectWrapper<T>::get_instantiated_java_descriptor();
}
friend class JClass;
};
#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(); } \
}; \
template<> \
struct jtype_traits<TYPE ## Array> { \
static std::string descriptor() { return std::string{"[" #DSC}; } \
static std::string base_name() { return descriptor(); } \
};
// 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")
// JField<T> ///////////////////////////////////////////////////////////////////////////////////////

View File

@ -19,11 +19,25 @@
#include <jni.h>
#include "References.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 {
class JObject;
/// Wrapper of a jmethodID. Provides a common base for JMethod specializations
class JMethodBase {
public:
@ -65,7 +79,7 @@ class JMethod<TYPE(Args...)> : public JMethodBase {
\
TYPE operator()(alias_ref<jobject> self, Args... args); \
\
friend class JObjectWrapper<jclass>; \
friend class JClass; \
}
DEFINE_PRIMITIVE_METHOD_CLASS(void);
@ -82,26 +96,15 @@ DEFINE_PRIMITIVE_METHOD_CLASS(jdouble);
/// @endcond
/// JMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JMethod<T*(Args...)> : public JMethodBase {
public:
static_assert(IsPlainJniReference<T*>(), "T* 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<T*> operator()(alias_ref<jobject> self, Args... args);
friend class JObjectWrapper<jclass>;
};
/// Convenience type representing constructors
/// These should only be used with JClass::getConstructor and JClass::newObject.
template<typename F>
using JConstructor = JMethod<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>
@ -126,7 +129,7 @@ class JStaticMethod<TYPE(Args...)> : public JMethodBase { \
\
TYPE operator()(alias_ref<jclass> cls, Args... args); \
\
friend class JObjectWrapper<jclass>; \
friend class JClass; \
}
DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void);
@ -143,22 +146,6 @@ DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble);
/// @endcond
/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JStaticMethod<T*(Args...)> : public JMethodBase {
static_assert(IsPlainJniReference<T*>(), "T* must be a JNI reference");
public:
using JMethodBase::JMethodBase;
JStaticMethod() noexcept {};
JStaticMethod(const JStaticMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<T*> operator()(alias_ref<jclass> cls, Args... args);
friend class JObjectWrapper<jclass>;
};
/// Representation of a jNonvirtualMethodID
template<typename F>
class JNonvirtualMethod;
@ -180,9 +167,9 @@ class JNonvirtualMethod<TYPE(Args...)> : public JMethodBase { \
JNonvirtualMethod() noexcept {}; \
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \
\
TYPE operator()(alias_ref<jobject> self, jclass cls, Args... args); \
TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args); \
\
friend class JObjectWrapper<jclass>; \
friend class JClass; \
}
DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void);
@ -199,23 +186,6 @@ DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble);
/// @endcond
/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref
template<typename T, typename... Args>
class JNonvirtualMethod<T*(Args...)> : public JMethodBase {
static_assert(IsPlainJniReference<T*>(), "T* must be a JNI reference");
public:
using JMethodBase::JMethodBase;
JNonvirtualMethod() noexcept {};
JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;
/// Invoke a method and return a local reference wrapping the result
local_ref<T*> operator()(alias_ref<jobject> self, jclass cls, Args... args);
friend class JObjectWrapper<jclass>;
};
/**
* JField represents typed fields and simplifies their access. Note that object types return
* raw pointers which generally should promptly get a wrap_local treatment.
@ -245,7 +215,7 @@ class JField {
/// @pre object != nullptr
void set(jobject object, T value) noexcept;
friend class JObjectWrapper<jobject>;
friend class JObject;
};
@ -278,20 +248,11 @@ class JStaticField {
/// @pre object != nullptr
void set(jclass jcls, T value) noexcept;
friend class JObjectWrapper<jclass>;
friend class JClass;
friend class JObject;
};
/// 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;
/// Template magic to provide @ref jmethod_traits
template<typename R, typename... Args>
struct jmethod_traits<R(Args...)> {
@ -299,4 +260,75 @@ struct jmethod_traits<R(Args...)> {
static std::string constructor_descriptor();
};
// jtype_traits ////////////////////////////////////////////////////////////////////////////////////
template<typename T>
struct jtype_traits {
private:
using Repr = ReprType<T>;
public:
// The jni type signature (described at
// http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).
static std::string descriptor() {
std::string descriptor;
if (Repr::kJavaDescriptor == nullptr) {
descriptor = Repr::get_instantiated_java_descriptor();
} else {
descriptor = Repr::kJavaDescriptor;
}
return descriptor;
}
// The signature used for class lookups. See
// http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName().
static std::string base_name() {
if (Repr::kJavaDescriptor != nullptr) {
std::string base_name = Repr::kJavaDescriptor;
return base_name.substr(1, base_name.size() - 2);
}
return Repr::get_instantiated_base_name();
}
};
#pragma push_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
#undef DEFINE_FIELD_AND_ARRAY_TRAIT
#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC) \
template<> \
struct jtype_traits<TYPE> { \
static std::string descriptor() { return std::string{#DSC}; } \
static std::string base_name() { return descriptor(); } \
using array_type = TYPE ## Array; \
}; \
template<> \
struct jtype_traits<TYPE ## Array> { \
static std::string descriptor() { return std::string{"[" #DSC}; } \
static std::string base_name() { return descriptor(); } \
using entry_type = TYPE; \
};
// There is no voidArray, handle that without the macro.
template<>
struct jtype_traits<void> {
static std::string descriptor() { return std::string{"V"}; };
};
DEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z)
DEFINE_FIELD_AND_ARRAY_TRAIT(jbyte, B)
DEFINE_FIELD_AND_ARRAY_TRAIT(jchar, C)
DEFINE_FIELD_AND_ARRAY_TRAIT(jshort, S)
DEFINE_FIELD_AND_ARRAY_TRAIT(jint, I)
DEFINE_FIELD_AND_ARRAY_TRAIT(jlong, J)
DEFINE_FIELD_AND_ARRAY_TRAIT(jfloat, F)
DEFINE_FIELD_AND_ARRAY_TRAIT(jdouble, D)
#pragma pop_macro("DEFINE_FIELD_AND_ARRAY_TRAIT")
template <typename T>
struct jmethod_traits_from_cxx;
}}
#include "Meta-inl.h"

View File

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

View File

@ -14,7 +14,6 @@
#include <atomic>
#include "Exceptions.h"
#include "References.h"
namespace facebook {
namespace jni {

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include "ReferenceAllocators.h"
namespace facebook {
namespace jni {
template<typename T, typename Enable = void>
class JObjectWrapper;
namespace detail {
struct JObjectBase {
jobject get() const noexcept;
void set(jobject reference) noexcept;
jobject this_;
};
// RefReprType maps a type to the representation used by fbjni smart references.
template <typename T, typename Enable = void>
struct RefReprType;
template <typename T>
struct JavaObjectType;
template <typename T>
struct ReprAccess;
}
// Given T, either a jobject-like type or a JavaClass-derived type, ReprType<T>
// is the corresponding JavaClass-derived type and JniType<T> is the
// jobject-like type.
template <typename T>
using ReprType = typename detail::RefReprType<T>::type;
template <typename T>
using JniType = typename detail::JavaObjectType<T>::type;
template<typename T, typename Alloc>
class base_owned_ref;
template<typename T, typename Alloc>
class basic_strong_ref;
template<typename T>
class weak_ref;
template<typename T>
class alias_ref;
/// A smart unique reference owning a local JNI reference
template<typename T>
using local_ref = basic_strong_ref<T, LocalReferenceAllocator>;
/// A smart unique reference owning a global JNI reference
template<typename T>
using global_ref = basic_strong_ref<T, GlobalReferenceAllocator>;
}} // namespace facebook::jni

View File

@ -15,49 +15,87 @@
namespace facebook {
namespace jni {
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local(T ref) noexcept {
return local_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), global_ref<T>> adopt_global(T ref) noexcept {
return global_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), weak_ref<T>> adopt_weak_global(T ref) noexcept {
return weak_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {
return alias_ref<T>(ref);
}
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {
return ref;
}
template<typename T>
inline T getPlainJniReference(alias_ref<T> ref) {
inline JniType<T> getPlainJniReference(alias_ref<T> ref) {
return ref.get();
}
template<typename T, typename A>
inline T getPlainJniReference(const base_owned_ref<T, A>& ref) {
return ref.getPlainJniReference();
inline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {
return ref.get();
}
namespace internal {
namespace detail {
template <typename Repr>
struct ReprAccess {
using javaobject = JniType<Repr>;
static void set(Repr& repr, javaobject obj) noexcept {
repr.JObjectBase::set(obj);
}
static javaobject get(const Repr& repr) {
return static_cast<javaobject>(repr.JObject::get());
}
};
namespace {
template <typename Repr>
void StaticAssertValidRepr() noexcept {
static_assert(std::is_base_of<JObject, Repr>::value,
"A smart ref representation must be derived from JObject.");
static_assert(IsPlainJniReference<JniType<Repr>>(), "T must be a JNI reference");
static_assert(sizeof(Repr) == sizeof(JObjectBase), "");
static_assert(alignof(Repr) == alignof(JObjectBase), "");
}
}
template <typename Repr>
ReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {
StaticAssertValidRepr<Repr>();
set(obj);
}
template <typename Repr>
void ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {
new (&storage_) Repr;
ReprAccess<Repr>::set(get(), obj);
}
template <typename Repr>
Repr& ReprStorage<Repr>::get() noexcept {
return *reinterpret_cast<Repr*>(&storage_);
}
template <typename Repr>
const Repr& ReprStorage<Repr>::get() const noexcept {
return *reinterpret_cast<const Repr*>(&storage_);
}
template <typename Repr>
JniType<Repr> ReprStorage<Repr>::jobj() const noexcept {
ReprAccess<Repr>::get(get());
return ReprAccess<Repr>::get(get());
}
template <typename Repr>
void ReprStorage<Repr>::swap(ReprStorage& other) noexcept {
StaticAssertValidRepr<Repr>();
using std::swap;
swap(get(), other.get());
}
inline void JObjectBase::set(jobject reference) noexcept {
this_ = reference;
}
inline jobject JObjectBase::get() const noexcept {
return this_;
}
template<typename T, typename Alloc>
enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T& reference) {
@ -77,24 +115,53 @@ enable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T&
return static_cast<plain_jni_reference_t<T>>(ref);
}
} // namespace detail
template<typename T>
inline local_ref<T> adopt_local(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return local_ref<T>{ref};
}
template<typename T>
inline global_ref<T> adopt_global(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return global_ref<T>{ref};
}
template<typename T>
inline weak_ref<T> adopt_weak_global(T ref) noexcept {
static_assert(IsPlainJniReference<T>(), "T must be a plain jni reference");
return weak_ref<T>{ref};
}
template<typename T>
inline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {
return alias_ref<T>(ref);
}
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
template<typename T>
enable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>
make_local(const T& ref) {
return adopt_local(internal::make_ref<T, LocalReferenceAllocator>(ref));
return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));
}
template<typename T>
enable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>
make_global(const T& ref) {
return adopt_global(internal::make_ref<T, GlobalReferenceAllocator>(ref));
return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));
}
template<typename T>
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T& ref) {
return adopt_weak_global(internal::make_ref<T, WeakGlobalReferenceAllocator>(ref));
return adopt_weak_global(detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));
}
template<typename T1, typename T2>
@ -113,69 +180,67 @@ operator!=(const T1& a, const T2& b) {
// base_owned_ref ///////////////////////////////////////////////////////////////////////
template<typename T, typename Alloc>
inline constexpr base_owned_ref<T, Alloc>::base_owned_ref() noexcept
: object_{nullptr}
inline base_owned_ref<T, Alloc>::base_owned_ref() noexcept
: base_owned_ref(nullptr)
{}
template<typename T, typename Alloc>
inline constexpr base_owned_ref<T, Alloc>::base_owned_ref(
std::nullptr_t t) noexcept
: object_{nullptr}
inline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept
: base_owned_ref(static_cast<javaobject>(nullptr))
{}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(
const base_owned_ref& other)
: object_{Alloc{}.newReference(other.getPlainJniReference())}
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
{}
template<typename T, typename Alloc>
template<typename U>
inline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)
: object_{Alloc{}.newReference(other.getPlainJniReference())}
: storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}
{}
template<typename T, typename Alloc>
inline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(
T reference) noexcept
: object_{reference} {
javaobject reference) noexcept
: storage_(reference) {
assert(Alloc{}.verifyReference(reference));
internal::dbglog("New wrapped ref=%p this=%p", getPlainJniReference(), this);
internal::dbglog("New wrapped ref=%p this=%p", get(), this);
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::base_owned_ref(
base_owned_ref<T, Alloc>&& other) noexcept
: object_{other.object_} {
internal::dbglog("New move from ref=%p other=%p", other.getPlainJniReference(), &other);
internal::dbglog("New move to ref=%p this=%p", getPlainJniReference(), this);
// JObjectWrapper is a simple type and does not support move semantics so we explicitly
: storage_(other.get()) {
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
internal::dbglog("New move to ref=%p this=%p", get(), this);
// JObject is a simple type and does not support move semantics so we explicitly
// clear other
other.object_.set(nullptr);
other.set(nullptr);
}
template<typename T, typename Alloc>
template<typename U>
base_owned_ref<T, Alloc>::base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept
: object_{other.object_} {
internal::dbglog("New move from ref=%p other=%p", other.getPlainJniReference(), &other);
internal::dbglog("New move to ref=%p this=%p", getPlainJniReference(), this);
// JObjectWrapper is a simple type and does not support move semantics so we explicitly
: storage_(other.get()) {
internal::dbglog("New move from ref=%p other=%p", other.get(), &other);
internal::dbglog("New move to ref=%p this=%p", get(), this);
// JObject is a simple type and does not support move semantics so we explicitly
// clear other
other.object_.set(nullptr);
other.set(nullptr);
}
template<typename T, typename Alloc>
inline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {
reset();
internal::dbglog("Ref destruct ref=%p this=%p", getPlainJniReference(), this);
internal::dbglog("Ref destruct ref=%p this=%p", get(), this);
}
template<typename T, typename Alloc>
inline T base_owned_ref<T, Alloc>::release() noexcept {
auto value = getPlainJniReference();
inline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {
auto value = get();
internal::dbglog("Ref release ref=%p this=%p", value, this);
object_.set(nullptr);
set(nullptr);
return value;
}
@ -185,17 +250,22 @@ inline void base_owned_ref<T,Alloc>::reset() noexcept {
}
template<typename T, typename Alloc>
inline void base_owned_ref<T,Alloc>::reset(T reference) noexcept {
if (getPlainJniReference()) {
inline void base_owned_ref<T,Alloc>::reset(javaobject reference) noexcept {
if (get()) {
assert(Alloc{}.verifyReference(reference));
Alloc{}.deleteReference(getPlainJniReference());
Alloc{}.deleteReference(get());
}
object_.set(reference);
set(reference);
}
template<typename T, typename Alloc>
inline T base_owned_ref<T, Alloc>::getPlainJniReference() const noexcept {
return static_cast<T>(object_.get());
inline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {
return storage_.jobj();
}
template<typename T, typename Alloc>
inline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {
storage_.set(ref);
}
@ -213,19 +283,21 @@ template<typename T>
inline weak_ref<T>& weak_ref<T>::operator=(
weak_ref<T>&& other) noexcept {
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
getPlainJniReference(), this, other.getPlainJniReference(), &other);
get(), this, other.get(), &other);
reset(other.release());
return *this;
}
template<typename T>
local_ref<T> weak_ref<T>::lockLocal() {
return adopt_local(static_cast<T>(LocalReferenceAllocator{}.newReference(getPlainJniReference())));
local_ref<T> weak_ref<T>::lockLocal() const {
return adopt_local(
static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));
}
template<typename T>
global_ref<T> weak_ref<T>::lockGlobal() {
return adopt_global(static_cast<T>(GlobalReferenceAllocator{}.newReference(getPlainJniReference())));
global_ref<T> weak_ref<T>::lockGlobal() const {
return adopt_global(
static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));
}
template<typename T>
@ -233,9 +305,8 @@ inline void swap(
weak_ref<T>& a,
weak_ref<T>& b) noexcept {
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
a.getPlainJniReference(), &a, b.getPlainJniReference(), &b);
using std::swap;
swap(a.object_, b.object_);
a.get(), &a, b.get(), &b);
a.storage_.swap(b.storage_);
}
@ -253,7 +324,7 @@ template<typename T, typename Alloc>
inline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(
basic_strong_ref<T, Alloc>&& other) noexcept {
internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p",
getPlainJniReference(), this, other.getPlainJniReference(), &other);
get(), this, other.get(), &other);
reset(other.release());
return *this;
}
@ -269,28 +340,23 @@ inline basic_strong_ref<T, Alloc>::operator bool() const noexcept {
}
template<typename T, typename Alloc>
inline T basic_strong_ref<T, Alloc>::get() const noexcept {
return getPlainJniReference();
inline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {
return &storage_.get();
}
template<typename T, typename Alloc>
inline JObjectWrapper<T>* basic_strong_ref<T, Alloc>::operator->() noexcept {
return &object_;
inline auto basic_strong_ref<T, Alloc>::operator->() const noexcept -> const Repr* {
return &storage_.get();
}
template<typename T, typename Alloc>
inline const JObjectWrapper<T>* basic_strong_ref<T, Alloc>::operator->() const noexcept {
return &object_;
inline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {
return storage_.get();
}
template<typename T, typename Alloc>
inline JObjectWrapper<T>& basic_strong_ref<T, Alloc>::operator*() noexcept {
return object_;
}
template<typename T, typename Alloc>
inline const JObjectWrapper<T>& basic_strong_ref<T, Alloc>::operator*() const noexcept {
return object_;
inline auto basic_strong_ref<T, Alloc>::operator*() const noexcept -> const Repr& {
return storage_.get();
}
template<typename T, typename Alloc>
@ -298,33 +364,32 @@ inline void swap(
basic_strong_ref<T, Alloc>& a,
basic_strong_ref<T, Alloc>& b) noexcept {
internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p",
a.getPlainJniReference(), &a, b.getPlainJniReference(), &b);
a.get(), &a, b.get(), &b);
using std::swap;
swap(a.object_, b.object_);
a.storage_.swap(b.storage_);
}
// alias_ref //////////////////////////////////////////////////////////////////////////////
template<typename T>
inline constexpr alias_ref<T>::alias_ref() noexcept
: object_{nullptr}
inline alias_ref<T>::alias_ref() noexcept
: storage_{nullptr}
{}
template<typename T>
inline constexpr alias_ref<T>::alias_ref(std::nullptr_t) noexcept
: object_{nullptr}
inline alias_ref<T>::alias_ref(std::nullptr_t) noexcept
: storage_{nullptr}
{}
template<typename T>
inline alias_ref<T>::alias_ref(const alias_ref& other) noexcept
: object_{other.object_}
: storage_{other.get()}
{}
template<typename T>
inline alias_ref<T>::alias_ref(T ref) noexcept
: object_{ref} {
inline alias_ref<T>::alias_ref(javaobject ref) noexcept
: storage_(ref) {
assert(
LocalReferenceAllocator{}.verifyReference(ref) ||
GlobalReferenceAllocator{}.verifyReference(ref));
@ -333,13 +398,13 @@ inline alias_ref<T>::alias_ref(T ref) noexcept
template<typename T>
template<typename TOther, typename /* for SFINAE */>
inline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept
: object_{other.get()}
: storage_{other.get()}
{}
template<typename T>
template<typename TOther, typename AOther, typename /* for SFINAE */>
inline alias_ref<T>::alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept
: object_{other.get()}
: storage_{other.get()}
{}
template<typename T>
@ -354,34 +419,90 @@ inline alias_ref<T>::operator bool() const noexcept {
}
template<typename T>
inline T facebook::jni::alias_ref<T>::get() const noexcept {
return static_cast<T>(object_.get());
inline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {
return storage_.jobj();
}
template<typename T>
inline JObjectWrapper<T>* alias_ref<T>::operator->() noexcept {
return &object_;
inline auto alias_ref<T>::operator->() noexcept -> Repr* {
return &(**this);
}
template<typename T>
inline const JObjectWrapper<T>* alias_ref<T>::operator->() const noexcept {
return &object_;
inline auto alias_ref<T>::operator->() const noexcept -> const Repr* {
return &(**this);
}
template<typename T>
inline JObjectWrapper<T>& alias_ref<T>::operator*() noexcept {
return object_;
inline auto alias_ref<T>::operator*() noexcept -> Repr& {
return storage_.get();
}
template<typename T>
inline const JObjectWrapper<T>& alias_ref<T>::operator*() const noexcept {
return object_;
inline auto alias_ref<T>::operator*() const noexcept -> const Repr& {
return storage_.get();
}
template<typename T>
inline void alias_ref<T>::set(javaobject ref) noexcept {
storage_.set(ref);
}
template<typename T>
inline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {
using std::swap;
swap(a.object_, b.object_);
a.storage_.swap(b.storage_);
}
// Could reduce code duplication by using a pointer-to-function
// template argument. I'm not sure whether that would make the code
// more maintainable (DRY), or less (too clever/confusing.).
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
static_ref_cast(const local_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return make_local(p);
}
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
static_ref_cast(const global_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return make_global(p);
}
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
static_ref_cast(const alias_ref<U>& ref) noexcept
{
T p = static_cast<T>(ref.get());
return wrap_alias(p);
}
template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>
{
if (! ref) {
return decltype(static_ref_cast<T>(ref))();
}
std::string target_class_name{jtype_traits<T>::base_name()};
// If not found, will throw an exception.
alias_ref<jclass> target_class = findClassStatic(target_class_name.c_str());
local_ref<jclass> source_class = ref->getClass();
if ( ! source_class->isAssignableFrom(target_class)) {
throwNewJavaException("java/lang/ClassCastException",
"Tried to cast from %s to %s.",
source_class->toString().c_str(),
target_class_name.c_str());
}
return static_ref_cast<T>(ref);
}
}}

View File

@ -79,54 +79,132 @@
#include "ReferenceAllocators.h"
#include "TypeTraits.h"
#include "References-forward.h"
namespace facebook {
namespace jni {
/**
* The JObjectWrapper is specialized to provide functionality for various Java classes, some
* specializations are provided, and it is easy to add your own. See example
* @sample WrapperSample.cpp
*/
template<typename T, typename Enable = void>
class JObjectWrapper;
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>;
/// Convenience function to wrap an existing local reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local(T ref) noexcept;
local_ref<T> adopt_local(T ref) noexcept;
/// Convenience function to wrap an existing global reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>> adopt_global(T ref) noexcept;
global_ref<T> adopt_global(T ref) noexcept;
/// Convenience function to wrap an existing weak reference
template<typename T>
enable_if_t<IsPlainJniReference<T>(), weak_ref<T>> adopt_weak_global(T ref) noexcept;
weak_ref<T> adopt_weak_global(T ref) noexcept;
/// Swaps two owning references of the same type
template<typename T>
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
/// Swaps two owning references of the same type
template<typename T, typename Alloc>
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
/**
* Retrieve the plain reference from a plain reference.
*/
template<typename T>
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
/**
* Retrieve the plain reference from an alias reference.
*/
template<typename T>
JniType<T> getPlainJniReference(alias_ref<T> ref);
/**
* Retrieve the plain JNI reference from any reference owned reference.
*/
template<typename T, typename Alloc>
JniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
class JObject;
class JClass;
namespace detail {
template <typename T, typename Enable = void>
struct HasJniRefRepr : std::false_type {};
template <typename T>
struct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {
using type = typename T::JniRefRepr;
};
template <typename T>
struct RefReprType<T*> {
using type = typename std::conditional<HasJniRefRepr<T>::value, typename HasJniRefRepr<T>::type, JObjectWrapper<T*>>::type;
static_assert(std::is_base_of<JObject, type>::value,
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct RefReprType<T, typename std::enable_if<std::is_base_of<JObject, T>::value, void>::type> {
using type = T;
static_assert(std::is_base_of<JObject, type>::value,
"Repr type missing JObject base.");
static_assert(std::is_same<type, typename RefReprType<type>::type>::value,
"RefReprType<T> not idempotent");
};
template <typename T>
struct JavaObjectType {
using type = typename RefReprType<T>::type::javaobject;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct JavaObjectType<JObjectWrapper<T>> {
using type = T;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename T>
struct JavaObjectType<T*> {
using type = T*;
static_assert(IsPlainJniReference<type>(),
"JavaObjectType<T> not a plain jni reference");
static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,
"JavaObjectType<T> not idempotent");
};
template <typename Repr>
struct ReprStorage {
explicit ReprStorage(JniType<Repr> obj) noexcept;
void set(JniType<Repr> obj) noexcept;
Repr& get() noexcept;
const Repr& get() const noexcept;
JniType<Repr> jobj() const noexcept;
void swap(ReprStorage& other) noexcept;
private:
ReprStorage() = delete;
ReprStorage(const ReprStorage&) = delete;
ReprStorage(ReprStorage&&) = delete;
ReprStorage& operator=(const ReprStorage&) = delete;
ReprStorage& operator=(ReprStorage&&) = delete;
using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;
Storage storage_;
};
} // namespace detail
/**
* Create a new local reference from an existing reference
*
@ -160,33 +238,6 @@ template<typename T>
enable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>
make_weak(const T& r);
/// Swaps two owning references of the same type
template<typename T>
void swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;
/// Swaps two owning references of the same type
template<typename T, typename Alloc>
void swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;
/**
* Retrieve the plain reference from a plain reference.
*/
template<typename T>
enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);
/**
* Retrieve the plain reference from an alias reference.
*/
template<typename T>
T getPlainJniReference(alias_ref<T> ref);
/**
* Retrieve the plain JNI reference from any reference owned reference.
*/
template<typename T, typename Alloc>
T getPlainJniReference(const base_owned_ref<T, Alloc>& ref);
/**
* Compare two references to see if they refer to the same object
*/
@ -201,19 +252,16 @@ template<typename T1, typename T2>
enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>
operator!=(const T1& a, const T2& b);
template<typename T, typename Alloc>
class base_owned_ref {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
using javaobject = JniType<T>;
/**
* Release the ownership and set the reference to null. Thus no deleter is invoked.
* @return Returns the reference
*/
T release() noexcept;
javaobject release() noexcept;
/**
* Reset the reference to refer to nullptr.
@ -221,20 +269,23 @@ class base_owned_ref {
void reset() noexcept;
protected:
using Repr = ReprType<T>;
detail::ReprStorage<Repr> storage_;
JObjectWrapper<T> object_;
javaobject get() const noexcept;
void set(javaobject ref) noexcept;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit base_owned_ref(T reference) noexcept;
explicit base_owned_ref(javaobject reference) noexcept;
/// Create a null reference
constexpr base_owned_ref() noexcept;
base_owned_ref() noexcept;
/// Create a null reference
constexpr explicit base_owned_ref(std::nullptr_t) noexcept;
explicit base_owned_ref(std::nullptr_t) noexcept;
/// Copy constructor (note creates a new reference)
base_owned_ref(const base_owned_ref& other);
@ -256,13 +307,9 @@ class base_owned_ref {
/// Assignment by moving a reference thus not creating a new reference
base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;
void reset(javaobject reference) noexcept;
T getPlainJniReference() const noexcept;
void reset(T reference) noexcept;
friend T jni::getPlainJniReference<>(const base_owned_ref& ref);
friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref);
template<typename U, typename UAlloc>
friend class base_owned_ref;
@ -278,29 +325,31 @@ class base_owned_ref {
*/
template<typename T>
class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
public:
using javaobject = JniType<T>;
using PlainJniType = T;
using Allocator = WeakGlobalReferenceAllocator;
// This inherits non-default, non-copy, non-move ctors.
using base_owned_ref<T, Allocator>::base_owned_ref;
/// Create a null reference
constexpr weak_ref() noexcept
weak_ref() noexcept
: base_owned_ref<T, Allocator>{} {}
/// Create a null reference
constexpr explicit weak_ref(std::nullptr_t) noexcept
explicit weak_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Allocator>{nullptr} {}
/// Copy constructor (note creates a new reference)
weak_ref(const weak_ref& other)
: base_owned_ref<T, Allocator>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
weak_ref(const weak_ref<U>& other)
: base_owned_ref<T, Allocator>{other} {}
/// Transfers ownership of an underlying reference from one unique reference to another
weak_ref(weak_ref&& other) noexcept
: base_owned_ref<T, Allocator>{std::move(other)} {}
@ -312,28 +361,26 @@ class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
/// Assignment by moving a reference thus not creating a new reference
weak_ref& operator=(weak_ref&& rhs) noexcept;
// Creates an owned local reference to the referred object or to null if the object is reclaimed
local_ref<T> lockLocal();
local_ref<T> lockLocal() const;
// Creates an owned global reference to the referred object or to null if the object is reclaimed
global_ref<T> lockGlobal();
global_ref<T> lockGlobal() const;
private:
using base_owned_ref<T, Allocator>::getPlainJniReference;
// get/release/reset on weak_ref are not exposed to users.
using base_owned_ref<T, Allocator>::get;
using base_owned_ref<T, Allocator>::release;
using base_owned_ref<T, Allocator>::reset;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit weak_ref(T reference) noexcept
explicit weak_ref(javaobject reference) noexcept
: base_owned_ref<T, Allocator>{reference} {}
template<typename T2> friend class weak_ref;
friend weak_ref<enable_if_t<IsPlainJniReference<T>(), T>>
adopt_weak_global<T>(T ref) noexcept;
friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;
friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;
};
@ -344,12 +391,10 @@ class weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {
*/
template<typename T, typename Alloc>
class basic_strong_ref : public base_owned_ref<T, Alloc> {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
using typename base_owned_ref<T, Alloc>::Repr;
public:
using javaobject = JniType<T>;
using PlainJniType = T;
using Allocator = Alloc;
// This inherits non-default, non-copy, non-move ctors.
@ -358,17 +403,22 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
using base_owned_ref<T, Alloc>::reset;
/// Create a null reference
constexpr basic_strong_ref() noexcept
basic_strong_ref() noexcept
: base_owned_ref<T, Alloc>{} {}
/// Create a null reference
constexpr explicit basic_strong_ref(std::nullptr_t) noexcept
explicit basic_strong_ref(std::nullptr_t) noexcept
: base_owned_ref<T, Alloc>{nullptr} {}
/// Copy constructor (note creates a new reference)
basic_strong_ref(const basic_strong_ref& other)
: base_owned_ref<T, Alloc>{other} {}
// This needs to be explicit to change its visibility.
template<typename U>
basic_strong_ref(const basic_strong_ref<U, Alloc>& other)
: base_owned_ref<T, Alloc>{other} {}
/// Transfers ownership of an underlying reference from one unique reference to another
basic_strong_ref(basic_strong_ref&& other) noexcept
: base_owned_ref<T, Alloc>{std::move(other)} {}
@ -379,6 +429,8 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
/// Assignment by moving a reference thus not creating a new reference
basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;
/// Get the plain JNI reference
using base_owned_ref<T, Allocator>::get;
/// Release the ownership of the reference and return the wrapped reference in an alias
alias_ref<T> releaseAlias() noexcept;
@ -386,37 +438,33 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
/// Checks if the reference points to a non-null object
explicit operator bool() const noexcept;
/// Get the plain JNI reference
T get() const noexcept;
/// Access the functionality provided by the object wrappers
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
JObjectWrapper<T>* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const JObjectWrapper<T>* operator->() const noexcept;
const Repr* operator->() const noexcept;
/// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)
JObjectWrapper<T>& operator*() noexcept;
Repr& operator*() noexcept;
/// Provide a const reference to the underlying wrapper (be sure that it is non-null
/// before invoking)
const JObjectWrapper<T>& operator*() const noexcept;
const Repr& operator*() const noexcept;
private:
using base_owned_ref<T, Alloc>::object_;
using base_owned_ref<T, Alloc>::getPlainJniReference;
using base_owned_ref<T, Alloc>::storage_;
/*
* Wrap an existing reference and transfers its ownership to the newly created unique reference.
* NB! Does not create a new reference
*/
explicit basic_strong_ref(T reference) noexcept
explicit basic_strong_ref(javaobject reference) noexcept
: base_owned_ref<T, Alloc>{reference} {}
friend enable_if_t<IsPlainJniReference<T>(), local_ref<T>> adopt_local<T>(T ref) noexcept;
friend enable_if_t<IsPlainJniReference<T>(), global_ref<T>> adopt_global<T>(T ref) noexcept;
friend local_ref<T> adopt_local<T>(T ref) noexcept;
friend global_ref<T> adopt_global<T>(T ref) noexcept;
friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;
};
@ -424,7 +472,7 @@ class basic_strong_ref : public base_owned_ref<T, Alloc> {
template<typename T>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;
/// Swaps to alias referencec of the same type
/// Swaps to alias reference of the same type
template<typename T>
void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
@ -437,35 +485,40 @@ void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;
*/
template<typename T>
class alias_ref {
static_assert(IsPlainJniReference<T>(), "T must be a JNI reference");
using Repr = ReprType<T>;
public:
using PlainJniType = T;
using javaobject = JniType<T>;
/// Create a null reference
constexpr alias_ref() noexcept;
alias_ref() noexcept;
/// Create a null reference
constexpr alias_ref(std::nullptr_t) noexcept;
alias_ref(std::nullptr_t) noexcept;
/// Copy constructor
alias_ref(const alias_ref& other) noexcept;
/// Wrap an existing plain JNI reference
alias_ref(T ref) noexcept;
/* implicit */ alias_ref(javaobject ref) noexcept;
/// Wrap an existing smart reference of any type convertible to T
template<typename TOther, typename = enable_if_t<IsConvertible<TOther, T>(), T>>
template<
typename TOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(alias_ref<TOther> other) noexcept;
/// Wrap an existing alias reference of a type convertible to T
template<typename TOther, typename AOther, typename = enable_if_t<IsConvertible<TOther, T>(), T>>
template<
typename TOther,
typename AOther,
typename = enable_if_t<
IsConvertible<JniType<TOther>, javaobject>(), T>
>
alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;
/// Assignment operator
alias_ref& operator=(alias_ref other) noexcept;
@ -473,22 +526,24 @@ class alias_ref {
explicit operator bool() const noexcept;
/// Converts back to a plain JNI reference
T get() const noexcept;
javaobject get() const noexcept;
/// Access the functionality provided by the object wrappers
JObjectWrapper<T>* operator->() noexcept;
Repr* operator->() noexcept;
/// Access the functionality provided by the object wrappers
const JObjectWrapper<T>* operator->() const noexcept;
const Repr* operator->() const noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
JObjectWrapper<T>& operator*() noexcept;
Repr& operator*() noexcept;
/// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)
const JObjectWrapper<T>& operator*() const noexcept;
const Repr& operator*() const noexcept;
private:
JObjectWrapper<T> object_;
void set(javaobject ref) noexcept;
detail::ReprStorage<Repr> storage_;
friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;
};
@ -510,6 +565,22 @@ private:
bool hasFrame_;
};
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), local_ref<T>>
static_ref_cast(const local_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), global_ref<T>>
static_ref_cast(const global_ref<U>& ref) noexcept;
template<typename T, typename U>
enable_if_t<IsPlainJniReference<T>(), alias_ref<T>>
static_ref_cast(const alias_ref<U>& ref) noexcept;
template<typename T, typename RefType>
auto dynamic_ref_cast(const RefType& ref) ->
enable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> ;
}}
#include "References-inl.h"

View File

@ -17,27 +17,24 @@ namespace jni {
namespace detail {
// 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 facebook::jni::cthis(wrap_alias(t));
}
// There is no automatic return conversion for objects.
};
#ifdef __i386__
// X86 ABI forces 16 byte stack allignment on calls. Unfortunately
// sometimes Dalvik chooses not to obey the ABI:
// - https://code.google.com/p/android/issues/detail?id=61012
// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/
// Therefore, we tell the compiler to re-align the stack on entry
// to our JNI functions.
#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))
#else
#define JNI_ENTRY_POINT
#endif
// registration wrapper for legacy JNI-style functions
template<typename F, F func, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args... args)) {
struct funcWrapper {
static void call(JNIEnv* env, jobject obj, Args... args) {
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, Args... args) {
// Note that if func was declared noexcept, then both gcc and clang are smart
// enough to elide the try/catch.
try {
@ -55,9 +52,9 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args...
template<typename F, F func, typename C, typename R, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {
struct funcWrapper {
static R call(JNIEnv* env, jobject obj, Args... args) {
JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {
try {
return (*func)(env, static_cast<C>(obj), args...);
return (*func)(env, static_cast<JniType<C>>(obj), args...);
} catch (...) {
translatePendingCppExceptionToJavaException();
return R{};
@ -74,10 +71,10 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... arg
template<typename F, F func, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref<C>, Args... args)) {
struct funcWrapper {
static void call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
(*func)(static_cast<C>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...);
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...);
} catch (...) {
translatePendingCppExceptionToJavaException();
}
@ -93,11 +90,11 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... a
struct funcWrapper {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
static jniRet call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
JNI_ENTRY_POINT static jniRet call(JNIEnv*, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
return Convert<typename std::decay<R>::type>::toJniRet(
(*func)(static_cast<C>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
(*func)(static_cast<JniType<C>>(obj), Convert<typename std::decay<Args>::type>::fromJni(args)...));
} catch (...) {
translatePendingCppExceptionToJavaException();
return jniRet{};
@ -114,8 +111,8 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... a
template<typename M, M method, typename C, typename... Args>
inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) {
struct funcWrapper {
static void call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
try {
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
@ -143,8 +140,8 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)
struct funcWrapper {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
static jniRet call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj,
typename Convert<typename std::decay<Args>::type>::jniType... args) {
try {
try {
auto aref = wrap_alias(static_cast<typename C::jhybridobject>(obj));
@ -176,16 +173,12 @@ inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) {
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (*)(alias_ref<C>, Args... args)) {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
return jmethod_traits<jniRet(typename Convert<typename std::decay<Args>::type>::jniType...)>
::descriptor();
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
template<typename R, typename C, typename... Args>
inline std::string makeDescriptor(R (C::*)(Args... args)) {
typedef typename Convert<typename std::decay<R>::type>::jniType jniRet;
return jmethod_traits<jniRet(typename Convert<typename std::decay<Args>::type>::jniType...)>
::descriptor();
return jmethod_traits_from_cxx<R(Args...)>::descriptor();
}
}

View File

@ -11,6 +11,8 @@
#include <type_traits>
#include "References-forward.h"
namespace facebook {
namespace jni {
@ -69,6 +71,25 @@ constexpr bool IsJniPrimitive() {
return is_jni_primitive<T>::value;
}
/// Metafunction to determine whether a type is a JNI array of primitives or not
template <typename T>
struct is_jni_primitive_array :
std::integral_constant<bool,
std::is_same<jbooleanArray, T>::value ||
std::is_same<jbyteArray, T>::value ||
std::is_same<jcharArray, T>::value ||
std::is_same<jshortArray, T>::value ||
std::is_same<jintArray, T>::value ||
std::is_same<jlongArray, T>::value ||
std::is_same<jfloatArray, T>::value ||
std::is_same<jdoubleArray, T>::value> {};
/// Helper to simplify use of is_jni_primitive_array
template <typename T>
constexpr bool IsJniPrimitiveArray() {
return is_jni_primitive_array<T>::value;
}
/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type
template<typename T>
struct is_jni_scalar :
@ -95,15 +116,6 @@ constexpr bool IsJniType() {
return is_jni_type<T>::value;
}
template<typename T>
class weak_global_ref;
template<typename T, typename Alloc>
class basic_strong_ref;
template<typename T>
class alias_ref;
template<typename T>
struct is_non_weak_reference :
std::integral_constant<bool,
@ -120,7 +132,7 @@ template<typename T>
struct is_any_reference :
std::integral_constant<bool,
IsPlainJniReference<T>() ||
IsInstantiationOf<weak_global_ref, T>() ||
IsInstantiationOf<weak_ref, T>() ||
IsInstantiationOf<basic_strong_ref, T>() ||
IsInstantiationOf<alias_ref, T>()> {};
@ -131,19 +143,18 @@ constexpr bool IsAnyReference() {
template<typename T>
struct reference_traits {
static_assert(IsPlainJniReference<T>(), "Need a plain JNI reference");
using plain_jni_reference_t = T;
using plain_jni_reference_t = JniType<T>;
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
};
template<template <typename...> class R, typename T, typename... A>
struct reference_traits<R<T, A...>> {
static_assert(IsAnyReference<T>(), "Need an fbjni reference");
using plain_jni_reference_t = T;
using plain_jni_reference_t = JniType<T>;
static_assert(IsPlainJniReference<plain_jni_reference_t>(), "Need a plain JNI reference");
};
template<typename T>
using plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;
}
}
} // namespace jni
} // namespace facebook