@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.
Files changed (152) hide show
  1. package/README.md +97 -0
  2. package/app.plugin.js +46 -10
  3. package/dist/{bench-CR3E7e2e.js → bench-eEez4jhQ.js} +10 -6
  4. package/dist/{build-CAIrRSYA.js → build-CpUjeHFz.js} +1 -1
  5. package/dist/{build-check-QozuNJm5.js → build-check-BA1ZEp9s.js} +28 -13
  6. package/dist/{check-1SCmYlqA.js → check-BZx3FgI6.js} +1 -1
  7. package/dist/cli.js +62 -13
  8. package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
  9. package/dist/compiler.js +4 -4
  10. package/dist/{dashboard-B-NzjjaN.js → dashboard-CC-HXhcd.js} +2 -2
  11. package/dist/{dev-BYWMP7oC.js → dev-DI5yL8zx.js} +62 -23
  12. package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
  13. package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
  14. package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
  15. package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
  16. package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
  17. package/dist/pipeline-BM_VM2X_.js +700 -0
  18. package/dist/project-BKNoTEEp.js +342 -0
  19. package/dist/sdk-coverage-iiv3JqMp.js +113 -0
  20. package/dist/sdk-diff-ByJ1Gw-F.js +109 -0
  21. package/dist/sdk-lock-BQn7IyRs.js +38 -0
  22. package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-Bv2pNega.js} +2 -2
  23. package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-CPGJm_Ne.js} +2 -2
  24. package/dist/{sdk-show-CkrdlqlP.js → sdk-show-BUkLqRnA.js} +5 -4
  25. package/dist/sdk-usage-0ZFMAYg1.js +95 -0
  26. package/dist/src-ChnmWLwn.js +40851 -0
  27. package/dist/trace-Bi4hkmPx.js +139 -0
  28. package/lib/core.cjs +368 -1
  29. package/lib/sdk/android.d.ts +54 -1
  30. package/lib/sdk/compose.d.ts +94 -0
  31. package/lib/sdk/compose.schemas.json.gz +0 -0
  32. package/lib/sdk/core.d.ts +201 -0
  33. package/lib/sdk/ios.d.ts +75 -2
  34. package/lib/sdk/ui.d.ts +129 -0
  35. package/metro/transformer.cjs +11 -2
  36. package/package.json +4 -3
  37. package/runtime/cpp/lucent/abort.cpp +59 -11
  38. package/runtime/cpp/lucent/abort.h +22 -6
  39. package/runtime/cpp/lucent/array.h +12 -2
  40. package/runtime/cpp/lucent/async.h +101 -21
  41. package/runtime/cpp/lucent/bigint.cpp +898 -0
  42. package/runtime/cpp/lucent/bigint.h +302 -0
  43. package/runtime/cpp/lucent/buffer.cpp +228 -0
  44. package/runtime/cpp/lucent/buffer.h +328 -0
  45. package/runtime/cpp/lucent/builtins.cpp +4 -1
  46. package/runtime/cpp/lucent/bytes.h +21 -2
  47. package/runtime/cpp/lucent/callback.cpp +29 -0
  48. package/runtime/cpp/lucent/callback.h +230 -0
  49. package/runtime/cpp/lucent/compute.cpp +370 -0
  50. package/runtime/cpp/lucent/compute.h +455 -0
  51. package/runtime/cpp/lucent/conversions.h +111 -0
  52. package/runtime/cpp/lucent/core.h +16 -5
  53. package/runtime/cpp/lucent/equality.h +107 -28
  54. package/runtime/cpp/lucent/execution.cpp +416 -0
  55. package/runtime/cpp/lucent/execution.h +270 -0
  56. package/runtime/cpp/lucent/extension.cpp +97 -0
  57. package/runtime/cpp/lucent/extension.h +210 -0
  58. package/runtime/cpp/lucent/helpers.h +12 -5
  59. package/runtime/cpp/lucent/items.h +52 -0
  60. package/runtime/cpp/lucent/jserror.cpp +9 -0
  61. package/runtime/cpp/lucent/jserror.h +11 -0
  62. package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
  63. package/runtime/cpp/lucent/jsi/convert.h +128 -59
  64. package/runtime/cpp/lucent/jsi/host.cpp +213 -35
  65. package/runtime/cpp/lucent/jsi/host.h +116 -16
  66. package/runtime/cpp/lucent/json.h +19 -1
  67. package/runtime/cpp/lucent/lifecycle.cpp +260 -0
  68. package/runtime/cpp/lucent/lifecycle.h +124 -0
  69. package/runtime/cpp/lucent/lucent.h +7 -0
  70. package/runtime/cpp/lucent/map.h +41 -7
  71. package/runtime/cpp/lucent/native.cpp +35 -47
  72. package/runtime/cpp/lucent/native.h +116 -26
  73. package/runtime/cpp/lucent/number.cpp +38 -38
  74. package/runtime/cpp/lucent/number.h +33 -0
  75. package/runtime/cpp/lucent/operation.h +65 -0
  76. package/runtime/cpp/lucent/ops.h +14 -6
  77. package/runtime/cpp/lucent/platform/android.cpp +223 -10
  78. package/runtime/cpp/lucent/platform/android.h +227 -4
  79. package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
  80. package/runtime/cpp/lucent/platform/android_requests.h +140 -0
  81. package/runtime/cpp/lucent/platform/compose.h +256 -0
  82. package/runtime/cpp/lucent/platform/ios.h +325 -25
  83. package/runtime/cpp/lucent/platform/ios.mm +1 -14
  84. package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
  85. package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
  86. package/runtime/cpp/lucent/platform/swiftui.h +190 -0
  87. package/runtime/cpp/lucent/presentation.cpp +11 -0
  88. package/runtime/cpp/lucent/presentation.h +109 -0
  89. package/runtime/cpp/lucent/reactive.cpp +406 -0
  90. package/runtime/cpp/lucent/reactive.h +566 -0
  91. package/runtime/cpp/lucent/report.cpp +40 -0
  92. package/runtime/cpp/lucent/report.h +16 -0
  93. package/runtime/cpp/lucent/resource.cpp +83 -0
  94. package/runtime/cpp/lucent/resource.h +75 -0
  95. package/runtime/cpp/lucent/scheduler.cpp +38 -124
  96. package/runtime/cpp/lucent/scheduler.h +102 -83
  97. package/runtime/cpp/lucent/scope.cpp +308 -0
  98. package/runtime/cpp/lucent/scope.h +452 -0
  99. package/runtime/cpp/lucent/sizing.h +263 -0
  100. package/runtime/cpp/lucent/slots.h +231 -0
  101. package/runtime/cpp/lucent/trace.cpp +539 -0
  102. package/runtime/cpp/lucent/trace.h +251 -0
  103. package/runtime/cpp/lucent/transport.cpp +9 -0
  104. package/runtime/cpp/lucent/transport.h +341 -0
  105. package/runtime/cpp/lucent/view.cpp +123 -0
  106. package/runtime/cpp/lucent/view.h +157 -0
  107. package/runtime/cpp/rn/LucentComponentView.h +121 -0
  108. package/runtime/cpp/rn/LucentComponentView.mm +479 -0
  109. package/runtime/cpp/rn/LucentModule.cpp +37 -7
  110. package/runtime/cpp/rn/LucentModule.h +3 -0
  111. package/runtime/cpp/rn/LucentViewChildren.h +80 -0
  112. package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
  113. package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
  114. package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
  115. package/runtime/cpp/rn/LucentViewRequests.h +104 -0
  116. package/runtime/cpp/rn/LucentViewSizing.h +345 -0
  117. package/runtime/cpp/rn/LucentViewSlots.h +242 -0
  118. package/runtime/cpp/rn/LucentViewValues.h +125 -0
  119. package/runtime/cpp/rn/LucentViews.h +440 -0
  120. package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
  121. package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
  122. package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
  123. package/runtime/cpp/third_party/dragonbox/README.md +16 -0
  124. package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
  125. package/runtime/js/index.d.ts +11 -1
  126. package/runtime/js/index.js +84 -8
  127. package/runtime/js/views.d.ts +38 -0
  128. package/runtime/js/views.js +226 -0
  129. package/runtime/native/android/CMakeLists.txt +4 -1
  130. package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
  131. package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
  132. package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
  133. package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
  134. package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
  135. package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
  136. package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
  137. package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
  138. package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
  139. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
  140. package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
  141. package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
  142. package/runtime/test/jsi/harness.cpp +9 -0
  143. package/schemas/build.schema.json +96 -1
  144. package/schemas/check.schema.json +56 -1
  145. package/schemas/lucent.schema.json +495 -9
  146. package/schemas/sdk-diff.schema.json +70 -0
  147. package/schemas/sdk-lock.schema.json +67 -0
  148. package/ts-plugin/index.js +85 -4
  149. package/dist/pipeline-CH7-2cxu.js +0 -232
  150. package/dist/project-D8tEet0k.js +0 -150
  151. package/dist/sdk-coverage-BoW_3Efd.js +0 -64
  152. package/dist/src-BD7f1_aZ.js +0 -11493
