diff --git a/React/CxxBridge/RCTJSIExecutorRuntimeInstaller.mm b/React/CxxBridge/RCTJSIExecutorRuntimeInstaller.mm index f5c82368f31920..da0f919a3f86af 100644 --- a/React/CxxBridge/RCTJSIExecutorRuntimeInstaller.mm +++ b/React/CxxBridge/RCTJSIExecutorRuntimeInstaller.mm @@ -22,8 +22,14 @@ bindNativeLogger(runtime, iosLoggingBinder); PerformanceNow iosPerformanceNowBinder = []() { - auto time = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast(time).count(); + auto time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast( + time.time_since_epoch()) + .count(); + + constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0; + + return duration / NANOSECONDS_IN_MILLISECOND; }; bindNativePerformanceNow(runtime, iosPerformanceNowBinder); diff --git a/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java b/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java new file mode 100644 index 00000000000000..7e960263cc55da --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react; + +/** + * An enum that specifies the JS Engine to be used in the app Old Logic uses the legacy code + * JSC/HERMES loads the respective engine using the revamped logic + */ +public enum JSInterpreter { + OLD_LOGIC, + JSC, + HERMES +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index 199e4033cf01dd..3d140cd36a9911 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -66,6 +66,7 @@ public class ReactInstanceManagerBuilder { private @Nullable Map mCustomPackagerCommandHandlers; private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; + private JSInterpreter jsInterpreter = JSInterpreter.OLD_LOGIC; /* package protected */ ReactInstanceManagerBuilder() {} @@ -125,6 +126,31 @@ public ReactInstanceManagerBuilder setJSBundleLoader(JSBundleLoader jsBundleLoad return this; } + /** + * Sets the jsEngine as JSC or HERMES as per the setJsEngineAsHermes call Uses the enum {@link + * JSInterpreter} + * + * @param jsInterpreter + */ + private void setJSEngine(JSInterpreter jsInterpreter) { + this.jsInterpreter = jsInterpreter; + } + + /** + * Utility setter to set the required JSEngine as HERMES or JSC Defaults to OLD_LOGIC if not + * called by the host app + * + * @param hermesEnabled hermesEnabled = true sets the JS Engine as HERMES and JSC otherwise + */ + public ReactInstanceManagerBuilder setJsEngineAsHermes(boolean hermesEnabled) { + if (hermesEnabled) { + setJSEngine(JSInterpreter.HERMES); + } else { + setJSEngine(JSInterpreter.JSC); + } + return this; + } + /** * Path to your app's main module on Metro. This is used when reloading JS during development. All * paths are relative to the root folder the packager is serving files from. Examples: {@code @@ -345,41 +371,35 @@ public ReactInstanceManager build() { private JavaScriptExecutorFactory getDefaultJSExecutorFactory( String appName, String deviceName, Context applicationContext) { - try { - // If JSC is included, use it as normal - initializeSoLoaderIfNecessary(applicationContext); - JSCExecutor.loadLibrary(); - return new JSCExecutorFactory(appName, deviceName); - } 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 + // Relying solely on try catch block and loading jsc even when + // project is using hermes can lead to launch-time crashes especially in + // monorepo architectures and hybrid apps using both native android + // and react native. + // So we can use the value of enableHermes received by the constructor + // to decide which library to load at launch + + // if nothing is specified, use old loading method + // else load the required engine + if (jsInterpreter == JSInterpreter.OLD_LOGIC) { try { + // If JSC is included, use it as normal + initializeSoLoaderIfNecessary(applicationContext); + JSCExecutor.loadLibrary(); + return new JSCExecutorFactory(appName, deviceName); + } catch (UnsatisfiedLinkError jscE) { + if (jscE.getMessage().contains("__cxa_bad_typeid")) { + throw jscE; + } HermesExecutor.loadLibrary(); 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; } + } else if (jsInterpreter == JSInterpreter.HERMES) { + HermesExecutor.loadLibrary(); + return new HermesExecutorFactory(); + } else { + JSCExecutor.loadLibrary(); + return new JSCExecutorFactory(appName, deviceName); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 3d5ee968202ac3..1cadadac30f88d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -719,12 +719,22 @@ private void attachToReactInstanceManager() { // React Native requires that the RootView id be managed entirely by React Native, and will // crash in addRootView/startSurface if the native View id isn't set to NO_ID. + + // This behavior can not be guaranteed in hybrid apps that have a native android layer over + // which reactRootViews are added and the native views need to have ids on them in order to + // work. + // Hence this can cause unnecessary crashes at runtime for hybrid apps. + // So converting this to a soft exception such that pure react-native devs can still see the + // warning while hybrid apps continue to run without crashes + if (getId() != View.NO_ID) { - throw new IllegalViewOperationException( - "Trying to attach a ReactRootView with an explicit id already set to [" - + getId() - + "]. React Native uses the id field to track react tags and will overwrite this" - + " field. If that is fine, explicitly overwrite the id field to View.NO_ID."); + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalViewOperationException( + "Trying to attach a ReactRootView with an explicit id already set to [" + + getId() + + "]. React Native uses the id field to track react tags and will overwrite this" + + " field. If that is fine, explicitly overwrite the id field to View.NO_ID.")); } try { diff --git a/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp b/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp index a338db11ee36a6..042805ff66da38 100644 --- a/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp +++ b/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp @@ -12,8 +12,14 @@ namespace facebook { namespace react { double reactAndroidNativePerformanceNowHook() { - auto time = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast(time).count(); + auto time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast( + time.time_since_epoch()) + .count(); + + constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0; + + return duration / NANOSECONDS_IN_MILLISECOND; } } // namespace react diff --git a/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java new file mode 100644 index 00000000000000..6af916ffa6f35d --- /dev/null +++ b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/JSInterpreter.java @@ -0,0 +1,10 @@ +--- "C:\\github\\react-native\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\JSInterpreter.java" 2022-08-03 18:39:00.555240200 +0530 ++++ "C:\\github\\react-native-macos\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\JSInterpreter.java" 2022-08-05 12:35:15.030031000 +0530 +@@ -14,5 +14,6 @@ + public enum JSInterpreter { + OLD_LOGIC, + JSC, +- HERMES ++ HERMES, ++ V8 + } diff --git a/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java index dd89ac6c90f7c0..f92b4667ca7e79 100644 --- a/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java +++ b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java @@ -1,77 +1,21 @@ ---- ./ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java 2022-01-11 17:41:29.000000000 -0800 -+++ /var/folders/vs/8_b205053dddbcv7btj0w0v80000gn/T/update-1h8V3n/merge/V8/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java 2022-01-12 15:04:31.000000000 -0800 -@@ -31,6 +31,7 @@ - import com.facebook.react.devsupport.interfaces.DevSupportManager; - import com.facebook.react.jscexecutor.JSCExecutor; - import com.facebook.react.jscexecutor.JSCExecutorFactory; -+import com.facebook.react.v8executor.V8ExecutorFactory; +--- "C:\\github\\react-native\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\ReactInstanceManagerBuilder.java" 2022-08-05 12:59:58.286964700 +0530 ++++ "C:\\github\\react-native-macos\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\ReactInstanceManagerBuilder.java" 2022-08-05 15:08:37.009898300 +0530 +@@ -34,6 +34,8 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.packagerconnection.RequestHandler; import com.facebook.react.uimanager.UIImplementationProvider; -@@ -67,8 +68,21 @@ - private @Nullable ReactPackageTurboModuleManagerDelegate.Builder mTMMDelegateBuilder; - private @Nullable SurfaceDelegateFactory mSurfaceDelegateFactory; - -+ public enum JSEngine { -+ Hermes, -+ V8 -+ } -+ -+ private JSEngine mJSEngine = JSEngine.V8; -+ - /* package protected */ ReactInstanceManagerBuilder() {} - -+ public ReactInstanceManagerBuilder setJSEngine( -+ JSEngine jsEngine) { -+ mJSEngine = jsEngine; -+ return this; -+ } -+ - /** Sets a provider of {@link UIImplementation}. Uses default provider if null is passed. */ - public ReactInstanceManagerBuilder setUIImplementationProvider( - @Nullable UIImplementationProvider uiImplementationProvider) { -@@ -345,41 +359,11 @@ - - private JavaScriptExecutorFactory getDefaultJSExecutorFactory( - String appName, String deviceName, Context applicationContext) { -- try { -- // If JSC is included, use it as normal -- initializeSoLoaderIfNecessary(applicationContext); -- JSCExecutor.loadLibrary(); -- return new JSCExecutorFactory(appName, deviceName); -- } 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 { -+ if(mJSEngine == JSEngine.V8) { -+ return new V8ExecutorFactory(appName, deviceName); -+ } else { - HermesExecutor.loadLibrary(); - 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; - } -- } - } - } ++import com.facebook.react.v8executor.V8ExecutorFactory; ++import com.facebook.react.v8executor.V8Executor; + import java.util.ArrayList; + import java.util.List; + import java.util.Map; +@@ -397,6 +399,9 @@ + } else if (jsInterpreter == JSInterpreter.HERMES) { + HermesExecutor.loadLibrary(); + return new HermesExecutorFactory(); ++ } else if(jsInterpreter == JSInterpreter.V8) { ++ V8Executor.loadLibrary(); ++ return new V8ExecutorFactory(appName, deviceName); + } else { + JSCExecutor.loadLibrary(); + return new JSCExecutorFactory(appName, deviceName); diff --git a/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java index 0dc9c0d1160b70..2df32511a76d1d 100644 --- a/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java +++ b/android-patches/patches/V8/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java @@ -1,9 +1,6 @@ -diff --git a/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java b/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java -new file mode 100644 -index 0000000000..c28186d613 ---- /dev/null -+++ b/ReactAndroid/src/main/java/com/facebook/react/v8executor/V8Executor.java -@@ -0,0 +1,32 @@ +--- "C:\\github\\react-native\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\v8executor\\V8Executor.java" 1970-01-01 05:30:00.000000000 +0530 ++++ "C:\\github\\react-native-macos\\ReactAndroid\\src\\main\\java\\com\\facebook\\react\\v8executor\\V8Executor.java" 2022-08-05 12:38:10.946736100 +0530 +@@ -0,0 +1,36 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * @@ -20,8 +17,12 @@ +import com.facebook.soloader.SoLoader; + +@DoNotStrip -+/* package */ class V8Executor extends JavaScriptExecutor { ++public class V8Executor extends JavaScriptExecutor { + static { ++ loadLibrary(); ++ } ++ ++ public static void loadLibrary() throws UnsatisfiedLinkError { + SoLoader.loadLibrary("v8executor"); + } + diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index 67e8f06bc5a5ec..f52c914d64a4f9 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -11,7 +11,7 @@ "scripts": { "build": "yarn clean && node scripts/build.js --verbose", "clean": "rm -rf lib", - "prepublish": "yarn run build" + "prepare": "yarn run build" }, "license": "MIT", "files": [