@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,70 @@
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 "QuickJSModule.h"
9
+
10
+ #include <algorithm>
11
+ #include <cstring>
12
+
13
+ #include "QuickJSRuntime.h"
14
+
15
+ namespace qjs {
16
+
17
+ namespace {
18
+
19
+ // Function-local rather than namespace-scope, so registration from another
20
+ // translation unit's static initializer cannot run before the vector is
21
+ // constructed. QJS_REGISTER_MODULE exists to be used from other translation
22
+ // units, and initialization order across them is unspecified.
23
+ std::vector<ModuleRegistration> &registry() {
24
+ static std::vector<ModuleRegistration> modules;
25
+ return modules;
26
+ }
27
+
28
+ } // namespace
29
+
30
+ void registerModule(const ModuleRegistration &module) {
31
+ if (module.install == nullptr || module.name == nullptr) {
32
+ return;
33
+ }
34
+
35
+ auto &modules = registry();
36
+ for (const auto &existing : modules) {
37
+ if (std::strcmp(existing.name, module.name) == 0) {
38
+ return;
39
+ }
40
+ }
41
+ modules.push_back(module);
42
+
43
+ // stable_sort so equal priorities keep registration order, which is what a
44
+ // module author leaving priority at its default would expect.
45
+ std::stable_sort(
46
+ modules.begin(), modules.end(),
47
+ [](const ModuleRegistration &a, const ModuleRegistration &b) {
48
+ return a.priority < b.priority;
49
+ });
50
+ }
51
+
52
+ const std::vector<ModuleRegistration> &registeredModules() {
53
+ return registry();
54
+ }
55
+
56
+ void installModules(jsi::Runtime &runtime) {
57
+ for (const auto &module : registry()) {
58
+ module.install(runtime);
59
+ }
60
+ }
61
+
62
+ JSContext *contextFromRuntime(jsi::Runtime &runtime) noexcept {
63
+ // dynamic_cast rather than a type tag: the reference may be any
64
+ // implementation, including a decorator around ours, and guessing wrong
65
+ // would corrupt memory rather than fail cleanly.
66
+ auto *quickjs = dynamic_cast<QuickJSRuntime *>(&runtime);
67
+ return quickjs != nullptr ? quickjs->context() : nullptr;
68
+ }
69
+
70
+ } // namespace qjs
@@ -0,0 +1,108 @@
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
+ #pragma once
9
+
10
+ #include <jsi/jsi.h>
11
+
12
+ #include <vector>
13
+
14
+ struct JSContext;
15
+
16
+ namespace qjs {
17
+
18
+ namespace jsi = facebook::jsi;
19
+
20
+ /**
21
+ * The QuickJS module ABI.
22
+ *
23
+ * A module is a standalone npm package containing C or C++ that installs
24
+ * globals into the runtime -- a native implementation of an API React Native
25
+ * would otherwise ship as a JavaScript shim. `TextEncoder` is the archetype:
26
+ * the shim walks a string one code unit at a time building an array, where the
27
+ * native version is a bounds-checked loop into a pre-sized buffer.
28
+ *
29
+ * Modules are ordinary consumers of JSI, so they stay portable and testable on
30
+ * the host -- until they need not to be, at which point see
31
+ * `contextFromRuntime()`.
32
+ */
33
+
34
+ /// Installs a module's globals. Called once per runtime, before any
35
+ /// application JavaScript runs.
36
+ using ModuleInstaller = void (*)(jsi::Runtime &);
37
+
38
+ struct ModuleRegistration {
39
+ /// Conventionally the npm package name. Used in diagnostics and to make
40
+ /// double registration detectable.
41
+ const char *name;
42
+
43
+ ModuleInstaller install;
44
+
45
+ /// Ascending install order, so a module depending on another's globals can
46
+ /// order itself after it. Ties keep registration order.
47
+ int priority;
48
+ };
49
+
50
+ /**
51
+ * Registers a module to be installed into every runtime created afterwards.
52
+ * Safe to call before any runtime exists.
53
+ *
54
+ * A duplicate name is ignored rather than fatal: static libraries get linked
55
+ * twice more often than anyone expects, and crashing an app over it would be a
56
+ * poor trade.
57
+ */
58
+ void registerModule(const ModuleRegistration &module);
59
+
60
+ inline void registerModule(
61
+ const char *name, ModuleInstaller install, int priority = 0) {
62
+ registerModule(ModuleRegistration{name, install, priority});
63
+ }
64
+
65
+ /// `makeQuickJSRuntime()` calls this. Call it yourself only for a runtime you
66
+ /// built by other means.
67
+ void installModules(jsi::Runtime &runtime);
68
+
69
+ const std::vector<ModuleRegistration> &registeredModules();
70
+
71
+ /**
72
+ * The `JSContext *` behind `runtime`, or nullptr if it is not one of ours.
73
+ *
74
+ * Use it where JSI cannot express something efficiently. The cost is
75
+ * portability, so check the return value rather than assuming it, and keep a
76
+ * plain-JSI path behind it. `QuickJSModuleNative.h` has the companion helpers;
77
+ * it is a separate header because it pulls in `quickjs.h`, which a portable
78
+ * module has no reason to see.
79
+ */
80
+ JSContext *contextFromRuntime(jsi::Runtime &runtime) noexcept;
81
+
82
+ /**
83
+ * Registers a module at static-initialization time.
84
+ *
85
+ * Convenient, but a static initializer in a static library is dropped by the
86
+ * linker unless something in its translation unit is referenced. The primary
87
+ * mechanism for a shipped module is therefore generated registration code,
88
+ * which cannot be stripped. Use this for shared libraries and tests.
89
+ */
90
+ /* The indirection is so INSTALL_FN may be a qualified name such as
91
+ ns::install -- pasting that into an identifier would not compile. */
92
+ #define QJS_REGISTER_MODULE_IMPL2(NAME, INSTALL_FN, UNIQUE) \
93
+ namespace { \
94
+ struct QjsModuleAutoRegister_##UNIQUE { \
95
+ QjsModuleAutoRegister_##UNIQUE() { \
96
+ ::qjs::registerModule(NAME, INSTALL_FN); \
97
+ } \
98
+ }; \
99
+ static QjsModuleAutoRegister_##UNIQUE qjs_module_auto_register_##UNIQUE; \
100
+ }
101
+
102
+ #define QJS_REGISTER_MODULE_IMPL(NAME, INSTALL_FN, UNIQUE) \
103
+ QJS_REGISTER_MODULE_IMPL2(NAME, INSTALL_FN, UNIQUE)
104
+
105
+ #define QJS_REGISTER_MODULE(NAME, INSTALL_FN) \
106
+ QJS_REGISTER_MODULE_IMPL(NAME, INSTALL_FN, __LINE__)
107
+
108
+ } // namespace qjs
@@ -0,0 +1,93 @@
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
+ #pragma once
9
+
10
+ #include <jsi/jsi.h>
11
+ #include <quickjs.h>
12
+
13
+ #include "QuickJSModule.h"
14
+ #include "QuickJSRuntime.h"
15
+
16
+ /**
17
+ * The engine escape hatch for QuickJS modules.
18
+ *
19
+ * Include this to work directly against the engine, which is the recommended
20
+ * way to write a module here. A module written against `QuickJSModule.h` alone
21
+ * is portable JSI and runs on any jsi::Runtime; that is the right choice only
22
+ * when the module must also run on Hermes.
23
+ *
24
+ * The decisive reason is constructors. A jsi::Function from
25
+ * createFromHostFunction is not a constructor, and `new` against it does not
26
+ * fail loudly: `thisVal` is the constructor function itself, so writes to
27
+ * `this` mutate the constructor, `instanceof` is false, and
28
+ * `class S extends Thing {}` gives undefined from `new S()`. JSI's
29
+ * HostFunctionType has no new.target and no way to build `this` from
30
+ * `.prototype`, so it cannot be fixed from above. `JS_NewCFunction2` with
31
+ * `JS_CFUNC_constructor` gives a real constructor.
32
+ *
33
+ * The rest is cost. Creating a Uint8Array in plain JSI means fetching the
34
+ * global constructor and calling it, per call, against one `JS_NewUint8Array`;
35
+ * `JS_NewStringLen` takes UTF-8 bytes directly; `JS_GetUint8Array` reads a
36
+ * typed array without copying. Atoms, classes and opaque pointers have no JSI
37
+ * spelling at all.
38
+ *
39
+ * A module that needs both writes the fast path behind a check:
40
+ *
41
+ * JSContext *ctx = qjs::contextFromRuntime(rt);
42
+ * if (ctx) { ... } else { ... }
43
+ *
44
+ * Ownership follows the rest of the runtime. `adopt*` takes the reference you
45
+ * pass; `borrow*` returns one you must not free, valid only as long as the
46
+ * original. A `JS_*` function returning a JSValue gives you a reference you
47
+ * own.
48
+ */
49
+
50
+ namespace qjs {
51
+
52
+ /// The concrete runtime, or nullptr if `runtime` is not ours.
53
+ inline QuickJSRuntime *quickJSRuntime(jsi::Runtime &runtime) noexcept {
54
+ return dynamic_cast<QuickJSRuntime *>(&runtime);
55
+ }
56
+
57
+ /// Wraps a JSValue you own as a jsi::Value, transferring ownership. The usual
58
+ /// return path for a host function that used the engine directly.
59
+ inline jsi::Value adoptJSValue(jsi::Runtime &runtime, JSValue value) {
60
+ auto *rt = quickJSRuntime(runtime);
61
+ if (rt == nullptr) {
62
+ // Nothing owns this value now and it cannot be freed without a context, so
63
+ // this is a programming error rather than a runtime condition.
64
+ throw jsi::JSINativeException(
65
+ "adoptJSValue called on a runtime that is not QuickJS-backed");
66
+ }
67
+ return rt->createValue(value);
68
+ }
69
+
70
+ /// Borrows the JSValue behind a jsi::Value. Owned by `value`, valid only while
71
+ /// it is, and must not be freed.
72
+ inline JSValue borrowJSValue(jsi::Runtime &runtime, const jsi::Value &value) {
73
+ auto *rt = quickJSRuntime(runtime);
74
+ if (rt == nullptr) {
75
+ throw jsi::JSINativeException(
76
+ "borrowJSValue called on a runtime that is not QuickJS-backed");
77
+ }
78
+ return rt->toJSValue(value);
79
+ }
80
+
81
+ /// Converts a pending quickjs exception into a thrown jsi::JSError. Call it
82
+ /// whenever an engine API returns the exception sentinel: a pending exception
83
+ /// left alive past the host function's return corrupts the engine's error
84
+ /// state and fails far from its cause.
85
+ [[noreturn]] inline void throwPendingQuickJSError(jsi::Runtime &runtime) {
86
+ auto *rt = quickJSRuntime(runtime);
87
+ if (rt == nullptr) {
88
+ throw jsi::JSINativeException("QuickJS error on a non-QuickJS runtime");
89
+ }
90
+ rt->throwPendingError();
91
+ }
92
+
93
+ } // namespace qjs
@@ -0,0 +1,37 @@
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 "QuickJSInstance.h"
9
+
10
+ #include "QuickJSRuntimeFactory.h"
11
+
12
+ #if defined(RNQJS_ENABLE_CDP) && RNQJS_ENABLE_CDP
13
+ #include "QuickJSInspector.h"
14
+ #endif
15
+
16
+ namespace facebook::react {
17
+
18
+ std::unique_ptr<JSRuntime> QuickJSInstance::createJSRuntime(
19
+ std::shared_ptr<MessageQueueThread> /*msgQueueThread*/) noexcept {
20
+ // The message queue thread is unused: the debugger work that has to happen on
21
+ // the JS thread is routed through React Native's RuntimeExecutor instead.
22
+ auto runtime = qjs::makeQuickJSRuntime(config_);
23
+
24
+ #if defined(RNQJS_ENABLE_CDP) && RNQJS_ENABLE_CDP
25
+ if (config_.enableDebugger) {
26
+ return std::make_unique<qjs::QuickJSInspectorRuntime>(std::move(runtime));
27
+ }
28
+ #endif
29
+
30
+ // JSIRuntimeHolder's base class vends a FallbackRuntimeTargetDelegate, which
31
+ // is the honest answer for a runtime that was never instrumented: DevTools
32
+ // still gets the Runtime domain through generic JSI, and the Debugger domain
33
+ // reports that it cannot bind breakpoints.
34
+ return std::make_unique<JSIRuntimeHolder>(std::move(runtime));
35
+ }
36
+
37
+ } // namespace facebook::react
@@ -0,0 +1,74 @@
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
+ #pragma once
9
+
10
+ #include <react/runtime/JSRuntimeFactory.h>
11
+
12
+ #include <memory>
13
+ #include <utility>
14
+
15
+ #include "QuickJSRuntimeConfig.h"
16
+
17
+ namespace facebook::react {
18
+
19
+ class MessageQueueThread;
20
+
21
+ /**
22
+ * The React Native engine entry point: hands a QuickJS-backed jsi::Runtime to
23
+ * ReactInstance. Shared by the Android and Apple layers, which do nothing but
24
+ * construct this and pass it through.
25
+ */
26
+ class QuickJSInstance : public JSRuntimeFactory {
27
+ public:
28
+ /**
29
+ * Default construction turns the debugger on in debug builds.
30
+ *
31
+ * Decided here rather than in the two platform layers, which would otherwise
32
+ * each need the same decision and fail silently on getting it wrong: the app
33
+ * runs, DevTools attaches, and no breakpoint ever binds -- on that platform
34
+ * only.
35
+ *
36
+ * It cannot be deferred until a frontend connects. Statement traps are
37
+ * emitted at parse time, so a runtime that was not instrumented before the
38
+ * bundle ran cannot be made debuggable afterwards. "Is this app debuggable?"
39
+ * is therefore a build-time question, and the honest default is yes in debug
40
+ * and no in release -- matching where React Native enables its own inspector.
41
+ */
42
+ QuickJSInstance() {
43
+ config_.enableDebugger = debuggerEnabledByDefault();
44
+ }
45
+
46
+ explicit QuickJSInstance(qjs::QuickJSRuntimeConfig config)
47
+ : config_(std::move(config)) {}
48
+
49
+ ~QuickJSInstance() override = default;
50
+
51
+ /// Exposed so an embedder building a config by hand can ask for the same
52
+ /// answer rather than re-deriving it. DEBUG is what CocoaPods defines for
53
+ /// Debug configurations and NDEBUG what CMake defines for Release; neither
54
+ /// platform sets the other's macro, so both are checked.
55
+ static constexpr bool debuggerEnabledByDefault() {
56
+ #if !defined(RNQJS_ENABLE_CDP) || !RNQJS_ENABLE_CDP
57
+ // No debugger compiled into this binary, so setting the flag would only buy
58
+ // parse-time instrumentation nothing can use.
59
+ return false;
60
+ #elif (defined(DEBUG) && DEBUG) || !defined(NDEBUG)
61
+ return true;
62
+ #else
63
+ return false;
64
+ #endif
65
+ }
66
+
67
+ std::unique_ptr<JSRuntime> createJSRuntime(
68
+ std::shared_ptr<MessageQueueThread> msgQueueThread) noexcept override;
69
+
70
+ private:
71
+ qjs::QuickJSRuntimeConfig config_;
72
+ };
73
+
74
+ } // namespace facebook::react