@react-native-quickjs/quickjs 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +31 -0
  4. package/README.md +217 -0
  5. package/ReactNativeQuickJS.podspec +197 -0
  6. package/android/CMakeLists.txt +134 -0
  7. package/android/build.gradle +214 -0
  8. package/android/quickjs.gradle +39 -0
  9. package/android/src/main/AndroidManifest.xml +2 -0
  10. package/android/src/main/java/com/reactnativequickjs/quickjs/QuickJSInstance.kt +39 -0
  11. package/android/src/main/java/com/reactnativequickjs/quickjs/QuickJSPackage.kt +32 -0
  12. package/android/src/main/jni/JJSRuntimeFactory.h +37 -0
  13. package/android/src/main/jni/JQuickJSInstance.cpp +27 -0
  14. package/android/src/main/jni/JQuickJSInstance.h +46 -0
  15. package/android/src/main/jni/OnLoad.cpp +15 -0
  16. package/app.plugin.js +11 -0
  17. package/apple/RCTQuickJSInstanceFactory.h +32 -0
  18. package/apple/RCTQuickJSInstanceFactory.mm +16 -0
  19. package/bin/qjsc/darwin-arm64/qjsc +0 -0
  20. package/bin/qjsc/darwin-x64/qjsc +0 -0
  21. package/bin/qjsc/linux-arm64/qjsc +0 -0
  22. package/bin/qjsc/linux-x64/qjsc +0 -0
  23. package/bin/qjsc/manifest.json +11 -0
  24. package/bin/qjsc/win32-x64/qjsc.exe +0 -0
  25. package/bin/react-native-quickjs.js +28 -0
  26. package/cmake/jsi.cmake +24 -0
  27. package/cmake/quickjs.cmake +125 -0
  28. package/engine/quickjs-rel/LICENSE +24 -0
  29. package/engine/quickjs-rel/MANIFEST.json +55 -0
  30. package/engine/quickjs-rel/builtin-array-fromasync.h +120 -0
  31. package/engine/quickjs-rel/builtin-iterator-zip-keyed.h +332 -0
  32. package/engine/quickjs-rel/builtin-iterator-zip.h +337 -0
  33. package/engine/quickjs-rel/cutils.h +1998 -0
  34. package/engine/quickjs-rel/dtoa.c +1619 -0
  35. package/engine/quickjs-rel/dtoa.h +87 -0
  36. package/engine/quickjs-rel/libregexp-opcode.h +73 -0
  37. package/engine/quickjs-rel/libregexp.c +3478 -0
  38. package/engine/quickjs-rel/libregexp.h +101 -0
  39. package/engine/quickjs-rel/libunicode-table.h +5173 -0
  40. package/engine/quickjs-rel/libunicode.c +2069 -0
  41. package/engine/quickjs-rel/libunicode.h +172 -0
  42. package/engine/quickjs-rel/list.h +107 -0
  43. package/engine/quickjs-rel/quickjs-atom.h +280 -0
  44. package/engine/quickjs-rel/quickjs-c-atomics.h +54 -0
  45. package/engine/quickjs-rel/quickjs-opcode.h +385 -0
  46. package/engine/quickjs-rel/quickjs.c +64854 -0
  47. package/engine/quickjs-rel/quickjs.h +1577 -0
  48. package/modules/cdp/quickjs-cdp.c +1386 -0
  49. package/modules/cdp/quickjs-cdp.h +122 -0
  50. package/modules/cdp/react/QuickJSInspector.cpp +322 -0
  51. package/modules/cdp/react/QuickJSInspector.h +114 -0
  52. package/modules/hermes-compat/include/hermes/DebuggerAPI.h +20 -0
  53. package/modules/hermes-compat/include/hermes/Public/CrashManager.h +47 -0
  54. package/modules/hermes-compat/include/hermes/Public/CtorConfig.h +88 -0
  55. package/modules/hermes-compat/include/hermes/Public/GCConfig.h +83 -0
  56. package/modules/hermes-compat/include/hermes/Public/HermesExport.h +17 -0
  57. package/modules/hermes-compat/include/hermes/Public/RuntimeConfig.h +66 -0
  58. package/modules/hermes-compat/include/hermes/Public/SamplingProfiler.h +22 -0
  59. package/modules/hermes-compat/include/hermes/hermes.h +195 -0
  60. package/modules/hermes-compat/include/hermes/inspector/RuntimeAdapter.h +43 -0
  61. package/modules/hermes-compat/include/hermes/inspector-modern/chrome/Registration.h +31 -0
  62. package/modules/hermes-compat/include/hermes-compat/Diagnostics.h +35 -0
  63. package/modules/hermes-compat/src/HermesCompat.cpp +557 -0
  64. package/package.json +104 -0
  65. package/scripts/bytecode/compile.js +99 -0
  66. package/scripts/expo/plugin.js +231 -0
  67. package/scripts/postinstall.js +74 -0
  68. package/scripts/react_native_quickjs_pods.rb +237 -0
  69. package/scripts/setup/edits.js +311 -0
  70. package/scripts/setup/run.js +121 -0
  71. package/src/bytecode/QuickJSBytecode.cpp +50 -0
  72. package/src/bytecode/QuickJSBytecode.h +50 -0
  73. package/src/module/QuickJSCompat.cpp +92 -0
  74. package/src/module/QuickJSCompat.h +19 -0
  75. package/src/module/QuickJSModule.cpp +70 -0
  76. package/src/module/QuickJSModule.h +108 -0
  77. package/src/module/QuickJSModuleNative.h +93 -0
  78. package/src/runtime/QuickJSInstance.cpp +37 -0
  79. package/src/runtime/QuickJSInstance.h +74 -0
  80. package/src/runtime/QuickJSRuntime.cpp +1864 -0
  81. package/src/runtime/QuickJSRuntime.h +529 -0
  82. package/src/runtime/QuickJSRuntimeConfig.h +103 -0
  83. package/src/runtime/QuickJSRuntimeFactory.cpp +34 -0
  84. package/src/runtime/QuickJSRuntimeFactory.h +24 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ Nothing has been published yet. This section becomes the first release entry.
