@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.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/NOTICE +31 -0
- package/README.md +217 -0
- package/ReactNativeQuickJS.podspec +197 -0
- package/android/CMakeLists.txt +134 -0
- package/android/build.gradle +214 -0
- package/android/quickjs.gradle +39 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/reactnativequickjs/quickjs/QuickJSInstance.kt +39 -0
- package/android/src/main/java/com/reactnativequickjs/quickjs/QuickJSPackage.kt +32 -0
- package/android/src/main/jni/JJSRuntimeFactory.h +37 -0
- package/android/src/main/jni/JQuickJSInstance.cpp +27 -0
- package/android/src/main/jni/JQuickJSInstance.h +46 -0
- package/android/src/main/jni/OnLoad.cpp +15 -0
- package/app.plugin.js +11 -0
- package/apple/RCTQuickJSInstanceFactory.h +32 -0
- package/apple/RCTQuickJSInstanceFactory.mm +16 -0
- package/bin/qjsc/darwin-arm64/qjsc +0 -0
- package/bin/qjsc/darwin-x64/qjsc +0 -0
- package/bin/qjsc/linux-arm64/qjsc +0 -0
- package/bin/qjsc/linux-x64/qjsc +0 -0
- package/bin/qjsc/manifest.json +11 -0
- package/bin/qjsc/win32-x64/qjsc.exe +0 -0
- package/bin/react-native-quickjs.js +28 -0
- package/cmake/jsi.cmake +24 -0
- package/cmake/quickjs.cmake +125 -0
- package/engine/quickjs-rel/LICENSE +24 -0
- package/engine/quickjs-rel/MANIFEST.json +55 -0
- package/engine/quickjs-rel/builtin-array-fromasync.h +120 -0
- package/engine/quickjs-rel/builtin-iterator-zip-keyed.h +332 -0
- package/engine/quickjs-rel/builtin-iterator-zip.h +337 -0
- package/engine/quickjs-rel/cutils.h +1998 -0
- package/engine/quickjs-rel/dtoa.c +1619 -0
- package/engine/quickjs-rel/dtoa.h +87 -0
- package/engine/quickjs-rel/libregexp-opcode.h +73 -0
- package/engine/quickjs-rel/libregexp.c +3478 -0
- package/engine/quickjs-rel/libregexp.h +101 -0
- package/engine/quickjs-rel/libunicode-table.h +5173 -0
- package/engine/quickjs-rel/libunicode.c +2069 -0
- package/engine/quickjs-rel/libunicode.h +172 -0
- package/engine/quickjs-rel/list.h +107 -0
- package/engine/quickjs-rel/quickjs-atom.h +280 -0
- package/engine/quickjs-rel/quickjs-c-atomics.h +54 -0
- package/engine/quickjs-rel/quickjs-opcode.h +385 -0
- package/engine/quickjs-rel/quickjs.c +64854 -0
- package/engine/quickjs-rel/quickjs.h +1577 -0
- package/modules/cdp/quickjs-cdp.c +1386 -0
- package/modules/cdp/quickjs-cdp.h +122 -0
- package/modules/cdp/react/QuickJSInspector.cpp +322 -0
- package/modules/cdp/react/QuickJSInspector.h +114 -0
- package/modules/hermes-compat/include/hermes/DebuggerAPI.h +20 -0
- package/modules/hermes-compat/include/hermes/Public/CrashManager.h +47 -0
- package/modules/hermes-compat/include/hermes/Public/CtorConfig.h +88 -0
- package/modules/hermes-compat/include/hermes/Public/GCConfig.h +83 -0
- package/modules/hermes-compat/include/hermes/Public/HermesExport.h +17 -0
- package/modules/hermes-compat/include/hermes/Public/RuntimeConfig.h +66 -0
- package/modules/hermes-compat/include/hermes/Public/SamplingProfiler.h +22 -0
- package/modules/hermes-compat/include/hermes/hermes.h +195 -0
- package/modules/hermes-compat/include/hermes/inspector/RuntimeAdapter.h +43 -0
- package/modules/hermes-compat/include/hermes/inspector-modern/chrome/Registration.h +31 -0
- package/modules/hermes-compat/include/hermes-compat/Diagnostics.h +35 -0
- package/modules/hermes-compat/src/HermesCompat.cpp +557 -0
- package/package.json +104 -0
- package/scripts/bytecode/compile.js +99 -0
- package/scripts/expo/plugin.js +231 -0
- package/scripts/postinstall.js +74 -0
- package/scripts/react_native_quickjs_pods.rb +237 -0
- package/scripts/setup/edits.js +311 -0
- package/scripts/setup/run.js +121 -0
- package/src/bytecode/QuickJSBytecode.cpp +50 -0
- package/src/bytecode/QuickJSBytecode.h +50 -0
- package/src/module/QuickJSCompat.cpp +92 -0
- package/src/module/QuickJSCompat.h +19 -0
- package/src/module/QuickJSModule.cpp +70 -0
- package/src/module/QuickJSModule.h +108 -0
- package/src/module/QuickJSModuleNative.h +93 -0
- package/src/runtime/QuickJSInstance.cpp +37 -0
- package/src/runtime/QuickJSInstance.h +74 -0
- package/src/runtime/QuickJSRuntime.cpp +1864 -0
- package/src/runtime/QuickJSRuntime.h +529 -0
- package/src/runtime/QuickJSRuntimeConfig.h +103 -0
- package/src/runtime/QuickJSRuntimeFactory.cpp +34 -0
- 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()
|