Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSC for Android #1842

Merged
merged 14 commits into from
Mar 29, 2021
1 change: 1 addition & 0 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ NativeReanimatedModule::NativeReanimatedModule(std::shared_ptr<CallInvoker> jsIn
workletsCache(std::make_shared<WorkletsCache>()),
scheduler(scheduler)
{

auto requestAnimationFrame = [=](FrameCallback callback) {
frameCallbacks.push_back(callback);
maybeRequestRender();
Expand Down
2 changes: 0 additions & 2 deletions Common/cpp/SharedItems/ShareableValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
auto& hostRuntime = hostFunctionWrapper->value->hostRuntime;
if (hostRuntime == &rt) {
// function is accessed from the same runtime it was crated, we just return same function obj

return jsi::Value(rt, *hostFunctionWrapper->value->getPureFunction().get());
} else {
// function is accessed from a different runtime, we wrap function in host func that'd enqueue
Expand Down Expand Up @@ -305,7 +304,6 @@ jsi::Value ShareableValue::toJSValue(jsi::Runtime &rt) {
for (int i = 0; i < params.size(); ++i) {
args[i] = params[i]->getValue(*hostRuntime);
}

jsi::Value returnedValue = hostFunction->getPureFunction().get()->call(*hostRuntime,
static_cast<const jsi::Value*>(args),
(size_t)params.size());
Expand Down
2 changes: 1 addition & 1 deletion Common/cpp/headers/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec
{
friend ShareableValue;
friend MutableValue;

public:
NativeReanimatedModule(std::shared_ptr<CallInvoker> jsInvoker,
std::shared_ptr<Scheduler> scheduler,
Expand Down
3 changes: 0 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ task prepareHermes() {
}
}

// Create Android.mk library module based on jsc from npm
task prepareJSC {
doLast {
def jscPackagePath = findNodeModulePath(projectDir, "jsc-android")
Expand Down Expand Up @@ -407,8 +406,6 @@ task buildReanimated(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversi
"FIRST_PARTY=$reactNativeThirdParty/../first-party",
"REACT_NATIVE_JNI=$reactNative/ReactAndroid/src/main/jni/react/jni",
"REACT_NATIVE_REACT=$reactNative/ReactAndroid/src/main/jni/react",
"HERMES_ENGINE=$projectDir/../node_modules/hermes-engine",
"VM=HERMES",
"NATIVE_DEBUG=${debugNativeLibraries}",
"-C", file("src/main/JNI").absolutePath,
"--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors(),
Expand Down
3 changes: 1 addition & 2 deletions android/src/main/JNI/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../Common/cpp/headers/Registries
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../Common/cpp/headers/SharedItems
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../Common/cpp/headers/SpecTools
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../Common/cpp/headers/Tools
LOCAL_C_INCLUDES += $(HERMES_ENGINE)/android/include

LOCAL_CFLAGS += -DONANDROID -fexceptions -frtti

LOCAL_STATIC_LIBRARIES := libjsi callinvokerholder
LOCAL_SHARED_LIBRARIES := libhermes libfolly_json libfbjni libreactnativejni
LOCAL_SHARED_LIBRARIES := libfolly_json libfbjni libreactnativejni

include $(BUILD_SHARED_LIBRARY)

Expand Down
9 changes: 1 addition & 8 deletions android/src/main/JNI/react/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ include $(BUILD_SHARED_LIBRARY)
$(call import-module,folly)
$(call import-module,fb)
$(call import-module,fbjni)
#$(call import-module,jsc)
$(call import-module,fbgloginit)
$(call import-module,yogajni)
$(call import-module,cxxreact)
$(call import-module,jsi)
$(call import-module,jsiexecutor)
$(call import-module,callinvoker)
$(call import-module,hermes)

ifeq ($(shell test $(REACT_NATIVE_TARGET_VERSION) -ge 64; echo $$?),0)
$(call import-module,reactperflogger)
$(call import-module,runtimeexecutor)
Expand All @@ -79,13 +79,6 @@ ifeq ($(shell test $(REACT_NATIVE_TARGET_VERSION) -ge 64; echo $$?),0)
endif
include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk

# TODO(ramanpreet):
# Why doesn't this import-module call generate a jscexecutor.so file?
# $(call import-module,jscexecutor)

#include $(REACT_SRC_DIR)/jscexecutor/Android.mk
#include $(REACT_SRC_DIR)/../hermes/reactexecutor/Android.mk
include $(REACT_SRC_DIR)/../hermes/instrumentation/Android.mk
include $(REACT_SRC_DIR)/modules/blob/jni/Android.mk


22 changes: 14 additions & 8 deletions android/src/main/cpp/NativeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#include <fbjni/fbjni.h>
#include <jsi/jsi.h>
#include <hermes/hermes.h>
#include <react/jni/ReadableNativeArray.h>
#include <react/jni/ReadableNativeMap.h>
#include <jsi/JSIDynamic.h>
Expand Down Expand Up @@ -31,21 +30,24 @@ NativeProxy::NativeProxy(
{
}

JavaScriptExecutorHolder* NativeProxy::_javaScriptExecutor = NULL;

jni::local_ref<NativeProxy::jhybriddata> NativeProxy::initHybrid(
jni::alias_ref<jhybridobject> jThis,
jlong jsContext,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<AndroidScheduler::javaobject> androidScheduler)
jni::alias_ref<AndroidScheduler::javaobject> androidScheduler,
JavaScriptExecutorHolder* javaScriptExecutor)
{
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
auto scheduler = androidScheduler->cthis()->getScheduler();
scheduler->setJSCallInvoker(jsCallInvoker);
_javaScriptExecutor = javaScriptExecutor;
return makeCxxInstance(jThis, (jsi::Runtime *)jsContext, jsCallInvoker, scheduler);
}

void NativeProxy::installJSIBindings()
{

auto propUpdater = [this](jsi::Runtime &rt, int viewTag, const jsi::Value &viewName, const jsi::Object &props) {
// viewName is for iOS only, we skip it here
this->updateProps(rt, viewTag, props);
Expand Down Expand Up @@ -90,7 +92,11 @@ void NativeProxy::installJSIBindings()
scrollTo(viewTag, x, y, animated);
};

std::unique_ptr<jsi::Runtime> animatedRuntime = facebook::hermes::makeHermesRuntime();
std::shared_ptr<ExecutorDelegate> delegate = std::shared_ptr<ExecutorDelegate>();
std::shared_ptr<MessageQueueThread> jsQueue = std::shared_ptr<MessageQueueThread>();
factory = _javaScriptExecutor->getExecutorFactory();
executor = factory.get()->createJSExecutor(delegate, jsQueue);
animatedRuntime.reset(static_cast<jsi::Runtime*>(executor.get()->getJavaScriptContext()));

std::shared_ptr<ErrorHandler> errorHandler = std::make_shared<AndroidErrorHandler>(scheduler_);

Expand All @@ -104,7 +110,7 @@ void NativeProxy::installJSIBindings()

auto module = std::make_shared<NativeReanimatedModule>(jsCallInvoker_,
scheduler_,
std::move(animatedRuntime),
animatedRuntime,
errorHandler,
propObtainer,
platformDepMethodsHolder);
Expand All @@ -118,9 +124,9 @@ void NativeProxy::installJSIBindings()
});

runtime_->global().setProperty(
*runtime_,
jsi::PropNameID::forAscii(*runtime_, "__reanimatedModuleProxy"),
jsi::Object::createFromHostObject(*runtime_, module));
*runtime_,
jsi::PropNameID::forAscii(*runtime_, "__reanimatedModuleProxy"),
jsi::Object::createFromHostObject(*runtime_, module));
}

bool NativeProxy::isAnyHandlerWaitingForEvent(std::string s) {
Expand Down
8 changes: 7 additions & 1 deletion android/src/main/cpp/headers/NativeProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <react/jni/WritableNativeMap.h>
#include "NativeReanimatedModule.h"
#include <ReactCommon/CallInvokerHolder.h>
#include <react/jni/JavaScriptExecutorHolder.h>
#include <memory>
#include <unordered_map>

Expand Down Expand Up @@ -82,14 +83,19 @@ class NativeProxy : public jni::HybridClass<NativeProxy> {
jni::alias_ref<jhybridobject> jThis,
jlong jsContext,
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
jni::alias_ref<AndroidScheduler::javaobject> scheduler);
jni::alias_ref<AndroidScheduler::javaobject> scheduler,
JavaScriptExecutorHolder* javaScriptExecutor);
static void registerNatives();
static JavaScriptExecutorHolder* _javaScriptExecutor;


private:
friend HybridBase;
jni::global_ref<NativeProxy::javaobject> javaPart_;
jsi::Runtime *runtime_;
std::shared_ptr<JSExecutorFactory> factory;
std::unique_ptr<JSExecutor> executor;
std::unique_ptr<jsi::Runtime> animatedRuntime;
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
std::shared_ptr<NativeReanimatedModule> _nativeReanimatedModule;
std::shared_ptr<Scheduler> scheduler_;
Expand Down
64 changes: 55 additions & 9 deletions android/src/main/java/com/swmansion/reanimated/NativeProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@

import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.JSIModule;
import com.facebook.react.bridge.JavaScriptExecutor;
import com.facebook.react.bridge.JavaScriptExecutorFactory;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
import com.facebook.react.jscexecutor.JSCExecutorFactory;
import com.facebook.soloader.SoLoader;

public class NativeProxy {

static {
Expand Down Expand Up @@ -73,18 +73,63 @@ public void receiveTouches(String eventName, WritableArray touches, WritableArra
private final HybridData mHybridData;
private NodesManager mNodesManager;
private final WeakReference<ReactApplicationContext> mContext;
private JavaScriptExecutor mJavaScriptExecutor;
private Scheduler mScheduler = null;

public NativeProxy(ReactApplicationContext context) {
CallInvokerHolderImpl holder = (CallInvokerHolderImpl)context.getCatalystInstance().getJSCallInvokerHolder();
try {
mJavaScriptExecutor = getDefaultJSExecutorFactory(context).create();
} catch (Exception jscE) {
jscE.printStackTrace();
}

CallInvokerHolderImpl holder = (CallInvokerHolderImpl)context.getCatalystInstance().getJSCallInvokerHolder();
mScheduler = new Scheduler(context);
mHybridData = initHybrid(context.getJavaScriptContextHolder().get(), holder, mScheduler);
mHybridData = initHybrid(context.getJavaScriptContextHolder().get(), holder, mScheduler, mJavaScriptExecutor);
mContext = new WeakReference<>(context);
prepare();
}

private native HybridData initHybrid(long jsContext, CallInvokerHolderImpl jsCallInvokerHolder, Scheduler scheduler);
// method from React native
private JavaScriptExecutorFactory getDefaultJSExecutorFactory(ReactApplicationContext context) {
try {
// If JSC is included, use it as normal
SoLoader.init(context, /* native exopackage */ false);
SoLoader.loadLibrary("jscexecutor");
return new JSCExecutorFactory("Reanimated", "Reanimated");
} catch (UnsatisfiedLinkError jscE) {
// https://github.com/facebook/hermes/issues/78 shows that
// people who aren't trying to use Hermes are having issues.
// https://github.com/facebook/react-native/issues/25923#issuecomment-554295179
// includes the actual JSC error in at least one case.
//
// So, if "__cxa_bad_typeid" shows up in the jscE exception
// message, then we will assume that's the failure and just
// throw now.

if (jscE.getMessage().contains("__cxa_bad_typeid")) {
throw jscE;
}

// Otherwise use Hermes
try {
return new HermesExecutorFactory();
} catch (UnsatisfiedLinkError hermesE) {
// If we get here, either this is a JSC build, and of course
// Hermes failed (since it's not in the APK), or it's a Hermes
// build, and Hermes had a problem.

// We suspect this is a JSC issue (it's the default), so we
// will throw that exception, but we will print hermesE first,
// since it could be a Hermes issue and we don't want to
// swallow that.
hermesE.printStackTrace();
throw jscE;
}
}
}

private native HybridData initHybrid(long jsContext, CallInvokerHolderImpl jsCallInvokerHolder, Scheduler scheduler, JavaScriptExecutor mJavaScriptExecutor);
private native void installJSIBindings();

public native boolean isAnyHandlerWaitingForEvent(String eventName);
Expand Down Expand Up @@ -128,6 +173,7 @@ private void registerEventHandler(EventHandler handler) {
public void onCatalystInstanceDestroy() {
mScheduler.deactivate();
mHybridData.resetNative();
mJavaScriptExecutor.close();
}

public void prepare() {
Expand Down