Git Product home page Git Product logo

react-native-quickjs's Introduction

react-native-quickjs

An lightweight javascript engine for RN.

Run the example

cd example && yarn
yarn android
yarn ios

Run with existing project

Only tested for React Native >= 0.71.8. Please create an issue if it does not work for other version. Will fix it ASAP.

  1. Install dependency
yarn add react-native-quickjs
For Android
  1. Override "getJavaScriptExecutorFactory" to use QuickJS as JS runtime
diff --git a/android/app/src/main/java/com/awesomeproject/MainApplication.java b/android/app/src/main/java/com/awesomeproject/MainApplication.java
index 105e48c..b309fea 100644
--- a/android/app/src/main/java/com/awesomeproject/MainApplication.java
+++ b/android/app/src/main/java/com/awesomeproject/MainApplication.java
@@ -5,11 +5,16 @@ import com.facebook.react.PackageList;
 import com.facebook.react.ReactApplication;
 import com.facebook.react.ReactNativeHost;
 import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptExecutorFactory;
 import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
 import com.facebook.react.defaults.DefaultReactNativeHost;
 import com.facebook.soloader.SoLoader;
+import com.quickjs.QuickJSExecutorFactory;
+
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 public class MainApplication extends Application implements ReactApplication {
 
   private final ReactNativeHost mReactNativeHost =
@@ -42,6 +47,13 @@ public class MainApplication extends Application implements ReactApplication {
         protected Boolean isHermesEnabled() {
           return BuildConfig.IS_HERMES_ENABLED;
         }
+
+        @Nullable
+        @Override
+        protected JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
+          // Pass empty string to disable code cache.
+          return new QuickJSExecutorFactory(getApplication().getCacheDir().getAbsolutePath() + "/qjs");
+        }
       };
 
   @Override
  1. Disable Hermes and its bundling procedure
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -41,4 +41,4 @@ newArchEnabled=false

 # Use this property to enable or disable the Hermes JS engine.
 # If set to false, you will be using JSC instead.
-hermesEnabled=true
+hermesEnabled=false
  1. (Optional) Exclude unused libraries to reduce APK size
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -161,11 +161,18 @@ android {
             }
         }
     }
+
+    packagingOptions {
+        // Make sure libjsc.so does not packed in APK
+        exclude "**/libjsc.so"
+    }
 }
  1. Run your application

    a. For Debug.

    yarn android

    b. For Release. Run this command in the project root.

    cd android && ./gradlew installRelease
For iOS
  1. Disable Hermes and its bundling procedure
USE_HERMES=0 pod install
  1. Use "QuickJSExecutorFactory" in your application
diff --git a/ios/AwesomeProject/AppDelegate.mm b/ios/AwesomeProject/AppDelegate.mm
index 029aa44..2f579c3 100644
--- a/ios/AwesomeProject/AppDelegate.mm
+++ b/ios/AwesomeProject/AppDelegate.mm
@@ -2,6 +2,27 @@
 
 #import <React/RCTBundleURLProvider.h>
 
+#import <React/RCTCxxBridgeDelegate.h>
+#import <React/RCTJSIExecutorRuntimeInstaller.h>
+#import <ReactCommon/RCTTurboModuleManager.h>
+#ifndef RCT_USE_HERMES
+#if __has_include(<reacthermes/HermesExecutorFactory.h>)
+#define RCT_USE_HERMES 1
+#else
+#define RCT_USE_HERMES 0
+#endif
+#endif
+
+#if RCT_USE_HERMES
+#import <reacthermes/HermesExecutorFactory.h>
+//#else
+//#import <React/JSCExecutorFactory.h>
+#endif
+#import <QuickJSExecutorFactory.h>
+
+@interface AppDelegate () <RCTCxxBridgeDelegate>
+@end
+
 @implementation AppDelegate
 
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
@@ -14,6 +35,20 @@
   return [super application:application didFinishLaunchingWithOptions:launchOptions];
 }
 