6
+
7
+ ### Added
8
+
9
+ - QuickJS as a React Native JavaScript engine, compiled from source in the app
10
+ build on Android and iOS. The engine is [quickjs-ng][ng], pre-patched with the
11
+ six changes described in `engine/patches/README.md`.
12
+ - `npx react-native-quickjs install`, plus `revert` and `doctor`, and an Expo
13
+ config plugin for `npx expo prebuild`.
14
+ - Release bundles compile to QuickJS bytecode on both platforms.
15
+ - Chrome DevTools debugging: breakpoints, stepping, the call stack, variable
16
+ inspection and expression evaluation.
17
+ - A Hermes compatibility shim, so libraries that link against Hermes -- most
18
+ importantly `react-native-worklets`, and through it `react-native-reanimated`
19
+ -- run unmodified. It is built only for an app that has left Hermes.
20
+ - `TextEncoder` and `TextDecoder`, as `@react-native-quickjs/text-encoding`,
21
+ a dependency of this package so every app has them.
22
+
23
+ ### Known limitations
24
+
25
+ - iOS compiles React Native core from source: removing Hermes removes the JSI
26
+ implementation that ships inside `hermesvm.framework`.
27
+ - Conditional breakpoints stop unconditionally; the condition is stored and
28
+ reported but never evaluated.
29
+ - React Native's non-Hermes path is patched at `pod install`. Each workaround
30
+ fails with a named error if React Native changes.
31
+ - Expo's `getDefaultReactHost` hardcodes Hermes, so the config plugin edits
32
+ `node_modules`. Until [expo#PENDING][expo-pr] lands, an `npm install` reverts
33
+ that edit and the app silently launches on Hermes.
34
+
35
+ [ng]: https://github.com/quickjs-ng/quickjs
36
+ [expo-pr]: https://github.com/expo/expo/pulls
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 React Native QuickJS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,31 @@
1
+ @react-native-quickjs/quickjs
2
+ Copyright (c) 2026 React Native QuickJS
3
+
4
+ This product includes software developed by third parties, listed below. The
5
+ license for this project is in LICENSE.
6
+
7
+ --------------------------------------------------------------------------------
8
+ QuickJS-NG
9
+ https://github.com/quickjs-ng/quickjs
10
+
11
+ The JavaScript engine. Its sources are redistributed in this package under
12
+ engine/quickjs-rel, pre-patched with the changes in engine/patches -- each of
13
+ which is described in engine/patches/README.md.
14
+
15
+ Copyright (c) 2016-2026 Fabrice Bellard
16
+ Copyright (c) 2017-2025 Charlie Gordon
17
+ Copyright (c) 2023-2026 Ben Noordhuis
18
+ Copyright (c) 2023-2026 Saúl Ibarra Corretgé
19
+ Copyright (c) 2023 Marcin Kolny
20
+
21
+ Licensed under the MIT License.
22
+
23
+ --------------------------------------------------------------------------------
24
+ React Native (JSI)
25
+
26
+ This package compiles React Native's own copy of JSI rather than vendoring one,
27
+ so no JSI source is redistributed here. JSI is part of React Native.
28
+
29
+ Copyright (c) Meta Platforms, Inc. and affiliates.
30
+
31
+ Licensed under the MIT License.
package/README.md ADDED
@@ -0,0 +1,217 @@
1
+ <!-- GitHub strips <style> from SVG in Markdown, so the animated banner would
2
+ render as a still here anyway — this points at the PNG. The SVG in
3
+ docs/public/banner.svg is the source of truth and animates everywhere
4
+ else. -->
5
+ <p align="center">
6
+ <img src=".github/assets/banner.png"
7
+ alt="react-native-quickjs"
8
+ width="880">
9
+ </p>
10
+
11
+ <h1 align="center">react-native-quickjs</h1>
12
+
13
+ <p align="center">
14
+ A <a href="https://github.com/quickjs-ng/quickjs">QuickJS</a> JSI runtime for
15
+ React Native, compiled from source in every app.
16
+ </p>
17
+
18
+ Provides a `jsi::Runtime` backed by quickjs-ng, and the `JSRuntimeFactory` glue
19
+ React Native uses to select it. The engine is compiled from source as part of
20
+ the app build. Neither Hermes nor JavaScriptCore is linked into the result.
21
+
22
+ > **Alpha.** See [Known limitations](#known-limitations). Breaking changes are
23
+ > expected.
24
+
25
+ ## Requirements
26
+
27
+ - React Native **0.85 or newer**, New Architecture enabled
28
+ - iOS 15.1+ · Android 7.0+ (API 24)
29
+
30
+ ## Install
31
+
32
+ ```sh
33
+ npm install @react-native-quickjs/quickjs@alpha
34
+ npx react-native-quickjs install
35
+ cd ios && pod install
36
+ ```
37
+
38
+ For Expo, add the config plugin instead and run `expo prebuild`:
39
+
40
+ ```json
41
+ { "expo": { "plugins": ["@react-native-quickjs/quickjs"] } }
42
+ ```
43
+
44
+ `npx react-native-quickjs doctor` reports which parts are configured, and
45
+ `revert` puts the app back on Hermes.
46
+
47
+ The two sections below are the changes `install` makes. Follow them by hand if
48
+ you would rather, or if `install` reports a file it could not edit.
49
+
50
+ ## iOS
51
+
52
+ **1. Podfile**
53
+
54
+ ```ruby
55
+ require_relative '../node_modules/@react-native-quickjs/quickjs/scripts/react_native_quickjs_pods.rb'
56
+
57
+ target 'YourApp' do
58
+ config = use_native_modules!
59
+
60
+ use_quickjs! # must precede use_react_native!
61
+
62
+ use_react_native!(:path => config[:reactNativePath])
63
+
64
+ post_install do |installer|
65
+ react_native_post_install(installer, config[:reactNativePath])
66
+ react_native_quickjs_post_install(installer) # must follow it
67
+ end
68
+ end
69
+ ```
70
+
71
+ `use_quickjs!` removes Hermes from the pod graph.
72
+ `react_native_quickjs_post_install` sets `USE_HERMES=false`, which stops release
73
+ builds compiling the bundle to Hermes bytecode.
74
+
75
+ **2. AppDelegate.swift**
76
+
77
+ ```swift
78
+ import ReactNativeQuickJS
79
+
80
+ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
81
+ override func createJSRuntimeFactory() -> JSRuntimeFactoryRef {
82
+ jsrt_create_quickjs_factory()
83
+ }
84
+ }
85
+ ```
86
+
87
+ Then run `pod install`.
88
+
89
+ ## Android
90
+
91
+ **1. `android/gradle.properties`**
92
+
93
+ ```properties
94
+ hermesEnabled=false
95
+ ```
96
+
97
+ **2. `android/app/build.gradle`** — remove the engine selection block:
98
+
99
+ ```diff
100
+ dependencies {
101
+ implementation("com.facebook.react:react-android")
102
+ -
103
+ - if (hermesEnabled.toBoolean()) {
104
+ - implementation("com.facebook.react:hermes-android")
105
+ - } else {
106
+ - implementation jscFlavor
107
+ - }
108
+ }
109
+ ```
110
+
111
+ `libjsc.so` is about 10 MB per architecture. The build warns if either
112
+ dependency is still declared.
113
+
114
+ **3. `android/app/build.gradle`** — apply the Gradle script, as the last line:
115
+
116
+ ```groovy
117
+ apply from: file("../../node_modules/@react-native-quickjs/quickjs/android/quickjs.gradle")
118
+ ```
119
+
120
+ React Native strips the engine the app is not using, and with only two engines
121
+ to choose from it reads "not Hermes" as "delete `libhermesvm.so`" — the name the
122
+ Hermes compatibility shim has to carry for `react-native-worklets` to find it.
123
+ This script does the same removals, minus that one file, and fails the build if
124
+ a real Hermes ever reaches packaging.
125
+
126
+ **4. `MainApplication.kt`**
127
+
128
+ ```kotlin
129
+ import com.reactnativequickjs.quickjs.QuickJSInstance
130
+
131
+ override val reactHost: ReactHost by lazy {
132
+ getDefaultReactHost(
133
+ context = applicationContext,
134
+ packageList = PackageList(this).packages,
135
+ jsRuntimeFactory = QuickJSInstance(),
136
+ )
137
+ }
138
+ ```
139
+
140
+ ## Debugging
141
+
142
+ Chrome DevTools attaches over React Native's inspector: breakpoints, stepping,
143
+ call stacks, scope inspection and console. The backend is compiled into debug
144
+ builds and omitted from release builds.
145
+
146
+ ## Bytecode bundles
147
+
148
+ Release builds compile the JavaScript bundle to QuickJS bytecode, which is what
149
+ the app then loads. Debug builds are untouched -- they load JavaScript from
150
+ Metro. There is nothing to configure.
151
+
152
+ The compiler is pinned to the engine by `BC_VERSION`, because bytecode is only
153
+ loadable by the engine build that produced it. If the two ever disagree the
154
+ build stops rather than shipping a bundle the app cannot read.
155
+
156
+ Set `RNQJS_BYTECODE=0` (iOS) or `-PrnqjsBytecode=false` (Android) to ship plain
157
+ JavaScript instead.
158
+
159
+ ## Hermes compatibility
160
+
161
+ Many React Native libraries create their own JavaScript runtime by calling into
162
+ Hermes directly -- `react-native-worklets` does, and so `react-native-reanimated`
163
+ does through it. They include `<hermes/hermes.h>` and link the Hermes library by
164
+ name, neither of which exists in a QuickJS app.
165
+
166
+ A compatibility shim ships that header and library, backed by this engine, so
167
+ those libraries build and run unmodified. `makeHermesRuntime()` returns a QuickJS
168
+ runtime; on Android the library is named `libhermesvm.so`, which is the name they
169
+ look for. It is about 100 KB and contains no second engine.
170
+
171
+ This is on by default. `RNQJS_HERMES_COMPAT=0` (iOS) or `-PrnqjsHermesCompat=false`
172
+ (Android) turns it off, which is worth doing only if nothing in the app wants
173
+ Hermes: with it on, any library that feature-detects `__has_include(<hermes/hermes.h>)`
174
+ takes its Hermes path, and gets this engine.
175
+
176
+ The shim covers the whole public Hermes API. Calls that have no QuickJS meaning
177
+ return sensible values rather than failing, and report themselves once through
178
+ `hermes-compat/Diagnostics.h`.
179
+
180
+ ## Known limitations
181
+
182
+ | | |
183
+ |---|---|
184
+ | **iOS compiles React Native core from source** | `use_quickjs!` sets `RCT_USE_PREBUILT_RNCORE=0`. On the prebuilt path `hermesvm.framework` also carries the JSI implementation, so removing Hermes removes JSI with it. First builds and cold CI are slower. |
185
+ | **Conditional breakpoints always stop** | A breakpoint `condition` is stored and reported back, never evaluated. |
186
+ | **React Native is patched at `pod install`** | `scripts/react_native_quickjs_pods.rb` applies five workarounds to React Native 0.85's non-Hermes path. Each fails with a named error if React Native changes. |
187
+
188
+ ## Building this repository
189
+
190
+ ```sh
191
+ git clone --recurse-submodules https://github.com/react-native-quickjs/quickjs
192
+ cd quickjs && npm install
193
+ npm test # configures, builds, and runs the suites
194
+ ```
195
+
196
+ | Path | What it is |
197
+ | --- | --- |
198
+ | `src/` | The `jsi::Runtime` implementation, shared by both platforms. |
199
+ | `android/` | Gradle module, CMake build, fbjni hybrid, and `QuickJSInstance.kt`. |
200
+ | `apple/` | `jsrt_create_quickjs_factory()`, the iOS entry point. |
201
+ | `engine/quickjs-ng/` | quickjs-ng, as a pinned git submodule. |
202
+ | `engine/patches/` | The engine patches applied to it. See [its README](engine/patches/README.md). |
203
+ | `engine/quickjs-rel/` | Generated: the submodule with the patches applied. This is what ships. |
204
+ | `modules/cdp/` | The Chrome DevTools Protocol backend, in C. See [its README](modules/cdp/README.md). |
205
+ | `cmake/quickjs.cmake` | The quickjs target, shared by the Android and host builds. |
206
+ | `tools/bytecode/` | `qjsc`, the ahead-of-time bytecode compiler. |
207
+ | `tests/` | Host suites, Hermes' JSI conformance suite, and the differential corpus. |
208
+ | `example/` | An app that runs on QuickJS, used as the end-to-end check. |
209
+ | `docs/` | The landing page. |
210
+
211
+ `engine/quickjs-rel` is generated, never hand-edited. The build fails if it
212
+ disagrees with the submodule plus the patches.
213
+
214
+ ## License
215
+
216
+ MIT. QuickJS-ng is MIT, © Fabrice Bellard, Charlie Gordon and the quickjs-ng
217
+ contributors.
@@ -0,0 +1,197 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ # The engine compiled here is engine/quickjs-rel: the committed, pre-patched
6
+ # projection of the engine/quickjs-ng submodule, generated by
7
+ # scripts/sync-quickjs-rel.js. The submodule cannot be what ships -- our patches
8
+ # live in its working tree, which `git submodule update` reverts, and npm does
9
+ # not pack submodules at all.
10
+ #
11
+ # One existence check remains, because a wrong `files` entry in package.json
12
+ # still produces a tarball without these sources, and a missing-source failure
13
+ # at `pod install` is far cheaper to read than the hundreds of compiler errors
14
+ # it becomes otherwise.
15
+ quickjs_dir = File.join(__dir__, "engine", "quickjs-rel")
16
+ unless File.exist?(File.join(quickjs_dir, "quickjs.c"))
17
+ raise "[ReactNativeQuickJS] engine/quickjs-rel is missing. Run: node scripts/sync-quickjs-rel.js"
18
+ end
19
+
20
+ Pod::Spec.new do |s|
21
+ s.name = "ReactNativeQuickJS"
22
+ s.version = package["version"]
23
+ s.summary = package["description"]
24
+ s.homepage = package["homepage"]
25
+ s.license = package["license"]
26
+ s.authors = package["author"]
27
+
28
+ s.platforms = { :ios => min_ios_version_supported }
29
+ s.source = { :git => package["repository"]["url"], :tag => "v#{s.version}" }
30
+
31
+ # engine/quickjs-rel contains only these files -- the sync script derives the
32
+ # list from the #include closure and deletes anything else -- so the globs
33
+ # cannot pick up a stray main() the way they could against the full submodule.
34
+ s.source_files = [
35
+ "apple/*.{h,mm}",
36
+ "src/**/*.{h,cpp}",
37
+ # The Chrome DevTools Protocol backend. quickjs-cdp.c needs only
38
+ # <quickjs.h>; react/ needs jsinspector-modern and folly, which the
39
+ # React-jsinspector dependency below provides.
40
+ "modules/cdp/*.{h,c}",
41
+ "modules/cdp/react/*.{h,cpp}",
42
+ "engine/quickjs-rel/*.h",
43
+ # libregexp.c #includes libregexp-backtrack.c.inc twice, to build the 8-bit
44
+ # and 16-bit matchers, so the .inc has to be copied too.
45
+ "engine/quickjs-rel/*.inc",
46
+ "engine/quickjs-rel/quickjs.c",
47
+ "engine/quickjs-rel/libregexp.c",
48
+ "engine/quickjs-rel/libunicode.c",
49
+ "engine/quickjs-rel/dtoa.c",
50
+ ]
51
+
52
+ # Consumers only ever need the C factory entry point.
53
+ s.public_header_files = "apple/RCTQuickJSInstanceFactory.h"
54
+
55
+ # DEFINES_MODULE makes CocoaPods emit a module map even though this builds as
56
+ # a static library, which is what lets an app's AppDelegate.swift write
57
+ # `import ReactNativeQuickJS`.
58
+ s.module_name = "ReactNativeQuickJS"
59
+
60
+ # Expo's ExpoModulesJSI is a prebuilt dynamic framework that resolves JSI from
61
+ # the flat namespace when it loads. Two of the symbols it needs come from
62
+ # React-jsi and nothing linked statically into the app refers to them, so the
63
+ # linker drops both and dyld kills the app before main() with
64
+ # "symbol not found in flat namespace '__ZTIN8facebook3jsi13MutableBufferE'".
65
+ #
66
+ # Both are named, not just the destructor: ld64 dead-strips per atom rather
67
+ # than per object file, so keeping the destructor does not keep the typeinfo
68
+ # sitting beside it in the same object.
69
+ #
70
+ # Plain React Native never loads JSI dynamically and does not need this; there
71
+ # it retains two symbols nothing asks for.
72
+ s.user_target_xcconfig = {
73
+ "OTHER_LDFLAGS" => "$(inherited) " \
74
+ "-Wl,-u,__ZN8facebook3jsi13MutableBufferD2Ev " \
75
+ "-Wl,-u,__ZTIN8facebook3jsi13MutableBufferE"
76
+ }
77
+
78
+ s.pod_target_xcconfig = {
79
+ "DEFINES_MODULE" => "YES",
80
+ # The interpreter recurses on the native stack: one JavaScript call is one
81
+ # JS_CallInternal frame. Unoptimized that frame is around 10 KB, so on
82
+ # React Native's JavaScript thread only about 80 JavaScript calls fit --
83
+ # too few for React to render, and a Debug app comes up blank or reports
84
+ # "Maximum call stack size exceeded" from whichever library recurses first.
85
+ #
86
+ # Debug only: Release already optimizes, and forcing a level there would
87
+ # override the -Os the rest of the pods are built with. This covers the
88
+ # whole pod rather than the engine alone, which CocoaPods gives no way to
89
+ # single out, so the JSI layer is optimized in a Debug app too -- this
90
+ # project's own debugging is done through the host CMake build, which is
91
+ # left unoptimized.
92
+ "GCC_OPTIMIZATION_LEVEL[config=Debug]" => "2",
93
+ # Must be quickjs-rel and not quickjs-ng. Pointing this at the submodule
94
+ # while compiling the -rel sources would take headers from one copy and
95
+ # translation units from the other: fine while they agree, and a
96
+ # struct-layout mismatch with no diagnostic the moment they do not.
97
+ "HEADER_SEARCH_PATHS" =>
98
+ "\"$(PODS_TARGET_SRCROOT)/src/bytecode\" " \
99
+ "\"$(PODS_TARGET_SRCROOT)/src/module\" " \
100
+ "\"$(PODS_TARGET_SRCROOT)/src/runtime\" " \
101
+ "\"$(PODS_TARGET_SRCROOT)/engine/quickjs-rel\" " \
102
+ "\"$(PODS_TARGET_SRCROOT)/modules/cdp\" " \
103
+ "\"$(PODS_TARGET_SRCROOT)/modules/cdp/react\"",
104
+ "GCC_C_LANGUAGE_STANDARD" => "gnu11",
105
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
106
+ # JS_ENABLE_DEBUGGER must agree with cmake/quickjs.cmake, which defaults it
107
+ # on for the host and Android builds. quickjs.h declares the debugger entry
108
+ # points either way, so a build that compiles them out fails at link rather
109
+ # than at the call site.
110
+ #
111
+ # RNQJS_ENABLE_CDP is absent on purpose: undefined means no DevTools
112
+ # backend, and react_native_quickjs_post_install defines it for debug only.
113
+ "GCC_PREPROCESSOR_DEFINITIONS" =>
114
+ "$(inherited) _GNU_SOURCE=1 JS_ENABLE_DEBUGGER=1",
115
+ "USE_HEADERMAP" => "YES",
116
+ }
117
+
118
+ # quickjs builds warning-clean upstream, but not under React Native's flags.
119
+ s.compiler_flags = "-Wno-unused-parameter -Wno-unused-variable -Wno-sign-compare -Wno-implicit-fallthrough"
120
+
121
+ # The Hermes compatibility shim, opt-in.
122
+ #
123
+ # modules/hermes-compat publishes a source-compatible <hermes/hermes.h> whose
124
+ # makeHermesRuntime() returns a QuickJS-backed runtime, so react-native-worklets
125
+ # -- and so react-native-reanimated -- build and run unmodified.
126
+ #
127
+ # On by default: react-native-worklets does not build without it, and neither
128
+ # does react-native-reanimated, which is most apps. RNQJS_HERMES_COMPAT=0 in
129
+ # the Podfile turns it off.
130
+ #
131
+ # react_native_quickjs_post_install puts these headers on every pod's header
132
+ # search path, so any library feature-detecting with
133
+ # __has_include(<hermes/hermes.h>) believes this is a Hermes app -- which is
134
+ # the point: what it then calls is this engine.
135
+ hermes_compat = ENV["RNQJS_HERMES_COMPAT"] != "0"
136
+
137
+ # The shim is source-compatible with Hermes, not ABI-compatible. With the
138
+ # hermes-engine pod also installed the target has two <hermes/hermes.h> on one
139
+ # search path and two definitions of makeHermesRuntime on one link line, and
140
+ # pod ordering decides which wins. use_hermes() is what decides whether that
141
+ # pod is there, so ask it rather than reading ENV["USE_HERMES"]: React Native
142
+ # sets that pod up from use_hermes(), which never consults that variable.
143
+ #
144
+ # Skipped rather than raised. Now that the shim is on by default this is
145
+ # reached by installing the package and running pod install before
146
+ # `npx react-native-quickjs install` has edited the Podfile -- a normal step
147
+ # in setting the package up, and no place to abort.
148
+ if hermes_compat && defined?(use_hermes) && use_hermes()
149
+ Pod::UI.warn(
150
+ "[ReactNativeQuickJS] the Hermes compatibility shim was skipped because " \
151
+ "the hermes-engine pod is installed. Add use_quickjs! to the Podfile " \
152
+ "(`npx react-native-quickjs install` does it) and run pod install again."
153
+ )
154
+ hermes_compat = false
155
+ end
156
+
157
+ if hermes_compat
158
+ s.subspec "HermesCompat" do |ss|
159
+ ss.source_files = [
160
+ "modules/hermes-compat/src/*.cpp",
161
+ "modules/hermes-compat/include/**/*.h",
162
+ ]
163
+
164
+ # Private on purpose. This pod sets DEFINES_MODULE, so a public header
165
+ # joins ReactNativeQuickJS-umbrella.h, which Clang compiles as Objective-C
166
+ # while building the module -- and <hermes/hermes.h> reaches <cstdint>,
167
+ # which that compile cannot resolve. CocoaPods would also install them
168
+ # under Headers/Public/ReactNativeQuickJS/hermes/, so <hermes/hermes.h>
169
+ # would not resolve for a consumer either.
170
+ #
171
+ # react_native_quickjs_post_install puts the source directory itself on
172
+ # every pod's header search path instead, which is what worklets needs.
173
+ ss.private_header_files = "modules/hermes-compat/include/**/*.h"
174
+
175
+ ss.pod_target_xcconfig = {
176
+ # Our own sources include <hermes/hermes.h> and
177
+ # <hermes-compat/Diagnostics.h> from the source tree.
178
+ "HEADER_SEARCH_PATHS" =>
179
+ "$(inherited) \"$(PODS_TARGET_SRCROOT)/modules/hermes-compat/include\"",
180
+ # Unconditional, not tied to the configuration: RNWorklets.podspec sets
181
+ # HERMES_ENABLE_DEBUGGER=1 for every Debug build regardless of
182
+ # USE_HERMES, so a Debug worklets references enableDebugging and needs
183
+ # it defined. Safe only because no member declaration in the shim's
184
+ # headers is conditional on this macro.
185
+ "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) HERMES_ENABLE_DEBUGGER=1",
186
+ }
187
+ end
188
+ end
189
+
190
+ install_modules_dependencies(s)
191
+
192
+ s.dependency "React-jsi"
193
+ s.dependency "React-cxxreact"
194
+ s.dependency "React-RuntimeCore"
195
+ add_dependency(s, "React-jsitooling", :framework_name => "JSITooling")
196
+ add_dependency(s, "React-jsinspector", :framework_name => "jsinspector_modern")
197
+ end
@@ -0,0 +1,134 @@
1
+ # Copyright (c) Ammar Ahmed.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ #
6
+ # Builds libquickjsinstancejni.so, the shared library the Kotlin QuickJSInstance
7
+ # loads. Driven by build.gradle, never configured directly.
8
+
9
+ cmake_minimum_required(VERSION 3.20)
10
+ project(quickjsinstancejni CXX C)
11
+
12
+ set(CMAKE_CXX_STANDARD 20)
13
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
14
+
15
+ if(NOT DEFINED REACT_NATIVE_DIR)
16
+ message(FATAL_ERROR "REACT_NATIVE_DIR must be passed in from build.gradle")
17
+ endif()
18
+
19
+ set(RNQJS_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..")
20
+
21
+ # React Native's headers reach into folly, which does not compile without these
22
+ # flags unless a generated folly-config.h is on the include path.
23
+ include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
24
+ add_compile_options(${folly_FLAGS})
25
+
26
+ include("${RNQJS_ROOT}/cmake/quickjs.cmake")
27
+
28
+ # Every src/ translation unit has to be listed. The Apple build globs src/,
29
+ # so a file added there builds on iOS and fails to link on Android -- an
30
+ # asymmetry worth remembering when adding a source.
31
+ add_library(quickjsinstancejni SHARED
32
+ "${RNQJS_ROOT}/src/bytecode/QuickJSBytecode.cpp"
33
+ "${RNQJS_ROOT}/src/module/QuickJSCompat.cpp"
34
+ "${RNQJS_ROOT}/src/module/QuickJSModule.cpp"
35
+ "${RNQJS_ROOT}/src/runtime/QuickJSInstance.cpp"
36
+ "${RNQJS_ROOT}/src/runtime/QuickJSRuntime.cpp"
37
+ "${RNQJS_ROOT}/src/runtime/QuickJSRuntimeFactory.cpp"
38
+ src/main/jni/JQuickJSInstance.cpp
39
+ src/main/jni/OnLoad.cpp
40
+ )
41
+
42
+ target_include_directories(quickjsinstancejni PRIVATE
43
+ "${RNQJS_ROOT}/src/bytecode"
44
+ "${RNQJS_ROOT}/src/module"
45
+ "${RNQJS_ROOT}/src/runtime"
46
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/main/jni"
47
+ )
48
+
49
+ find_package(ReactAndroid REQUIRED CONFIG)
50
+ find_package(fbjni REQUIRED CONFIG)
51
+
52
+ target_link_libraries(quickjsinstancejni
53
+ quickjs
54
+ fbjni::fbjni
55
+ ReactAndroid::jsi
56
+ ReactAndroid::reactnative
57
+ android
58
+ log
59
+ )
60
+
61
+ # --- Chrome DevTools Protocol ----------------------------------------------
62
+ #
63
+ # In debug builds, out of release builds, which is what
64
+ # QuickJSInstance::debuggerEnabledByDefault() already decides at runtime.
65
+ # -DRNQJS_ENABLE_CDP=ON puts it back.
66
+ #
67
+ # The two sources are compiled here rather than through modules/cdp's own
68
+ # CMakeLists because that file also declares a host test target. Two files is
69
+ # less machinery than teaching it to be cross-compiled.
70
+ if(CMAKE_BUILD_TYPE MATCHES Debug)
71
+ set(RNQJS_CDP_DEFAULT ON)
72
+ else()
73
+ set(RNQJS_CDP_DEFAULT OFF)
74
+ endif()
75
+ option(RNQJS_ENABLE_CDP "Compile in the Chrome DevTools Protocol backend"
76
+ ${RNQJS_CDP_DEFAULT})
77
+
78
+ if(RNQJS_ENABLE_CDP)
79
+ set(CDP_DIR "${RNQJS_ROOT}/modules/cdp")
80
+ target_sources(quickjsinstancejni PRIVATE
81
+ "${CDP_DIR}/quickjs-cdp.c"
82
+ "${CDP_DIR}/react/QuickJSInspector.cpp"
83
+ )
84
+ target_include_directories(quickjsinstancejni PRIVATE
85
+ "${CDP_DIR}" "${CDP_DIR}/react")
86
+ target_compile_definitions(quickjsinstancejni PRIVATE RNQJS_ENABLE_CDP=1)
87
+ endif()
88
+
89
+ # The Hermes compatibility shim, opt-in, built as libhermesvm.so.
90
+ #
91
+ # react-native-worklets picks its engine at compile time and links React
92
+ # Native's imported `hermes` target, so what it records as NEEDED is the file
93
+ # that target points at. On React Native 0.85 that is libhermesvm.so, not
94
+ # libhermes.so -- see ReactAndroid/src/main/jni/first-party/hermes/CMakeLists.txt.
95
+ # Giving it a library of that name, whose makeHermesRuntime returns a QuickJS
96
+ # runtime, is what lets it run here unmodified.
97
+ #
98
+ # It links libquickjsinstancejni.so rather than compiling the engine again:
99
+ # two copies would mean two engines in one app, and roughly a megabyte of
100
+ # duplicate code per architecture.
101
+ #
102
+ # On by default: react-native-worklets does not load without it, and neither
103
+ # does react-native-reanimated, which is most apps. -PrnqjsHermesCompat=false
104
+ # turns it off.
105
+ option(RNQJS_HERMES_COMPAT "Build the Hermes compatibility shim as libhermesvm.so" ON)
106
+ if(RNQJS_HERMES_COMPAT)
107
+ add_library(hermes SHARED
108
+ "${RNQJS_ROOT}/modules/hermes-compat/src/HermesCompat.cpp")
109
+
110
+ target_include_directories(hermes PUBLIC
111
+ "${RNQJS_ROOT}/modules/hermes-compat/include")
112
+ target_include_directories(hermes PRIVATE
113
+ "${RNQJS_ROOT}/src/bytecode"
114
+ "${RNQJS_ROOT}/src/module"
115
+ "${RNQJS_ROOT}/src/runtime")
116
+
117
+ # Unconditional, matching the podspec: RNWorklets defines this for every
118
+ # Debug build, so enableDebugging must exist whatever this library was
119
+ # compiled with. Safe only because no member declaration in the shim's
120
+ # headers is conditional on it.
121
+ target_compile_definitions(hermes PRIVATE HERMES_ENABLE_DEBUGGER=1)
122
+
123
+ target_link_libraries(hermes
124
+ quickjsinstancejni
125
+ ReactAndroid::jsi
126
+ android
127
+ log
128
+ )
129
+
130
+ set_target_properties(hermes PROPERTIES
131
+ OUTPUT_NAME hermesvm
132
+ POSITION_INDEPENDENT_CODE ON
133
+ )
134
+ endif()