@@ -0,0 +1,260 @@
1
+ #include "lifecycle.h"
2
+
3
+ #include <algorithm>
4
+ #include <atomic>
5
+ #include <map>
6
+ #include <mutex>
7
+ #include <stdexcept>
8
+ #include <string>
9
+
10
+ #include "execution.h"
11
+ #include "report.h"
12
+
13
+ namespace lucent {
14
+
15
+ namespace detail {
16
+
17
+ /// One subscription's listener. `live` turns false when it closes, so a
18
+ /// report that already took it skips it.
19
+ struct LifecycleListener {
20
+ enum class Kind : uint8_t { App, Scene };
21
+
22
+ Kind kind;
23
+ uint8_t event;
24
+ Lifecycle::AppListener app;
25
+ Lifecycle::SceneListener scene;
26
+ std::atomic<bool> live{true};
27
+ };
28
+
29
+ struct LifecycleState {
30
+ struct Scene {
31
+ SceneId id;
32
+ /// When it last connected or activated: the order of scenes().
33
+ uint64_t touched;
34
+ std::shared_ptr<Scope> scope;
35
+ };
36
+
37
+ struct Subscription {
38
+ std::shared_ptr<LifecycleListener> listener;
39
+ /// Kept here: the subscription lasts until it closes, not until its
40
+ /// handle goes.
41
+ std::shared_ptr<Resource> handle;
42
+ };
43
+
44
+ mutable std::mutex m;
45
+ std::vector<Scene> scenes;
46
+ uint64_t touches = 0;
47
+ uint64_t nextSubscription = 1;
48
+ // Keyed by id, so listeners run in subscription order.
49
+ std::map<uint64_t, Subscription> subscriptions;
50
+
51
+ Scene* find(SceneId id) {
52
+ auto it = std::find_if(scenes.begin(), scenes.end(), [id](const Scene& s) { return s.id == id; });
53
+ return it == scenes.end() ? nullptr : &*it;
54
+ }
55
+
56
+ /// The open listeners of an event, in subscription order.
57
+ std::vector<std::shared_ptr<LifecycleListener>> listenersOf(LifecycleListener::Kind kind, uint8_t event) const {
58
+ std::lock_guard<std::mutex> g(m);
59
+ std::vector<std::shared_ptr<LifecycleListener>> out;
60
+
61
+ for (const auto& [id, s] : subscriptions)
62
+ if (s.listener->kind == kind && s.listener->event == event) out.push_back(s.listener);
63
+
64
+ return out;
65
+ }
66
+ };
67
+
68
+ } // namespace detail
69
+
70
+ namespace {
71
+
72
+ // Constant-initialized: safe to use during static initialization.
73
+ std::atomic<SceneId> nextScene{1};
74
+
75
+ void requireMain(const char* what) {
76
+ if (!onMainThread()) throw std::logic_error(std::string("Lifecycle::") + what + " is reported on the main thread");
77
+ }
78
+
79
+ /// Runs the listeners a report took, skipping those closed since.
80
+ void run(const std::vector<std::shared_ptr<detail::LifecycleListener>>& listeners, SceneId scene) {
81
+ for (const auto& l : listeners) {
82
+ if (!l->live.load(std::memory_order_acquire)) continue;
83
+
84
+ try {
85
+ if (l->kind == detail::LifecycleListener::Kind::App) {
86
+ l->app();
87
+ } else {
88
+ l->scene(scene);
89
+ }
90
+ } catch (...) {
91
+ reportUncaught(std::current_exception(), "lifecycle listener");
92
+ }
93
+ }
94
+ }
95
+
96
+ } // namespace
97
+
98
+ Lifecycle::Lifecycle() : state_(std::make_shared<detail::LifecycleState>()) {}
99
+
100
+ Lifecycle::~Lifecycle() {
101
+ std::vector<detail::LifecycleState::Scene> scenes;
102
+ std::map<uint64_t, detail::LifecycleState::Subscription> subscriptions;
103
+ {
104
+ std::lock_guard<std::mutex> g(state_->m);
105
+ scenes.swap(state_->scenes);
106
+ subscriptions.swap(state_->subscriptions);
107
+ }
108
+
109
+ for (auto& [id, s] : subscriptions) s.listener->live.store(false, std::memory_order_release);
110
+
111
+ for (auto& scene : scenes)
112
+ if (auto e = scene.scope->dispose()) reportUncaught(e, "scene");
113
+ }
114
+
115
+ Lifecycle& Lifecycle::shared() {
116
+ // Never destroyed: the platform may report while the process exits.
117
+ static auto* lifecycle = new Lifecycle();
118
+ return *lifecycle;
119
+ }
120
+
121
+ SceneId Lifecycle::connect() {
122
+ requireMain("connect");
123
+
124
+ SceneId id = nextScene.fetch_add(1);
125
+
126
+ std::lock_guard<std::mutex> g(state_->m);
127
+ state_->scenes.push_back({id, ++state_->touches, Scope::create(0)});
128
+
129
+ return id;
130
+ }
131
+
132
+ void Lifecycle::report(SceneId scene, SceneEvent event) {
133
+ requireMain("report");
134
+
135
+ {
136
+ std::lock_guard<std::mutex> g(state_->m);
137
+ detail::LifecycleState::Scene* known = state_->find(scene);
138
+ if (!known) return;
139
+
140
+ if (event == SceneEvent::DidActivate) known->touched = ++state_->touches;
141
+ }
142
+
143
+ run(state_->listenersOf(detail::LifecycleListener::Kind::Scene, static_cast<uint8_t>(event)), scene);
144
+
145
+ if (event != SceneEvent::DidDisconnect) return;
146
+
147
+ std::shared_ptr<Scope> scope;
148
+ {
149
+ std::lock_guard<std::mutex> g(state_->m);
150
+ auto& scenes = state_->scenes;
151
+ auto it = std::find_if(scenes.begin(), scenes.end(), [scene](const auto& s) { return s.id == scene; });
152
+ if (it == scenes.end()) return;
153
+
154
+ scope = std::move(it->scope);
155
+ scenes.erase(it);
156
+ }
157
+
158
+ if (auto e = scope->dispose()) reportUncaught(e, "scene");
159
+ }
160
+
161
+ void Lifecycle::report(AppEvent event) {
162
+ requireMain("report");
163
+
164
+ run(state_->listenersOf(detail::LifecycleListener::Kind::App, static_cast<uint8_t>(event)), 0);
165
+ }
166
+
167
+ std::vector<SceneId> Lifecycle::scenes() const {
168
+ std::vector<detail::LifecycleState::Scene> scenes;
169
+ {
170
+ std::lock_guard<std::mutex> g(state_->m);
171
+ scenes = state_->scenes;
172
+ }
173
+
174
+ std::sort(scenes.begin(), scenes.end(), [](const auto& a, const auto& b) { return a.touched > b.touched; });
175
+
176
+ std::vector<SceneId> out;
177
+ for (const auto& s : scenes) out.push_back(s.id);
178
+ return out;
179
+ }
180
+
181
+ std::shared_ptr<Scope> Lifecycle::sceneScope(SceneId scene) const {
182
+ std::lock_guard<std::mutex> g(state_->m);
183
+ detail::LifecycleState::Scene* known = state_->find(scene);
184
+
185
+ return known ? known->scope : nullptr;
186
+ }
187
+
188
+ std::shared_ptr<Resource> Lifecycle::subscribe(AppEvent event, AppListener listener, const std::shared_ptr<Scope>& scope) {
189
+ if (!listener) throw std::invalid_argument("A lifecycle listener must be a function");
190
+
191
+ auto l = std::make_shared<detail::LifecycleListener>();
192
+ l->kind = detail::LifecycleListener::Kind::App;
193
+ l->event = static_cast<uint8_t>(event);
194
+ l->app = std::move(listener);
195
+
196
+ return add(std::move(l), scope);
197
+ }
198
+
199
+ std::shared_ptr<Resource> Lifecycle::subscribe(SceneEvent event, SceneListener listener, const std::shared_ptr<Scope>& scope) {
200
+ if (!listener) throw std::invalid_argument("A lifecycle listener must be a function");
201
+
202
+ auto l = std::make_shared<detail::LifecycleListener>();
203
+ l->kind = detail::LifecycleListener::Kind::Scene;
204
+ l->event = static_cast<uint8_t>(event);
205
+ l->scene = std::move(listener);
206
+
207
+ return add(std::move(l), scope);
208
+ }
209
+
210
+ std::shared_ptr<Resource> Lifecycle::add(std::shared_ptr<detail::LifecycleListener> listener, const std::shared_ptr<Scope>& scope) {
211
+ uint64_t id;
212
+ {
213
+ std::lock_guard<std::mutex> g(state_->m);
214
+ id = state_->nextSubscription++;
215
+ }
216
+
217
+ std::weak_ptr<detail::LifecycleState> weak = state_;
218
+ auto release = [weak, listener, id] {
219
+ listener->live.store(false, std::memory_order_release);
220
+
221
+ // Taken out under the lock and destroyed after it: the listener's
222
+ // captures, and the table's reference to this subscription.
223
+ detail::LifecycleState::Subscription removed;
224
+ if (auto state = weak.lock()) {
225
+ std::lock_guard<std::mutex> g(state->m);
226
+ auto it = state->subscriptions.find(id);
227
+ if (it == state->subscriptions.end()) return;
228
+
229
+ removed = std::move(it->second);
230
+ state->subscriptions.erase(it);
231
+ }
232
+ };
233
+
234
+ // Opened first: under a scope that has ended, it closes at once, before
235
+ // it is ever listed.
236
+ std::shared_ptr<Resource> handle = Resource::open(String::fromLatin1("Lifecycle subscription"), std::move(release), nullptr, scope);
237
+ if (!handle->isOpen()) return handle;
238
+
239
+ std::lock_guard<std::mutex> g(state_->m);
240
+ // Closed by its scope on another thread in the meantime: never listed.
241
+ if (!listener->live.load(std::memory_order_acquire)) return handle;
242
+
243
+ state_->subscriptions.emplace(id, detail::LifecycleState::Subscription{std::move(listener), handle});
244
+ return handle;
245
+ }
246
+
247
+ size_t Lifecycle::subscriptions() const {
248
+ std::lock_guard<std::mutex> g(state_->m);
249
+ return state_->subscriptions.size();
250
+ }
251
+
252
+ std::optional<SceneId> presentingScene(const std::vector<SceneId>& scenes, const std::function<SceneState(SceneId)>& stateOf) {
253
+ for (SceneState wanted : {SceneState::ForegroundActive, SceneState::ForegroundInactive})
254
+ for (SceneId s : scenes)
255
+ if (stateOf(s) == wanted) return s;
256
+
257
+ return std::nullopt;
258
+ }
259
+
260
+ } // namespace lucent
@@ -0,0 +1,124 @@
1
+ // Lucent runtime — the app's and its scenes' lifecycle.
2
+ //
3
+ // The platform adapter (lucent/platform/ios_ui.mm) reports what the
4
+ // platform posts, on the main thread: the app becoming active or going to
5
+ // the background, and each scene connecting, activating, and
6
+ // disconnecting. Lucent code subscribes to those events. Nothing here
7
+ // knows UIKit: a scene is a SceneId, never a pointer, so nothing here can
8
+ // keep a window or view controller alive.
9
+ //
10
+ // Each connected scene has a scope, disposed when the scene disconnects:
11
+ // work tied to what the scene shows (a presentation, say) ends with it.
12
+ #pragma once
13
+
14
+ #include <cstdint>
15
+ #include <functional>
16
+ #include <memory>
17
+ #include <optional>
18
+ #include <vector>
19
+
20
+ #include "resource.h"
21
+ #include "scope.h"
22
+
23
+ namespace lucent {
24
+
25
+ /// Process-unique and never reused; 0 is none.
26
+ using SceneId = uint64_t;
27
+
28
+ /// UIApplication's lifecycle notifications.
29
+ enum class AppEvent : uint8_t {
30
+ DidBecomeActive,
31
+ WillResignActive,
32
+ DidEnterBackground,
33
+ WillEnterForeground,
34
+ DidReceiveMemoryWarning,
35
+ WillTerminate,
36
+ };
37
+
38
+ /// UIScene's lifecycle notifications.
39
+ enum class SceneEvent : uint8_t {
40
+ WillConnect,
41
+ DidDisconnect,
42
+ DidActivate,
43
+ WillDeactivate,
44
+ WillEnterForeground,
45
+ DidEnterBackground,
46
+ };
47
+
48
+ /// UIScene.ActivationState.
49
+ enum class SceneState : uint8_t { Unattached, ForegroundActive, ForegroundInactive, Background };
50
+
51
+ namespace detail {
52
+ struct LifecycleState;
53
+ struct LifecycleListener;
54
+ }
55
+
56
+ /// What the platform reported, and who listens. Construction does nothing
57
+ /// else; destruction disposes the scopes of the scenes still connected.
58
+ class Lifecycle {
59
+ public:
60
+ using AppListener = std::function<void()>;
61
+ using SceneListener = std::function<void(SceneId)>;
62
+
63
+ Lifecycle();
64
+ ~Lifecycle();
65
+
66
+ Lifecycle(const Lifecycle&) = delete;
67
+ Lifecycle& operator=(const Lifecycle&) = delete;
68
+
69
+ /// The process's, which the platform adapter reports to.
70
+ static Lifecycle& shared();
71
+
72
+ // --- what the platform reports: on the main thread only (std::logic_error
73
+ // elsewhere) -------------------------------------------------------------
74
+
75
+ /// A scene the platform connected, or found connected: its new id, and a
76
+ /// scope for it. Nothing is announced: the platform reports WillConnect
77
+ /// once it can name the scene.
78
+ SceneId connect();
79
+
80
+ /// Runs the event's listeners with the scene. DidActivate makes it the
81
+ /// most recently activated; DidDisconnect, after the listeners ran,
82
+ /// disposes its scope and forgets it. A scene it does not know is
83
+ /// ignored.
84
+ void report(SceneId scene, SceneEvent event);
85
+
86
+ void report(AppEvent event);
87
+
88
+ // --- what it knows: any thread ------------------------------------------
89
+
90
+ /// The connected scenes, most recently activated first (or, until
91
+ /// activated, connected).
92
+ std::vector<SceneId> scenes() const;
93
+
94
+ /// The scene's scope while it is connected, else null.
95
+ std::shared_ptr<Scope> sceneScope(SceneId scene) const;
96
+
97
+ // --- subscriptions: any thread ------------------------------------------
98
+
99
+ /// Calls `listener` on each `event`, on the main thread, from the next
100
+ /// report on. The subscription is open until it is closed or `scope` is
101
+ /// disposed (under a scope that has ended, it is closed at once);
102
+ /// dropping the handle does not end it. Closed on the main thread, its
103
+ /// listener never runs again; closed on another thread while an event is
104
+ /// being reported, it may still hear that event (closing never waits).
105
+ /// What a listener throws is reported (reportUncaught), and the rest
106
+ /// still run.
107
+ std::shared_ptr<Resource> subscribe(AppEvent event, AppListener listener, const std::shared_ptr<Scope>& scope = nullptr);
108
+ std::shared_ptr<Resource> subscribe(SceneEvent event, SceneListener listener, const std::shared_ptr<Scope>& scope = nullptr);
109
+
110
+ /// Open subscriptions (for debug ownership reports and tests).
111
+ size_t subscriptions() const;
112
+
113
+ private:
114
+ std::shared_ptr<Resource> add(std::shared_ptr<detail::LifecycleListener> listener, const std::shared_ptr<Scope>& scope);
115
+
116
+ const std::shared_ptr<detail::LifecycleState> state_;
117
+ };
118
+
119
+ /// The scene to present from, of `scenes` (most recently activated first):
120
+ /// the first in the foreground and active, else the first in the
121
+ /// foreground; none while every scene is in the background or unattached.
122
+ std::optional<SceneId> presentingScene(const std::vector<SceneId>& scenes, const std::function<SceneState(SceneId)>& stateOf);
123
+
124
+ } // namespace lucent
@@ -4,11 +4,16 @@
4
4
  #include "abort.h"
