@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
@@ -0,0 +1,557 @@
1
+ /*
2
+ * Copyright (c) Ammar Ahmed.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #include <hermes-compat/Diagnostics.h>
9
+ #include <hermes/hermes.h>
10
+ #include <hermes/inspector/RuntimeAdapter.h>
11
+ #include <jsi/decorator.h>
12
+ #include <jsi/threadsafe.h>
13
+
14
+ #include <atomic>
15
+ #include <chrono>
16
+ #include <cstdio>
17
+ #include <cstring>
18
+ #include <ctime>
19
+ #include <mutex>
20
+ #include <string>
21
+ #include <unordered_set>
22
+
23
+ #include "QuickJSRuntime.h"
24
+ #include "QuickJSRuntimeFactory.h"
25
+
26
+ #ifdef __ANDROID__
27
+ #include <android/log.h>
28
+ #endif
29
+
30
+ // ---------------------------------------------------------------- diagnostics
31
+
32
+ namespace qjs::hermescompat {
33
+ namespace {
34
+
35
+ std::mutex gMutex;
36
+ Handler gHandler;
37
+ std::unordered_set<const void *> gSeen;
38
+
39
+ const char *name(Severity severity) {
40
+ switch (severity) {
41
+ case Severity::Degraded:
42
+ return "degraded";
43
+ case Severity::Ignored:
44
+ return "ignored";
45
+ case Severity::Unsupported:
46
+ return "unsupported";
47
+ }
48
+ return "unknown";
49
+ }
50
+
51
+ } // namespace
52
+
53
+ void setHandler(Handler handler) {
54
+ std::lock_guard<std::mutex> lock(gMutex);
55
+ gHandler = std::move(handler);
56
+ }
57
+
58
+ void report(Severity severity, const char *api, const char *detail) {
59
+ Handler handler;
60
+ {
61
+ std::lock_guard<std::mutex> lock(gMutex);
62
+ if (!gSeen.insert(api).second) {
63
+ return;
64
+ }
65
+ handler = gHandler;
66
+ }
67
+
68
+ if (handler) {
69
+ handler(severity, api, detail);
70
+ return;
71
+ }
72
+ #ifdef __ANDROID__
73
+ __android_log_print(
74
+ ANDROID_LOG_WARN, "hermes-compat", "%s: %s -- %s", name(severity), api,
75
+ detail);
76
+ #else
77
+ std::fprintf(
78
+ stderr, "[hermes-compat] %s: %s -- %s\n", name(severity), api, detail);
79
+ #endif
80
+ }
81
+
82
+ void resetForTesting() {
83
+ std::lock_guard<std::mutex> lock(gMutex);
84
+ gSeen.clear();
85
+ gHandler = nullptr;
86
+ }
87
+
88
+ } // namespace qjs::hermescompat
89
+
90
+ // Out of line so that this library, not its consumer, carries the vtables.
91
+ // Both classes need one: worklets references hermes::vm::CrashManager's vtable
92
+ // as well as NopCrashManager's.
93
+ namespace hermes::vm {
94
+ CrashManager::~CrashManager() = default;
95
+ NopCrashManager::~NopCrashManager() = default;
96
+ } // namespace hermes::vm
97
+
98
+ // -------------------------------------------------------------------- runtime
99
+
100
+ namespace facebook::hermes {
101
+ namespace {
102
+
103
+ namespace diag = qjs::hermescompat;
104
+ using diag::Severity;
105
+
106
+ /// Hermes bytecode files open with this, little-endian (BytecodeFileFormat.h).
107
+ /// Sniffed so that "this is plain JavaScript" and "this is an HBC bundle this
108
+ /// app can never run" are not reported as the same answer.
109
+ constexpr uint64_t kHermesMagic = 0x1F1903C103BC1FC6ULL;
110
+
111
+ void degraded(const char *api, const char *why) {
112
+ diag::report(Severity::Degraded, api, why);
113
+ }
114
+ void ignored(const char *api, const char *why) {
115
+ diag::report(Severity::Ignored, api, why);
116
+ }
117
+ void unsupported(const char *api, const char *why) {
118
+ diag::report(Severity::Unsupported, api, why);
119
+ }
120
+
121
+ bool looksLikeHermesBytecode(const uint8_t *data, size_t len) {
122
+ if (data == nullptr || len < sizeof(kHermesMagic)) {
123
+ return false;
124
+ }
125
+ uint64_t magic = 0;
126
+ std::memcpy(&magic, data, sizeof(magic));
127
+ return magic == kHermesMagic;
128
+ }
129
+
130
+ class RootAPI final : public IHermesRootAPI, public ISetFatalHandler {
131
+ public:
132
+ jsi::ICast *castInterface(const jsi::UUID &uuid) override {
133
+ if (uuid == IHermesRootAPI::uuid)
134
+ return static_cast<IHermesRootAPI *>(this);
135
+ if (uuid == ISetFatalHandler::uuid)
136
+ return static_cast<ISetFatalHandler *>(this);
137
+ return nullptr;
138
+ }
139
+
140
+ std::unique_ptr<HermesRuntime> makeHermesRuntime(
141
+ const ::hermes::vm::RuntimeConfig &config) override;
142
+
143
+ bool isHermesBytecode(const uint8_t *data, size_t len) override {
144
+ if (looksLikeHermesBytecode(data, len)) {
145
+ unsupported(
146
+ "IHermesRootAPI::isHermesBytecode",
147
+ "cannot execute HBC; rebuild as JavaScript or with "
148
+ "tools/bytecode/qjsc");
149
+ }
150
+ return false;
151
+ }
152
+
153
+ uint32_t getBytecodeVersion() override {
154
+ unsupported(
155
+ "IHermesRootAPI::getBytecodeVersion",
156
+ "no HBC version here; returning 0");
157
+ return 0;
158
+ }
159
+
160
+ void prefetchHermesBytecode(const uint8_t *, size_t) override {
161
+ ignored(
162
+ "IHermesRootAPI::prefetchHermesBytecode",
163
+ "an madvise hint for HBC files; nothing to do");
164
+ }
165
+
166
+ bool hermesBytecodeSanityCheck(
167
+ const uint8_t *data, size_t len, std::string *errorMessage) override {
168
+ if (errorMessage != nullptr) {
169
+ *errorMessage = looksLikeHermesBytecode(data, len)
170
+ ? "Hermes bytecode, which this app cannot execute"
171
+ : "not Hermes bytecode (this app runs QuickJS)";
172
+ }
173
+ return false;
174
+ }
175
+
176
+ void setFatalHandler(void (*)(const std::string &)) override {
177
+ unsupported(
178
+ "IHermesRootAPI::setFatalHandler",
179
+ "no fatal handler exists; it would never be called");
180
+ }
181
+
182
+ std::pair<const uint8_t *, size_t> getBytecodeEpilogue(
183
+ const uint8_t *, size_t) override {
184
+ unsupported(
185
+ "IHermesRootAPI::getBytecodeEpilogue",
186
+ "an HBC concept; returning empty");
187
+ return {nullptr, 0};
188
+ }
189
+
190
+ void enableSamplingProfiler(double) override {
191
+ noProfiler("enableSamplingProfiler");
192
+ }
193
+ void disableSamplingProfiler() override {
194
+ noProfiler("disableSamplingProfiler");
195
+ }
196
+ void dumpSampledTraceToFile(const std::string &) override {
197
+ noProfiler("dumpSampledTraceToFile");
198
+ }
199
+ void dumpSampledTraceToStream(std::ostream &) override {
200
+ noProfiler("dumpSampledTraceToStream");
201
+ }
202
+
203
+ std::unordered_map<std::string, std::vector<std::string>>
204
+ getExecutedFunctions() override {
205
+ unsupported(
206
+ "IHermesRootAPI::getExecutedFunctions", "not tracked; returning empty");
207
+ return {};
208
+ }
209
+
210
+ bool isCodeCoverageProfilerEnabled() override {
211
+ return false;
212
+ }
213
+ void enableCodeCoverageProfiler() override {
214
+ noCoverage("enableCodeCoverageProfiler");
215
+ }
216
+ void disableCodeCoverageProfiler() override {
217
+ noCoverage("disableCodeCoverageProfiler");
218
+ }
219
+
220
+ private:
221
+ static void noProfiler(const char *api) {
222
+ unsupported(api, "QuickJS has no sampling profiler");
223
+ }
224
+ static void noCoverage(const char *api) {
225
+ unsupported(api, "QuickJS has no code coverage profiler");
226
+ }
227
+ };
228
+
229
+ RootAPI &rootAPI() {
230
+ static RootAPI api;
231
+ return api;
232
+ }
233
+
234
+ /// RuntimeDecorator forwards every jsi::Runtime virtual to the QuickJS runtime,
235
+ /// so a change to JSI is a compile error here rather than a silent divergence.
236
+ /// Only the Hermes-specific half is written out below.
237
+ class CompatRuntime final
238
+ : public jsi::RuntimeDecorator<jsi::Runtime, HermesRuntime> {
239
+ public:
240
+ explicit CompatRuntime(std::unique_ptr<jsi::Runtime> plain)
241
+ : jsi::RuntimeDecorator<jsi::Runtime, HermesRuntime>(*plain),
242
+ owned_(std::move(plain)),
243
+ quickjs_(dynamic_cast<qjs::QuickJSRuntime *>(owned_.get())) {}
244
+
245
+ ~CompatRuntime() override {
246
+ unwatchTimeLimit();
247
+ }
248
+
249
+ jsi::ICast *castInterface(const jsi::UUID &uuid) override {
250
+ if (uuid == IHermes::uuid) return static_cast<IHermes *>(this);
251
+ return plain().castInterface(uuid);
252
+ }
253
+
254
+ ICast *getHermesRootAPI() override {
255
+ return static_cast<IHermesRootAPI *>(&rootAPI());
256
+ }
257
+
258
+ void sampledTraceToStreamInDevToolsFormat(std::ostream &) override {
259
+ noProfiler("sampledTraceToStreamInDevToolsFormat");
260
+ }
261
+
262
+ sampling_profiler::Profile dumpSampledTraceToProfile() override {
263
+ noProfiler("dumpSampledTraceToProfile");
264
+ return {};
265
+ }
266
+
267
+ /// QuickJS reads the zone on every conversion and keeps no cache of its
268
+ /// own, so the one that can go stale is libc's.
269
+ void resetTimezoneCache() override {
270
+ ::tzset();
271
+ }
272
+
273
+ void loadSegment(
274
+ std::unique_ptr<const jsi::Buffer>, const jsi::Value &) override {
275
+ unsupported(
276
+ "IHermes::loadSegment", "segmented and RAM bundles are HBC features");
277
+ }
278
+
279
+ uint64_t getUniqueID(const jsi::Object &o) const override {
280
+ return id(o);
281
+ }
282
+ uint64_t getUniqueID(const jsi::BigInt &b) const override {
283
+ return id(b);
284
+ }
285
+ uint64_t getUniqueID(const jsi::String &s) const override {
286
+ return id(s);
287
+ }
288
+ uint64_t getUniqueID(const jsi::PropNameID &p) const override {
289
+ return id(p);
290
+ }
291
+ uint64_t getUniqueID(const jsi::Symbol &s) const override {
292
+ return id(s);
293
+ }
294
+
295
+ uint64_t getUniqueID(const jsi::Value &v) const override {
296
+ if (quickjs_ == nullptr ||
297
+ !(v.isObject() || v.isString() || v.isSymbol() || v.isBigInt())) {
298
+ return 0; // Hermes documents 0 as "no id for this value".
299
+ }
300
+ return reinterpret_cast<uint64_t>(JS_VALUE_GET_PTR(quickjs_->toJSValue(v)));
301
+ }
302
+
303
+ jsi::Value getObjectForID(uint64_t) override {
304
+ unsupported(
305
+ "IHermes::getObjectForID",
306
+ "no id-to-object registry is kept; returning null");
307
+ return jsi::Value::null();
308
+ }
309
+
310
+ const ::hermes::vm::GCExecTrace &getGCExecTrace() const override {
311
+ static const ::hermes::vm::GCExecTrace kEmpty;
312
+ unsupported("IHermes::getGCExecTrace", "not recorded");
313
+ return kEmpty;
314
+ }
315
+
316
+ std::string getIOTrackingInfoJSON() override {
317
+ unsupported("IHermes::getIOTrackingInfoJSON", "not tracked; returning {}");
318
+ return "{}";
319
+ }
320
+
321
+ debugger::Debugger &getDebugger() override {
322
+ static debugger::Debugger kEmpty;
323
+ unsupported(
324
+ "IHermes::getDebugger",
325
+ "debugging goes through jsinspector-modern, not this API");
326
+ return kEmpty;
327
+ }
328
+
329
+ /// Hermes disables optimisation so debug info stays exact; QuickJS's
330
+ /// interpreter has no separate optimised mode, so this is a plain evaluate.
331
+ void debugJavaScript(
332
+ const std::string &src, const std::string &sourceURL,
333
+ const DebugFlags &) override {
334
+ plain().evaluateJavaScript(
335
+ std::make_shared<jsi::StringBuffer>(src), sourceURL);
336
+ }
337
+
338
+ void registerForProfiling() override {
339
+ noProfiler("registerForProfiling");
340
+ }
341
+ void unregisterForProfiling() override {
342
+ noProfiler("unregisterForProfiling");
343
+ }
344
+
345
+ void asyncTriggerTimeout() override {
346
+ interrupt_.store(true, std::memory_order_relaxed);
347
+ }
348
+
349
+ void watchTimeLimit(uint32_t timeoutInMs) override {
350
+ if (quickjs_ == nullptr) {
351
+ unsupported(
352
+ "IHermes::watchTimeLimit",
353
+ "the wrapped runtime is not a QuickJSRuntime; no limit is enforced");
354
+ return;
355
+ }
356
+ deadline_.store(nowMs() + timeoutInMs, std::memory_order_relaxed);
357
+ JS_SetInterruptHandler(
358
+ quickjs_->runtime(), &CompatRuntime::onInterrupt, this);
359
+ }
360
+
361
+ void unwatchTimeLimit() override {
362
+ deadline_.store(0, std::memory_order_relaxed);
363
+ interrupt_.store(false, std::memory_order_relaxed);
364
+ if (quickjs_ != nullptr) {
365
+ JS_SetInterruptHandler(quickjs_->runtime(), nullptr, nullptr);
366
+ }
367
+ }
368
+
369
+ /// The source map is accepted and ignored: QuickJS does not apply source
370
+ /// maps to stack traces.
371
+ jsi::Value evaluateJavaScriptWithSourceMap(
372
+ const std::shared_ptr<const jsi::Buffer> &buffer,
373
+ const std::shared_ptr<const jsi::Buffer> &,
374
+ const std::string &sourceURL) override {
375
+ degraded(
376
+ "IHermes::evaluateJavaScriptWithSourceMap",
377
+ "source map ignored; stack traces point at generated code");
378
+ return plain().evaluateJavaScript(buffer, sourceURL);
379
+ }
380
+
381
+ jsi::Value evaluateSHUnit(SHUnit *(*)()) override {
382
+ unsupported("IHermes::evaluateSHUnit", "Static Hermes only");
383
+ return jsi::Value::undefined();
384
+ }
385
+
386
+ SHRuntime *getSHRuntime() noexcept override {
387
+ return nullptr;
388
+ }
389
+
390
+ void *getVMRuntimeUnsafe() const override {
391
+ unsupported(
392
+ "IHermes::getVMRuntimeUnsafe",
393
+ "returning null; a JSContext cast to hermes::vm::Runtime corrupts");
394
+ return nullptr;
395
+ }
396
+
397
+ private:
398
+ template <typename T>
399
+ uint64_t id(const T &pointer) const {
400
+ return reinterpret_cast<uint64_t>(
401
+ JS_VALUE_GET_PTR(qjs::QuickJSRuntime::toJSValue(pointer)));
402
+ }
403
+
404
+ static void noProfiler(const char *api) {
405
+ unsupported(api, "QuickJS has no sampling profiler");
406
+ }
407
+
408
+ static uint64_t nowMs() {
409
+ return static_cast<uint64_t>(
410
+ std::chrono::duration_cast<std::chrono::milliseconds>(
411
+ std::chrono::steady_clock::now().time_since_epoch())
412
+ .count());
413
+ }
414
+
415
+ static int onInterrupt(JSRuntime *, void *opaque) {
416
+ auto *self = static_cast<CompatRuntime *>(opaque);
417
+ if (self->interrupt_.load(std::memory_order_relaxed)) return 1;
418
+ const uint64_t deadline = self->deadline_.load(std::memory_order_relaxed);
419
+ return deadline != 0 && nowMs() > deadline ? 1 : 0;
420
+ }
421
+
422
+ std::unique_ptr<jsi::Runtime> owned_;
423
+ qjs::QuickJSRuntime *quickjs_;
424
+ std::atomic<uint64_t> deadline_{0};
425
+ std::atomic<bool> interrupt_{false};
426
+ };
427
+
428
+ /// A config field set away from its Hermes default is a request this engine
429
+ /// cannot carry out. Silently running with the opposite of what was asked is
430
+ /// the failure this shim exists to prevent, so each one is named: unsupported
431
+ /// where it changes what JavaScript may do, ignored where nothing observable
432
+ /// depends on it.
433
+ void reportUnhonoured(const ::hermes::vm::RuntimeConfig &config) {
434
+ using Config = ::hermes::vm::RuntimeConfig;
435
+ #define QJS_IF_SET(FIELD) \
436
+ if (config.get##FIELD() != Config::getDefault##FIELD())
437
+
438
+ QJS_IF_SET(EnableEval)
439
+ unsupported("RuntimeConfig::EnableEval", "eval and Function stay enabled");
440
+ QJS_IF_SET(ES6Promise)
441
+ unsupported(
442
+ "RuntimeConfig::ES6Promise", "Promise stays as the engine has it");
443
+ QJS_IF_SET(ES6Proxy)
444
+ unsupported("RuntimeConfig::ES6Proxy", "Proxy stays as the engine has it");
445
+ QJS_IF_SET(Intl)
446
+ unsupported("RuntimeConfig::Intl", "Intl stays as the engine has it");
447
+ QJS_IF_SET(EnableGenerator)
448
+ unsupported("RuntimeConfig::EnableGenerator", "generators stay enabled");
449
+ QJS_IF_SET(EnableHermesInternal)
450
+ unsupported(
451
+ "RuntimeConfig::EnableHermesInternal",
452
+ "HermesInternal is installed by the runtime, not by this");
453
+ QJS_IF_SET(MaxNumRegisters)
454
+ unsupported(
455
+ "RuntimeConfig::MaxNumRegisters", "the stack limit is the engine's own");
456
+ QJS_IF_SET(VMExperimentFlags)
457
+ unsupported("RuntimeConfig::VMExperimentFlags", "no Hermes VM to configure");
458
+
459
+ // MicrotaskQueue is deliberately not reported. Hermes has to be asked for a
460
+ // microtask queue; QuickJS always runs one, so a caller enabling it -- which
461
+ // is what react-native-worklets does -- gets what it asked for.
462
+
463
+ QJS_IF_SET(EnableJIT)
464
+ ignored("RuntimeConfig::EnableJIT", "QuickJS interprets; there is no JIT");
465
+ QJS_IF_SET(TraceEnabled)
466
+ ignored("RuntimeConfig::TraceEnabled", "no synth trace is recorded");
467
+ QJS_IF_SET(TrackIO)
468
+ ignored("RuntimeConfig::TrackIO", "no IO tracking");
469
+ QJS_IF_SET(EnableSampleProfiling)
470
+ ignored("RuntimeConfig::EnableSampleProfiling", "no sampling profiler");
471
+ QJS_IF_SET(EnableSampledStats)
472
+ ignored("RuntimeConfig::EnableSampledStats", "no sampled stats");
473
+ QJS_IF_SET(BytecodeWarmupPercent)
474
+ ignored("RuntimeConfig::BytecodeWarmupPercent", "an HBC warmup hint");
475
+ QJS_IF_SET(RandomizeMemoryLayout)
476
+ ignored("RuntimeConfig::RandomizeMemoryLayout", "layout is the allocator's");
477
+ #undef QJS_IF_SET
478
+
479
+ const auto &gc = config.getGCConfig();
480
+ using GC = ::hermes::vm::GCConfig;
481
+ if (gc.getMinHeapSize() != GC::getDefaultMinHeapSize() ||
482
+ gc.getInitHeapSize() != GC::getDefaultInitHeapSize() ||
483
+ gc.getMaxHeapSize() != GC::getDefaultMaxHeapSize()) {
484
+ ignored("RuntimeConfig::GCConfig", "QuickJS sizes its own heap");
485
+ }
486
+ }
487
+
488
+ std::unique_ptr<HermesRuntime> RootAPI::makeHermesRuntime(
489
+ const ::hermes::vm::RuntimeConfig &config) {
490
+ reportUnhonoured(config);
491
+ return std::make_unique<CompatRuntime>(qjs::makeQuickJSRuntime());
492
+ }
493
+
494
+ } // namespace
495
+
496
+ jsi::ICast *makeHermesRootAPI() {
497
+ // RootAPI reaches ICast through both of its bases, so the path is named.
498
+ return static_cast<IHermesRootAPI *>(&rootAPI());
499
+ }
500
+
501
+ ::hermes::vm::RuntimeConfig hardenedHermesRuntimeConfig() {
502
+ unsupported(
503
+ "hardenedHermesRuntimeConfig",
504
+ "no hardening switches exist; returning a default config");
505
+ return {};
506
+ }
507
+
508
+ std::unique_ptr<HermesRuntime> makeHermesRuntime(
509
+ const ::hermes::vm::RuntimeConfig &config) {
510
+ return rootAPI().makeHermesRuntime(config);
511
+ }
512
+
513
+ std::unique_ptr<jsi::ThreadSafeRuntime> makeThreadSafeHermesRuntime(
514
+ const ::hermes::vm::RuntimeConfig &) {
515
+ unsupported(
516
+ "makeThreadSafeHermesRuntime",
517
+ "returning null rather than an unlocked runtime");
518
+ return nullptr;
519
+ }
520
+
521
+ // ------------------------------------------------------------------- adapters
522
+
523
+ namespace inspector_modern {
524
+
525
+ RuntimeAdapter::~RuntimeAdapter() = default;
526
+ void RuntimeAdapter::tickleJs() {}
527
+
528
+ SharedRuntimeAdapter::SharedRuntimeAdapter(
529
+ std::shared_ptr<HermesRuntime> runtime)
530
+ : runtime_(std::move(runtime)) {}
531
+ SharedRuntimeAdapter::~SharedRuntimeAdapter() = default;
532
+ HermesRuntime &SharedRuntimeAdapter::getRuntime() {
533
+ return *runtime_;
534
+ }
535
+
536
+ } // namespace inspector_modern
537
+ } // namespace facebook::hermes
538
+
539
+ #ifdef HERMES_ENABLE_DEBUGGER
540
+ #include <hermes/inspector-modern/chrome/Registration.h>
541
+
542
+ namespace facebook::hermes::inspector_modern::chrome {
543
+
544
+ DebugSessionToken enableDebugging(
545
+ std::unique_ptr<RuntimeAdapter>, const std::string &) {
546
+ qjs::hermescompat::report(
547
+ qjs::hermescompat::Severity::Unsupported, "chrome::enableDebugging",
548
+ "not registered as a DevTools target; the app's main runtime is, a "
549
+ "runtime made through this shim is not");
550
+ static std::atomic<DebugSessionToken> next{1};
551
+ return next.fetch_add(1, std::memory_order_relaxed);
552
+ }
553
+
554
+ void disableDebugging(DebugSessionToken) {}
555
+
556
+ } // namespace facebook::hermes::inspector_modern::chrome
557
+ #endif // HERMES_ENABLE_DEBUGGER
package/package.json ADDED
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "@react-native-quickjs/quickjs",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "QuickJS JSI runtime for React Native, built from source",
5
+ "keywords": [
6
+ "react-native",
7
+ "quickjs",
8
+ "jsi",
9
+ "javascript-engine",
10
+ "ios",
11
+ "android"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Ammar Ahmed <ammarahmed6506@gmail.com> (https://github.com/ammarahm-ed)",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/react-native-quickjs/quickjs.git"
18
+ },
19
+ "homepage": "https://github.com/react-native-quickjs/quickjs#readme",
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "files": [
24
+ "CHANGELOG.md",
25
+ "NOTICE",
26
+ "src",
27
+ "android",
28
+ "apple",
29
+ "cmake",
30
+ "engine/quickjs-rel",
31
+ "modules/cdp/quickjs-cdp.h",
32
+ "modules/cdp/quickjs-cdp.c",
33
+ "modules/cdp/react",
34
+ "modules/hermes-compat/include",
35
+ "modules/hermes-compat/src",
36
+ "scripts/postinstall.js",
37
+ "scripts/bytecode",
38
+ "scripts/react_native_quickjs_pods.rb",
39
+ "*.podspec",
40
+ "bin",
41
+ "app.plugin.js",
42
+ "scripts/setup",
43
+ "scripts/expo",
44
+ "!android/build",
45
+ "!android/.cxx",
46
+ "!**/__tests__",
47
+ "!**/.*"
48
+ ],
49
+ "workspaces": [
50
+ "modules/text-encoding",
51
+ "example"
52
+ ],
53
+ "scripts": {
54
+ "submodules": "git submodule update --init --recursive",
55
+ "patches": "node scripts/apply-patches.js",
56
+ "sync-engine": "node scripts/sync-quickjs-rel.js",
57
+ "guard:selftest": "node scripts/guard-selftest.js",
58
+ "configure": "cmake -B build -DCMAKE_BUILD_TYPE=Debug",
59
+ "build": "cmake --build build -j8",
60
+ "test": "npm run configure && npm run build && ctest --test-dir build --output-on-failure -j4",
61
+ "bench": "cmake -B build-release -DCMAKE_BUILD_TYPE=Release && cmake --build build-release -j8 && ctest --test-dir build-release -V -R jsi_bench",
62
+ "bytecode": "./build/qjsc-ng",
63
+ "clean": "rm -rf build build-release",
64
+ "prepack": "node scripts/check-submodule.js && node scripts/apply-patches.js --check && node scripts/sync-quickjs-rel.js --check",
65
+ "postinstall": "node scripts/postinstall.js",
66
+ "example": "npm run --workspace @react-native-quickjs/example",
67
+ "docs:dev": "vitepress dev docs",
68
+ "docs:build": "vitepress build docs",
69
+ "release:module": "npm publish -w @react-native-quickjs/text-encoding",
70
+ "release:alpha": "npm publish --tag alpha"
71
+ },
72
+ "peerDependencies": {
73
+ "react": "*",
74
+ "react-native": ">=0.85.0",
75
+ "@expo/config-plugins": "*"
76
+ },
77
+ "devDependencies": {
78
+ "@commitlint/config-conventional": "^21.0.2",
79
+ "commitlint": "^21.0.2",
80
+ "lefthook": "^2.1.9",
81
+ "react": "19.2.3",
82
+ "react-native": "0.85.0",
83
+ "vitepress": "^1.6.4"
84
+ },
85
+ "commitlint": {
86
+ "extends": [
87
+ "@commitlint/config-conventional"
88
+ ]
89
+ },
90
+ "publishConfig": {
91
+ "access": "public"
92
+ },
93
+ "bin": {
94
+ "react-native-quickjs": "bin/react-native-quickjs.js"
95
+ },
96
+ "peerDependenciesMeta": {
97
+ "@expo/config-plugins": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "dependencies": {
102
+ "@react-native-quickjs/text-encoding": "1.0.0"
103
+ }
104
+ }