@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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// Lucent runtime — a component's React children and slot on iOS (see
|
|
2
|
+
// LucentViewChildren.h). Built only when the app's Lucent code has
|
|
3
|
+
// components, as their host is.
|
|
4
|
+
#if __has_include(<views/lucent_views.h>)
|
|
5
|
+
|
|
6
|
+
#import "LucentViewChildren.h"
|
|
7
|
+
|
|
8
|
+
#include <lucent/report.h>
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
/** Reports what went wrong with a component's children: nothing throws into UIKit. */
|
|
15
|
+
void report(NSString *message)
|
|
16
|
+
{
|
|
17
|
+
lucent::logError((std::string("[lucent] ") + message.UTF8String).c_str());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
} // namespace
|
|
21
|
+
|
|
22
|
+
@implementation LucentSlotView {
|
|
23
|
+
__weak UIView *_host;
|
|
24
|
+
void (^_placed)(void);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithHost:(UIView *)host placed:(void (^)(void))placed
|
|
28
|
+
{
|
|
29
|
+
if ((self = [super initWithFrame:CGRectZero])) {
|
|
30
|
+
_host = host;
|
|
31
|
+
_placed = [placed copy];
|
|
32
|
+
self.clipsToBounds = YES;
|
|
33
|
+
// Follows the view it fills as that view resizes.
|
|
34
|
+
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return self;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Unsized when setup adds it: it fills the view it is added to.
|
|
41
|
+
- (void)didMoveToSuperview
|
|
42
|
+
{
|
|
43
|
+
[super didMoveToSuperview];
|
|
44
|
+
|
|
45
|
+
UIView *parent = self.superview;
|
|
46
|
+
|
|
47
|
+
if (parent && CGRectIsEmpty(self.frame)) self.frame = parent.bounds;
|
|
48
|
+
|
|
49
|
+
[self setNeedsLayout];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)setFrame:(CGRect)frame
|
|
53
|
+
{
|
|
54
|
+
[super setFrame:frame];
|
|
55
|
+
[self setNeedsLayout];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (void)setCenter:(CGPoint)center
|
|
59
|
+
{
|
|
60
|
+
[super setCenter:center];
|
|
61
|
+
[self setNeedsLayout];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
- (void)layoutSubviews
|
|
65
|
+
{
|
|
66
|
+
[super layoutSubviews];
|
|
67
|
+
[self lucentAlign];
|
|
68
|
+
|
|
69
|
+
if (_placed) _placed();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)lucentAlign
|
|
73
|
+
{
|
|
74
|
+
UIView *host = _host;
|
|
75
|
+
UIView *parent = self.superview;
|
|
76
|
+
|
|
77
|
+
if (!host || !parent || ![self isDescendantOfView:host]) return;
|
|
78
|
+
|
|
79
|
+
CGPoint origin = [parent convertPoint:self.frame.origin toView:host];
|
|
80
|
+
CGRect bounds = self.bounds;
|
|
81
|
+
|
|
82
|
+
if (CGPointEqualToPoint(bounds.origin, origin)) return;
|
|
83
|
+
|
|
84
|
+
bounds.origin = origin;
|
|
85
|
+
self.bounds = bounds;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@end
|
|
89
|
+
|
|
90
|
+
@implementation LucentChildren {
|
|
91
|
+
__weak UIView *_host;
|
|
92
|
+
NSString *_name;
|
|
93
|
+
BOOL _takesChildren;
|
|
94
|
+
void (^_placed)(void);
|
|
95
|
+
/// React Native's children of the component, in its order.
|
|
96
|
+
NSMutableArray<UIView *> *_children;
|
|
97
|
+
/// Whether children given to a component taking none were reported.
|
|
98
|
+
BOOL _refused;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
- (instancetype)initWithHost:(UIView *)host
|
|
102
|
+
name:(NSString *)name
|
|
103
|
+
takesChildren:(BOOL)takesChildren
|
|
104
|
+
placed:(void (^)(void))placed
|
|
105
|
+
{
|
|
106
|
+
if ((self = [super init])) {
|
|
107
|
+
_host = host;
|
|
108
|
+
_name = [name copy];
|
|
109
|
+
_takesChildren = takesChildren;
|
|
110
|
+
_placed = [placed copy];
|
|
111
|
+
_children = [NSMutableArray new];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return self;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
- (void)mount:(UIView *)child index:(NSInteger)index
|
|
118
|
+
{
|
|
119
|
+
NSInteger count = static_cast<NSInteger>(_children.count);
|
|
120
|
+
|
|
121
|
+
if (index < 0 || index > count) {
|
|
122
|
+
report([NSString stringWithFormat:@"%@: React Native mounted a child at %ld of %ld", _name, (long)index, (long)count]);
|
|
123
|
+
index = index < 0 ? 0 : count;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[_children insertObject:child atIndex:static_cast<NSUInteger>(index)];
|
|
127
|
+
|
|
128
|
+
if (!_takesChildren && !_refused) {
|
|
129
|
+
_refused = YES;
|
|
130
|
+
report([NSString stringWithFormat:@"%@ takes no React children: React Native gave it %lu, which it does not show",
|
|
131
|
+
_name,
|
|
132
|
+
(unsigned long)_children.count]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[self place:child];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (void)unmount:(UIView *)child index:(NSInteger)index
|
|
139
|
+
{
|
|
140
|
+
NSUInteger at = index >= 0 && static_cast<NSUInteger>(index) < _children.count && _children[index] == child
|
|
141
|
+
? static_cast<NSUInteger>(index)
|
|
142
|
+
: [_children indexOfObjectIdenticalTo:child];
|
|
143
|
+
|
|
144
|
+
if (at == NSNotFound) {
|
|
145
|
+
report([NSString stringWithFormat:@"%@: React Native unmounted a child it never mounted here", _name]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[_children removeObjectAtIndex:at];
|
|
150
|
+
|
|
151
|
+
// Out of the slot it was put in (an earlier mount's, if React Native kept it past a recycle).
|
|
152
|
+
if ([child.superview isKindOfClass:[LucentSlotView class]]) [child removeFromSuperview];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
- (void)startMount
|
|
156
|
+
{
|
|
157
|
+
_slot = _takesChildren ? [[LucentSlotView alloc] initWithHost:_host placed:_placed] : nil;
|
|
158
|
+
|
|
159
|
+
for (UIView *child in _children) [self place:child];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
- (void)mountedContent:(UIView *)content
|
|
163
|
+
{
|
|
164
|
+
if (_slot && content && ![_slot isDescendantOfView:content])
|
|
165
|
+
report([NSString stringWithFormat:@"%@'s setup did not put its slot in the view it returned: its React children do not show",
|
|
166
|
+
_name]);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
- (void)contentChanged
|
|
170
|
+
{
|
|
171
|
+
[_slot setNeedsLayout];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
- (void)endMount
|
|
175
|
+
{
|
|
176
|
+
// React Native unmounts a view's children before it recycles the view: any left stay where they are.
|
|
177
|
+
if (_children.count)
|
|
178
|
+
report([NSString stringWithFormat:@"%@ was recycled holding %lu React children: they stay where React Native left them",
|
|
179
|
+
_name,
|
|
180
|
+
(unsigned long)_children.count]);
|
|
181
|
+
|
|
182
|
+
[_children removeAllObjects];
|
|
183
|
+
_slot = nil;
|
|
184
|
+
_refused = NO;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Puts `child` in the slot, below the next child there: React Native's order. */
|
|
188
|
+
- (void)place:(UIView *)child
|
|
189
|
+
{
|
|
190
|
+
if (!_slot || child.superview == _slot) return;
|
|
191
|
+
|
|
192
|
+
if (child.superview) {
|
|
193
|
+
report([NSString stringWithFormat:@"%@: a React child is in another view: React Native keeps it there", _name]);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
NSUInteger at = [_children indexOfObjectIdenticalTo:child];
|
|
198
|
+
|
|
199
|
+
for (NSUInteger i = at + 1; i < _children.count; i++) {
|
|
200
|
+
if (_children[i].superview == _slot) {
|
|
201
|
+
[_slot insertSubview:child belowSubview:_children[i]];
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
[_slot addSubview:child];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@end
|
|
210
|
+
|
|
211
|
+
#endif
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Lucent runtime — what the renderer made for each view, found again by
|
|
2
|
+
// the view's surface and tag.
|
|
3
|
+
//
|
|
4
|
+
// The Android host never gets a view's event emitter from Java: its
|
|
5
|
+
// descriptor records each one as the renderer makes it (HostDescriptor),
|
|
6
|
+
// and the host finds it when the view mounts. A tag alone does not name a
|
|
7
|
+
// view: each JavaScript runtime numbers its views again, so across a reload
|
|
8
|
+
// an old surface's view and a new one's share tags. Surfaces are numbered
|
|
9
|
+
// for the process, so the two together do.
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#include <algorithm>
|
|
13
|
+
#include <cstddef>
|
|
14
|
+
#include <cstdint>
|
|
15
|
+
#include <map>
|
|
16
|
+
#include <memory>
|
|
17
|
+
#include <mutex>
|
|
18
|
+
#include <utility>
|
|
19
|
+
|
|
20
|
+
namespace lucent::views {
|
|
21
|
+
|
|
22
|
+
/** Per view, by surface and tag, what the renderer made: held weakly. Any thread. */
|
|
23
|
+
template <class T>
|
|
24
|
+
class ViewRegistry {
|
|
25
|
+
public:
|
|
26
|
+
/// `value` is what the renderer made for view `tag` of `surface`, from now on.
|
|
27
|
+
void record(int32_t surface, int32_t tag, const std::shared_ptr<T>& value) {
|
|
28
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
29
|
+
|
|
30
|
+
// Views gone since leave expired entries: dropped whenever the registry has doubled.
|
|
31
|
+
if (entries_.size() > limit_) {
|
|
32
|
+
std::erase_if(entries_, [](const auto& entry) { return entry.second.expired(); });
|
|
33
|
+
limit_ = std::max(kFloor, 2 * entries_.size());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
entries_[{surface, tag}] = value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// What the renderer made for view `tag` of `surface`, or null (none, or gone).
|
|
40
|
+
std::shared_ptr<T> find(int32_t surface, int32_t tag) const {
|
|
41
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
42
|
+
auto found = entries_.find({surface, tag});
|
|
43
|
+
|
|
44
|
+
return found == entries_.end() ? nullptr : found->second.lock();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// How many entries it holds, gone ones included until pruned.
|
|
48
|
+
std::size_t size() const {
|
|
49
|
+
std::lock_guard<std::mutex> g(mutex_);
|
|
50
|
+
|
|
51
|
+
return entries_.size();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private:
|
|
55
|
+
static constexpr std::size_t kFloor = 256;
|
|
56
|
+
|
|
57
|
+
mutable std::mutex mutex_;
|
|
58
|
+
std::map<std::pair<int32_t, int32_t>, std::weak_ptr<T>> entries_;
|
|
59
|
+
std::size_t limit_ = kFloor;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
} // namespace lucent::views
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#include "LucentViewRequests.h"
|
|
2
|
+
|
|
3
|
+
#include <lucent/jsi/host.h>
|
|
4
|
+
#include <lucent/report.h>
|
|
5
|
+
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
namespace lucent::views {
|
|
10
|
+
|
|
11
|
+
namespace jsi = facebook::jsi;
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
struct Connection {
|
|
16
|
+
std::weak_ptr<js::Host> host;
|
|
17
|
+
uint64_t settle = 0;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
std::mutex& connectionMutex() {
|
|
21
|
+
static std::mutex mutex;
|
|
22
|
+
return mutex;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Connection& connection() {
|
|
26
|
+
static Connection current;
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
} // namespace
|
|
31
|
+
|
|
32
|
+
Requester Requester::current() {
|
|
33
|
+
std::lock_guard<std::mutex> lock(connectionMutex());
|
|
34
|
+
|
|
35
|
+
return Requester(connection().host, connection().settle);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Requester::operator bool() const {
|
|
39
|
+
auto host = host_.lock();
|
|
40
|
+
|
|
41
|
+
return host && host->alive();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void Requester::settle(double id, std::function<void(jsi::Runtime&, jsi::Function&)> call) const {
|
|
45
|
+
auto host = host_.lock();
|
|
46
|
+
|
|
47
|
+
if (!host) return;
|
|
48
|
+
|
|
49
|
+
host->postToJs([weak = host_, settle = settle_, id, call = std::move(call)](jsi::Runtime& runtime) {
|
|
50
|
+
auto host = weak.lock();
|
|
51
|
+
jsi::Function* fn = host ? host->retained(settle) : nullptr;
|
|
52
|
+
|
|
53
|
+
if (!fn) return;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
call(runtime, *fn);
|
|
57
|
+
} catch (const std::exception& e) {
|
|
58
|
+
logError((std::string("[lucent] answering view request ") + std::to_string(id) + ": " + e.what()).c_str());
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void Requester::resolve(double id, AnswerValue value) const {
|
|
64
|
+
settle(id, [id, value = std::move(value)](jsi::Runtime& runtime, jsi::Function& fn) {
|
|
65
|
+
fn.call(runtime, jsi::Value(id), jsi::Value::null(), value(runtime));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void Requester::reject(double id, std::string message) const {
|
|
70
|
+
settle(id, [id, message = std::move(message)](jsi::Runtime& runtime, jsi::Function& fn) {
|
|
71
|
+
fn.call(runtime, jsi::Value(id), jsi::String::createFromUtf8(runtime, message));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void connectRequests(const std::shared_ptr<js::Host>& host, jsi::Runtime& runtime, jsi::Function settle) {
|
|
76
|
+
if (!host->alive()) throw jsi::JSError(runtime, "Lucent: this JavaScript runtime's Lucent host is torn down");
|
|
77
|
+
|
|
78
|
+
uint64_t id = host->retain(runtime, std::move(settle));
|
|
79
|
+
Connection previous;
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
std::lock_guard<std::mutex> lock(connectionMutex());
|
|
83
|
+
|
|
84
|
+
previous = std::exchange(connection(), Connection{host, id});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// The same runtime connecting again (a second views runtime): its old function goes.
|
|
88
|
+
if (auto old = previous.host.lock(); old == host && previous.settle != id) host->release(previous.settle);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
} // namespace lucent::views
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Lucent runtime — answers to the commands of Lucent components that return
|
|
2
|
+
// a value: requests, settled in the JavaScript runtime that sent them.
|
|
3
|
+
//
|
|
4
|
+
// A component's proxy numbers each request and hands the views runtime's
|
|
5
|
+
// settle function to the Lucent module once (its __lucentViewRequests
|
|
6
|
+
// property, connectRequests below). A host view captures the Requester
|
|
7
|
+
// current when a command arrives, and answers through it from any thread.
|
|
8
|
+
// The answer runs on that runtime's JavaScript thread, or not at all once
|
|
9
|
+
// that runtime is torn down (a reload): request ids start again in each
|
|
10
|
+
// runtime, so an answer never reaches another one.
|
|
11
|
+
#pragma once
|
|
12
|
+
|
|
13
|
+
#include <jsi/jsi.h>
|
|
14
|
+
|
|
15
|
+
#include <cstdint>
|
|
16
|
+
#include <functional>
|
|
17
|
+
#include <memory>
|
|
18
|
+
#include <optional>
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <utility>
|
|
21
|
+
|
|
22
|
+
namespace lucent::js {
|
|
23
|
+
class Host;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
namespace lucent::views {
|
|
27
|
+
|
|
28
|
+
/// A request's answer, built on the JavaScript thread.
|
|
29
|
+
using AnswerValue = std::function<facebook::jsi::Value(facebook::jsi::Runtime&)>;
|
|
30
|
+
|
|
31
|
+
/** The runtime a request came from. Copyable; any thread. */
|
|
32
|
+
class Requester {
|
|
33
|
+
public:
|
|
34
|
+
Requester() = default;
|
|
35
|
+
|
|
36
|
+
/// The runtime connected now: a command's, when it arrives. Empty when no
|
|
37
|
+
/// runtime has connected (its answers are dropped).
|
|
38
|
+
static Requester current();
|
|
39
|
+
|
|
40
|
+
/// Settles request `id` with `value`. Dropped when the runtime is gone.
|
|
41
|
+
void resolve(double id, AnswerValue value) const;
|
|
42
|
+
|
|
43
|
+
/// Rejects request `id` with an Error whose message is `message`.
|
|
44
|
+
void reject(double id, std::string message) const;
|
|
45
|
+
|
|
46
|
+
/// Whether its runtime is still there (not torn down).
|
|
47
|
+
explicit operator bool() const;
|
|
48
|
+
|
|
49
|
+
private:
|
|
50
|
+
Requester(std::weak_ptr<js::Host> host, uint64_t settle) : host_(std::move(host)), settle_(settle) {}
|
|
51
|
+
|
|
52
|
+
void settle(double id, std::function<void(facebook::jsi::Runtime&, facebook::jsi::Function&)> call) const;
|
|
53
|
+
|
|
54
|
+
std::weak_ptr<js::Host> host_;
|
|
55
|
+
/// The settle function, retained by the host.
|
|
56
|
+
uint64_t settle_ = 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/// Makes `host`'s runtime the one requests come from, settled through
|
|
60
|
+
/// `settle(id, error, value)`. On the JavaScript thread.
|
|
61
|
+
void connectRequests(const std::shared_ptr<js::Host>& host, facebook::jsi::Runtime& runtime, facebook::jsi::Function settle);
|
|
62
|
+
|
|
63
|
+
/// The name the Lucent module gives the function JavaScript connects with.
|
|
64
|
+
inline constexpr const char* kRequestsName = "__lucentViewRequests";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A request command's answer. The host settles JavaScript's promise with it
|
|
68
|
+
* on the JS thread (`settle(request, error, value)`): rejected with an
|
|
69
|
+
* Error of `error`'s message when set, else resolved with `value()`.
|
|
70
|
+
*/
|
|
71
|
+
struct Answer {
|
|
72
|
+
double request{0};
|
|
73
|
+
std::optional<std::string> error{};
|
|
74
|
+
AnswerValue value{};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Where a mount sends its answers. A mount answers each request once:
|
|
79
|
+
* with its result or error, or, still unanswered when the mount ends,
|
|
80
|
+
* with a rejection then.
|
|
81
|
+
*/
|
|
82
|
+
using Respond = std::function<void(Answer)>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A mount's answers, to `requester`'s runtime while `host` (the platform's
|
|
86
|
+
* HostView: `current()`, whether its view still holds that mount) says so.
|
|
87
|
+
* Once the view holds another mount, or none (recycled, remounted: a view
|
|
88
|
+
* or a tag reused), an answer is dropped with what it carries: JavaScript
|
|
89
|
+
* rejected the request when React unmounted its view. On the thread the
|
|
90
|
+
* host reads its view on (the main thread).
|
|
91
|
+
*/
|
|
92
|
+
template <class Host>
|
|
93
|
+
Respond answerTo(Host host, Requester requester) {
|
|
94
|
+
return [host = std::move(host), requester = std::move(requester)](Answer answer) {
|
|
95
|
+
if (!host.current()) return;
|
|
96
|
+
|
|
97
|
+
if (answer.error)
|
|
98
|
+
requester.reject(answer.request, std::move(*answer.error));
|
|
99
|
+
else
|
|
100
|
+
requester.resolve(answer.request, std::move(answer.value));
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
} // namespace lucent::views
|