5
5
  #include "array.h"
6
6
  #include "async.h"
7
+ #include "bigint.h"
8
+ #include "buffer.h"
7
9
  #include "bytes.h"
10
+ #include "callback.h"
8
11
  #include "console.h"
12
+ #include "conversions.h"
9
13
  #include "core.h"
10
14
  #include "date.h"
11
15
  #include "equality.h"
16
+ #include "extension.h"
12
17
  #include "jserror.h"
13
18
  #include "function.h"
14
19
  #include "generator.h"
@@ -19,6 +24,8 @@
19
24
  #include "native.h"
20
25
  #include "number.h"
21
26
  #include "regexp.h"
27
+ #include "report.h"
28
+ #include "resource.h"
22
29
  #include "ops.h"
23
30
  #include "scheduler.h"
24
31
  #include "jsstring.h"
@@ -10,6 +10,7 @@
10
10
  #include <vector>
11
11
 
12
12
  #include "array.h"
13
+ #include "bigint.h"
13
14
  #include "core.h"
14
15
  #include "equality.h"
15
16
 
@@ -28,10 +29,16 @@ struct KeyHash {
28
29
  return std::hash<uint64_t>()(bits);
29
30
  } else if constexpr (std::is_same_v<K, bool>) {
30
31
  return k ? 1 : 2;
31
- } else if constexpr (std::is_same_v<K, String>) {
32
+ } else if constexpr (std::is_same_v<K, String> || std::is_same_v<K, BigInt>) {
32
33
  return k.hash();
33
34
  } else if constexpr (IsRef<K>::value) {
34
35
  return std::hash<const void*>()(static_cast<const void*>(k.get()));
36
+ } else if constexpr (IsOpt<K>::value) {
37
+ if (!k.has()) return k.isNull() ? 3 : 4;
38
+ return KeyHash<std::decay_t<decltype(k.get())>>()(k.get());
39
+ } else if constexpr (IsVariant<K>::value) {
40
+ // Equal members hash alike whichever alternative holds them.
41
+ return std::visit([](const auto& x) { return KeyHash<std::decay_t<decltype(x)>>()(x); }, k);
35
42
  } else {
36
43
  return std::hash<const void*>()(k.identity());
37
44
  }
