@lucent-lang/lucent 0.0.4 → 0.1.1
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/README.md +97 -0
- package/app.plugin.js +46 -10
- package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
- package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
- package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
- package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
- package/dist/cli.js +62 -13
- package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
- package/dist/compiler.js +4 -4
- package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
- package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
- package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
- package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
- package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
- package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
- package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
- package/dist/pipeline-BM_VM2X_.js +700 -0
- package/dist/project-BKNoTEEp.js +342 -0
- package/dist/sdk-coverage-iiv3JqMp.js +113 -0
- package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
- package/dist/sdk-lock-BQn7IyRs.js +38 -0
- package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
- package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
- package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
- package/dist/sdk-usage-0ZFMAYg1.js +95 -0
- package/dist/src-ChnmWLwn.js +40851 -0
- package/dist/trace-Bi4hkmPx.js +139 -0
- package/lib/core.cjs +368 -1
- package/lib/sdk/android.d.ts +54 -1
- package/lib/sdk/compose.d.ts +94 -0
- package/lib/sdk/compose.schemas.json.gz +0 -0
- package/lib/sdk/core.d.ts +201 -0
- package/lib/sdk/ios.d.ts +75 -2
- package/lib/sdk/ui.d.ts +129 -0
- package/metro/transformer.cjs +11 -2
- package/package.json +4 -3
- package/runtime/cpp/lucent/abort.cpp +59 -11
- package/runtime/cpp/lucent/abort.h +22 -6
- package/runtime/cpp/lucent/array.h +12 -2
- package/runtime/cpp/lucent/async.h +101 -21
- package/runtime/cpp/lucent/bigint.cpp +898 -0
- package/runtime/cpp/lucent/bigint.h +302 -0
- package/runtime/cpp/lucent/buffer.cpp +228 -0
- package/runtime/cpp/lucent/buffer.h +328 -0
- package/runtime/cpp/lucent/builtins.cpp +4 -1
- package/runtime/cpp/lucent/bytes.h +21 -2
- package/runtime/cpp/lucent/callback.cpp +29 -0
- package/runtime/cpp/lucent/callback.h +230 -0
- package/runtime/cpp/lucent/compute.cpp +370 -0
- package/runtime/cpp/lucent/compute.h +455 -0
- package/runtime/cpp/lucent/conversions.h +111 -0
- package/runtime/cpp/lucent/core.h +16 -5
- package/runtime/cpp/lucent/equality.h +107 -28
- package/runtime/cpp/lucent/execution.cpp +416 -0
- package/runtime/cpp/lucent/execution.h +270 -0
- package/runtime/cpp/lucent/extension.cpp +97 -0
- package/runtime/cpp/lucent/extension.h +210 -0
- package/runtime/cpp/lucent/helpers.h +12 -5
- package/runtime/cpp/lucent/items.h +52 -0
- package/runtime/cpp/lucent/jserror.cpp +9 -0
- package/runtime/cpp/lucent/jserror.h +11 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
- package/runtime/cpp/lucent/jsi/convert.h +128 -59
- package/runtime/cpp/lucent/jsi/host.cpp +213 -35
- package/runtime/cpp/lucent/jsi/host.h +116 -16
- package/runtime/cpp/lucent/json.h +19 -1
- package/runtime/cpp/lucent/lifecycle.cpp +260 -0
- package/runtime/cpp/lucent/lifecycle.h +124 -0
- package/runtime/cpp/lucent/lucent.h +7 -0
- package/runtime/cpp/lucent/map.h +41 -7
- package/runtime/cpp/lucent/native.cpp +35 -47
- package/runtime/cpp/lucent/native.h +116 -26
- package/runtime/cpp/lucent/number.cpp +38 -38
- package/runtime/cpp/lucent/number.h +33 -0
- package/runtime/cpp/lucent/operation.h +65 -0
- package/runtime/cpp/lucent/ops.h +14 -6
- package/runtime/cpp/lucent/platform/android.cpp +223 -10
- package/runtime/cpp/lucent/platform/android.h +227 -4
- package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
- package/runtime/cpp/lucent/platform/android_requests.h +140 -0
- package/runtime/cpp/lucent/platform/compose.h +256 -0
- package/runtime/cpp/lucent/platform/ios.h +325 -25
- package/runtime/cpp/lucent/platform/ios.mm +1 -14
- package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
- package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
- package/runtime/cpp/lucent/platform/swiftui.h +190 -0
- package/runtime/cpp/lucent/presentation.cpp +11 -0
- package/runtime/cpp/lucent/presentation.h +109 -0
- package/runtime/cpp/lucent/reactive.cpp +406 -0
- package/runtime/cpp/lucent/reactive.h +566 -0
- package/runtime/cpp/lucent/report.cpp +40 -0
- package/runtime/cpp/lucent/report.h +16 -0
- package/runtime/cpp/lucent/resource.cpp +83 -0
- package/runtime/cpp/lucent/resource.h +75 -0
- package/runtime/cpp/lucent/scheduler.cpp +38 -124
- package/runtime/cpp/lucent/scheduler.h +102 -83
- package/runtime/cpp/lucent/scope.cpp +308 -0
- package/runtime/cpp/lucent/scope.h +452 -0
- package/runtime/cpp/lucent/sizing.h +263 -0
- package/runtime/cpp/lucent/slots.h +231 -0
- package/runtime/cpp/lucent/trace.cpp +539 -0
- package/runtime/cpp/lucent/trace.h +251 -0
- package/runtime/cpp/lucent/transport.cpp +9 -0
- package/runtime/cpp/lucent/transport.h +341 -0
- package/runtime/cpp/lucent/view.cpp +123 -0
- package/runtime/cpp/lucent/view.h +157 -0
- package/runtime/cpp/rn/LucentComponentView.h +121 -0
- package/runtime/cpp/rn/LucentComponentView.mm +479 -0
- package/runtime/cpp/rn/LucentModule.cpp +37 -7
- package/runtime/cpp/rn/LucentModule.h +3 -0
- package/runtime/cpp/rn/LucentViewChildren.h +80 -0
- package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
- package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
- package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
- package/runtime/cpp/rn/LucentViewRequests.h +104 -0
- package/runtime/cpp/rn/LucentViewSizing.h +345 -0
- package/runtime/cpp/rn/LucentViewSlots.h +242 -0
- package/runtime/cpp/rn/LucentViewValues.h +125 -0
- package/runtime/cpp/rn/LucentViews.h +440 -0
- package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
- package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
- package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
- package/runtime/cpp/third_party/dragonbox/README.md +16 -0
- package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
- package/runtime/js/index.d.ts +11 -1
- package/runtime/js/index.js +84 -8
- package/runtime/js/views.d.ts +38 -0
- package/runtime/js/views.js +226 -0
- package/runtime/native/android/CMakeLists.txt +4 -1
- package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
- package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
- package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
- package/runtime/test/jsi/harness.cpp +9 -0
- package/schemas/build.schema.json +96 -1
- package/schemas/check.schema.json +56 -1
- package/schemas/lucent.schema.json +495 -9
- package/schemas/sdk-diff.schema.json +70 -0
- package/schemas/sdk-lock.schema.json +67 -0
- package/ts-plugin/index.js +85 -4
- package/dist/pipeline-CH7-2cxu.js +0 -232
- package/dist/project-D8tEet0k.js +0 -150
- package/dist/sdk-coverage-BoW_3Efd.js +0 -64
- package/dist/src-BD7f1_aZ.js +0 -11493
|
@@ -20,6 +20,9 @@ std::unordered_map<jsi::Runtime*, std::weak_ptr<Host>>& registry() {
|
|
|
20
20
|
// stale. Constant-initialized: reading it needs no guard.
|
|
21
21
|
std::atomic<uint64_t> registryGeneration{1};
|
|
22
22
|
|
|
23
|
+
// Constant-initialized: safe to use during static initialization.
|
|
24
|
+
std::atomic<RuntimeId> nextRuntimeId{1};
|
|
25
|
+
|
|
23
26
|
// The last host this thread looked up: every call from JavaScript asks for
|
|
24
27
|
// it, and a thread nearly always talks to one runtime. One thread_local, as
|
|
25
28
|
// each costs a lookup on some platforms (__tlv_get_addr on Apple's).
|
|
@@ -30,29 +33,109 @@ struct CachedHost {
|
|
|
30
33
|
};
|
|
31
34
|
thread_local CachedHost cached;
|
|
32
35
|
|
|
36
|
+
/// Runs `job` on the legacy module context: here if this thread is in it,
|
|
37
|
+
/// else as a turn there. Either way `job` is destroyed there, holding the
|
|
38
|
+
/// Lucent lock, like the values it carries.
|
|
39
|
+
void onModuleContext(Job job) {
|
|
40
|
+
if (!Scheduler::lock().heldByCurrentThread()) {
|
|
41
|
+
Scheduler::instance().post(std::move(job));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
detail::runGuarded(job, "job");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Counts one task or job in flight for a host while it lives.
|
|
49
|
+
class Counted {
|
|
50
|
+
public:
|
|
51
|
+
explicit Counted(std::shared_ptr<std::atomic<size_t>> count) : count_(std::move(count)) { ++*count_; }
|
|
52
|
+
|
|
53
|
+
~Counted() { --*count_; }
|
|
54
|
+
|
|
55
|
+
Counted(const Counted&) = delete;
|
|
56
|
+
Counted& operator=(const Counted&) = delete;
|
|
57
|
+
|
|
58
|
+
private:
|
|
59
|
+
std::shared_ptr<std::atomic<size_t>> count_;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/// A task for the JS thread. If it never runs there, it is released on the
|
|
63
|
+
/// legacy module context, after `dropped`: the values it carries are the
|
|
64
|
+
/// module context's, whichever thread gave up on the task. It counts as in
|
|
65
|
+
/// flight until then.
|
|
66
|
+
struct Parcel {
|
|
67
|
+
std::shared_ptr<Counted> counted;
|
|
68
|
+
JsTask task;
|
|
69
|
+
Job dropped;
|
|
70
|
+
std::atomic<bool> ran{false};
|
|
71
|
+
|
|
72
|
+
Parcel(std::shared_ptr<Counted> c, JsTask t, Job d) : counted(std::move(c)), task(std::move(t)), dropped(std::move(d)) {}
|
|
73
|
+
|
|
74
|
+
~Parcel() {
|
|
75
|
+
if (ran.load()) return;
|
|
76
|
+
|
|
77
|
+
onModuleContext([counted = std::move(counted), task = std::move(task), dropped = std::move(dropped)]() mutable {
|
|
78
|
+
if (dropped) dropped();
|
|
79
|
+
|
|
80
|
+
task = nullptr;
|
|
81
|
+
dropped = nullptr;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
} // namespace
|
|
87
|
+
|
|
33
88
|
// Installed on the JS global object so the runtime's teardown releases our
|
|
34
|
-
// JSI references on the JS thread while the runtime still exists.
|
|
35
|
-
|
|
89
|
+
// JSI references on the JS thread while the runtime still exists. It runs
|
|
90
|
+
// no JavaScript: the runtime may be being destroyed.
|
|
91
|
+
class Host::Anchor : public jsi::HostObject {
|
|
36
92
|
public:
|
|
37
|
-
explicit
|
|
38
|
-
|
|
39
|
-
|
|
93
|
+
explicit Anchor(std::shared_ptr<Host> host) : host_(std::move(host)) {}
|
|
94
|
+
|
|
95
|
+
~Anchor() override { host_->tearDown(false); }
|
|
96
|
+
|
|
97
|
+
#ifndef NDEBUG
|
|
98
|
+
jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
|
|
99
|
+
if (name.utf8(rt) != "ownership") return jsi::Value::undefined();
|
|
100
|
+
|
|
101
|
+
Ownership held = host_->ownership();
|
|
102
|
+
jsi::Object o(rt);
|
|
103
|
+
o.setProperty(rt, "runtime", static_cast<double>(held.runtime));
|
|
104
|
+
o.setProperty(rt, "promises", static_cast<double>(held.promises));
|
|
105
|
+
o.setProperty(rt, "callbacks", static_cast<double>(held.callbacks));
|
|
106
|
+
o.setProperty(rt, "identities", static_cast<double>(held.identities));
|
|
107
|
+
o.setProperty(rt, "prototypes", static_cast<double>(held.prototypes));
|
|
108
|
+
o.setProperty(rt, "modules", static_cast<double>(held.modules));
|
|
109
|
+
o.setProperty(rt, "registrations", static_cast<double>(held.registrations));
|
|
110
|
+
o.setProperty(rt, "inFlight", static_cast<double>(held.inFlight));
|
|
111
|
+
return o;
|
|
40
112
|
}
|
|
113
|
+
#endif
|
|
41
114
|
|
|
42
115
|
private:
|
|
43
116
|
std::shared_ptr<Host> host_;
|
|
44
117
|
};
|
|
45
118
|
|
|
46
|
-
} // namespace
|
|
47
|
-
|
|
48
119
|
std::shared_ptr<Host> Host::create(jsi::Runtime& rt, JsPoster poster) {
|
|
120
|
+
std::shared_ptr<Host> previous;
|
|
121
|
+
{
|
|
122
|
+
std::lock_guard<std::mutex> g(registryMutex());
|
|
123
|
+
auto it = registry().find(&rt);
|
|
124
|
+
if (it != registry().end()) previous = it->second.lock();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (previous) previous->invalidate();
|
|
128
|
+
|
|
49
129
|
std::shared_ptr<Host> host(new Host(rt, std::move(poster)));
|
|
50
130
|
{
|
|
51
131
|
std::lock_guard<std::mutex> g(registryMutex());
|
|
52
132
|
registry()[&rt] = host;
|
|
53
133
|
registryGeneration++;
|
|
54
134
|
}
|
|
55
|
-
rt.global().setProperty(rt, "__lucentHost", jsi::Object::createFromHostObject(rt, std::make_shared<
|
|
135
|
+
rt.global().setProperty(rt, "__lucentHost", jsi::Object::createFromHostObject(rt, std::make_shared<Anchor>(host)));
|
|
136
|
+
|
|
137
|
+
// Module code's work from now on belongs to this runtime: tearing it down cancels that work.
|
|
138
|
+
setModuleScope(host->scope());
|
|
56
139
|
{
|
|
57
140
|
LucentScope scope;
|
|
58
141
|
resetModuleState();
|
|
@@ -81,7 +164,12 @@ const jsi::PropNameID& Host::makeProp(jsi::Runtime& rt, const PropName& name) {
|
|
|
81
164
|
return *props_[name.id];
|
|
82
165
|
}
|
|
83
166
|
|
|
84
|
-
Host::Host(jsi::Runtime& rt, JsPoster poster)
|
|
167
|
+
Host::Host(jsi::Runtime& rt, JsPoster poster)
|
|
168
|
+
: id_(nextRuntimeId.fetch_add(1, std::memory_order_relaxed)),
|
|
169
|
+
rt_(rt),
|
|
170
|
+
poster_(std::move(poster)),
|
|
171
|
+
jsThread_(std::this_thread::get_id()),
|
|
172
|
+
scope_(Scope::create(id_, Scheduler::instance().root())) {}
|
|
85
173
|
|
|
86
174
|
Host::~Host() {
|
|
87
175
|
std::lock_guard<std::mutex> g(registryMutex());
|
|
@@ -90,31 +178,102 @@ Host::~Host() {
|
|
|
90
178
|
registryGeneration++;
|
|
91
179
|
}
|
|
92
180
|
|
|
93
|
-
|
|
94
|
-
|
|
181
|
+
bool Host::postToJs(JsTask task, Job dropped) {
|
|
182
|
+
auto parcel = std::make_shared<Parcel>(std::make_shared<Counted>(inFlight_), std::move(task), std::move(dropped));
|
|
183
|
+
|
|
184
|
+
if (!alive_.load()) return false;
|
|
185
|
+
|
|
95
186
|
std::weak_ptr<Host> weak = weak_from_this();
|
|
96
|
-
poster_([weak,
|
|
187
|
+
poster_([weak, parcel](jsi::Runtime& rt) {
|
|
97
188
|
auto self = weak.lock();
|
|
98
189
|
if (!self || !self->alive()) return;
|
|
99
|
-
|
|
190
|
+
|
|
191
|
+
parcel->ran = true;
|
|
192
|
+
parcel->task(rt);
|
|
100
193
|
});
|
|
194
|
+
|
|
195
|
+
return true;
|
|
101
196
|
}
|
|
102
197
|
|
|
103
|
-
void Host::
|
|
198
|
+
void Host::postToModule(Job job) {
|
|
199
|
+
std::weak_ptr<Host> weak = weak_from_this();
|
|
200
|
+
|
|
201
|
+
// The scope drops the job once disposed; until its disposal has run on
|
|
202
|
+
// the module context, the host's own state says it is torn down.
|
|
203
|
+
Scheduler::instance().post(
|
|
204
|
+
[weak, counted = std::make_shared<Counted>(inFlight_), job = std::move(job)]() mutable {
|
|
205
|
+
auto self = weak.lock();
|
|
206
|
+
if (self && self->alive()) job();
|
|
207
|
+
|
|
208
|
+
job = nullptr;
|
|
209
|
+
},
|
|
210
|
+
scope_);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Host::Ownership Host::ownership() const {
|
|
214
|
+
size_t identities = 0;
|
|
215
|
+
for (auto& [native, weak] : identities_) {
|
|
216
|
+
if (weak.lock(rt_).isObject()) identities++;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {id_,
|
|
220
|
+
promises_.size(),
|
|
221
|
+
functions_.size(),
|
|
222
|
+
identities,
|
|
223
|
+
prototypes_.size(),
|
|
224
|
+
modules_.size(),
|
|
225
|
+
scope_->registrations(),
|
|
226
|
+
inFlight_->load()};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
Error Host::goneError() { return makeError(String::fromLatin1("AbortError"), String::fromLatin1("The JavaScript runtime is gone")); }
|
|
230
|
+
|
|
231
|
+
void Host::invalidate() { tearDown(true); }
|
|
232
|
+
|
|
233
|
+
void Host::tearDown(bool runtimeUsable) {
|
|
104
234
|
if (!alive_.exchange(false)) return;
|
|
235
|
+
|
|
236
|
+
// JavaScript still waiting for Lucent learns that it never will.
|
|
237
|
+
auto owed = std::move(promises_);
|
|
105
238
|
promises_.clear();
|
|
239
|
+
|
|
240
|
+
if (runtimeUsable && !owed.empty()) {
|
|
241
|
+
Error torn = makeError(String::fromLatin1("AbortError"), String::fromLatin1("Lucent was torn down for this JavaScript runtime"));
|
|
242
|
+
|
|
243
|
+
for (auto& [id, resolvers] : owed) {
|
|
244
|
+
try {
|
|
245
|
+
resolvers.reject.call(rt_, errorToJs(rt_, torn));
|
|
246
|
+
} catch (...) {
|
|
247
|
+
reportUncaught(std::current_exception(), "host");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
owed.clear();
|
|
253
|
+
|
|
254
|
+
// Cancels its operations and runs its cleanups on the module context.
|
|
255
|
+
if (auto e = scope_->dispose()) reportUncaught(e, "host");
|
|
256
|
+
|
|
106
257
|
functions_.clear();
|
|
107
258
|
prototypes_.clear();
|
|
108
259
|
modules_.clear();
|
|
109
260
|
identities_.clear();
|
|
110
261
|
props_.clear();
|
|
262
|
+
|
|
263
|
+
// Only its own entry: the runtime may have a newer host by now.
|
|
264
|
+
std::weak_ptr<Host> self = weak_from_this();
|
|
111
265
|
std::lock_guard<std::mutex> g(registryMutex());
|
|
112
266
|
auto it = registry().find(&rt_);
|
|
113
|
-
if (it != registry().end()) registry().erase(it);
|
|
267
|
+
if (it != registry().end() && !it->second.owner_before(self) && !self.owner_before(it->second)) registry().erase(it);
|
|
114
268
|
registryGeneration++;
|
|
115
269
|
}
|
|
116
270
|
|
|
117
271
|
jsi::Value Host::module(jsi::Runtime& rt, const std::string& name) {
|
|
272
|
+
// A replaced host's module (a TurboModule that kept it) is the new one's.
|
|
273
|
+
if (!alive()) return get(rt).module(rt, name);
|
|
274
|
+
|
|
275
|
+
if (name == kIdentityName) return identity(rt);
|
|
276
|
+
|
|
118
277
|
auto it = modules_.find(name);
|
|
119
278
|
if (it != modules_.end()) return jsi::Value(rt, it->second);
|
|
120
279
|
size_t count = 0;
|
|
@@ -139,9 +298,27 @@ jsi::Object Host::modules(jsi::Runtime& rt) {
|
|
|
139
298
|
size_t count = 0;
|
|
140
299
|
const ModuleDef* defs = registeredModules(count);
|
|
141
300
|
for (size_t i = 0; i < count; i++) all.setProperty(rt, defs[i].name, module(rt, defs[i].name));
|
|
301
|
+
all.setProperty(rt, kIdentityName, identity(rt));
|
|
142
302
|
return all;
|
|
143
303
|
}
|
|
144
304
|
|
|
305
|
+
jsi::Object Host::identity(jsi::Runtime& rt) {
|
|
306
|
+
const BuildIdentity& built = buildIdentity();
|
|
307
|
+
|
|
308
|
+
jsi::Object modules(rt);
|
|
309
|
+
for (size_t i = 0; i < built.moduleCount; i++)
|
|
310
|
+
modules.setProperty(rt, built.modules[i].name, jsi::String::createFromUtf8(rt, built.modules[i].api));
|
|
311
|
+
|
|
312
|
+
jsi::Object o(rt);
|
|
313
|
+
o.setProperty(rt, "host", static_cast<double>(id_));
|
|
314
|
+
o.setProperty(rt, "runtimeAbi", kRuntimeAbi);
|
|
315
|
+
o.setProperty(rt, "target", jsi::String::createFromUtf8(rt, built.target));
|
|
316
|
+
o.setProperty(rt, "program", jsi::String::createFromUtf8(rt, built.program));
|
|
317
|
+
o.setProperty(rt, "modules", modules);
|
|
318
|
+
|
|
319
|
+
return o;
|
|
320
|
+
}
|
|
321
|
+
|
|
145
322
|
jsi::Value Host::createPromise(jsi::Runtime& rt, uint64_t& id) {
|
|
146
323
|
id = nextId_++;
|
|
147
324
|
auto slot = std::make_shared<std::pair<std::optional<jsi::Function>, std::optional<jsi::Function>>>();
|
|
@@ -215,16 +392,20 @@ jsi::Object& Host::prototype(jsi::Runtime& rt, const char* key, PrototypeInit in
|
|
|
215
392
|
return prototypes_.emplace(key, std::move(proto)).first->second;
|
|
216
393
|
}
|
|
217
394
|
|
|
218
|
-
namespace {
|
|
219
|
-
std::atomic<uint64_t> nextIdentity{1};
|
|
220
|
-
} // namespace
|
|
221
|
-
|
|
222
395
|
Ref<Object> instanceOf(jsi::Runtime& rt, const jsi::Value& v) {
|
|
223
396
|
if (!v.isObject()) return nullptr;
|
|
224
397
|
jsi::Object o = v.getObject(rt);
|
|
225
398
|
if (!o.hasNativeState(rt)) return nullptr;
|
|
226
399
|
auto s = std::dynamic_pointer_cast<InstanceState>(o.getNativeState(rt));
|
|
227
|
-
|
|
400
|
+
if (!s) return nullptr;
|
|
401
|
+
|
|
402
|
+
// One host per runtime: a live one is this runtime's.
|
|
403
|
+
if (!s->host->alive()) {
|
|
404
|
+
jsi::Value error = rt.global().getPropertyAsFunction(rt, "TypeError").callAsConstructor(rt, "This Lucent object belongs to a host that was torn down");
|
|
405
|
+
throw jsi::JSError(rt, std::move(error));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return s->object;
|
|
228
409
|
}
|
|
229
410
|
|
|
230
411
|
void defineFunction(jsi::Runtime& rt, jsi::Object& target, const char* name, unsigned argc, HostFn fn) {
|
|
@@ -254,34 +435,31 @@ void defineClass(jsi::Runtime& rt, Host& host, jsi::Object& exports, const char*
|
|
|
254
435
|
|
|
255
436
|
jsi::Value Host::wrap(jsi::Runtime& rt, const Ref<Object>& instance, const char* key, PrototypeInit init) {
|
|
256
437
|
if (!instance) return jsi::Value::null();
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (v.isObject()) return v;
|
|
262
|
-
}
|
|
263
|
-
} else {
|
|
264
|
-
instance->jsIdentity = nextIdentity.fetch_add(1);
|
|
438
|
+
auto it = identities_.find(instance.get());
|
|
439
|
+
if (it != identities_.end()) {
|
|
440
|
+
jsi::Value v = it->second.lock(rt);
|
|
441
|
+
if (v.isObject()) return v;
|
|
265
442
|
}
|
|
266
443
|
jsi::Object& proto = prototype(rt, key, init);
|
|
267
444
|
jsi::Object obj = jsi::Object::create(rt, jsi::Value(rt, proto));
|
|
268
|
-
obj.setNativeState(rt, std::make_shared<InstanceState>(instance));
|
|
445
|
+
obj.setNativeState(rt, std::make_shared<InstanceState>(instance, shared_from_this()));
|
|
269
446
|
// Periodically drop cache entries whose JS objects were collected.
|
|
270
447
|
if (++identitySweep_ >= 256) {
|
|
271
448
|
identitySweep_ = 0;
|
|
272
|
-
std::vector<
|
|
273
|
-
for (auto& [
|
|
274
|
-
if (!weak.lock(rt).isObject()) dead.push_back(
|
|
449
|
+
std::vector<const Object*> dead;
|
|
450
|
+
for (auto& [native, weak] : identities_) {
|
|
451
|
+
if (!weak.lock(rt).isObject()) dead.push_back(native);
|
|
275
452
|
}
|
|
276
|
-
for (
|
|
453
|
+
for (const Object* native : dead) identities_.erase(native);
|
|
277
454
|
}
|
|
278
|
-
identities_.insert_or_assign(instance
|
|
455
|
+
identities_.insert_or_assign(instance.get(), jsi::WeakObject(rt, obj));
|
|
279
456
|
return jsi::Value(std::move(obj));
|
|
280
457
|
}
|
|
281
458
|
|
|
282
459
|
jsi::Value Host::errorToJs(jsi::Runtime& rt, const Error& e) {
|
|
283
460
|
std::string name = e->name.toUtf8();
|
|
284
|
-
|
|
461
|
+
// The kinds Lucent throws (SyntaxError from BigInt(string)), as JavaScript's own.
|
|
462
|
+
const char* ctor = (name == "TypeError" || name == "RangeError" || name == "SyntaxError") ? name.c_str() : "Error";
|
|
285
463
|
jsi::Object err = rt.global()
|
|
286
464
|
.getPropertyAsFunction(rt, ctor)
|
|
287
465
|
.callAsConstructor(rt, jsi::String::createFromUtf8(rt, e->message.toUtf8()))
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
// pending promise resolvers, JS callbacks, class prototypes and the identity
|
|
6
6
|
// cache. JSI values may only be touched on the JS thread, so native code
|
|
7
7
|
// refers to them by id and hops to the JS thread to use them.
|
|
8
|
+
//
|
|
9
|
+
// Work for a runtime belongs to its host's scope, and ends with the host:
|
|
10
|
+
// torn down (a reload, the runtime's end), the host disposes its scope,
|
|
11
|
+
// drops what it posted that has not run, and releases what that carried on
|
|
12
|
+
// the legacy module context.
|
|
8
13
|
#pragma once
|
|
9
14
|
|
|
10
15
|
#include <jsi/jsi.h>
|
|
@@ -57,9 +62,39 @@ const ModuleDef* registeredModules(size_t& count);
|
|
|
57
62
|
/// Implemented by generated code: resets module-level state for a new runtime.
|
|
58
63
|
void resetModuleState();
|
|
59
64
|
|
|
65
|
+
/// What generated code and JavaScript proxies expect of this runtime: a
|
|
66
|
+
/// change that breaks either takes a new number. The compiler's
|
|
67
|
+
/// RUNTIME_ABI; generated code checks it as it builds.
|
|
68
|
+
inline constexpr int kRuntimeAbi = 1;
|
|
69
|
+
|
|
70
|
+
/// A module as the program was compiled: its name, and a hash of what
|
|
71
|
+
/// JavaScript sees of it (its exports and their signatures).
|
|
72
|
+
struct ModuleIdentity {
|
|
73
|
+
const char* name;
|
|
74
|
+
const char* api;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/// What this app's native code was built from, which JavaScript checks its
|
|
78
|
+
/// proxies against before they call it: the target it was compiled for
|
|
79
|
+
/// ("all" for code every target shares), a hash of the program, and each
|
|
80
|
+
/// module's API.
|
|
81
|
+
struct BuildIdentity {
|
|
82
|
+
const char* target;
|
|
83
|
+
const char* program;
|
|
84
|
+
const ModuleIdentity* modules;
|
|
85
|
+
size_t moduleCount;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/// Implemented by generated code.
|
|
89
|
+
const BuildIdentity& buildIdentity();
|
|
90
|
+
|
|
91
|
+
/// The name JavaScript reads the build identity by, next to the modules.
|
|
92
|
+
inline constexpr const char* kIdentityName = "__lucentIdentity";
|
|
93
|
+
|
|
60
94
|
class Host : public std::enable_shared_from_this<Host> {
|
|
61
95
|
public:
|
|
62
|
-
/// Creates the host for `rt`. Call on the JS thread.
|
|
96
|
+
/// Creates the host for `rt`. Call on the JS thread. A runtime has one
|
|
97
|
+
/// host: one it had already is torn down (invalidate) and replaced.
|
|
63
98
|
static std::shared_ptr<Host> create(jsi::Runtime& rt, JsPoster poster);
|
|
64
99
|
static Host& get(jsi::Runtime& rt);
|
|
65
100
|
/// The host for a call to a function `installed` defined: that host while
|
|
@@ -70,20 +105,73 @@ class Host : public std::enable_shared_from_this<Host> {
|
|
|
70
105
|
}
|
|
71
106
|
~Host();
|
|
72
107
|
|
|
108
|
+
/// Nonzero, and no other host has had it: what scopes and tokens of
|
|
109
|
+
/// work for this runtime carry.
|
|
110
|
+
RuntimeId id() const { return id_; }
|
|
111
|
+
|
|
73
112
|
jsi::Runtime& runtime() { return rt_; }
|
|
74
113
|
bool onJsThread() const { return std::this_thread::get_id() == jsThread_; }
|
|
75
114
|
bool alive() const { return alive_.load(); }
|
|
76
115
|
|
|
77
|
-
///
|
|
78
|
-
|
|
79
|
-
///
|
|
116
|
+
/// The root of the work done for this runtime, under the legacy module
|
|
117
|
+
/// context's root scope: disposed there when the host is torn down,
|
|
118
|
+
/// cancelling its operations and running its cleanups.
|
|
119
|
+
const std::shared_ptr<Scope>& scope() const { return scope_; }
|
|
120
|
+
|
|
121
|
+
/// Runs `task` on the JS thread unless the host is torn down first. A
|
|
122
|
+
/// task that never runs (the host torn down, or the poster dropped it)
|
|
123
|
+
/// is released on the legacy module context, after `dropped` runs there.
|
|
124
|
+
/// False if the host was already torn down.
|
|
125
|
+
bool postToJs(JsTask task, Job dropped = nullptr);
|
|
126
|
+
|
|
127
|
+
/// Runs `job` as a turn of the legacy module context unless the host is
|
|
128
|
+
/// torn down before it starts; dropped, it is released there.
|
|
129
|
+
void postToModule(Job job);
|
|
130
|
+
|
|
131
|
+
/// Tears the host down, on the JS thread while its runtime is usable:
|
|
132
|
+
/// the JS promises it owes reject with an AbortError, then its scope is
|
|
133
|
+
/// disposed and every JSI reference dropped. Idempotent.
|
|
80
134
|
void invalidate();
|
|
81
135
|
|
|
82
|
-
///
|
|
136
|
+
/// What Lucent code waiting on this runtime gets once the host is torn
|
|
137
|
+
/// down: an AbortError.
|
|
138
|
+
static Error goneError();
|
|
139
|
+
|
|
140
|
+
/// What the host holds, for debug ownership reports and tests (debug
|
|
141
|
+
/// builds show it to JavaScript as `__lucentHost.ownership`).
|
|
142
|
+
struct Ownership {
|
|
143
|
+
RuntimeId runtime = 0;
|
|
144
|
+
/// JS promises it will settle.
|
|
145
|
+
size_t promises = 0;
|
|
146
|
+
/// JS functions Lucent holds.
|
|
147
|
+
size_t callbacks = 0;
|
|
148
|
+
/// Live JS objects of native instances.
|
|
149
|
+
size_t identities = 0;
|
|
150
|
+
size_t prototypes = 0;
|
|
151
|
+
size_t modules = 0;
|
|
152
|
+
/// What its scope holds: JS promises Lucent awaits, operations, …
|
|
153
|
+
size_t registrations = 0;
|
|
154
|
+
size_t inFlight = 0;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/// On the JS thread.
|
|
158
|
+
Ownership ownership() const;
|
|
159
|
+
|
|
160
|
+
/// Tasks for the JS thread and jobs for the module context posted for
|
|
161
|
+
/// this runtime that have not yet run or been released. Any thread.
|
|
162
|
+
size_t inFlight() const { return inFlight_->load(); }
|
|
163
|
+
|
|
164
|
+
/// The exports object of a module, created on first use; for
|
|
165
|
+
/// kIdentityName, identity().
|
|
83
166
|
jsi::Value module(jsi::Runtime& rt, const std::string& name);
|
|
84
|
-
/// The object with every module as a property
|
|
167
|
+
/// The object with every module as a property, and the identity (for
|
|
168
|
+
/// test hosts, which install it as React Native's module would be).
|
|
85
169
|
jsi::Object modules(jsi::Runtime& rt);
|
|
86
170
|
|
|
171
|
+
/// The build identity for JavaScript: `{host, runtimeAbi, target,
|
|
172
|
+
/// program, modules: {name: api}}`, `host` being this host's id.
|
|
173
|
+
jsi::Object identity(jsi::Runtime& rt);
|
|
174
|
+
|
|
87
175
|
// --- promises ---------------------------------------------------------
|
|
88
176
|
/// Creates a JS promise; settle it later with resolve/reject on the JS thread.
|
|
89
177
|
jsi::Value createPromise(jsi::Runtime& rt, uint64_t& id);
|
|
@@ -101,8 +189,9 @@ class Host : public std::enable_shared_from_this<Host> {
|
|
|
101
189
|
/// The prototype for class `key`: the runtime's own across hosts (kept on
|
|
102
190
|
/// the global object), with this host's methods.
|
|
103
191
|
jsi::Object& prototype(jsi::Runtime& rt, const char* key, PrototypeInit init);
|
|
104
|
-
/// The JS object for a native instance: the same object
|
|
105
|
-
/// JavaScript still references it.
|
|
192
|
+
/// The JS object for a native instance in this runtime: the same object
|
|
193
|
+
/// each time while JavaScript still references it. Each runtime has its
|
|
194
|
+
/// own for the same instance.
|
|
106
195
|
jsi::Value wrap(jsi::Runtime& rt, const Ref<Object>& instance, const char* key, PrototypeInit init);
|
|
107
196
|
|
|
108
197
|
/// Converts a Lucent error into a JS Error object (not thrown).
|
|
@@ -117,39 +206,50 @@ class Host : public std::enable_shared_from_this<Host> {
|
|
|
117
206
|
}
|
|
118
207
|
|
|
119
208
|
private:
|
|
209
|
+
/// On the global object: tears the host down when the runtime does.
|
|
210
|
+
class Anchor;
|
|
211
|
+
|
|
120
212
|
Host(jsi::Runtime& rt, JsPoster poster);
|
|
121
213
|
|
|
214
|
+
/// invalidate(), but JS runs only if `runtimeUsable`: not while the
|
|
215
|
+
/// runtime is being destroyed.
|
|
216
|
+
void tearDown(bool runtimeUsable);
|
|
217
|
+
|
|
122
218
|
struct Resolvers {
|
|
123
219
|
jsi::Function resolve;
|
|
124
220
|
jsi::Function reject;
|
|
125
221
|
};
|
|
126
222
|
|
|
223
|
+
const RuntimeId id_;
|
|
127
224
|
jsi::Runtime& rt_;
|
|
128
225
|
JsPoster poster_;
|
|
129
226
|
std::thread::id jsThread_;
|
|
130
227
|
std::atomic<bool> alive_{true};
|
|
228
|
+
const std::shared_ptr<Scope> scope_;
|
|
229
|
+
const std::shared_ptr<std::atomic<size_t>> inFlight_ = std::make_shared<std::atomic<size_t>>(0);
|
|
131
230
|
uint64_t nextId_ = 1;
|
|
132
231
|
std::unordered_map<uint64_t, Resolvers> promises_;
|
|
133
232
|
std::unordered_map<uint64_t, jsi::Function> functions_;
|
|
134
233
|
std::unordered_map<std::string, jsi::Object> prototypes_;
|
|
135
234
|
std::unordered_map<std::string, jsi::Value> modules_;
|
|
136
|
-
|
|
235
|
+
// Keyed by the native instance: its JS object keeps it alive, so while
|
|
236
|
+
// an entry's object lives, no other instance can have its address.
|
|
237
|
+
std::unordered_map<const Object*, jsi::WeakObject> identities_;
|
|
137
238
|
size_t identitySweep_ = 0;
|
|
138
239
|
std::vector<std::unique_ptr<jsi::PropNameID>> props_;
|
|
139
240
|
const jsi::PropNameID& makeProp(jsi::Runtime& rt, const PropName& name);
|
|
140
241
|
};
|
|
141
242
|
|
|
142
|
-
///
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/// NativeState attached to the JS object of a Lucent class instance.
|
|
243
|
+
/// NativeState attached to the JS object of a Lucent class instance, and
|
|
244
|
+
/// the host that made it.
|
|
146
245
|
struct InstanceState : jsi::NativeState {
|
|
147
|
-
|
|
246
|
+
InstanceState(Ref<Object> o, std::shared_ptr<Host> h) : object(std::move(o)), host(std::move(h)) {}
|
|
148
247
|
Ref<Object> object;
|
|
248
|
+
std::shared_ptr<Host> host;
|
|
149
249
|
};
|
|
150
|
-
using InstanceStateBase = InstanceState;
|
|
151
250
|
|
|
152
|
-
/// The native instance behind a JS object, or null.
|
|
251
|
+
/// The native instance behind a JS object, or null. Throws a TypeError for
|
|
252
|
+
/// an object a torn-down host made: the state it belongs to has ended.
|
|
153
253
|
Ref<Object> instanceOf(jsi::Runtime& rt, const jsi::Value& v);
|
|
154
254
|
|
|
155
255
|
using HostFn = jsi::HostFunctionType;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#include <variant>
|
|
9
9
|
|
|
10
10
|
#include "array.h"
|
|
11
|
+
#include "bigint.h"
|
|
11
12
|
#include "bytes.h"
|
|
12
13
|
#include "date.h"
|
|
13
14
|
#include "regexp.h"
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
#include "jserror.h"
|
|
16
17
|
#include "function.h"
|
|
17
18
|
#include "map.h"
|
|
19
|
+
#include "native.h"
|
|
18
20
|
#include "number.h"
|
|
19
21
|
#include "jsstring.h"
|
|
20
22
|
|
|
@@ -51,10 +53,17 @@ inline void jsonWrite(JsonWriter& w, double v) {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
inline void jsonWrite(JsonWriter& w, bool v) { w.raw(v ? "true" : "false"); }
|
|
56
|
+
/// JSON has no bigints: JSON.stringify throws, as in JavaScript.
|
|
57
|
+
inline void jsonWrite(JsonWriter&, const BigInt&) { throwTypeError("Do not know how to serialize a BigInt"); }
|
|
54
58
|
inline void jsonWrite(JsonWriter& w, const String& s) { w.quote(s); }
|
|
55
59
|
inline void jsonWrite(JsonWriter& w, Undefined) { w.raw("null"); }
|
|
56
60
|
inline void jsonWrite(JsonWriter& w, Null) { w.raw("null"); }
|
|
57
61
|
inline void jsonWrite(JsonWriter& w, const Error&) { w.raw("{}"); }
|
|
62
|
+
/// An SDK object: a host object, with no enumerable own properties.
|
|
63
|
+
inline void jsonWrite(JsonWriter& w, const NativeRef&) { w.raw("{}"); }
|
|
64
|
+
class NativeBufferObject;
|
|
65
|
+
/// A NativeBuffer: an opaque handle, with no enumerable own properties either.
|
|
66
|
+
inline void jsonWrite(JsonWriter& w, const Ref<NativeBufferObject>&) { w.raw("{}"); }
|
|
58
67
|
template <class Sig>
|
|
59
68
|
void jsonWrite(JsonWriter& w, const Fn<Sig>&) {
|
|
60
69
|
w.raw("null");
|
|
@@ -123,7 +132,9 @@ inline void jsonWrite(JsonWriter& w, const Date& d) {
|
|
|
123
132
|
if (std::isnan(d->getTime())) w.raw("null");
|
|
124
133
|
else w.quote(d->toISOString());
|
|
125
134
|
}
|
|
126
|
-
|
|
135
|
+
/// A Uint8Array, or the bytes a buffer's borrow lends: its indexes as keys.
|
|
136
|
+
template <class B>
|
|
137
|
+
void jsonWriteBytes(JsonWriter& w, const B& b) {
|
|
127
138
|
w.raw("{");
|
|
128
139
|
for (size_t i = 0; i < b.size(); i++) {
|
|
129
140
|
if (i) w.raw(",");
|
|
@@ -133,6 +144,13 @@ inline void jsonWrite(JsonWriter& w, const Bytes& b) {
|
|
|
133
144
|
}
|
|
134
145
|
w.raw("}");
|
|
135
146
|
}
|
|
147
|
+
inline void jsonWrite(JsonWriter& w, const Bytes& b) { jsonWriteBytes(w, b); }
|
|
148
|
+
template <bool Writable>
|
|
149
|
+
class BasicByteSpan;
|
|
150
|
+
template <bool Writable>
|
|
151
|
+
void jsonWrite(JsonWriter& w, const BasicByteSpan<Writable>& b) {
|
|
152
|
+
jsonWriteBytes(w, b);
|
|
153
|
+
}
|
|
136
154
|
/// Helper for generated struct writers: one `"name":value` member.
|
|
137
155
|
template <class T>
|
|
138
156
|
void jsonField(JsonWriter& w, bool& first, const char* name, const T& v) {
|