Skip to content

Commit 99f8c5d

Browse files
javachefacebook-github-bot
authored andcommitted
Sync fbjni to React Native
Reviewed By: mhorowitz Differential Revision: D5086537 fbshipit-source-id: a95863113b3c63530a2550d29dfdc9626be86dc0
1 parent c6dd3d1 commit 99f8c5d

26 files changed

+876
-347
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include <stdlib.h>
6+
7+
#if defined(__ANDROID__)
8+
# include <sys/system_properties.h>
9+
#endif
10+
11+
namespace facebook {
12+
namespace build {
13+
14+
struct Build {
15+
static int getAndroidSdk() {
16+
static auto android_sdk = ([] {
17+
char sdk_version_str[PROP_VALUE_MAX];
18+
__system_property_get("ro.build.version.sdk", sdk_version_str);
19+
return atoi(sdk_version_str);
20+
})();
21+
return android_sdk;
22+
}
23+
};
24+
25+
} // build
26+
} // facebook

ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
namespace facebook {
1818
namespace jni {
1919

20+
namespace internal {
21+
struct CacheEnvTag {};
22+
}
23+
2024
// Keeps a thread-local reference to the current thread's JNIEnv.
2125
struct Environment {
2226
// May be null if this thread isn't attached to the JVM
@@ -70,7 +74,16 @@ class FBEXPORT ThreadScope {
7074
static void WithClassLoader(std::function<void()>&& runnable);
7175

7276
static void OnLoad();
77+
78+
// This constructor is only used internally by fbjni.
79+
ThreadScope(JNIEnv*, internal::CacheEnvTag);
7380
private:
81+
friend struct Environment;
82+
ThreadScope* previous_;
83+
// If the JNIEnv* is set, it is guaranteed to be valid at least through the
84+
// lifetime of this ThreadScope. The only case where that guarantee can be
85+
// made is when there is a java frame in the stack below this.
86+
JNIEnv* env_;
7487
bool attachedWithThisScope_;
7588
};
7689
}

ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Boxed.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ DEFINE_BOXED_PRIMITIVE(double, Double)
5858

5959
#undef DEFINE_BOXED_PRIMITIVE
6060

61+
struct JVoid : public jni::JavaClass<JVoid> {
62+
static auto constexpr kJavaDescriptor = "Ljava/lang/Void;";
63+
};
64+
6165
inline local_ref<jobject> autobox(alias_ref<jobject> val) {
6266
return make_local(val);
6367
}
6468

6569
}}
66-

ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses-inl.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ inline void JClass::registerNatives(std::initializer_list<NativeMethod> methods)
177177

178178
inline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {
179179
const auto env = internal::getEnv();
180-
const auto result = env->IsAssignableFrom(self(), other.get());
180+
// Ths method has behavior compatible with the
181+
// java.lang.Class#isAssignableFrom method. The order of the
182+
// arguments to the JNI IsAssignableFrom C function is "opposite"
183+
// from what some might expect, which makes this code look a little
184+
// odd, but it is correct.
185+
const auto result = env->IsAssignableFrom(other.get(), self());
181186
return result;
182187
}
183188

@@ -341,13 +346,6 @@ struct Convert<const char*> {
341346
};
342347
}
343348

344-
// jthrowable //////////////////////////////////////////////////////////////////////////////////////
345-
346-
inline local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
347-
static auto meth = javaClassStatic()->getMethod<javaobject(javaobject)>("initCause");
348-
return meth(self(), cause.get());
349-
}
350-
351349
// jtypeArray //////////////////////////////////////////////////////////////////////////////////////
352350

353351
namespace detail {

ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,14 +340,6 @@ class FBEXPORT JString : public JavaClass<JString, JObject, jstring> {
340340
FBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);
341341
FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
342342

343-
/// Wrapper to provide functionality to jthrowable references
344-
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
345-
public:
346-
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
347-
348-
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
349-
};
350-
351343
namespace detail {
352344
template<typename Target>
353345
class ElementProxy {
@@ -565,6 +557,24 @@ class PinnedPrimitiveArray {
565557
friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;
566558
};
567559

560+
struct JStackTraceElement : JavaClass<JStackTraceElement> {
561+
static auto constexpr kJavaDescriptor = "Ljava/lang/StackTraceElement;";
562+
563+
static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);
564+
};
565+
566+
/// Wrapper to provide functionality to jthrowable references
567+
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
568+
public:
569+
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";
570+
571+
using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;
572+
573+
local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
574+
local_ref<JStackTrace> getStackTrace();
575+
void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);
576+
};
577+
568578
#pragma push_macro("PlainJniRefMap")
569579
#undef PlainJniRefMap
570580
#define PlainJniRefMap(rtype, jtype) \

ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Exceptions.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
#include "References.h"
3333
#include "CoreClasses.h"
3434

35+
#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)
36+
// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.
37+
#define FBJNI_NO_EXCEPTION_PTR
38+
#endif
39+
3540
namespace facebook {
3641
namespace jni {
3742

@@ -108,9 +113,16 @@ template<typename... Args>
108113
}
109114

110115
// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
111-
// be thrown, it aborts the program. This is a noexcept function at C++ level.
112-
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept;
116+
// be thrown, it aborts the program.
117+
FBEXPORT void translatePendingCppExceptionToJavaException();
118+
119+
#ifndef FBJNI_NO_EXCEPTION_PTR
120+
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);
121+
#endif
122+
123+
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace();
113124

125+
FBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);
114126
// For convenience, some exception names in java.lang are available here.
115127