@@ -224,13 +231,22 @@ class Map {
224
231
 
225
232
  Table& table() const { return *t_; }
226
233
  const void* identity() const { return t_.get(); }
227
- friend bool strictEquals(const Map& a, const Map& b) { return a.t_ == b.t_; }
228
- friend String toJsString(const Map&) { return String::fromLatin1("[object Map]"); }
229
234
 
230
235
  private:
231
236
  std::shared_ptr<Table> t_;
232
237
  };
233
238
 
239
+ template <class K, class V>
240
+ String toJsString(const Map<K, V>&) {
241
+ return String::fromLatin1("[object Map]");
242
+ }
243
+
244
+ /// `===`: the same map.
245
+ template <class K, class V>
246
+ bool strictEquals(const Map<K, V>& a, const Map<K, V>& b) {
247
+ return a.identity() == b.identity();
248
+ }
249
+
234
250
  template <class T>
235
251
  class Set {
236
252
  public:
@@ -268,13 +284,22 @@ class Set {
268
284
 
269
285
  Table& table() const { return *t_; }
270
286
  const void* identity() const { return t_.get(); }
271
- friend bool strictEquals(const Set& a, const Set& b) { return a.t_ == b.t_; }
272
- friend String toJsString(const Set&) { return String::fromLatin1("[object Set]"); }
273
287
 
274
288
  private:
275
289
  std::shared_ptr<Table> t_;
276
290
  };
277
291
 
292
+ template <class T>
293
+ String toJsString(const Set<T>&) {
294
+ return String::fromLatin1("[object Set]");
295
+ }
296
+
297
+ /// `===`: the same set.
298
+ template <class T>
299
+ bool strictEquals(const Set<T>& a, const Set<T>& b) {
300
+ return a.identity() == b.identity();
301
+ }
302
+
278
303
  /// `Record<string, V>` / `{ [key: string]: V }`: a plain object used as a
279
304
  /// dictionary.
280
305
  template <class V>
@@ -319,8 +344,6 @@ class Dict {
319
344
 
320
345
  Table& table() const { return *t_; }
321
346
  const void* identity() const { return t_.get(); }
322
- friend bool strictEquals(const Dict& a, const Dict& b) { return a.t_ == b.t_; }
323
- friend String toJsString(const Dict&) { return String::fromLatin1("[object Object]"); }
324
347
 
325
348
  static bool isArrayIndex(const String& k, double& out) {
326
349
  size_t n = k.length();
@@ -341,4 +364,15 @@ class Dict {
341
364
  std::shared_ptr<Table> t_;
342
365
  };
343
366
 
367
+ template <class V>
368
+ String toJsString(const Dict<V>&) {
369
+ return String::fromLatin1("[object Object]");
370
+ }
371
+
372
+ /// `===`: the same record.
373
+ template <class V>
374
+ bool strictEquals(const Dict<V>& a, const Dict<V>& b) {
375
+ return a.identity() == b.identity();
376
+ }
377
+
344
378
  } // namespace lucent
@@ -11,65 +11,53 @@
11
11
  namespace lucent {
12
12
  namespace {
13
13
  std::atomic<long> nativeRefs{0};
14
- }
14
+ std::atomic<long> moduleRefs{0};
15
+ } // namespace
16
+
15
17
  long liveNativeRefs() { return nativeRefs.load(); }
16
- void detail::countNativeRef(int delta) { nativeRefs += delta; }
18
+
19
+ long moduleNativeRefs() { return moduleRefs.load(); }
20
+
21
+ bool detail::countNativeRef() {
22
+ nativeRefs++;
23
+
24
+ // Made in the main context: a view's, which the renderer releases with it.
25
+ if (ExecutionContext::current() == &ExecutionContext::main()) return false;
26
+
27
+ moduleRefs++;
28
+ return true;
29
+ }
30
+
31
+ void detail::uncountNativeRef(bool module, bool held) {
32
+ if (module) moduleRefs--;
33
+ if (!held) nativeRefs--;
34
+ }
35
+
36
+ void detail::releaseNative(void* handle, void (*release)(void*), ExecutionContext* on, bool module) {
37
+ detail::runOn(on, [handle, release, module] {
38
+ release(handle);
39
+ uncountNativeRef(module);
40
+ });
41
+ }
17
42
  } // namespace lucent
18
43
 
19
44
  #if !defined(__ANDROID__) && !(defined(TARGET_OS_IOS) && TARGET_OS_IOS)
20
45
 
21
- #include <condition_variable>
22
- #include <deque>
23
- #include <mutex>
24
- #include <thread>
25
-
26
46
  namespace lucent {
27
47
 
28
48
  namespace {
29
49
 
30
- class HostMainThread {
31
- public:
32
- static HostMainThread& instance() {
33
- static HostMainThread* t = new HostMainThread(); // lives for the process
34
- return *t;
35
- }
36
- void post(std::function<void()> job) {
37
- {
38
- std::lock_guard<std::mutex> g(m_);
39
- jobs_.push_back(std::move(job));
40
- }
41
- cv_.notify_one();
42
- }
43
- bool current() const { return std::this_thread::get_id() == id_; }
44
-
45
- private:
46
- HostMainThread() {
47
- std::thread t([this] { run(); });
48
- id_ = t.get_id();
49
- t.detach();
50
- }
51
- void run() {
52
- for (;;) {
53
- std::function<void()> job;
54
- {
55
- std::unique_lock<std::mutex> g(m_);
56
- cv_.wait(g, [this] { return !jobs_.empty(); });
57
- job = std::move(jobs_.front());
58
- jobs_.pop_front();
59
- }
60
- job();
61
- }
62
- }
63
- std::mutex m_;
64
- std::condition_variable cv_;
65
- std::deque<std::function<void()>> jobs_;
66
- std::thread::id id_;
67
- };
50
+ /// The host's stand-in for the platform's main thread: lives for the process.
51
+ WorkerThread& hostMainThread() {
52
+ static auto* thread = new std::shared_ptr<WorkerThread>(WorkerThread::start([](Job& job) { detail::runGuarded(job, "job"); }));
53
+ return **thread;
54
+ }
68
55
 
69
56
  } // namespace
70
57
 
71
- void postToMain(std::function<void()> job) { HostMainThread::instance().post(std::move(job)); }
72
- bool onMainThread() { return HostMainThread::instance().current(); }
58
+ void postToMain(std::function<void()> job) { hostMainThread().post(std::move(job)); }
59
+
60
+ bool onMainThread() { return hostMainThread().isCurrent(); }
73
61
 
74
62
  } // namespace lucent
75
63