diff --git a/ReactAndroid/src/main/java/com/facebook/jni/CppException.java b/ReactAndroid/src/main/java/com/facebook/jni/CppException.java index 3006da53a..a0c845dd6 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/CppException.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/CppException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * diff --git a/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java b/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java index 18f754bf4..13090a18c 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/CppSystemErrorException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * diff --git a/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java b/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java index fa6e971f6..45e9bfe0c 100644 --- a/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java +++ b/ReactAndroid/src/main/java/com/facebook/jni/UnknownCppException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * diff --git a/ReactAndroid/src/main/jni/first-party/jni/Android.mk b/ReactAndroid/src/main/jni/first-party/jni/Android.mk index e77eaf1a0..1ee4d0898 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/Android.mk +++ b/ReactAndroid/src/main/jni/first-party/jni/Android.mk @@ -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 diff --git a/ReactAndroid/src/main/jni/first-party/jni/BUCK b/ReactAndroid/src/main/jni/first-party/jni/BUCK index 56d891234..36574b0b3 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/BUCK +++ b/ReactAndroid/src/main/jni/first-party/jni/BUCK @@ -10,6 +10,7 @@ cxx_library( 'LocalString.cpp', 'OnLoad.cpp', 'WeakReference.cpp', + 'fbjni/ByteBuffer.cpp', 'fbjni/Exceptions.cpp', 'fbjni/Hybrid.cpp', 'fbjni/References.cpp', diff --git a/ReactAndroid/src/main/jni/first-party/jni/Countable.cpp b/ReactAndroid/src/main/jni/first-party/jni/Countable.cpp index 6ff7efe90..3ed52f782 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/Countable.cpp +++ b/ReactAndroid/src/main/jni/first-party/jni/Countable.cpp @@ -41,7 +41,7 @@ void setCountableForJava(JNIEnv* env, jobject obj, RefPtr&& 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) { diff --git a/ReactAndroid/src/main/jni/first-party/jni/LocalReference.h b/ReactAndroid/src/main/jni/first-party/jni/LocalReference.h index b5d9c54a1..09aa641fd 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/LocalReference.h +++ b/ReactAndroid/src/main/jni/first-party/jni/LocalReference.h @@ -27,7 +27,7 @@ struct LocalReferenceDeleter { if (localReference != nullptr) { Environment::current()->DeleteLocalRef(localReference); } - } + } }; template diff --git a/ReactAndroid/src/main/jni/first-party/jni/LocalString.cpp b/ReactAndroid/src/main/jni/first-party/jni/LocalString.cpp index 5fc8d0c84..a26807267 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/LocalString.cpp +++ b/ReactAndroid/src/main/jni/first-party/jni/LocalString.cpp @@ -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(modified), length); - env->ReleaseStringUTFChars(str, modified); - return s; + auto utf16String = JStringUtf16Extractor(env, str); + auto length = env->GetStringLength(str); + return detail::utf16toUTF8(utf16String, length); } } } diff --git a/ReactAndroid/src/main/jni/first-party/jni/LocalString.h b/ReactAndroid/src/main/jni/first-party/jni/LocalString.h index a85efa48a..2290af10d 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/LocalString.h +++ b/ReactAndroid/src/main/jni/first-party/jni/LocalString.h @@ -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); diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni.cpp b/ReactAndroid/src/main/jni/first-party/jni/fbjni.cpp index a083371b2..22d4dc2a4 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni.cpp +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni.cpp @@ -16,36 +16,33 @@ namespace facebook { namespace jni { -template -static void log(Args... args) { -// TODO (7623232) Migrate to glog -#ifdef __ANDROID__ - facebook::alog::loge("fbjni", args...); -#endif -} +jint initialize(JavaVM* vm, std::function&& 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 findClassStatic(const char* name) { +alias_ref 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 findClassStatic(const char* name) { return wrap_alias(leaking_ref); } -local_ref findClassLocal(const char* name) { +local_ref 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 findClassLocal(const char* name) { // jstring ///////////////////////////////////////////////////////////////////////////////////////// -std::string JObjectWrapper::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(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 make_jstring(const char* utf8) { +local_ref make_jstring(const char* utf8) { if (!utf8) { return {}; } @@ -111,96 +106,76 @@ local_ref 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::get() { \ - FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \ - const auto env = internal::getEnv(); \ - elements_ = env->Get ## NAME ## ArrayElements( \ - static_cast(array_.get()), &isCopy_); \ - size_ = array_->size(); \ - return elements_; \ -} \ -template<> \ -void PinnedPrimitiveArray::release() { \ - FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); \ - const auto env = internal::getEnv(); \ - env->Release ## NAME ## ArrayElements( \ - static_cast(array_.get()), elements_, 0); \ - elements_ = nullptr; \ - size_ = 0; \ -} +#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME) \ + \ +template<> \ +TYPE* JPrimitiveArray::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::releaseElements( \ + TYPE* elements, jint mode) { \ + auto env = internal::getEnv(); \ + env->Release ## NAME ## ArrayElements(self(), elements, mode); \ + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ +} \ + \ +template<> \ +void JPrimitiveArray::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::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 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 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 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 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 JArray ## NAME::getRegion(jsize start, jsize length) { \ - auto buf = std::unique_ptr{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 JArray ## NAME::pin() { \ - return PinnedPrimitiveArray{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; } + } }} - diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni.h index 7ea816b60..7728153cd 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni.h @@ -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" diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.cpp b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.cpp new file mode 100644 index 000000000..a41f9106e --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.cpp @@ -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 + +namespace facebook { +namespace jni { + +namespace { +local_ref createEmpty() { + static auto cls = JByteBuffer::javaClassStatic(); + static auto meth = cls->getStaticMethod("allocateDirect"); + return meth(cls, 0); +} +} + +local_ref 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(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(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); +} + +bool JByteBuffer::isDirect() { + static auto meth = javaClassStatic()->getMethod("isDirect"); + return meth(self()); +} + +}} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.h new file mode 100644 index 000000000..b10573752 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ByteBuffer.h @@ -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 { + public: + static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;"; + + static local_ref wrapBytes(uint8_t* data, size_t size); + + bool isDirect(); + + uint8_t* getDirectBytes(); + size_t getDirectSize(); +}; + +}} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Common.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Common.h index 479f43713..9fa80c7a4 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Common.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Common.h @@ -14,10 +14,19 @@ #pragma once +#include + #include #include -#include + +#ifdef FBJNI_DEBUG_REFS +# ifdef __ANDROID__ +# include +# else +# include +# 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&&) 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 -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 -inline void dbglog(Args...) noexcept {} +inline void dbglog(const char*, Args...) { +} + #endif }}} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Context.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Context.h new file mode 100644 index 000000000..136ca82f1 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Context.h @@ -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 { + public: + static constexpr const char* kJavaDescriptor = "Landroid/content/Context;"; + + // Define a method that calls into the represented Java class + local_ref getCacheDir() { + static auto method = getClass()->getMethod("getCacheDir"); + return method(self()); + } + +}; + +} +} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses-inl.h index 134acc87e..f5f861ba3 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses-inl.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses-inl.h @@ -11,75 +11,134 @@ #include #include +#include #include "Common.h" #include "Exceptions.h" +#include "Meta.h" +#include "MetaConvert.h" namespace facebook { namespace jni { -inline bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept { +// jobject ///////////////////////////////////////////////////////////////////////////////////////// + +inline bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept { return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE; } - -// jobject ///////////////////////////////////////////////////////////////////////////////////////// - -inline JObjectWrapper::JObjectWrapper(jobject reference) noexcept - : this_{reference} -{} - -inline JObjectWrapper::JObjectWrapper(const JObjectWrapper& other) noexcept - : this_{other.this_} { - internal::dbglog("wrapper copy from this=%p ref=%p other=%p", this, other.this_, &other); -} - -inline local_ref JObjectWrapper::getClass() const noexcept { +inline local_ref JObject::getClass() const noexcept { return adopt_local(internal::getEnv()->GetObjectClass(self())); } -inline bool JObjectWrapper::isInstanceOf(alias_ref cls) const noexcept { +inline bool JObject::isInstanceOf(alias_ref cls) const noexcept { return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE; } template -inline T JObjectWrapper::getFieldValue(JField field) const noexcept { +inline T JObject::getFieldValue(JField field) const noexcept { return field.get(self()); } template -inline local_ref JObjectWrapper::getFieldValue(JField field) noexcept { +inline local_ref JObject::getFieldValue(JField field) const noexcept { return adopt_local(field.get(self())); } template -inline void JObjectWrapper::setFieldValue(JField field, T value) noexcept { +inline void JObject::setFieldValue(JField field, T value) noexcept { field.set(self(), value); } -inline std::string JObjectWrapper::toString() const { +inline std::string JObject::toString() const { static auto method = findClassLocal("java/lang/Object")->getMethod("toString"); return method(self())->toStdString(); } -inline void JObjectWrapper::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 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 owned_; +}; + +MonitorLock::MonitorLock() noexcept : owned_(nullptr) {} + +MonitorLock::MonitorLock(alias_ref object) noexcept + : owned_(object) { + internal::getEnv()->MonitorEnter(object.get()); } -inline jobject JObjectWrapper::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::self() const noexcept { - return this_; -} - -inline void swap(JObjectWrapper& a, JObjectWrapper& b) noexcept { +inline void swap(JObject& a, JObject& b) noexcept { using std::swap; swap(a.this_, b.this_); } +// JavaClass /////////////////////////////////////////////////////////////////////////////////////// + +namespace detail { +template +static local_ref newInstance(Args... args) { + static auto cls = JC::javaClassStatic(); + static auto constructor = cls->template getConstructor(); + return cls->newObject(constructor, args...); +} +} + + +template +auto JavaClass::self() const noexcept -> javaobject { + return static_cast(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 JObjectWrapper::getSuperclass() const noexcept { +inline local_ref JClass::getSuperclass() const noexcept { return adopt_local(internal::getEnv()->GetSuperclass(self())); } -inline void JObjectWrapper::registerNatives(std::initializer_list methods) { +inline void JClass::registerNatives(std::initializer_list methods) { const auto env = internal::getEnv(); JNINativeMethod jnimethods[methods.size()]; @@ -116,30 +175,30 @@ inline void JObjectWrapper::registerNatives(std::initializer_list::isAssignableFrom(alias_ref other) const noexcept { +inline bool JClass::isAssignableFrom(alias_ref other) const noexcept { const auto env = internal::getEnv(); const auto result = env->IsAssignableFrom(self(), other.get()); return result; } template -inline JConstructor JObjectWrapper::getConstructor() const { - return getConstructor(jmethod_traits::constructor_descriptor().c_str()); +inline JConstructor JClass::getConstructor() const { + return getConstructor(jmethod_traits_from_cxx::constructor_descriptor().c_str()); } template -inline JConstructor JObjectWrapper::getConstructor(const char* descriptor) const { +inline JConstructor JClass::getConstructor(const char* descriptor) const { constexpr auto constructor_method_name = ""; return getMethod(constructor_method_name, descriptor); } template -inline JMethod JObjectWrapper::getMethod(const char* name) const { - return getMethod(name, jmethod_traits::descriptor().c_str()); +inline JMethod JClass::getMethod(const char* name) const { + return getMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); } template -inline JMethod JObjectWrapper::getMethod( +inline JMethod JClass::getMethod( const char* name, const char* descriptor) const { const auto env = internal::getEnv(); @@ -149,12 +208,12 @@ inline JMethod JObjectWrapper::getMethod( } template -inline JStaticMethod JObjectWrapper::getStaticMethod(const char* name) const { - return getStaticMethod(name, jmethod_traits::descriptor().c_str()); +inline JStaticMethod JClass::getStaticMethod(const char* name) const { + return getStaticMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); } template -inline JStaticMethod JObjectWrapper::getStaticMethod( +inline JStaticMethod JClass::getStaticMethod( const char* name, const char* descriptor) const { const auto env = internal::getEnv(); @@ -164,12 +223,12 @@ inline JStaticMethod JObjectWrapper::getStaticMethod( } template -inline JNonvirtualMethod JObjectWrapper::getNonvirtualMethod(const char* name) const { - return getNonvirtualMethod(name, jmethod_traits::descriptor().c_str()); +inline JNonvirtualMethod JClass::getNonvirtualMethod(const char* name) const { + return getNonvirtualMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); } template -inline JNonvirtualMethod JObjectWrapper::getNonvirtualMethod( +inline JNonvirtualMethod JClass::getNonvirtualMethod( const char* name, const char* descriptor) const { const auto env = internal::getEnv(); @@ -180,12 +239,12 @@ inline JNonvirtualMethod JObjectWrapper::getNonvirtualMethod( template inline JField(), T>> -JObjectWrapper::getField(const char* name) const { +JClass::getField(const char* name) const { return getField(name, jtype_traits::descriptor().c_str()); } template -inline JField(), T>> JObjectWrapper::getField( +inline JField(), T>> JClass::getField( const char* name, const char* descriptor) const { const auto env = internal::getEnv(); @@ -195,13 +254,13 @@ inline JField(), T>> JObjectWrapper::getField } template -inline JStaticField(), T>> JObjectWrapper::getStaticField( +inline JStaticField(), T>> JClass::getStaticField( const char* name) const { return getStaticField(name, jtype_traits::descriptor().c_str()); } template -inline JStaticField(), T>> JObjectWrapper::getStaticField( +inline JStaticField(), T>> JClass::getStaticField( const char* name, const char* descriptor) const { const auto env = internal::getEnv(); @@ -211,32 +270,34 @@ inline JStaticField(), T>> JObjectWrapper::ge } template -inline T JObjectWrapper::getStaticFieldValue(JStaticField field) const noexcept { +inline T JClass::getStaticFieldValue(JStaticField field) const noexcept { return field.get(self()); } template -inline local_ref JObjectWrapper::getStaticFieldValue(JStaticField field) noexcept { +inline local_ref JClass::getStaticFieldValue(JStaticField field) noexcept { return adopt_local(field.get(self())); } template -inline void JObjectWrapper::setStaticFieldValue(JStaticField field, T value) noexcept { +inline void JClass::setStaticFieldValue(JStaticField field, T value) noexcept { field.set(self(), value); } template -inline local_ref JObjectWrapper::newObject( +inline local_ref JClass::newObject( JConstructor 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::type>::toCall(args))...); FACEBOOK_JNI_THROW_EXCEPTION_IF(!object); return adopt_local(static_cast(object)); } -inline jclass JObjectWrapper::self() const noexcept { - return static_cast(this_); +inline jclass JClass::self() const noexcept { + return static_cast(JObject::self()); } inline void registerNatives(const char* name, std::initializer_list methods) { @@ -246,197 +307,349 @@ inline void registerNatives(const char* name, std::initializer_list make_jstring(const std::string& modifiedUtf8) { +inline local_ref make_jstring(const std::string& modifiedUtf8) { return make_jstring(modifiedUtf8.c_str()); } -inline jstring JObjectWrapper::self() const noexcept { - return static_cast(this_); +namespace detail { +// convert to std::string from jstring +template <> +struct Convert { + 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 toCall(const std::string& t) { + return make_jstring(t); + } +}; + +// convert return from const char* +template <> +struct Convert { + 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 toCall(const char* t) { + return make_jstring(t); + } +}; } - -// jthrowable ////////////////////////////////////////////////////////////////////////////////////// - -inline jthrowable JObjectWrapper::self() const noexcept { - return static_cast(this_); -} - - // jtypeArray ////////////////////////////////////////////////////////////////////////////////////// -template -inline ElementProxy::ElementProxy( - JObjectWrapper<_jtypeArray*>* target, + +namespace detail { +inline size_t JArray::size() const noexcept { + const auto env = internal::getEnv(); + return env->GetArrayLength(self()); +} +} + +namespace detail { +template +inline ElementProxy::ElementProxy( + Target* target, size_t idx) : target_{target}, idx_{idx} {} -template -inline ElementProxy& ElementProxy::operator=(const T& o) { +template +inline ElementProxy& ElementProxy::operator=(const T& o) { target_->setElement(idx_, o); return *this; } -template -inline ElementProxy& ElementProxy::operator=(alias_ref& o) { +template +inline ElementProxy& ElementProxy::operator=(alias_ref& o) { target_->setElement(idx_, o.get()); return *this; } -template -inline ElementProxy& ElementProxy::operator=(alias_ref&& o) { +template +inline ElementProxy& ElementProxy::operator=(alias_ref&& o) { target_->setElement(idx_, o.get()); return *this; } -template -inline ElementProxy& ElementProxy::operator=(const ElementProxy& o) { +template +inline ElementProxy& ElementProxy::operator=(const ElementProxy& o) { auto src = o.target_->getElement(o.idx_); target_->setElement(idx_, src.get()); return *this; } -template -inline ElementProxy::ElementProxy::operator const local_ref () const { +template +inline ElementProxy::ElementProxy::operator const local_ref () const { return target_->getElement(idx_); } -template -inline ElementProxy::ElementProxy::operator local_ref () { +template +inline ElementProxy::ElementProxy::operator local_ref () { return target_->getElement(idx_); } +} + +template +std::string JArrayClass::get_instantiated_java_descriptor() { + return "[" + jtype_traits::descriptor(); +}; + +template +std::string JArrayClass::get_instantiated_base_name() { + return get_instantiated_java_descriptor(); +}; template -local_ref> JObjectWrapper>::newArray(size_t size) { +auto JArrayClass::newArray(size_t size) -> local_ref { static auto elementClass = findClassStatic(jtype_traits::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>(rawArray)); + return adopt_local(static_cast(rawArray)); } template -inline void JObjectWrapper>::setElement(size_t idx, const T& value) { +inline void JArrayClass::setElement(size_t idx, const T& value) { const auto env = internal::getEnv(); - env->SetObjectArrayElement(static_cast(self()), idx, value); + env->SetObjectArrayElement(this->self(), idx, value); } template -inline local_ref JObjectWrapper>::getElement(size_t idx) { +inline local_ref JArrayClass::getElement(size_t idx) { const auto env = internal::getEnv(); - auto rawElement = env->GetObjectArrayElement(static_cast(self()), idx); + auto rawElement = env->GetObjectArrayElement(this->self(), idx); return adopt_local(static_cast(rawElement)); } template -inline size_t JObjectWrapper>::size() { - const auto env = internal::getEnv(); - return env->GetArrayLength(static_cast(self())); +inline detail::ElementProxy> JArrayClass::operator[](size_t index) { + return detail::ElementProxy>(this, index); } -template -inline ElementProxy JObjectWrapper>::operator[](size_t index) { - return ElementProxy(this, index); -} - -template -inline jtypeArray JObjectWrapper>::self() const noexcept { - return static_cast>(this_); -} - - // jarray ///////////////////////////////////////////////////////////////////////////////////////// -inline size_t JObjectWrapper::size() const noexcept { - const auto env = internal::getEnv(); - return env->GetArrayLength(self()); +template +auto JPrimitiveArray::getRegion(jsize start, jsize length) + -> std::unique_ptr { + using T = typename jtype_traits::entry_type; + auto buf = std::unique_ptr{new T[length]}; + getRegion(start, length, buf.get()); + return buf; } -inline jarray JObjectWrapper::self() const noexcept { - return static_cast(this_); +template +std::string JPrimitiveArray::get_instantiated_java_descriptor() { + return jtype_traits::descriptor(); +} +template +std::string JPrimitiveArray::get_instantiated_base_name() { + return JPrimitiveArray::get_instantiated_java_descriptor(); } +template +auto JPrimitiveArray::pin() -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), 0, 0}; +} + +template +auto JPrimitiveArray::pinRegion(jsize start, jsize length) + -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), start, length}; +} + +template +auto JPrimitiveArray::pinCritical() + -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), 0, 0}; +} + +template +class PinnedArrayAlloc { + public: + static void allocate( + alias_ref::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::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + (void) start; + (void) size; + array->releaseElements(elements, mode); + } +}; + +template +class PinnedCriticalAlloc { + public: + static void allocate( + alias_ref::array_type> array, + jsize start, + jsize length, + T** elements, + size_t* size, + jboolean* isCopy) { + const auto env = internal::getEnv(); + *elements = static_cast(env->GetPrimitiveArrayCritical(array.get(), isCopy)); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements); + *size = array->size(); + } + static void release( + alias_ref::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + const auto env = internal::getEnv(); + env->ReleasePrimitiveArrayCritical(array.get(), elements, mode); + } +}; + +template +class PinnedRegionAlloc { + public: + static void allocate( + alias_ref::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::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + std::unique_ptr holder; + if (mode == 0 || mode == JNI_ABORT) { + holder.reset(elements); + } + if (mode == 0 || mode == JNI_COMMIT) { + array->setRegion(start, size, elements); + } + } +}; // PinnedPrimitiveArray /////////////////////////////////////////////////////////////////////////// -template -inline PinnedPrimitiveArray::PinnedPrimitiveArray(alias_ref array) noexcept - : array_{array} { - get(); +template +PinnedPrimitiveArray::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) { + *this = std::move(o); } -template -PinnedPrimitiveArray::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) noexcept { +template +PinnedPrimitiveArray& +PinnedPrimitiveArray::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 -PinnedPrimitiveArray& -PinnedPrimitiveArray::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 -inline T& PinnedPrimitiveArray::operator[](size_t index) { +template +T* PinnedPrimitiveArray::get() { + return elements_; +} + +template +inline void PinnedPrimitiveArray::release() { + releaseImpl(0); + clear(); +} + +template +inline void PinnedPrimitiveArray::commit() { + releaseImpl(JNI_COMMIT); +} + +template +inline void PinnedPrimitiveArray::abort() { + releaseImpl(JNI_ABORT); + clear(); +} + +template +inline void PinnedPrimitiveArray::releaseImpl(jint mode) { + FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); + Alloc::release(array_, elements_, start_, size_, mode); +} + +template +inline void PinnedPrimitiveArray::clear() noexcept { + array_ = nullptr; + elements_ = nullptr; + isCopy_ = false; + start_ = 0; + size_ = 0; +} + +template +inline T& PinnedPrimitiveArray::operator[](size_t index) { FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr); return elements_[index]; } -template -inline bool PinnedPrimitiveArray::isCopy() const noexcept { +template +inline bool PinnedPrimitiveArray::isCopy() const noexcept { return isCopy_ == JNI_TRUE; } -template -inline size_t PinnedPrimitiveArray::size() const noexcept { +template +inline size_t PinnedPrimitiveArray::size() const noexcept { return size_; } -template -inline PinnedPrimitiveArray::~PinnedPrimitiveArray() noexcept { +template +inline PinnedPrimitiveArray::~PinnedPrimitiveArray() noexcept { if (elements_) { release(); } } -#pragma push_macro("DECLARE_PRIMITIVE_METHODS") -#undef DECLARE_PRIMITIVE_METHODS -#define DECLARE_PRIMITIVE_METHODS(TYPE, NAME) \ -template<> TYPE* PinnedPrimitiveArray::get(); \ -template<> void PinnedPrimitiveArray::release(); \ +template +inline PinnedPrimitiveArray::PinnedPrimitiveArray(alias_ref::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 -inline alias_ref JavaClass::javaClassStatic() { - static auto cls = findClassStatic( - std::string(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2).c_str()); +template +inline alias_ref JavaClass::javaClassStatic() { + static auto cls = findClassStatic(jtype_traits::base_name().c_str()); return cls; } -template -inline local_ref JavaClass::javaClassLocal() { - std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2); +template +inline local_ref JavaClass::javaClassLocal() { + std::string className(jtype_traits::base_name().c_str()); return findClassLocal(className.c_str()); } diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses.h index 457b79b55..ed129af1b 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/CoreClasses.h @@ -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 @@ -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 findClassStatic(const char* name); +alias_ref 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 findClassStatic(const char* name); /// (like caching method ids). /// /// @return Returns a global reference to the class -local_ref findClassLocal(const char* name); +local_ref 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 lhs, alias_ref rhs) noexcept; +bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept; +// Together, these classes allow convenient use of any class with the fbjni +// helpers. To use: +// +// struct MyClass : public JavaClass { +// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; +// }; +// +// Then, an alias_ref 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 { +// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; +// +// void foo() { +// static auto method = javaClassStatic()->getMethod("foo"); +// method(self()); +// } +// +// static local_ref 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 { - public: - /// Java type descriptor - static constexpr const char* kJavaDescriptor = "Ljava/lang/Object;"; +namespace detail { +template +static local_ref 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 getClass() const noexcept; + local_ref getClass() const noexcept; /// Checks if the object is an instance of a class - bool isInstanceOf(alias_ref cls) const noexcept; + bool isInstanceOf(alias_ref cls) const noexcept; /// Get the primitive value of a field template @@ -78,7 +114,7 @@ class JObjectWrapper { /// Get and wrap the value of a field in a @ref local_ref template - local_ref getFieldValue(JField field) noexcept; + local_ref getFieldValue(JField 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 { /// 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 - friend class base_owned_ref; + typedef _jobject _javaobject; + typedef _javaobject* javaobject; - template - friend class alias_ref; - - friend void swap(JObjectWrapper& a, JObjectWrapper& 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 + friend struct detail::ReprAccess; + template + friend class JavaClass; + + template + friend class JObjectWrapper; }; -using JObject = JObjectWrapper; +// This is only to maintain backwards compatibility with things that are +// already providing a specialization of JObjectWrapper. Any such instances +// should be updated to use a JavaClass. +template<> +class JObjectWrapper : public JObject { +}; -void swap(JObjectWrapper& a, JObjectWrapper& b) noexcept; +namespace detail { +template +struct JTypeFor { + static_assert( + std::is_base_of< + std::remove_pointer::type, + typename std::remove_pointer::type + >::value, ""); + using _javaobject = typename std::remove_pointer::type; + using javaobject = JType; +}; + +template +struct JTypeFor { + // 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 { +// 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) 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 +class JavaClass : public Base { + using JObjType = typename detail::JTypeFor; +public: + using _javaobject = typename JObjType::_javaobject; + using javaobject = typename JObjType::javaobject; + + using JavaBase = JavaClass; + + static alias_ref javaClassStatic(); + static local_ref 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 + static local_ref newInstance(Args... args) { + return detail::newInstance(args...); + } + + javaobject self() const noexcept; +}; /// Wrapper to provide functionality to jclass references struct NativeMethod; -template<> -class JObjectWrapper : public JObjectWrapper { +class JClass : public JavaClass { public: /// Java type descriptor static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;"; - using JObjectWrapper::JObjectWrapper; - /// Get a @local_ref to the super class of this class - local_ref getSuperclass() const noexcept; + local_ref getSuperclass() const noexcept; /// Register native methods for the class. Usage looks like this: /// @@ -141,7 +244,7 @@ class JObjectWrapper : public JObjectWrapper { /// Check to see if the class is assignable from another class /// @pre cls != nullptr - bool isAssignableFrom(alias_ref cls) const noexcept; + bool isAssignableFrom(alias_ref cls) const noexcept; /// Convenience method to lookup the constructor with descriptor as specified by the /// type arguments @@ -212,95 +315,96 @@ class JObjectWrapper : public JObjectWrapper { template JNonvirtualMethod getNonvirtualMethod(const char* name, const char* descriptor) const; - private: +private: jclass self() const noexcept; }; -using JClass = JObjectWrapper; - // Convenience method to register methods on a class without holding // onto the class object. void registerNatives(const char* name, std::initializer_list methods); /// Wrapper to provide functionality to jstring references -template<> -class JObjectWrapper : public JObjectWrapper { +class JString : public JavaClass { public: /// Java type descriptor static constexpr const char* kJavaDescriptor = "Ljava/lang/String;"; - using JObjectWrapper::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 make_jstring(const char* modifiedUtf8); -local_ref make_jstring(const std::string& modifiedUtf8); - -using JString = JObjectWrapper; +local_ref make_jstring(const char* modifiedUtf8); +local_ref make_jstring(const std::string& modifiedUtf8); /// Wrapper to provide functionality to jthrowable references -template<> -class JObjectWrapper : public JObjectWrapper { +class JThrowable : public JavaClass { public: - /// Java type descriptor static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;"; - - using JObjectWrapper::JObjectWrapper; - - private: - jthrowable self() const noexcept; }; - -/// @cond INTERNAL -template class _jtypeArray : public _jobjectArray {}; -// @endcond -/// Wrapper to provide functionality for arrays of j-types -template using jtypeArray = _jtypeArray*; - -template +namespace detail { +template class ElementProxy { - private: - JObjectWrapper<_jtypeArray*>* target_; - size_t idx_; + private: + Target* target_; + size_t idx_; - public: - ElementProxy(JObjectWrapper<_jtypeArray*>* target, size_t idx); + public: + using T = typename Target::javaentry; + ElementProxy(Target* target, size_t idx); - ElementProxy& operator=(const T& o); + ElementProxy& operator=(const T& o); - ElementProxy& operator=(alias_ref& o); + ElementProxy& operator=(alias_ref& o); - ElementProxy& operator=(alias_ref&& o); + ElementProxy& operator=(alias_ref&& o); - ElementProxy& operator=(const ElementProxy& o); + ElementProxy& operator=(const ElementProxy& o); - operator const local_ref () const; + operator const local_ref () const; - operator local_ref (); - }; + operator local_ref (); +}; +} + +namespace detail { +class JArray : public JavaClass { + 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). + static constexpr const char* kJavaDescriptor = nullptr; + size_t size() const noexcept; +}; + +// This is used so that the JArrayClass javaobject extends jni's +// jobjectArray. This class should not be used directly. A general Object[] +// should use JArrayClass. +class JTypeArray : public JavaClass { + // This cannot be used in a scope that derives a descriptor (like in a method + // signature). + static constexpr const char* kJavaDescriptor = nullptr; +}; +} template -class JObjectWrapper> : public JObjectWrapper { +class JArrayClass : public JavaClass, detail::JTypeArray> { public: + static_assert(is_plain_jni_reference(), ""); + // 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, detail::JTypeArray>::javaobject; static constexpr const char* kJavaDescriptor = nullptr; - static std::string get_instantiated_java_descriptor() { - return "[" + jtype_traits::descriptor(); - }; - - using JObjectWrapper::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> newArray(size_t count); + static local_ref 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> : public JObjectWrapper { /// If you use auto, you'll get an ElementProxy, which may need to be cast. local_ref 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> : public JObjectWrapper { /// 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 operator[](size_t idx); - - private: - jtypeArray self() const noexcept; + detail::ElementProxy operator[](size_t idx); }; -template -using JArrayClass = JObjectWrapper>; +template +using jtypeArray = typename JArrayClass::javaobject; template -local_ref> adopt_local_array(jobjectArray ref) { - return adopt_local(static_cast>(ref)); +local_ref::javaobject> adopt_local_array(jobjectArray ref) { + return adopt_local(static_cast::javaobject>(ref)); } -template -local_ref adopt_local(ElementProxy elementProxy) { - return static_cast>(elementProxy); +template +local_ref adopt_local(detail::ElementProxy elementProxy) { + return static_cast>(elementProxy); } +template +class PinnedPrimitiveArray; + +template class PinnedArrayAlloc; +template class PinnedRegionAlloc; +template 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 : public JObjectWrapper { +template +class JPrimitiveArray : + public JavaClass, detail::JArray, JArrayType> { + static_assert(is_jni_primitive_array(), ""); public: static constexpr const char* kJavaDescriptor = nullptr; + static std::string get_instantiated_java_descriptor(); + static std::string get_instantiated_base_name(); - using JObjectWrapper::JObjectWrapper; - size_t size() const noexcept; + using T = typename jtype_traits::entry_type; - private: - jarray self() const noexcept; + static local_ref newArray(size_t count); + + void getRegion(jsize start, jsize length, T* buf); + std::unique_ptr 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> pin(); + + /// Returns a view of part of the underlying array. A pinned region is always + /// backed by a copy of the region. + PinnedPrimitiveArray> 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> pinCritical(); + +private: + friend class PinnedArrayAlloc; + T* getElements(jboolean* isCopy); + void releaseElements(T* elements, jint mode); }; -using JArray = JObjectWrapper; - -template -class PinnedPrimitiveArray; - -#pragma push_macro("DECLARE_PRIMITIVE_ARRAY_UTILS") -#undef DECLARE_PRIMITIVE_ARRAY_UTILS -#define DECLARE_PRIMITIVE_ARRAY_UTILS(TYPE, NAME, DESC) \ - \ -local_ref make_ ## TYPE ## _array(jsize size); \ - \ -template<> class JObjectWrapper : public JArray { \ - public: \ - static constexpr const char* kJavaDescriptor = "[" # DESC; \ - \ - using JArray::JArray; \ - \ - static local_ref newArray(size_t count); \ - \ - j ## TYPE* getRegion(jsize start, jsize length, j ## TYPE* buf); \ - std::unique_ptr getRegion(jsize start, jsize length); \ - void setRegion(jsize start, jsize length, const j ## TYPE* buf); \ - PinnedPrimitiveArray pin(); \ - \ - private: \ - j ## TYPE ## Array self() const noexcept { \ - return static_cast(this_); \ - } \ -}; \ - \ -using JArray ## NAME = JObjectWrapper \ - - -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 make_boolean_array(jsize size); +local_ref make_byte_array(jsize size); +local_ref make_char_array(jsize size); +local_ref make_short_array(jsize size); +local_ref make_int_array(jsize size); +local_ref make_long_array(jsize size); +local_ref make_float_array(jsize size); +local_ref make_double_array(jsize size); +using JArrayBoolean = JPrimitiveArray; +using JArrayByte = JPrimitiveArray; +using JArrayChar = JPrimitiveArray; +using JArrayShort = JPrimitiveArray; +using JArrayInt = JPrimitiveArray; +using JArrayLong = JPrimitiveArray; +using JArrayFloat = JPrimitiveArray; +using JArrayDouble = JPrimitiveArray; /// 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 +/// 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 class PinnedPrimitiveArray { public: static_assert(is_jni_primitive::value, "PinnedPrimitiveArray requires primitive jni type."); - PinnedPrimitiveArray(PinnedPrimitiveArray&&) noexcept; + using ArrayType = typename jtype_traits::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 array_; + alias_ref array_; + size_t start_; T* elements_; jboolean isCopy_; size_t size_; - PinnedPrimitiveArray(alias_ref) noexcept; + void allocate(alias_ref, jint start, jint length); + void releaseImpl(jint mode); + void clear() noexcept; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; - friend class JObjectWrapper; -}; - - -namespace detail { - -class BaseJavaClass { -public: - typedef _jobject _javaobject; - typedef _javaobject* javaobject; + PinnedPrimitiveArray(alias_ref, jint start, jint length); + + friend class JPrimitiveArray::array_type>; }; +#pragma push_macro("PlainJniRefMap") +#undef PlainJniRefMap +#define PlainJniRefMap(rtype, jtype) \ +namespace detail { \ +template<> \ +struct RefReprType { \ + using type = rtype; \ +}; \ } -// Together, these classes allow convenient use of any class with the fbjni -// helpers. To use: -// -// struct MyClass : public JavaClass { -// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; -// }; -// -// alias_ref 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 -class JavaClass { -public: - // JNI pattern for jobject assignable pointer - struct _javaobject : public Base::_javaobject { - typedef T javaClass; - }; - typedef _javaobject* javaobject; - - static alias_ref javaClassStatic(); - static local_ref javaClassLocal(); -}; - -template -class JObjectWrapper::value && - std::is_class::type::javaClass>::value - >::type> - : public JObjectWrapper { -public: - static constexpr const char* kJavaDescriptor = - std::remove_pointer::type::javaClass::kJavaDescriptor; - - using JObjectWrapper::JObjectWrapper; - - template - JObjectWrapper(const JObjectWrapper& w) - : JObjectWrapper(w) { - static_assert(std::is_convertible::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 to be concrete before it can work. -#include "Meta-inl.h" diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Exceptions.cpp b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Exceptions.cpp index 710a6836f..6a3516cd3 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Exceptions.cpp +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Exceptions.cpp @@ -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 +#include "jni/fbjni.h" #include diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/File.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/File.h new file mode 100644 index 000000000..29fc9850a --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/File.h @@ -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 { + 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("getAbsolutePath"); + return method(self())->toStdString(); + } + +}; + +} +} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.cpp b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.cpp index ebcb778de..c779cde4e 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.cpp +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.cpp @@ -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, - std::unique_ptr new_value) { - static auto pointerField = hybridData->getClass()->getField("mNativePointer"); - auto* old_value = reinterpret_cast(hybridData->getFieldValue(pointerField)); +void HybridData::setNativePointer(std::unique_ptr new_value) { + static auto pointerField = getClass()->getField("mNativePointer"); + auto* old_value = reinterpret_cast(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, // 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(new_value.release())); + setFieldValue(pointerField, reinterpret_cast(new_value.release())); } -BaseHybridClass* getNativePointer(alias_ref hybridData) { - static auto pointerField = hybridData->getClass()->getField("mNativePointer"); - auto* value = reinterpret_cast(hybridData->getFieldValue(pointerField)); +BaseHybridClass* HybridData::getNativePointer() { + static auto pointerField = getClass()->getField("mNativePointer"); + auto* value = reinterpret_cast(getFieldValue(pointerField)); if (!value) { throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); } return value; } -local_ref getHybridData(alias_ref jthis, - JField field) { - auto hybridData = jthis->getFieldValue(field); - if (!hybridData) { - throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); - } - return hybridData; +local_ref HybridData::create() { + return newInstance(); } } namespace { - -void resetNative(alias_ref jthis) { - detail::setNativePointer(jthis, nullptr); +void resetNative(alias_ref jthis) { + jthis->setNativePointer(nullptr); } - } void HybridDataOnLoad() { diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.h index 8b62ccded..70fc670f5 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Hybrid.h @@ -19,169 +19,108 @@ namespace jni { namespace detail { -class BaseHybridClass : public BaseJavaClass { +class BaseHybridClass { public: virtual ~BaseHybridClass() {} }; struct HybridData : public JavaClass { constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; + void setNativePointer(std::unique_ptr new_value); + BaseHybridClass* getNativePointer(); + static local_ref create(); }; -void setNativePointer(alias_ref hybridData, - std::unique_ptr new_value); -BaseHybridClass* getNativePointer(alias_ref hybridData); -local_ref getHybridData(alias_ref jthis, - JField field); - -// Normally, pass through types unmolested. -template -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 +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::value || + std::is_base_of::value, + "The base of a HybridClass must be either another HybridClass or derived from JObject."); }; -// This is needed for return conversion template <> -struct Convert { - typedef void jniType; +struct HybridTraits { + using CxxBase = BaseHybridClass; + using JavaBase = JObject; }; -// convert to std::string from jstring -template <> -struct Convert { - 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 toCall(const std::string& t) { - return make_jstring(t); - } +template +struct HybridTraits< + Base, + typename std::enable_if::value>::type> { + using CxxBase = Base; + using JavaBase = typename Base::JavaPart; }; -// convert return from const char* -template <> -struct Convert { - 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 toCall(const char* t) { - return make_jstring(t); - } +template +struct HybridTraits< + Base, + typename std::enable_if::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 { - 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 from T +// convert to HybridClass* from jhybridobject template -struct Convert> { - typedef T jniType; - static alias_ref fromJni(jniType t) { - return wrap_alias(t); - } - static jniType toJniRet(alias_ref t) { - return t.get(); - } - static jniType toCall(alias_ref t) { - return t.get(); +struct Convert< + T, typename std::enable_if< + std::is_base_of::type>::value>::type> { + typedef typename std::remove_pointer::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 -template -struct Convert> { - typedef T jniType; - // No automatic synthesis of local_ref - static jniType toJniRet(local_ref t) { - return t.release(); - } - static jniType toCall(local_ref t) { - return t.get(); - } +template +struct RefReprType::value, void>::type> { + static_assert(std::is_same::value, + "HybridFoo (where HybridFoo derives from HybridClass) is not supported in this context. " + "For an xxx_ref, you may want: xxx_ref or HybridFoo*."); + using Repr = T; }; -// convert return from global_ref -template -struct Convert> { - typedef T jniType; - // No automatic synthesis of global_ref - static jniType toJniRet(global_ref t) { - return t.get(); - } - static jniType toCall(global_ref 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 -inline T callToJni(T&& t) { - return t; -} - -inline jstring callToJni(local_ref&& sref) { - return sref.get(); -} - -struct jstring_holder { - local_ref s_; - jstring_holder(const char* s) : s_(make_jstring(s)) {} - operator jstring() { return s_.get(); } -}; - -template -struct HybridRoot {}; - -template -struct HybridRoot::value>::type> - : public BaseHybridClass {}; } template -class HybridClass : public Base - , public detail::HybridRoot - , public JavaClass { +class HybridClass : public detail::HybridTraits::CxxBase { public: - typedef detail::HybridData::javaobject jhybriddata; - typedef typename JavaClass::javaobject jhybridobject; + struct JavaPart : JavaClass::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::javaClassStatic; - using JavaClass::javaClassLocal; - using JavaClass::javaobject; - typedef typename JavaClass::_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 javaClassStatic() { + return JavaPart::javaClassStatic(); + } + + static local_ref 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::CxxBase::CxxBase; static void registerHybrid(std::initializer_list methods) { javaClassStatic()->registerNatives(methods); } - static local_ref makeHybridData(std::unique_ptr cxxPart) { - static auto dataCtor = detail::HybridData::javaClassStatic()->getConstructor(); - auto hybridData = detail::HybridData::javaClassStatic()->newObject(dataCtor); - detail::setNativePointer(hybridData, std::move(cxxPart)); + static local_ref makeHybridData(std::unique_ptr cxxPart) { + auto hybridData = detail::HybridData::create(); + hybridData->setNativePointer(std::move(cxxPart)); return hybridData; } template - static local_ref makeCxxInstance(Args&&... args) { + static local_ref makeCxxInstance(Args&&... args) { return makeHybridData(std::unique_ptr(new T(std::forward(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 - static local_ref newObjectCxxArgs(Args&&... args) { + static local_ref newObjectCxxArgs(Args&&... args) { auto hybridData = makeCxxInstance(std::forward(args)...); - static auto ctor = javaClassStatic()->template getConstructor(); - 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 + static local_ref allocateWithCxxArgs(Args&&... args) { + auto hybridData = makeCxxInstance(std::forward(args)...); + static auto allocateMethod = + javaClassStatic()->template getStaticMethod("allocate"); + return allocateMethod(javaClassStatic(), hybridData.get()); } // Factory method for creating a hybrid object where the arguments // are passed to the java ctor. template - static local_ref newObjectJavaArgs(Args&&... args) { - static auto ctor = - javaClassStatic()->template getConstructor< - jhybridobject(typename detail::Convert::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::type>::toCall(args))...); - return lref; + static local_ref 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 +inline T* HybridClass::JavaPart::cthis() { + static auto field = + HybridClass::JavaPart::javaClassStatic()->template getField("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(hybridData->getNativePointer()); + // This would require some serious programmer error. + FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field"); + return value; +}; + +template +/* static */ inline std::string HybridClass::JavaPart::get_instantiated_java_descriptor() { + return T::kJavaDescriptor; +} + +template +/* static */ inline std::string HybridClass::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 -inline typename std::remove_pointer::type::javaClass* cthis(T jthis) { - static auto dataField = - jthis->getClass()->template getField("mHybridData"); - // I'd like to use dynamic_cast here, but -fno-rtti is the default. - auto* value = static_cast::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(); diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator-inl.h new file mode 100644 index 000000000..206d2b4b1 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator-inl.h @@ -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 +struct IteratorHelper : public JavaClass> { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;"; + + typedef local_ref value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef std::forward_iterator_tag iterator_category; + + typedef JavaClass> JavaBase_; + + bool hasNext() const { + static auto hasNextMethod = + JavaBase_::javaClassStatic()->template getMethod("hasNext"); + return hasNextMethod(JavaBase_::self()); + } + + value_type next() { + static auto elementField = + JavaBase_::javaClassStatic()->template getField("mElement"); + return dynamic_ref_cast(JavaBase_::getFieldValue(elementField)); + } + + static void reset(value_type& v) { + v.reset(); + } +}; + +template +struct MapIteratorHelper : public JavaClass> { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;"; + + typedef std::pair, local_ref> value_type; + + typedef JavaClass> JavaBase_; + + bool hasNext() const { + static auto hasNextMethod = + JavaBase_::javaClassStatic()->template getMethod("hasNext"); + return hasNextMethod(JavaBase_::self()); + } + + value_type next() { + static auto keyField = JavaBase_::javaClassStatic()->template getField("mKey"); + static auto valueField = JavaBase_::javaClassStatic()->template getField("mValue"); + return std::make_pair(dynamic_ref_cast(JavaBase_::getFieldValue(keyField)), + dynamic_ref_cast(JavaBase_::getFieldValue(valueField))); + } + + static void reset(value_type& v) { + v.first.reset(); + v.second.reset(); + } +}; + +template +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&& 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 helper_; + // set to -1 at end + std::ptrdiff_t i_; + value_type entry_; +}; + +} + +template +struct JIterator::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +typename JIterator::Iterator JIterator::begin() const { + static auto ctor = detail::IteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JIterator::javaobject)>(); + return Iterator( + make_global( + detail::IteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JIterator::Iterator JIterator::end() const { + return Iterator(); +} + +template +struct JIterable::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +typename JIterable::Iterator JIterable::begin() const { + static auto ctor = detail::IteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JIterable::javaobject)>(); + return Iterator( + make_global( + detail::IteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JIterable::Iterator JIterable::end() const { + return Iterator(); +} + +template +size_t JCollection::size() const { + static auto sizeMethod = + JCollection::javaClassStatic()->template getMethod("size"); + return sizeMethod(this->self()); +} + +template +struct JMap::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +size_t JMap::size() const { + static auto sizeMethod = + JMap::javaClassStatic()->template getMethod("size"); + return sizeMethod(this->self()); +} + +template +typename JMap::Iterator JMap::begin() const { + static auto ctor = detail::MapIteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JMap::javaobject)>(); + return Iterator( + make_global( + detail::MapIteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JMap::Iterator JMap::end() const { + return Iterator(); +} + +} +} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator.h new file mode 100644 index 000000000..aa3652666 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Iterator.h @@ -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::javaobject> my_iter = ...; + * + * In the simplest case, it can be used just as alias_ref::javaobject>, + * for example in a method declaration. + */ +template +struct JIterator : JavaClass> { + constexpr static auto kJavaDescriptor = "Ljava/util/Iterator;"; + + struct Iterator; + + /** + * To iterate: + * + * for (const auto& element : *jiter) { ... } + * + * The JIterator iterator value_type is local_ref, 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 vs; + * for (const auto& elem : *jiter) { + * vs.push_back(elem->toStdString()); + * } + * + * Or if you prefer using std algorithms: + * + * std::vector vs; + * std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs), + * [](const local_ref& 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 +struct JIterable : JavaClass> { + 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 +struct JCollection : JavaClass, JIterable> { + constexpr static auto kJavaDescriptor = "Ljava/util/Collection;"; + + /** + * Returns the number of elements in the collection. + */ + size_t size() const; +}; + +template +struct JList : JavaClass, JCollection> { + constexpr static auto kJavaDescriptor = "Ljava/util/List;"; +}; + +template +struct JSet : JavaClass, JCollection> { + 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::javaobject> my_map = ...; + * + * In the simplest case, it can be used just as alias_ref::javaobject>, + * for example in a method declaration. + */ +template +struct JMap : JavaClass> { + 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> + * 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" diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-forward.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-forward.h new file mode 100644 index 000000000..2f524ad06 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-forward.h @@ -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 +class JMethod; +template +class JStaticMethod; +template +class JNonvirtualMethod; +template +class JConstructor; +template +class JField; +template +class JStaticField; + +/// Type traits for Java types (currently providing Java type descriptors) +template +struct jtype_traits; + +/// Type traits for Java methods (currently providing Java type descriptors) +template +struct jmethod_traits; + +}} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-inl.h index d51157b16..38fabc500 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-inl.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta-inl.h @@ -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 inline void JMethod::operator()(alias_ref self, Args... args) { const auto env = internal::getEnv(); - env->CallVoidMethod(self.get(), getId(), args...); + env->CallVoidMethod( + self.get(), + getId(), + detail::callToJni(detail::Convert::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 \ -inline TYPE JMethod::operator()(alias_ref 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 \ +inline TYPE JMethod::operator()(alias_ref self, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->Call ## METHOD ## Method( \ + self.get(), \ + getId(), \ + detail::callToJni(detail::Convert::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 -inline local_ref JMethod::operator()(alias_ref 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(result)); -} + +/// JMethod specialization for references that wraps the return value in a @ref local_ref +template +class JMethod : public JMethodBase { + public: + // TODO: static_assert is jobject-derived or local_ref jobject + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "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 operator()(alias_ref self, Args... args) { + const auto env = internal::getEnv(); + auto result = env->CallObjectMethod( + self.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(result)); + } + + friend class JClass; +}; template inline void JStaticMethod::operator()(alias_ref cls, Args... args) { const auto env = internal::getEnv(); - env->CallStaticVoidMethod(cls.get(), getId(), args...); + env->CallStaticVoidMethod( + cls.get(), + getId(), + detail::callToJni(detail::Convert::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 \ -inline TYPE JStaticMethod::operator()(alias_ref 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 \ +inline TYPE JStaticMethod::operator()(alias_ref cls, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->CallStatic ## METHOD ## Method( \ + cls.get(), \ + getId(), \ + detail::callToJni(detail::Convert::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 -inline local_ref JStaticMethod::operator()(alias_ref 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(result)); -} +/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref +template +class JStaticMethod : public JMethodBase { + public: + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "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 operator()(alias_ref cls, Args... args) { + const auto env = internal::getEnv(); + auto result = env->CallStaticObjectMethod( + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(result)); + } + + friend class JClass; +}; template inline void -JNonvirtualMethod::operator()(alias_ref self, jclass cls, Args... args) { +JNonvirtualMethod::operator()(alias_ref self, alias_ref 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::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 \ -inline TYPE \ -JNonvirtualMethod::operator()(alias_ref 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 \ +inline TYPE \ +JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->CallNonvirtual ## METHOD ## Method( \ + self.get(), \ + cls.get(), \ + getId(), \ + detail::callToJni(detail::Convert::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 -inline local_ref JNonvirtualMethod::operator()( - alias_ref 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(result)); -} +/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref +template +class JNonvirtualMethod : public JMethodBase { + public: + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "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 -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::kJavaDescriptor != nullptr ? - std::string{JObjectWrapper::kJavaDescriptor} : - JObjectWrapper::get_instantiated_java_descriptor(); - return descriptor; + /// Invoke a method and return a local reference wrapping the result + local_ref operator()(alias_ref self, alias_ref cls, Args... args){ + const auto env = internal::getEnv(); + auto result = env->CallNonvirtualObjectMethod( + self.get(), + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(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::kJavaDescriptor != nullptr) { - std::string base_name = JObjectWrapper::kJavaDescriptor; - return base_name.substr(1, base_name.size() - 2); - } - return JObjectWrapper::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 { \ - static std::string descriptor() { return std::string{#DSC}; } \ - static std::string base_name() { return descriptor(); } \ -}; \ -template<> \ -struct jtype_traits { \ - 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 { - 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 /////////////////////////////////////////////////////////////////////////////////////// diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta.h index de1bde0d5..23c745050 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Meta.h @@ -19,11 +19,25 @@ #include -#include "References.h" +#include "References-forward.h" + +#ifdef __ANDROID__ +# include +# 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 : public JMethodBase { \ TYPE operator()(alias_ref self, Args... args); \ \ - friend class JObjectWrapper; \ + 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 -class JMethod : public JMethodBase { - public: - static_assert(IsPlainJniReference(), "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 operator()(alias_ref self, Args... args); - - friend class JObjectWrapper; -}; - - /// Convenience type representing constructors +/// These should only be used with JClass::getConstructor and JClass::newObject. template -using JConstructor = JMethod; +struct JConstructor : private JMethod { + using JMethod::JMethod; + private: + JConstructor(const JMethod& other) : JMethod(other.getId()) {} + friend class JClass; +}; /// Representation of a jStaticMethodID template @@ -126,7 +129,7 @@ class JStaticMethod : public JMethodBase { \ \ TYPE operator()(alias_ref cls, Args... args); \ \ - friend class JObjectWrapper; \ + 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 -class JStaticMethod : public JMethodBase { - static_assert(IsPlainJniReference(), "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 operator()(alias_ref cls, Args... args); - - friend class JObjectWrapper; -}; - /// Representation of a jNonvirtualMethodID template class JNonvirtualMethod; @@ -180,9 +167,9 @@ class JNonvirtualMethod : public JMethodBase { \ JNonvirtualMethod() noexcept {}; \ JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \ \ - TYPE operator()(alias_ref self, jclass cls, Args... args); \ + TYPE operator()(alias_ref self, alias_ref cls, Args... args); \ \ - friend class JObjectWrapper; \ + 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 -class JNonvirtualMethod : public JMethodBase { - static_assert(IsPlainJniReference(), "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 operator()(alias_ref self, jclass cls, Args... args); - - friend class JObjectWrapper; -}; - - /** * 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; + friend class JObject; }; @@ -278,20 +248,11 @@ class JStaticField { /// @pre object != nullptr void set(jclass jcls, T value) noexcept; - friend class JObjectWrapper; - + friend class JClass; + friend class JObject; }; -/// Type traits for Java types (currently providing Java type descriptors) -template -struct jtype_traits; - - -/// Type traits for Java methods (currently providing Java type descriptors) -template -struct jmethod_traits; - /// Template magic to provide @ref jmethod_traits template struct jmethod_traits { @@ -299,4 +260,75 @@ struct jmethod_traits { static std::string constructor_descriptor(); }; + +// jtype_traits //////////////////////////////////////////////////////////////////////////////////// + +template +struct jtype_traits { +private: + using Repr = ReprType; +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 { \ + static std::string descriptor() { return std::string{#DSC}; } \ + static std::string base_name() { return descriptor(); } \ + using array_type = TYPE ## Array; \ +}; \ +template<> \ +struct jtype_traits { \ + 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 { + 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 +struct jmethod_traits_from_cxx; + }} + +#include "Meta-inl.h" diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/MetaConvert.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/MetaConvert.h new file mode 100644 index 000000000..33027c7e9 --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/MetaConvert.h @@ -0,0 +1,122 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include + +#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 +inline T callToJni(T&& t) { + return t; +} + +template +inline JniType callToJni(local_ref&& sref) { + return sref.get(); +} + +// Normally, pass through types unmolested. +template +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 { + typedef void jniType; +}; + +// jboolean is an unsigned char, not a bool. Allow it to work either way. +template<> +struct Convert { + 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 from T +template +struct Convert> { + typedef JniType jniType; + static alias_ref fromJni(jniType t) { + return wrap_alias(t); + } + static jniType toJniRet(alias_ref t) { + return t.get(); + } + static jniType toCall(alias_ref t) { + return t.get(); + } +}; + +// convert return from local_ref +template +struct Convert> { + typedef JniType jniType; + // No automatic synthesis of local_ref + static jniType toJniRet(local_ref t) { + return t.release(); + } + static jniType toCall(local_ref t) { + return t.get(); + } +}; + +// convert return from global_ref +template +struct Convert> { + typedef JniType jniType; + // No automatic synthesis of global_ref + static jniType toJniRet(global_ref t) { + return t.get(); + } + static jniType toCall(global_ref t) { + return t.get(); + } +}; + +template struct jni_sig_from_cxx_t; +template +struct jni_sig_from_cxx_t { + using JniRet = typename Convert::type>::jniType; + using JniSig = JniRet(typename Convert::type>::jniType...); +}; + +template +using jni_sig_from_cxx = typename jni_sig_from_cxx_t::JniSig; + +} // namespace detail + +template +struct jmethod_traits_from_cxx : jmethod_traits> { +}; + +}} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/ReferenceAllocators-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ReferenceAllocators-inl.h index d60c90022..5f69a3b41 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/ReferenceAllocators-inl.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/ReferenceAllocators-inl.h @@ -14,7 +14,6 @@ #include #include "Exceptions.h" -#include "References.h" namespace facebook { namespace jni { diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-forward.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-forward.h new file mode 100644 index 000000000..8dabf67cb --- /dev/null +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-forward.h @@ -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 +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 +struct RefReprType; + +template +struct JavaObjectType; + +template +struct ReprAccess; +} + +// Given T, either a jobject-like type or a JavaClass-derived type, ReprType +// is the corresponding JavaClass-derived type and JniType is the +// jobject-like type. +template +using ReprType = typename detail::RefReprType::type; + +template +using JniType = typename detail::JavaObjectType::type; + +template +class base_owned_ref; + +template +class basic_strong_ref; + +template +class weak_ref; + +template +class alias_ref; + +/// A smart unique reference owning a local JNI reference +template +using local_ref = basic_strong_ref; + +/// A smart unique reference owning a global JNI reference +template +using global_ref = basic_strong_ref; + +}} // namespace facebook::jni diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-inl.h index bae3d5d63..2176d1e02 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-inl.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References-inl.h @@ -15,49 +15,87 @@ namespace facebook { namespace jni { -template -inline enable_if_t(), local_ref> adopt_local(T ref) noexcept { - return local_ref{ref}; -} - -template -inline enable_if_t(), global_ref> adopt_global(T ref) noexcept { - return global_ref{ref}; -} - -template -inline enable_if_t(), weak_ref> adopt_weak_global(T ref) noexcept { - return weak_ref{ref}; -} - - -template -inline enable_if_t(), alias_ref> wrap_alias(T ref) noexcept { - return alias_ref(ref); -} - - -template -enable_if_t(), alias_ref> wrap_alias(T ref) noexcept; - - template inline enable_if_t(), T> getPlainJniReference(T ref) { return ref; } template -inline T getPlainJniReference(alias_ref ref) { +inline JniType getPlainJniReference(alias_ref ref) { return ref.get(); } template -inline T getPlainJniReference(const base_owned_ref& ref) { - return ref.getPlainJniReference(); +inline JniType getPlainJniReference(const base_owned_ref& ref) { + return ref.get(); } -namespace internal { +namespace detail { +template +struct ReprAccess { + using javaobject = JniType; + static void set(Repr& repr, javaobject obj) noexcept { + repr.JObjectBase::set(obj); + } + static javaobject get(const Repr& repr) { + return static_cast(repr.JObject::get()); + } +}; + +namespace { +template +void StaticAssertValidRepr() noexcept { + static_assert(std::is_base_of::value, + "A smart ref representation must be derived from JObject."); + static_assert(IsPlainJniReference>(), "T must be a JNI reference"); + static_assert(sizeof(Repr) == sizeof(JObjectBase), ""); + static_assert(alignof(Repr) == alignof(JObjectBase), ""); +} +} + +template +ReprStorage::ReprStorage(JniType obj) noexcept { + StaticAssertValidRepr(); + set(obj); +} + +template +void ReprStorage::set(JniType obj) noexcept { + new (&storage_) Repr; + ReprAccess::set(get(), obj); +} + +template +Repr& ReprStorage::get() noexcept { + return *reinterpret_cast(&storage_); +} + +template +const Repr& ReprStorage::get() const noexcept { + return *reinterpret_cast(&storage_); +} + +template +JniType ReprStorage::jobj() const noexcept { + ReprAccess::get(get()); + return ReprAccess::get(get()); +} + +template +void ReprStorage::swap(ReprStorage& other) noexcept { + StaticAssertValidRepr(); + 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 enable_if_t(), plain_jni_reference_t> make_ref(const T& reference) { @@ -77,24 +115,53 @@ enable_if_t(), plain_jni_reference_t> make_ref(const T& return static_cast>(ref); } +} // namespace detail + +template +inline local_ref adopt_local(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return local_ref{ref}; } +template +inline global_ref adopt_global(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return global_ref{ref}; +} + +template +inline weak_ref adopt_weak_global(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return weak_ref{ref}; +} + + +template +inline enable_if_t(), alias_ref> wrap_alias(T ref) noexcept { + return alias_ref(ref); +} + + +template +enable_if_t(), alias_ref> wrap_alias(T ref) noexcept; + + template enable_if_t(), local_ref>> make_local(const T& ref) { - return adopt_local(internal::make_ref(ref)); + return adopt_local(detail::make_ref(ref)); } template enable_if_t(), global_ref>> make_global(const T& ref) { - return adopt_global(internal::make_ref(ref)); + return adopt_global(detail::make_ref(ref)); } template enable_if_t(), weak_ref>> make_weak(const T& ref) { - return adopt_weak_global(internal::make_ref(ref)); + return adopt_weak_global(detail::make_ref(ref)); } template @@ -113,69 +180,67 @@ operator!=(const T1& a, const T2& b) { // base_owned_ref /////////////////////////////////////////////////////////////////////// template -inline constexpr base_owned_ref::base_owned_ref() noexcept - : object_{nullptr} +inline base_owned_ref::base_owned_ref() noexcept + : base_owned_ref(nullptr) {} template -inline constexpr base_owned_ref::base_owned_ref( - std::nullptr_t t) noexcept - : object_{nullptr} +inline base_owned_ref::base_owned_ref(std::nullptr_t t) noexcept + : base_owned_ref(static_cast(nullptr)) {} template -inline base_owned_ref::base_owned_ref( - const base_owned_ref& other) - : object_{Alloc{}.newReference(other.getPlainJniReference())} +inline base_owned_ref::base_owned_ref(const base_owned_ref& other) + : storage_{static_cast(Alloc{}.newReference(other.get()))} {} template template inline base_owned_ref::base_owned_ref(const base_owned_ref& other) - : object_{Alloc{}.newReference(other.getPlainJniReference())} + : storage_{static_cast(Alloc{}.newReference(other.get()))} {} template inline facebook::jni::base_owned_ref::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 inline base_owned_ref::base_owned_ref( base_owned_ref&& 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 template base_owned_ref::base_owned_ref(base_owned_ref&& 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 inline base_owned_ref::~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 -inline T base_owned_ref::release() noexcept { - auto value = getPlainJniReference(); +inline auto base_owned_ref::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::reset() noexcept { } template -inline void base_owned_ref::reset(T reference) noexcept { - if (getPlainJniReference()) { +inline void base_owned_ref::reset(javaobject reference) noexcept { + if (get()) { assert(Alloc{}.verifyReference(reference)); - Alloc{}.deleteReference(getPlainJniReference()); + Alloc{}.deleteReference(get()); } - object_.set(reference); + set(reference); } template -inline T base_owned_ref::getPlainJniReference() const noexcept { - return static_cast(object_.get()); +inline auto base_owned_ref::get() const noexcept -> javaobject { + return storage_.jobj(); +} + +template +inline void base_owned_ref::set(javaobject ref) noexcept { + storage_.set(ref); } @@ -213,19 +283,21 @@ template inline weak_ref& weak_ref::operator=( weak_ref&& 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 -local_ref weak_ref::lockLocal() { - return adopt_local(static_cast(LocalReferenceAllocator{}.newReference(getPlainJniReference()))); +local_ref weak_ref::lockLocal() const { + return adopt_local( + static_cast(LocalReferenceAllocator{}.newReference(get()))); } template -global_ref weak_ref::lockGlobal() { - return adopt_global(static_cast(GlobalReferenceAllocator{}.newReference(getPlainJniReference()))); +global_ref weak_ref::lockGlobal() const { + return adopt_global( + static_cast(GlobalReferenceAllocator{}.newReference(get()))); } template @@ -233,9 +305,8 @@ inline void swap( weak_ref& a, weak_ref& 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 inline basic_strong_ref& basic_strong_ref::operator=( basic_strong_ref&& 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::operator bool() const noexcept { } template -inline T basic_strong_ref::get() const noexcept { - return getPlainJniReference(); +inline auto basic_strong_ref::operator->() noexcept -> Repr* { + return &storage_.get(); } template -inline JObjectWrapper* basic_strong_ref::operator->() noexcept { - return &object_; +inline auto basic_strong_ref::operator->() const noexcept -> const Repr* { + return &storage_.get(); } template -inline const JObjectWrapper* basic_strong_ref::operator->() const noexcept { - return &object_; +inline auto basic_strong_ref::operator*() noexcept -> Repr& { + return storage_.get(); } template -inline JObjectWrapper& basic_strong_ref::operator*() noexcept { - return object_; -} - -template -inline const JObjectWrapper& basic_strong_ref::operator*() const noexcept { - return object_; +inline auto basic_strong_ref::operator*() const noexcept -> const Repr& { + return storage_.get(); } template @@ -298,33 +364,32 @@ inline void swap( basic_strong_ref& a, basic_strong_ref& 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 -inline constexpr alias_ref::alias_ref() noexcept - : object_{nullptr} +inline alias_ref::alias_ref() noexcept + : storage_{nullptr} {} template -inline constexpr alias_ref::alias_ref(std::nullptr_t) noexcept - : object_{nullptr} +inline alias_ref::alias_ref(std::nullptr_t) noexcept + : storage_{nullptr} {} template inline alias_ref::alias_ref(const alias_ref& other) noexcept - : object_{other.object_} + : storage_{other.get()} {} - template -inline alias_ref::alias_ref(T ref) noexcept - : object_{ref} { +inline alias_ref::alias_ref(javaobject ref) noexcept + : storage_(ref) { assert( LocalReferenceAllocator{}.verifyReference(ref) || GlobalReferenceAllocator{}.verifyReference(ref)); @@ -333,13 +398,13 @@ inline alias_ref::alias_ref(T ref) noexcept template template inline alias_ref::alias_ref(alias_ref other) noexcept - : object_{other.get()} + : storage_{other.get()} {} template template inline alias_ref::alias_ref(const basic_strong_ref& other) noexcept - : object_{other.get()} + : storage_{other.get()} {} template @@ -354,34 +419,90 @@ inline alias_ref::operator bool() const noexcept { } template -inline T facebook::jni::alias_ref::get() const noexcept { - return static_cast(object_.get()); +inline auto facebook::jni::alias_ref::get() const noexcept -> javaobject { + return storage_.jobj(); } template -inline JObjectWrapper* alias_ref::operator->() noexcept { - return &object_; +inline auto alias_ref::operator->() noexcept -> Repr* { + return &(**this); } template -inline const JObjectWrapper* alias_ref::operator->() const noexcept { - return &object_; +inline auto alias_ref::operator->() const noexcept -> const Repr* { + return &(**this); } template -inline JObjectWrapper& alias_ref::operator*() noexcept { - return object_; +inline auto alias_ref::operator*() noexcept -> Repr& { + return storage_.get(); } template -inline const JObjectWrapper& alias_ref::operator*() const noexcept { - return object_; +inline auto alias_ref::operator*() const noexcept -> const Repr& { + return storage_.get(); +} + +template +inline void alias_ref::set(javaobject ref) noexcept { + storage_.set(ref); } template inline void swap(alias_ref& a, alias_ref& 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 +enable_if_t(), local_ref> +static_ref_cast(const local_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return make_local(p); +} + +template +enable_if_t(), global_ref> +static_ref_cast(const global_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return make_global(p); +} + +template +enable_if_t(), alias_ref> +static_ref_cast(const alias_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return wrap_alias(p); +} + +template +auto dynamic_ref_cast(const RefType& ref) -> +enable_if_t(), decltype(static_ref_cast(ref))> +{ + if (! ref) { + return decltype(static_ref_cast(ref))(); + } + + std::string target_class_name{jtype_traits::base_name()}; + + // If not found, will throw an exception. + alias_ref target_class = findClassStatic(target_class_name.c_str()); + + local_ref 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(ref); } }} diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/References.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References.h index 575f2cc6e..b578c5c91 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/References.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/References.h @@ -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 -class JObjectWrapper; - - -template -class base_owned_ref; - -template -class basic_strong_ref; - -template -class weak_ref; - -template -class alias_ref; - - -/// A smart unique reference owning a local JNI reference -template -using local_ref = basic_strong_ref; - -/// A smart unique reference owning a global JNI reference -template -using global_ref = basic_strong_ref; - - /// Convenience function to wrap an existing local reference template -enable_if_t(), local_ref> adopt_local(T ref) noexcept; +local_ref adopt_local(T ref) noexcept; /// Convenience function to wrap an existing global reference template -enable_if_t(), global_ref> adopt_global(T ref) noexcept; +global_ref adopt_global(T ref) noexcept; /// Convenience function to wrap an existing weak reference template -enable_if_t(), weak_ref> adopt_weak_global(T ref) noexcept; +weak_ref adopt_weak_global(T ref) noexcept; +/// Swaps two owning references of the same type +template +void swap(weak_ref& a, weak_ref& b) noexcept; + +/// Swaps two owning references of the same type +template +void swap(basic_strong_ref& a, basic_strong_ref& b) noexcept; + +/** + * Retrieve the plain reference from a plain reference. + */ +template +enable_if_t(), T> getPlainJniReference(T ref); + +/** + * Retrieve the plain reference from an alias reference. + */ +template +JniType getPlainJniReference(alias_ref ref); + +/** + * Retrieve the plain JNI reference from any reference owned reference. + */ +template +JniType getPlainJniReference(const base_owned_ref& ref); + +class JObject; +class JClass; + +namespace detail { + +template +struct HasJniRefRepr : std::false_type {}; + +template +struct HasJniRefRepr::value, void>::type> : std::true_type { + using type = typename T::JniRefRepr; +}; + +template +struct RefReprType { + using type = typename std::conditional::value, typename HasJniRefRepr::type, JObjectWrapper>::type; + static_assert(std::is_base_of::value, + "Repr type missing JObject base."); + static_assert(std::is_same::type>::value, + "RefReprType not idempotent"); +}; + +template +struct RefReprType::value, void>::type> { + using type = T; + static_assert(std::is_base_of::value, + "Repr type missing JObject base."); + static_assert(std::is_same::type>::value, + "RefReprType not idempotent"); +}; + +template +struct JavaObjectType { + using type = typename RefReprType::type::javaobject; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct JavaObjectType> { + using type = T; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct JavaObjectType { + using type = T*; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct ReprStorage { + explicit ReprStorage(JniType obj) noexcept; + + void set(JniType obj) noexcept; + + Repr& get() noexcept; + const Repr& get() const noexcept; + JniType 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::type; + Storage storage_; +}; + +} // namespace detail + /** * Create a new local reference from an existing reference * @@ -160,33 +238,6 @@ template enable_if_t(), weak_ref>> make_weak(const T& r); - -/// Swaps two owning references of the same type -template -void swap(weak_ref& a, weak_ref& b) noexcept; - -/// Swaps two owning references of the same type -template -void swap(basic_strong_ref& a, basic_strong_ref& b) noexcept; - -/** - * Retrieve the plain reference from a plain reference. - */ -template -enable_if_t(), T> getPlainJniReference(T ref); - -/** - * Retrieve the plain reference from an alias reference. - */ -template -T getPlainJniReference(alias_ref ref); - -/** - * Retrieve the plain JNI reference from any reference owned reference. - */ -template -T getPlainJniReference(const base_owned_ref& ref); - /** * Compare two references to see if they refer to the same object */ @@ -201,19 +252,16 @@ template enable_if_t() && IsNonWeakReference(), bool> operator!=(const T1& a, const T2& b); - template class base_owned_ref { - - static_assert(IsPlainJniReference(), "T must be a JNI reference"); - public: + using javaobject = JniType; /** * 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; + detail::ReprStorage storage_; - JObjectWrapper 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 friend class base_owned_ref; @@ -278,29 +325,31 @@ class base_owned_ref { */ template class weak_ref : public base_owned_ref { - - static_assert(IsPlainJniReference(), "T must be a JNI reference"); - public: + using javaobject = JniType; - using PlainJniType = T; using Allocator = WeakGlobalReferenceAllocator; // This inherits non-default, non-copy, non-move ctors. using base_owned_ref::base_owned_ref; /// Create a null reference - constexpr weak_ref() noexcept + weak_ref() noexcept : base_owned_ref{} {} /// Create a null reference - constexpr explicit weak_ref(std::nullptr_t) noexcept + explicit weak_ref(std::nullptr_t) noexcept : base_owned_ref{nullptr} {} /// Copy constructor (note creates a new reference) weak_ref(const weak_ref& other) : base_owned_ref{other} {} + // This needs to be explicit to change its visibility. + template + weak_ref(const weak_ref& other) + : base_owned_ref{other} {} + /// Transfers ownership of an underlying reference from one unique reference to another weak_ref(weak_ref&& other) noexcept : base_owned_ref{std::move(other)} {} @@ -312,28 +361,26 @@ class weak_ref : public base_owned_ref { /// 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 lockLocal(); + local_ref lockLocal() const; // Creates an owned global reference to the referred object or to null if the object is reclaimed - global_ref lockGlobal(); + global_ref lockGlobal() const; private: - - using base_owned_ref::getPlainJniReference; - + // get/release/reset on weak_ref are not exposed to users. + using base_owned_ref::get; + using base_owned_ref::release; + using base_owned_ref::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{reference} {} - template friend class weak_ref; - friend weak_ref(), T>> - adopt_weak_global(T ref) noexcept; + friend weak_ref adopt_weak_global(javaobject ref) noexcept; friend void swap(weak_ref& a, weak_ref& b) noexcept; }; @@ -344,12 +391,10 @@ class weak_ref : public base_owned_ref { */ template class basic_strong_ref : public base_owned_ref { - - static_assert(IsPlainJniReference(), "T must be a JNI reference"); - + using typename base_owned_ref::Repr; public: + using javaobject = JniType; - 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 { using base_owned_ref::reset; /// Create a null reference - constexpr basic_strong_ref() noexcept + basic_strong_ref() noexcept : base_owned_ref{} {} /// Create a null reference - constexpr explicit basic_strong_ref(std::nullptr_t) noexcept + explicit basic_strong_ref(std::nullptr_t) noexcept : base_owned_ref{nullptr} {} /// Copy constructor (note creates a new reference) basic_strong_ref(const basic_strong_ref& other) : base_owned_ref{other} {} + // This needs to be explicit to change its visibility. + template + basic_strong_ref(const basic_strong_ref& other) + : base_owned_ref{other} {} + /// Transfers ownership of an underlying reference from one unique reference to another basic_strong_ref(basic_strong_ref&& other) noexcept : base_owned_ref{std::move(other)} {} @@ -379,6 +429,8 @@ class basic_strong_ref : public base_owned_ref { /// 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::get; /// Release the ownership of the reference and return the wrapped reference in an alias alias_ref releaseAlias() noexcept; @@ -386,37 +438,33 @@ class basic_strong_ref : public base_owned_ref { /// 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* operator->() noexcept; - - /// Access the functionality provided by the object wrappers - const JObjectWrapper* 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& operator*() noexcept; + Repr& operator*() noexcept; /// Provide a const reference to the underlying wrapper (be sure that it is non-null /// before invoking) - const JObjectWrapper& operator*() const noexcept; + const Repr& operator*() const noexcept; private: - using base_owned_ref::object_; - using base_owned_ref::getPlainJniReference; + using base_owned_ref::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{reference} {} - friend enable_if_t(), local_ref> adopt_local(T ref) noexcept; - friend enable_if_t(), global_ref> adopt_global(T ref) noexcept; + friend local_ref adopt_local(T ref) noexcept; + friend global_ref adopt_global(T ref) noexcept; friend void swap(basic_strong_ref& a, basic_strong_ref& b) noexcept; }; @@ -424,7 +472,7 @@ class basic_strong_ref : public base_owned_ref { template enable_if_t(), alias_ref> wrap_alias(T ref) noexcept; -/// Swaps to alias referencec of the same type +/// Swaps to alias reference of the same type template void swap(alias_ref& a, alias_ref& b) noexcept; @@ -437,35 +485,40 @@ void swap(alias_ref& a, alias_ref& b) noexcept; */ template class alias_ref { - - static_assert(IsPlainJniReference(), "T must be a JNI reference"); + using Repr = ReprType; public: - - using PlainJniType = T; - + using javaobject = JniType; /// 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(), T>> + template< + typename TOther, + typename = enable_if_t< + IsConvertible, javaobject>(), T> + > alias_ref(alias_ref other) noexcept; /// Wrap an existing alias reference of a type convertible to T - template(), T>> + template< + typename TOther, + typename AOther, + typename = enable_if_t< + IsConvertible, javaobject>(), T> + > alias_ref(const basic_strong_ref& 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* operator->() noexcept; + Repr* operator->() noexcept; /// Access the functionality provided by the object wrappers - const JObjectWrapper* operator->() const noexcept; + const Repr* operator->() const noexcept; /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking) - JObjectWrapper& operator*() noexcept; + Repr& operator*() noexcept; /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking) - const JObjectWrapper& operator*() const noexcept; + const Repr& operator*() const noexcept; private: - JObjectWrapper object_; + void set(javaobject ref) noexcept; + + detail::ReprStorage storage_; friend void swap(alias_ref& a, alias_ref& b) noexcept; }; @@ -510,6 +565,22 @@ private: bool hasFrame_; }; +template +enable_if_t(), local_ref> +static_ref_cast(const local_ref& ref) noexcept; + +template +enable_if_t(), global_ref> +static_ref_cast(const global_ref& ref) noexcept; + +template +enable_if_t(), alias_ref> +static_ref_cast(const alias_ref& ref) noexcept; + +template +auto dynamic_ref_cast(const RefType& ref) -> +enable_if_t(), decltype(static_ref_cast(ref))> ; + }} #include "References-inl.h" diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Registration-inl.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Registration-inl.h index 29414d195..2ac6abaf4 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/Registration-inl.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/Registration-inl.h @@ -17,27 +17,24 @@ namespace jni { namespace detail { -// convert to HybridClass* from jhybridobject -template -struct Convert< - T, typename std::enable_if< - std::is_base_of::type>::value>::type> { - typedef typename std::remove_pointer::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 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 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(obj), args...); + return (*func)(env, static_cast>(obj), args...); } catch (...) { translatePendingCppExceptionToJavaException(); return R{}; @@ -74,10 +71,10 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... arg template inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref, Args... args)) { struct funcWrapper { - static void call(JNIEnv*, jobject obj, - typename Convert::type>::jniType... args) { + JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj, + typename Convert::type>::jniType... args) { try { - (*func)(static_cast(obj), Convert::type>::fromJni(args)...); + (*func)(static_cast>(obj), Convert::type>::fromJni(args)...); } catch (...) { translatePendingCppExceptionToJavaException(); } @@ -93,11 +90,11 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref, Args... a struct funcWrapper { typedef typename Convert::type>::jniType jniRet; - static jniRet call(JNIEnv*, jobject obj, - typename Convert::type>::jniType... args) { + JNI_ENTRY_POINT static jniRet call(JNIEnv*, jobject obj, + typename Convert::type>::jniType... args) { try { return Convert::type>::toJniRet( - (*func)(static_cast(obj), Convert::type>::fromJni(args)...)); + (*func)(static_cast>(obj), Convert::type>::fromJni(args)...)); } catch (...) { translatePendingCppExceptionToJavaException(); return jniRet{}; @@ -114,8 +111,8 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref, Args... a template inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) { struct funcWrapper { - static void call(JNIEnv* env, jobject obj, - typename Convert::type>::jniType... args) { + JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, + typename Convert::type>::jniType... args) { try { try { auto aref = wrap_alias(static_cast(obj)); @@ -143,8 +140,8 @@ inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args) struct funcWrapper { typedef typename Convert::type>::jniType jniRet; - static jniRet call(JNIEnv* env, jobject obj, - typename Convert::type>::jniType... args) { + JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, + typename Convert::type>::jniType... args) { try { try { auto aref = wrap_alias(static_cast(obj)); @@ -176,16 +173,12 @@ inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) { template inline std::string makeDescriptor(R (*)(alias_ref, Args... args)) { - typedef typename Convert::type>::jniType jniRet; - return jmethod_traits::type>::jniType...)> - ::descriptor(); + return jmethod_traits_from_cxx::descriptor(); } template inline std::string makeDescriptor(R (C::*)(Args... args)) { - typedef typename Convert::type>::jniType jniRet; - return jmethod_traits::type>::jniType...)> - ::descriptor(); + return jmethod_traits_from_cxx::descriptor(); } } diff --git a/ReactAndroid/src/main/jni/first-party/jni/fbjni/TypeTraits.h b/ReactAndroid/src/main/jni/first-party/jni/fbjni/TypeTraits.h index b4bdd15ea..26472669d 100644 --- a/ReactAndroid/src/main/jni/first-party/jni/fbjni/TypeTraits.h +++ b/ReactAndroid/src/main/jni/first-party/jni/fbjni/TypeTraits.h @@ -11,6 +11,8 @@ #include +#include "References-forward.h" + namespace facebook { namespace jni { @@ -69,6 +71,25 @@ constexpr bool IsJniPrimitive() { return is_jni_primitive::value; } +/// Metafunction to determine whether a type is a JNI array of primitives or not +template +struct is_jni_primitive_array : + std::integral_constant::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value> {}; + +/// Helper to simplify use of is_jni_primitive_array +template +constexpr bool IsJniPrimitiveArray() { + return is_jni_primitive_array::value; +} + /// Metafunction to determine if a type is a scalar (primitive or reference) JNI type template struct is_jni_scalar : @@ -95,15 +116,6 @@ constexpr bool IsJniType() { return is_jni_type::value; } -template -class weak_global_ref; - -template -class basic_strong_ref; - -template -class alias_ref; - template struct is_non_weak_reference : std::integral_constant struct is_any_reference : std::integral_constant() || - IsInstantiationOf() || + IsInstantiationOf() || IsInstantiationOf() || IsInstantiationOf()> {}; @@ -131,19 +143,18 @@ constexpr bool IsAnyReference() { template struct reference_traits { - static_assert(IsPlainJniReference(), "Need a plain JNI reference"); - using plain_jni_reference_t = T; + using plain_jni_reference_t = JniType; + static_assert(IsPlainJniReference(), "Need a plain JNI reference"); }; template