+#pragma mark - RCTCxxBridgeDelegate
+
+- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
+{
+  auto installBindings = facebook::react::RCTJSIExecutorRuntimeInstaller(nullptr);
+#if RCT_USE_HERMES
+  return std::make_unique<facebook::react::HermesExecutorFactory>(installBindings);
+#else
+//  return std::make_unique<facebook::react::JSCExecutorFactory>(installBindings);
+  auto cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject UTF8String];
+  return std::make_unique<qjs::QuickJSExecutorFactory>(installBindings, ""); // pass empty string to disable code cache
+#endif
+}
+
 - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
 {
 #if DEBUG
  1. Run your application

    a. For Debug. Just run it in XCode.

    b. For Release. Run this command in the project root.

    npx react-native run-ios --mode Release

Performance

  1. Example performance
  2. Real world bundle performance
    1. We tested QuickJS/V8/JSC with an online bundle size of 1.43M.
    2. On Android, we enabled code cache for both QuickJS and V8. For TTI, QuickJS is 5-20% slower than V8. For PSS memory, QuickJS is 40-(-5)% lower than V8.
    3. On iOS, TTI using QuickJS is 15% slower than JSC without code cache. With code cache, QuickJS is 15% faster than JSC and 50% lower than JSC in footprint memory usage.

ESx Compatibility

As listed on official QuickJS website. QuickJS passed 82% of ECMA-262 tests. Meanwhile V8 passed 86% and JSC passed 85% in 2022. If internationalization tests which accounts for nearly 3% are excluded, QuickJS is fairly close to V8 and JSC. You can checkout https://test262.report/ for failed cases just in case.

Why would I choose QuickJS for React Native

IMHO:

  1. On Android, QuickJS is slower than V8 in most of my tests. Although it has advantages in memory and binary size which is important on some resource-limited devices. Also QuickJS is benefit from its startup time for some simple bundles.
  2. On iOS, QuickJS simply better than JSC with code cache and worst than JSC without code cache.
  3. QuickJS is easy to customize when it compared with V8 or Hermes. Like adding some high performance builtin functions or customized classes. It depends on your business.
  4. Currently QuickJS have no inspector. But it seems some open source projects have made it worked. Anyway it needs extra works to support inspector in the future.

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

Pull requests are always welcome.

License

MIT


Made with create-react-native-library

react-native-quickjs's People

Contributors

bojie-liu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

react-native-quickjs's Issues

Use openwebf/quickjs fork instead

It has about 40% faster performance than default quickjs. https://github.com/openwebf/quickjs. I believe it could get even faster than Hermes if you used it instead. I tried myself to replace the current engine with that one but I see you have added JS_Interceptor and exposed some other APIs from quickjs source so it crashes on launch but I think it's worth trying.

Hot reload failed

Hi, I'm trying to use this on [email protected].

And I find out that the hot reload isn't working, but anything else is perfect.

I wonder if this is normal or some kind of bug.

And some part of the log is here.

#117 pc 00000000000e2fc4  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #118 pc 00000000000e4d58  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #119 pc 00000000000e4d58  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #120 pc 00000000000e4aa4  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #121 pc 00000000000e4d58  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #122 pc 00000000000e2d90  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (JS_Call+108) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #123 pc 00000000000cc768  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #124 pc 00000000000e2fc4  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #125 pc 00000000000e2d90  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (JS_Call+108) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #126 pc 000000000007711c  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (qjs::QuickJSRuntime::call(facebook::jsi::Function const&, facebook::jsi::Value const&, facebook::jsi::Value const*, unsigned long)+532) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #127 pc 0000000000187604  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (facebook::jsi::Function::call(facebook::jsi::Runtime&, facebook::jsi::Value const*, unsigned long) const+104) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #128 pc 0000000000187558  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (facebook::jsi::Function::call(facebook::jsi::Runtime&, std::initializer_list<facebook::jsi::Value>) const+112) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #129 pc 000000000018c41c  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (facebook::jsi::Value facebook::jsi::Function::call<std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, facebook::jsi::Value>(facebook::jsi::Runtime&, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, facebook::jsi::Value&&) const+308) (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)
2023-07-26 14:56:36.892 21886-21886 DEBUG                   crash_dump64                         A        #130 pc 000000000017f318  /data/app/~~sxkugvAuEdAyDtLIev74fQ==/com.qjsproj-QSiv8Caos7izcy3dH3b20A==/lib/arm64/libquickjsexecutor.so (BuildId: 76c0ed52e6260230c145519a7491e3d79b70d00d)

Old Architecture Support

I saw your example code and it looked like you were using the new architecture:

+  auto cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject UTF8String];
+  return std::make_unique<qjs::QuickJSExecutorFactory>(installBindings, ""); // pass empty string to disable code cache
+#endif

Could you use this project with the old architecture?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.