116128
const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException";

ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Hybrid.h

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,59 @@ class BaseHybridClass {
2929

3030
struct FBEXPORT HybridData : public JavaClass<HybridData> {
3131
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;";
32-
void setNativePointer(std::unique_ptr<BaseHybridClass> new_value);
33-
BaseHybridClass* getNativePointer();
3432
static local_ref<HybridData> create();
3533
};
3634

35+
class HybridDestructor : public JavaClass<HybridDestructor> {
36+
public:
37+
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/HybridData$Destructor;";
38+
39+
template <typename T=detail::BaseHybridClass>
40+
T* getNativePointer() {
41+
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
42+
auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));
43+
if (!value) {
44+
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
45+
}
46+
return value;
47+
}
48+
49+
template <typename T=detail::BaseHybridClass>
50+
void setNativePointer(std::unique_ptr<T> new_value) {
51+
static auto pointerField = javaClassStatic()->getField<jlong>("mNativePointer");
52+
auto old_value = std::unique_ptr<T>(reinterpret_cast<T*>(getFieldValue(pointerField)));
53+
if (new_value && old_value) {
54+
FBCRASH("Attempt to set C++ native pointer twice");
55+
}
56+
setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));
57+
}
58+
};
59+
60+
template<typename T>
61+
detail::BaseHybridClass* getNativePointer(T t) {
62+
return getHolder(t)->getNativePointer();
63+
}
64+
65+
template<typename T>
66+
void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {
67+
getHolder(t)->setNativePointer(std::move(new_value));
68+
}
69+
70+
template<typename T>
71+
local_ref<HybridDestructor> getHolder(T t) {
72+
static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor");
73+
return t->getFieldValue(holderField);
74+
}
75+
76+
// JavaClass for HybridClassBase
77+
struct FBEXPORT HybridClassBase : public JavaClass<HybridClassBase> {
78+
constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridClassBase;";
79+
80+
static bool isHybridClassBase(alias_ref<jclass> jclass) {
81+
return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);
82+
}
83+
};
84+
3785
template <typename Base, typename Enabled = void>
3886
struct HybridTraits {
3987
// This static assert should actually always fail if we don't use one of the
@@ -139,7 +187,7 @@ class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
139187

140188
static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {
141189
auto hybridData = detail::HybridData::create();
142-
hybridData->setNativePointer(std::move(cxxPart));
190+
setNativePointer(hybridData, std::move(cxxPart));
143191
return hybridData;
144192
}
145193

@@ -148,6 +196,11 @@ class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
148196
return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
149197
}
150198

199+
template <typename... Args>
200+
static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {
201+
setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));
202+
}
203+
151204
public:
152205
// Factory method for creating a hybrid object where the arguments
153206
// are used to initialize the C++ part directly without passing them
@@ -161,11 +214,23 @@ class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
161214
// C++ object fails, or any JNI methods throw.
162215
template <typename... Args>
163216
static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {
164-
auto hybridData = makeCxxInstance(std::forward<Args>(args)...);
165-
return JavaPart::newInstance(hybridData);
217+
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());
218+
auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
219+
220+
local_ref<JavaPart> result;
221+
if (isHybrid) {
222+
result = JavaPart::newInstance();
223+
setNativePointer(result, std::move(cxxPart));
224+
}
225+
else {
226+
auto hybridData = makeHybridData(std::move(cxxPart));
227+
result = JavaPart::newInstance(hybridData);
228+
}
229+
230+
return result;
166231
}
167232

168-
// TODO? Create reusable interface for Allocatable classes and use it to
233+
// TODO? Create reusable interface for Allocatable classes and use it to
169234
// strengthen type-checking (and possibly provide a default
170235
// implementation of allocate().)
171236
template <typename... Args>
@@ -195,17 +260,25 @@ class FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {
195260

196261
template <typename T, typename B>
197262
inline T* HybridClass<T, B>::JavaPart::cthis() {
198-
static auto field =
199-
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
200-
auto hybridData = this->getFieldValue(field);
201-
if (!hybridData) {
202-
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
263+
detail::BaseHybridClass* result = 0;
264+
static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());
265+
if (isHybrid) {
266+
result = getNativePointer(this);
267+
} else {
268+
static auto field =
269+
HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>("mHybridData");
270+
auto hybridData = this->getFieldValue(field);
271+
if (!hybridData) {
272+
throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException");
273+
}
274+
275+
result = getNativePointer(hybridData);
203276
}
204-
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
205-
T* value = static_cast<T*>(hybridData->getNativePointer());
277+
206278
// This would require some serious programmer error.
207-
FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field");
208-
return value;
279+
FBASSERTMSGF(result != 0, "Incorrect C++ type in hybrid field");
280+
// I'd like to use dynamic_cast here, but -fno-rtti is the default.
281+
return static_cast<T*>(result);
209282
};
210283

211284
template <typename T, typename B>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include <fb/visibility.h>
6+
7+
#include "CoreClasses.h"
8+
9+
namespace facebook {
10+
namespace jni {
11+
12+
/**
13+
* Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.
14+
* A WeakGlobalRef can yield a strong reference even after the object has been
15+
* finalized. See comment in the djinni library.
16+
* https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp
17+
*/
18+
template<typename T = jobject>
19+
class JWeakReference : public JavaClass<JWeakReference<T>> {
20+
21+
typedef JavaClass<JWeakReference<T>> JavaBase_;
22+
23+
public:
24+
static constexpr const char* kJavaDescriptor = "Ljava/lang/ref/WeakReference;";
25+
26+
static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {
27+
return JavaBase_::newInstance(static_ref_cast<jobject>(object));
28+
}
29+
30+
local_ref<T> get() const {
31+
static auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>("get");
32+
return static_ref_cast<T>(method(JavaBase_::self()));
33+
}
34+
};
35+
36+
}
37+
}

0 commit comments

Comments
 (0)