Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public void loadApp(
ReactInstanceManager.Builder builder =
ReactTestHelper.getReactTestFactory().getReactInstanceManagerBuilder()
.setApplication(getApplication())
.setUseOldBridge(true)
.setBundleAssetName(bundleName)
// By not setting a JS module name, we force the bundle to be always loaded from
// assets, not the devserver, even if dev mode is enabled (such as when testing redboxes).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public void clear() {

protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setUseOldBridge(true)
.setApplication(mApplication)
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
Expand Down
57 changes: 28 additions & 29 deletions ReactAndroid/src/main/jni/first-party/fb/include/fb/Environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*/

#pragma once
#include <functional>
#include <string>
#include <jni.h>

Expand All @@ -22,35 +21,44 @@ struct Environment {
// May be null if this thread isn't attached to the JVM
FBEXPORT static JNIEnv* current();
static void initialize(JavaVM* vm);

// There are subtle issues with calling the next functions directly. It is
// much better to always use a ThreadScope to manage attaching/detaching for
// you.
FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached();
FBEXPORT static void detachCurrentThread();
};

/**
* RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it
* exits will cause a crash, as will calling Detach an extra time, and this guard class helps
* keep that straight. In addition, it remembers whether it performed the attach or not, so it
* is safe to nest it with itself or with non-fbjni code that manages the attachment correctly.
* RAII Object that attaches a thread to the JVM. Failing to detach from a
* thread before it
* exits will cause a crash, as will calling Detach an extra time, and this
* guard class helps
* keep that straight. In addition, it remembers whether it performed the attach
* or not, so it
* is safe to nest it with itself or with non-fbjni code that manages the
* attachment correctly.
*
* Potential concerns:
* - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the
* - Attaching to the JVM is fast (~100us on MotoG), but ideally you would
* attach while the
* app is not busy.
* - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that
* there is no Java code on the current stack or you run the risk of a crash like:
* - Having a thread detach at arbitrary points is not safe in Dalvik; you need
* to be sure that
* there is no Java code on the current stack or you run the risk of a crash
* like:
* ERROR: detaching thread with interp frames (count=18)
* (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)
* ThreadScope won't do a detach if the thread was already attached before the guard is
* (More detail at
* https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)
* ThreadScope won't do a detach if the thread was already attached before
* the guard is
* instantiated, but there's probably some usage that could trip this up.
* - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language
* classes, not any of our application's classes. This will be different behavior than threads
* that were initiated on the Java side. A workaround is to pass a global reference for a
* class or instance to the new thread; this bypasses the need for the class loader.
* (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
* If you need access to the application's classes, you can use ThreadScope::WithClassLoader.
* - Newly attached C++ threads only get the bootstrap class loader -- i.e.
* java language
* classes, not any of our application's classes. This will be different
* behavior than threads
* that were initiated on the Java side. A workaround is to pass a global
* reference for a
* class or instance to the new thread; this bypasses the need for the class
* loader.
* (See
* http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
*/
class FBEXPORT ThreadScope {
public:
Expand All @@ -61,15 +69,6 @@ class FBEXPORT ThreadScope {
ThreadScope& operator=(ThreadScope&&) = delete;
~ThreadScope();

/**
* This runs the closure in a scope with fbjni's classloader. This should be
* the same classloader as the rest of the application and thus anything
* running in the closure will have access to the same classes as in a normal
* java-create thread.
*/
static void WithClassLoader(std::function<void()>&& runnable);

static void OnLoad();
private:
bool attachedWithThisScope_;
};
Expand Down
20 changes: 0 additions & 20 deletions ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,11 @@
# endif
#endif

// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as
// a C++ exception.
#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \
::facebook::jni::throwPendingJniExceptionAsCppException()

// If the condition is true, throws a JniException object, which wraps the pending JNI Java
// exception if any. If no pending exception is found, throws a JniException object that wraps a
// RuntimeException throwable. 
#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \
::facebook::jni::throwCppExceptionIf(CONDITION)

/// @cond INTERNAL

namespace facebook {
namespace jni {

FBEXPORT void throwPendingJniExceptionAsCppException();
FBEXPORT void throwCppExceptionIf(bool condition);

[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);
[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);
template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);


/**
* This needs to be called at library load time, typically in your JNI_OnLoad method.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ class AContext : public JavaClass<AContext> {
return method(self());
}

local_ref<JFile::javaobject> getFilesDir() {
static auto method = getClass()->getMethod<JFile::javaobject()>("getFilesDir");
return method(self());
}
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,13 +341,6 @@ struct Convert<const char*> {
};
}

// jthrowable //////////////////////////////////////////////////////////////////////////////////////

inline local_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {
static auto meth = javaClassStatic()->getMethod<javaobject(javaobject)>("initCause");
return meth(self(), cause.get());
}

// jtypeArray //////////////////////////////////////////////////////////////////////////////////////

namespace detail {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class FBEXPORT JavaClass : public Base {
/// the Java class actually has (i.e. with static create() functions).
template<typename... Args>
static local_ref<T> newInstance(Args... args) {
return detail::newInstance<T>(args...);
return detail::newInstance<JavaClass>(args...);
}

javaobject self() const noexcept;
Expand Down Expand Up @@ -344,8 +344,6 @@ FBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);
class FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {
public:
static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;";

local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);
};

namespace detail {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,30 @@
#include <fb/visibility.h>

#include "Common.h"
#include "References.h"
#include "CoreClasses.h"

namespace facebook {
namespace jni {
// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as
// a C++ exception.
#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \
::facebook::jni::throwPendingJniExceptionAsCppException()

class JThrowable;
// If the condition is true, throws a JniException object, which wraps the pending JNI Java
// exception if any. If no pending exception is found, throws a JniException object that wraps a
// RuntimeException throwable. 
#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \
::facebook::jni::throwCppExceptionIf(CONDITION)

class JCppException : public JavaClass<JCppException, JThrowable> {
public:
static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;";
namespace facebook {
namespace jni {

static local_ref<JCppException> create(const char* str) {
return newInstance(make_jstring(str));
}
namespace internal {
void initExceptionHelpers();
}

static local_ref<JCppException> create(const std::exception& ex) {
return newInstance(make_jstring(ex.what()));
}
};
/**
* Before using any of the state initialized above, call this. It
* will assert if initialization has not yet occurred.
*/
FBEXPORT void assertIfExceptionsNotInitialized();

// JniException ////////////////////////////////////////////////////////////////////////////////////

Expand All @@ -63,22 +67,23 @@ class JCppException : public JavaClass<JCppException, JThrowable> {
class FBEXPORT JniException : public std::exception {
public:
JniException();
~JniException();

explicit JniException(alias_ref<jthrowable> throwable);
explicit JniException(jthrowable throwable);

JniException(JniException &&rhs);

JniException(const JniException &other);

local_ref<JThrowable> getThrowable() const noexcept;
~JniException() noexcept;

jthrowable getThrowable() const noexcept;

virtual const char* what() const noexcept;

void setJavaException() const noexcept;

private:
global_ref<JThrowable> throwable_;
jthrowable throwableGlobalRef_;
mutable std::string what_;
mutable bool isMessageExtracted_;
const static std::string kExceptionMessageFailure_;
Expand All @@ -90,8 +95,16 @@ class FBEXPORT JniException : public std::exception {

// Functions that throw C++ exceptions

FBEXPORT void throwPendingJniExceptionAsCppException();

FBEXPORT void throwCppExceptionIf(bool condition);

static const int kMaxExceptionMessageBufferSize = 512;

[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);

[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);

// These methods are the preferred way to throw a Java exception from
// a C++ function. They create and throw a C++ exception which wraps
// a Java exception, so the C++ flow is interrupted. Then, when
Expand All @@ -100,6 +113,7 @@ static const int kMaxExceptionMessageBufferSize = 512;
// thrown to the java caller.
template<typename... Args>
[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {
assertIfExceptionsNotInitialized();
int msgSize = snprintf(nullptr, 0, fmt, args...);

char *msg = (char*) alloca(msgSize + 1);
Expand All @@ -109,7 +123,7 @@ template<typename... Args>

// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't
// be thrown, it aborts the program. This is a noexcept function at C++ level.
FBEXPORT void translatePendingCppExceptionToJavaException() noexcept;
void translatePendingCppExceptionToJavaException() noexcept;

// For convenience, some exception names in java.lang are available here.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ local_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {
}


inline bool needsSlowPath(alias_ref<jobject> obj) {
bool needsSlowPath(alias_ref<jobject> obj) {
#if defined(__ANDROID__)
// On Android 6.0, art crashes when attempting to call a function on a Proxy.
// So, when we detect that case we must use the safe, slow workaround. That is,
Expand All @@ -88,6 +88,19 @@ inline bool needsSlowPath(alias_ref<jobject> obj) {

}

template <typename... Args>
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {
static auto invoke = findClassStatic("java/lang/reflect/Method")
->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>("invoke");
// TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.
auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!reflected) throw JniException();
auto argsArray = makeArgsArray(args...);
// No need to check for exceptions since invoke is itself a JMethod that will do that for us.
return invoke(reflected, self.get(), argsArray.get());
}

template<typename... Args>
inline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {
const auto env = Environment::current();
Expand Down Expand Up @@ -272,19 +285,6 @@ class JNonvirtualMethod<R(Args...)> : public JMethodBase {
friend class JClass;
};

template <typename... Args>
local_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {
static auto invoke = findClassStatic("java/lang/reflect/Method")
->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>("invoke");
// TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.
auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));
FACEBOOK_JNI_THROW_PENDING_EXCEPTION();
if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method");
auto argsArray = makeArgsArray(args...);
// No need to check for exceptions since invoke is itself a JMethod that will do that for us.
return invoke(reflected, self.get(), argsArray.get());
}


// JField<T> ///////////////////////////////////////////////////////////////////////////////////////

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <new>
#include <atomic>

#include "Exceptions.h"

namespace facebook {
namespace jni {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ namespace detail {
void utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength);
size_t modifiedLength(const std::string& str);
size_t modifiedLength(const uint8_t* str, size_t* length);
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept;
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;
std::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len);
std::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len);

}

Expand Down
Loading