@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,439 @@
|
|
|
1
|
+
// Lucent runtime — the Android host of Lucent components (see
|
|
2
|
+
// LucentViewsAndroid.h). Built into Android apps whose Lucent code has
|
|
3
|
+
// components (their Fabric sources are generated).
|
|
4
|
+
#if defined(__ANDROID__) && __has_include(<views/lucent_views.h>)
|
|
5
|
+
|
|
6
|
+
#include "LucentViewsAndroid.h"
|
|
7
|
+
#include "LucentViewRegistry.h"
|
|
8
|
+
#include "LucentViewSizing.h"
|
|
9
|
+
#include "LucentViewSlots.h"
|
|
10
|
+
|
|
11
|
+
#include <fbjni/fbjni.h>
|
|
12
|
+
#include <react/jni/NativeArray.h>
|
|
13
|
+
#include <react/fabric/StateWrapperImpl.h>
|
|
14
|
+
#include <react/jni/NativeMap.h>
|
|
15
|
+
#include <react/renderer/core/PropsParserContext.h>
|
|
16
|
+
#include <react/renderer/core/RawProps.h>
|
|
17
|
+
#include <react/utils/ContextContainer.h>
|
|
18
|
+
|
|
19
|
+
#include <lucent/platform/android.h>
|
|
20
|
+
#include <lucent/report.h>
|
|
21
|
+
#include <lucent/view.h>
|
|
22
|
+
|
|
23
|
+
#include <exception>
|
|
24
|
+
#include <iterator>
|
|
25
|
+
#include <mutex>
|
|
26
|
+
#include <string>
|
|
27
|
+
#include <unordered_map>
|
|
28
|
+
#include <utility>
|
|
29
|
+
|
|
30
|
+
namespace lucent::views {
|
|
31
|
+
|
|
32
|
+
using facebook::react::ComponentDescriptor;
|
|
33
|
+
using facebook::react::EventEmitter;
|
|
34
|
+
using facebook::react::Props;
|
|
35
|
+
using facebook::react::SurfaceId;
|
|
36
|
+
using facebook::react::Tag;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* One Java view's host: the props of its latest commit and of the one its
|
|
40
|
+
* mount has, and the mount, once a commit is on screen. Main thread; the
|
|
41
|
+
* Java view holds it (a heap shared_ptr) until it is dropped.
|
|
42
|
+
*/
|
|
43
|
+
struct HostState {
|
|
44
|
+
std::string name;
|
|
45
|
+
const AndroidComponent* component = nullptr;
|
|
46
|
+
Tag tag = 0;
|
|
47
|
+
SurfaceId surface = 0;
|
|
48
|
+
/// The LucentHostView (a weak global reference).
|
|
49
|
+
jweak view = nullptr;
|
|
50
|
+
Props::Shared committed;
|
|
51
|
+
Props::Shared applied;
|
|
52
|
+
std::unique_ptr<Mounted> mounted;
|
|
53
|
+
/// A zero generation while the view holds no mount.
|
|
54
|
+
MountToken token;
|
|
55
|
+
std::shared_ptr<const EventEmitter> emitter;
|
|
56
|
+
HostSizing sizing;
|
|
57
|
+
/// The mount's slot (dev.lucent.LucentSlotView), for a component taking React children.
|
|
58
|
+
NativeRef slot;
|
|
59
|
+
/// Where the slot is, as the shadow tree lays the children out (LucentViewSlots.h).
|
|
60
|
+
HostSlot place;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
bool HostView::current() const {
|
|
64
|
+
auto host = host_.lock();
|
|
65
|
+
|
|
66
|
+
return host && token_.generation != 0 && host->token == token_;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
std::shared_ptr<const EventEmitter> HostView::emitter() const {
|
|
70
|
+
auto host = host_.lock();
|
|
71
|
+
|
|
72
|
+
return host && token_.generation != 0 && host->token == token_ ? host->emitter : nullptr;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
NativeRef HostView::slot() const {
|
|
76
|
+
auto host = host_.lock();
|
|
77
|
+
|
|
78
|
+
return host && token_.generation != 0 && host->token == token_ ? host->slot : NativeRef();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
namespace {
|
|
82
|
+
|
|
83
|
+
// --- what the renderer tells the host (its threads) --------------------------------------
|
|
84
|
+
|
|
85
|
+
std::mutex& registryMutex() {
|
|
86
|
+
static std::mutex m;
|
|
87
|
+
return m;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
std::unordered_map<std::string, const ComponentDescriptor*>& descriptors() {
|
|
91
|
+
static auto* map = new std::unordered_map<std::string, const ComponentDescriptor*>();
|
|
92
|
+
return *map;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ViewRegistry<const EventEmitter>& emitters() {
|
|
96
|
+
static auto* registry = new ViewRegistry<const EventEmitter>();
|
|
97
|
+
return *registry;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// --- one view's host (the main thread) ---------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Runs `work`, reporting what it throws as `where`: nothing escapes into
|
|
104
|
+
* Java. A mount's entry points enter Lucent's main context themselves.
|
|
105
|
+
*/
|
|
106
|
+
template <class F>
|
|
107
|
+
void guarded(const char* where, F&& work) {
|
|
108
|
+
try {
|
|
109
|
+
work();
|
|
110
|
+
} catch (...) {
|
|
111
|
+
reportUncaught(std::current_exception(), where);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
std::shared_ptr<HostState>& hostOf(jlong handle) { return *reinterpret_cast<std::shared_ptr<HostState>*>(handle); }
|
|
116
|
+
|
|
117
|
+
/** The props of `host`'s latest commit with `changes` (a commit's) applied, as the component's Props. */
|
|
118
|
+
Props::Shared applied(const HostState& host, folly::dynamic changes) {
|
|
119
|
+
static const facebook::react::ContextContainer context;
|
|
120
|
+
facebook::react::PropsParserContext parser{host.surface, context};
|
|
121
|
+
|
|
122
|
+
// Held while parsing: the descriptor's registry is not torn down meanwhile.
|
|
123
|
+
std::lock_guard<std::mutex> g(registryMutex());
|
|
124
|
+
auto found = descriptors().find(host.name);
|
|
125
|
+
|
|
126
|
+
if (found == descriptors().end()) throw Mismatch("no descriptor is registered for " + host.name);
|
|
127
|
+
|
|
128
|
+
return found->second->cloneProps(parser, host.committed, facebook::react::RawProps(std::move(changes)));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The shell's method `name` (`signature`). */
|
|
132
|
+
jmethodID shellMethod(const char* name, const char* signature) {
|
|
133
|
+
return jni::method(jni::findClass("dev/lucent/LucentHostView"), name, signature);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Shows the mount's view in the host's shell: a setup run again may have returned another. */
|
|
137
|
+
void show(HostState& host) {
|
|
138
|
+
JNIEnv* env = jni::env();
|
|
139
|
+
jni::LocalFrame frame(env);
|
|
140
|
+
jobject view = env->NewLocalRef(host.view);
|
|
141
|
+
|
|
142
|
+
if (!view) return;
|
|
143
|
+
|
|
144
|
+
static jmethodID setContent = shellMethod("setContent", "(Landroid/view/View;)V");
|
|
145
|
+
|
|
146
|
+
env->CallVoidMethod(view, setContent, host.mounted ? jni::unwrap(host.mounted->view()) : nullptr);
|
|
147
|
+
jni::check(env);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The size of the content `host`'s shell shows within `constraints`, or nothing without one. */
|
|
151
|
+
std::optional<sizing::Size> measured(const HostState& host, sizing::Constraints constraints) {
|
|
152
|
+
if (!host.mounted) return std::nullopt;
|
|
153
|
+
|
|
154
|
+
JNIEnv* env = jni::env();
|
|
155
|
+
jni::LocalFrame frame(env);
|
|
156
|
+
jobject view = env->NewLocalRef(host.view);
|
|
157
|
+
|
|
158
|
+
if (!view) return std::nullopt;
|
|
159
|
+
|
|
160
|
+
static jmethodID measureContent = shellMethod("measureContent", "(FF)[F");
|
|
161
|
+
auto size = static_cast<jfloatArray>(
|
|
162
|
+
env->CallObjectMethod(view, measureContent, constraints.maxWidth, constraints.maxHeight));
|
|
163
|
+
|
|
164
|
+
jni::check(env);
|
|
165
|
+
|
|
166
|
+
if (!size) return std::nullopt;
|
|
167
|
+
|
|
168
|
+
jfloat values[2];
|
|
169
|
+
|
|
170
|
+
env->GetFloatArrayRegion(size, 0, 2, values);
|
|
171
|
+
|
|
172
|
+
return sizing::Size{values[0], values[1]};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Pixels per density-independent pixel, as the shell's resources say. */
|
|
176
|
+
float density(const HostState& host) {
|
|
177
|
+
JNIEnv* env = jni::env();
|
|
178
|
+
jni::LocalFrame frame(env);
|
|
179
|
+
jobject view = env->NewLocalRef(host.view);
|
|
180
|
+
|
|
181
|
+
if (!view) return 1;
|
|
182
|
+
|
|
183
|
+
static jmethodID method = shellMethod("density", "()F");
|
|
184
|
+
float out = env->CallFloatMethod(view, method);
|
|
185
|
+
|
|
186
|
+
jni::check(env);
|
|
187
|
+
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** A new slot for the mount's React children, if the component takes them; else empty. */
|
|
192
|
+
NativeRef makeSlot(const HostState& host) {
|
|
193
|
+
JNIEnv* env = jni::env();
|
|
194
|
+
jni::LocalFrame frame(env);
|
|
195
|
+
jobject view = env->NewLocalRef(host.view);
|
|
196
|
+
|
|
197
|
+
if (!view) return {};
|
|
198
|
+
|
|
199
|
+
static jmethodID method = shellMethod("makeSlot", "()Landroid/view/ViewGroup;");
|
|
200
|
+
jobject slot = env->CallObjectMethod(view, method);
|
|
201
|
+
|
|
202
|
+
jni::check(env);
|
|
203
|
+
|
|
204
|
+
return slot ? jni::wrap(env, slot, "the slot of a component's children") : NativeRef();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** A commit reached the screen (or JavaScript sent the view a command): the component sets up. */
|
|
208
|
+
void mount(const std::shared_ptr<HostState>& host) {
|
|
209
|
+
if (host->mounted || !host->committed || !host->component) return;
|
|
210
|
+
|
|
211
|
+
host->token = MountToken{host->tag, nextGeneration()};
|
|
212
|
+
|
|
213
|
+
auto scale = density(*host);
|
|
214
|
+
|
|
215
|
+
// The host holds its sizing: the host outlives it.
|
|
216
|
+
host->sizing.start(scale, host->tag, [raw = host.get()](sizing::Constraints constraints) {
|
|
217
|
+
return measured(*raw, constraints);
|
|
218
|
+
});
|
|
219
|
+
host->place.start(scale, host->tag);
|
|
220
|
+
host->slot = makeSlot(*host);
|
|
221
|
+
host->emitter = emitters().find(host->surface, host->tag);
|
|
222
|
+
|
|
223
|
+
if (!host->emitter) logError(("[lucent] " + host->name + " has no event emitter: its events are dropped").c_str());
|
|
224
|
+
|
|
225
|
+
guarded("a component's setup", [&] {
|
|
226
|
+
ui::ContentEntry entry(host->sizing.content());
|
|
227
|
+
JNIEnv* env = jni::env();
|
|
228
|
+
jni::LocalFrame frame(env);
|
|
229
|
+
// What setup makes for the shell takes its context (a Compose body's view).
|
|
230
|
+
jni::HostViewEntry hosting(env->NewLocalRef(host->view));
|
|
231
|
+
|
|
232
|
+
host->mounted = host->component->mount(*host->committed, HostView(host, host->token));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
host->applied = host->committed;
|
|
236
|
+
show(*host);
|
|
237
|
+
// Measured once the shell shows the content.
|
|
238
|
+
host->sizing.measure();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// --- the natives -------------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
jlong update(JNIEnv* env, jclass, jlong handle, jobject view, jstring name, jint tag, jint surface, jobject props) {
|
|
244
|
+
if (!handle) {
|
|
245
|
+
auto host = std::make_shared<HostState>();
|
|
246
|
+
|
|
247
|
+
host->name = jni::fromJString(env, name, "component").toUtf8();
|
|
248
|
+
host->component = findComponent(host->name);
|
|
249
|
+
host->tag = tag;
|
|
250
|
+
host->surface = surface;
|
|
251
|
+
host->view = env->NewWeakGlobalRef(view);
|
|
252
|
+
|
|
253
|
+
if (!host->component) logError(("[lucent] no Lucent code is compiled for the component " + host->name).c_str());
|
|
254
|
+
|
|
255
|
+
handle = reinterpret_cast<jlong>(new std::shared_ptr<HostState>(std::move(host)));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
auto& host = hostOf(handle);
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
auto map = facebook::jni::wrap_alias(static_cast<facebook::react::NativeMap::javaobject>(props));
|
|
262
|
+
|
|
263
|
+
host->committed = applied(*host, map->cthis()->consume());
|
|
264
|
+
} catch (...) {
|
|
265
|
+
reportUncaught(std::current_exception(), host->name.c_str());
|
|
266
|
+
return handle;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (host->mounted && host->committed != host->applied) {
|
|
270
|
+
auto previous = host->applied;
|
|
271
|
+
|
|
272
|
+
// The commit, whole, to the mount: setup does not run again.
|
|
273
|
+
guarded("a component's update", [&] {
|
|
274
|
+
ui::ContentEntry entry(host->sizing.content());
|
|
275
|
+
|
|
276
|
+
host->mounted->update(*host->committed, *previous);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
host->applied = host->committed;
|
|
280
|
+
host->place.contentChanged();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return handle;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void state(JNIEnv*, jclass, jlong handle, jobject wrapper) {
|
|
287
|
+
if (!handle || !wrapper) return;
|
|
288
|
+
|
|
289
|
+
auto& host = hostOf(handle);
|
|
290
|
+
auto impl = facebook::jni::wrap_alias(static_cast<facebook::react::StateWrapperImpl::javaobject>(wrapper));
|
|
291
|
+
|
|
292
|
+
auto state = impl->cthis()->getState();
|
|
293
|
+
|
|
294
|
+
host->sizing.setState(state);
|
|
295
|
+
host->sizing.measure();
|
|
296
|
+
host->place.setState(state);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
void contentChanged(JNIEnv*, jclass, jlong handle) {
|
|
300
|
+
if (!handle) return;
|
|
301
|
+
|
|
302
|
+
hostOf(handle)->sizing.contentChanged();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** The slot's place and the content box, in the shell's pixels (x, y, width, height each). */
|
|
306
|
+
void slotPlaced(JNIEnv* env, jclass, jlong handle, jintArray place, jboolean rtl, jboolean swapped) {
|
|
307
|
+
if (!handle || !place) return;
|
|
308
|
+
|
|
309
|
+
auto& host = hostOf(handle);
|
|
310
|
+
jint px[8];
|
|
311
|
+
|
|
312
|
+
env->GetIntArrayRegion(place, 0, 8, px);
|
|
313
|
+
|
|
314
|
+
if (env->ExceptionCheck()) {
|
|
315
|
+
env->ExceptionClear();
|
|
316
|
+
logError("[lucent] a slot's place came without its eight values");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
auto scale = density(*host);
|
|
321
|
+
auto rect = [&](int at) {
|
|
322
|
+
return slots::Rect{px[at] / scale, px[at + 1] / scale, px[at + 2] / scale, px[at + 3] / scale};
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
host->place.place(slots::insetsOf(rect(0), rect(4)), rtl, swapped);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
void attach(JNIEnv*, jclass, jlong handle) {
|
|
329
|
+
if (handle) mount(hostOf(handle));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
void command(JNIEnv* env, jclass, jlong handle, jstring name, jobject args) {
|
|
333
|
+
if (!handle) return;
|
|
334
|
+
|
|
335
|
+
auto host = hostOf(handle);
|
|
336
|
+
auto sent = jni::fromJString(env, name, "command").toUtf8();
|
|
337
|
+
auto list = facebook::jni::wrap_alias(static_cast<facebook::react::NativeArray::javaobject>(args));
|
|
338
|
+
folly::dynamic arguments = list->cthis()->consume();
|
|
339
|
+
auto requester = Requester::current();
|
|
340
|
+
|
|
341
|
+
// React sends a command once the view is committed, which may be before it is attached.
|
|
342
|
+
mount(host);
|
|
343
|
+
|
|
344
|
+
if (!host->mounted) {
|
|
345
|
+
std::string problem = "command " + sent + " reached a view with no mount (its setup failed)";
|
|
346
|
+
|
|
347
|
+
// A request would otherwise never settle while the view stays.
|
|
348
|
+
auto id = host->component ? host->component->requestId(sent, arguments) : std::nullopt;
|
|
349
|
+
|
|
350
|
+
if (id)
|
|
351
|
+
requester.reject(*id, problem);
|
|
352
|
+
else
|
|
353
|
+
logError(("[lucent] " + problem).c_str());
|
|
354
|
+
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
guarded("a component's command", [&] {
|
|
359
|
+
ui::ContentEntry entry(host->sizing.content());
|
|
360
|
+
|
|
361
|
+
host->mounted->command(sent, arguments, requester);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// A command may move the slot.
|
|
365
|
+
host->place.contentChanged();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
void drop(JNIEnv* env, jclass, jlong handle) {
|
|
369
|
+
if (!handle) return;
|
|
370
|
+
|
|
371
|
+
auto* held = reinterpret_cast<std::shared_ptr<HostState>*>(handle);
|
|
372
|
+
auto host = std::move(*held);
|
|
373
|
+
|
|
374
|
+
delete held;
|
|
375
|
+
|
|
376
|
+
// A recycled shell's next occupant gets a mount (and token, and slot) of its own.
|
|
377
|
+
host->token = MountToken{};
|
|
378
|
+
host->slot = NativeRef();
|
|
379
|
+
// Its teardown runs the mount's code: nothing to measure any more.
|
|
380
|
+
host->sizing.stop();
|
|
381
|
+
|
|
382
|
+
auto mounted = std::move(host->mounted);
|
|
383
|
+
|
|
384
|
+
guarded("a component's teardown", [&] { mounted.reset(); });
|
|
385
|
+
|
|
386
|
+
env->DeleteWeakGlobalRef(host->view);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
void report(JNIEnv* env, jclass, jstring message) {
|
|
390
|
+
logError(jni::fromJString(env, message, "message").toUtf8().c_str());
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
} // namespace
|
|
394
|
+
|
|
395
|
+
void installAndroidHost() {
|
|
396
|
+
static std::once_flag once;
|
|
397
|
+
|
|
398
|
+
std::call_once(once, [] {
|
|
399
|
+
JNIEnv* env = jni::env();
|
|
400
|
+
JNINativeMethod natives[] = {
|
|
401
|
+
{const_cast<char*>("update"),
|
|
402
|
+
const_cast<char*>("(JLdev/lucent/LucentHostView;Ljava/lang/String;IILcom/facebook/react/bridge/NativeMap;)J"),
|
|
403
|
+
reinterpret_cast<void*>(update)},
|
|
404
|
+
{const_cast<char*>("attach"), const_cast<char*>("(J)V"), reinterpret_cast<void*>(attach)},
|
|
405
|
+
{const_cast<char*>("state"), const_cast<char*>("(JLcom/facebook/react/uimanager/StateWrapper;)V"),
|
|
406
|
+
reinterpret_cast<void*>(state)},
|
|
407
|
+
{const_cast<char*>("contentChanged"), const_cast<char*>("(J)V"), reinterpret_cast<void*>(contentChanged)},
|
|
408
|
+
{const_cast<char*>("slotPlaced"), const_cast<char*>("(J[IZZ)V"), reinterpret_cast<void*>(slotPlaced)},
|
|
409
|
+
{const_cast<char*>("command"), const_cast<char*>("(JLjava/lang/String;Lcom/facebook/react/bridge/NativeArray;)V"),
|
|
410
|
+
reinterpret_cast<void*>(command)},
|
|
411
|
+
{const_cast<char*>("drop"), const_cast<char*>("(J)V"), reinterpret_cast<void*>(drop)},
|
|
412
|
+
{const_cast<char*>("report"), const_cast<char*>("(Ljava/lang/String;)V"), reinterpret_cast<void*>(report)},
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
env->RegisterNatives(jni::findClass("dev/lucent/LucentViews"), natives, std::size(natives));
|
|
416
|
+
jni::check(env);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
void registerDescriptor(const std::string& name, const ComponentDescriptor* descriptor) {
|
|
421
|
+
std::lock_guard<std::mutex> g(registryMutex());
|
|
422
|
+
|
|
423
|
+
descriptors()[name] = descriptor;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
void forgetDescriptor(const std::string& name, const ComponentDescriptor* descriptor) {
|
|
427
|
+
std::lock_guard<std::mutex> g(registryMutex());
|
|
428
|
+
auto found = descriptors().find(name);
|
|
429
|
+
|
|
430
|
+
if (found != descriptors().end() && found->second == descriptor) descriptors().erase(found);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
void recordEmitter(SurfaceId surface, Tag tag, const facebook::react::SharedEventEmitter& emitter) {
|
|
434
|
+
emitters().record(surface, tag, emitter);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
} // namespace lucent::views
|
|
438
|
+
|
|
439
|
+
#endif // __ANDROID__ && views
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Lucent runtime — the Android host of Lucent components in React Native's
|
|
2
|
+
// Fabric renderer.
|
|
3
|
+
//
|
|
4
|
+
// Android mounts views from Java: each component's manager
|
|
5
|
+
// (dev.lucent.LucentViewManager) hands its view's commits and commands to
|
|
6
|
+
// the C++ host (LucentViewsAndroid.cpp), which does what the iOS host
|
|
7
|
+
// (LucentComponentView) does: it parses each commit's props with the
|
|
8
|
+
// component's generated Props, mounts the component once a commit is on
|
|
9
|
+
// screen (the view attached, or sent a command) on the main thread, with a
|
|
10
|
+
// token of its own, hands the mount later commits and commands, and ends
|
|
11
|
+
// it when the view is dropped or recycled. Each component's generated glue
|
|
12
|
+
// (views/<registration>_android.cpp) provides its Mounted, over the
|
|
13
|
+
// component's Mount.
|
|
14
|
+
//
|
|
15
|
+
// React Native's autolinking registers each component's descriptor wrapped
|
|
16
|
+
// in HostDescriptor (ComponentDescriptors.h, written by lucent build, lists
|
|
17
|
+
// them through react-native.config.js): Java never hands a view's event
|
|
18
|
+
// emitter on, so the descriptor records it by tag as the renderer makes it.
|
|
19
|
+
#pragma once
|
|
20
|
+
|
|
21
|
+
#include <folly/dynamic.h>
|
|
22
|
+
#include <react/renderer/core/ComponentDescriptor.h>
|
|
23
|
+
#include <react/renderer/core/EventEmitter.h>
|
|
24
|
+
#include <react/renderer/core/Props.h>
|
|
25
|
+
#include <react/renderer/core/ReactPrimitives.h>
|
|
26
|
+
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
27
|
+
|
|
28
|
+
#include <lucent/lucent.h>
|
|
29
|
+
|
|
30
|
+
#include <memory>
|
|
31
|
+
#include <optional>
|
|
32
|
+
#include <string>
|
|
33
|
+
#include <string_view>
|
|
34
|
+
|
|
35
|
+
#include "LucentViewRequests.h"
|
|
36
|
+
#include "LucentViews.h"
|
|
37
|
+
|
|
38
|
+
namespace lucent::views {
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* One mount of a component on its host view: the component's Lucent side,
|
|
42
|
+
* which generated code implements over its Mount. Main thread.
|
|
43
|
+
*/
|
|
44
|
+
class Mounted {
|
|
45
|
+
public:
|
|
46
|
+
virtual ~Mounted() = default;
|
|
47
|
+
|
|
48
|
+
/// The view the component's setup returned (empty if setup failed).
|
|
49
|
+
virtual NativeRef view() const = 0;
|
|
50
|
+
|
|
51
|
+
/// Applies a later commit's props (the component's own Props type).
|
|
52
|
+
virtual void update(const facebook::react::Props& props, const facebook::react::Props& previous) = 0;
|
|
53
|
+
|
|
54
|
+
/// Runs command `name`; a request answers through `requester`.
|
|
55
|
+
virtual void command(const std::string& name, const folly::dynamic& args, const Requester& requester) = 0;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** The C++ host of one view (LucentViewsAndroid.cpp). */
|
|
59
|
+
struct HostState;
|
|
60
|
+
|
|
61
|
+
/** What a mount knows of its host view. */
|
|
62
|
+
class HostView {
|
|
63
|
+
public:
|
|
64
|
+
HostView() = default;
|
|
65
|
+
HostView(std::weak_ptr<HostState> host, MountToken token) : host_(std::move(host)), token_(token) {}
|
|
66
|
+
|
|
67
|
+
const MountToken& token() const { return token_; }
|
|
68
|
+
|
|
69
|
+
/// Whether the view still holds this mount (not dropped, recycled or remounted).
|
|
70
|
+
bool current() const;
|
|
71
|
+
|
|
72
|
+
/// The view's event emitter while the view holds this mount; else null,
|
|
73
|
+
/// and events are dropped.
|
|
74
|
+
std::shared_ptr<const facebook::react::EventEmitter> emitter() const;
|
|
75
|
+
|
|
76
|
+
/// The view the host made for this mount's React children (a component
|
|
77
|
+
/// taking them), while the view holds this mount; else empty.
|
|
78
|
+
NativeRef slot() const;
|
|
79
|
+
|
|
80
|
+
private:
|
|
81
|
+
std::weak_ptr<HostState> host_;
|
|
82
|
+
MountToken token_;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** A component as the Android host finds it (views/lucent_hosts.cpp). */
|
|
86
|
+
struct AndroidComponent {
|
|
87
|
+
const char* name;
|
|
88
|
+
/// Mounts the component for `props` (its own Props type) on `host`: its setup runs once.
|
|
89
|
+
std::unique_ptr<Mounted> (*mount)(const facebook::react::Props& props, HostView host);
|
|
90
|
+
/// The request id a command carries, if it is a request that parses.
|
|
91
|
+
std::optional<double> (*requestId)(const std::string& name, const folly::dynamic& args);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Generated: the app's component registered as `name`, or null. */
|
|
95
|
+
const AndroidComponent* findComponent(std::string_view name);
|
|
96
|
+
|
|
97
|
+
/** Binds the natives of dev.lucent.LucentViews, once. */
|
|
98
|
+
void installAndroidHost();
|
|
99
|
+
|
|
100
|
+
/** Where the host finds the descriptor of the component registered as `name`: the latest one. */
|
|
101
|
+
void registerDescriptor(const std::string& name, const facebook::react::ComponentDescriptor* descriptor);
|
|
102
|
+
|
|
103
|
+
/** The descriptor is going (its registry is): unless a newer one replaced it, the host has none. */
|
|
104
|
+
void forgetDescriptor(const std::string& name, const facebook::react::ComponentDescriptor* descriptor);
|
|
105
|
+
|
|
106
|
+
/** The event emitter of view `tag` of `surface`, as the renderer made it. Any thread. */
|
|
107
|
+
void recordEmitter(
|
|
108
|
+
facebook::react::SurfaceId surface,
|
|
109
|
+
facebook::react::Tag tag,
|
|
110
|
+
const facebook::react::SharedEventEmitter& emitter);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A component's generated descriptor as the Android host registers it:
|
|
114
|
+
* it binds the host's natives before any view exists, lets the host parse
|
|
115
|
+
* props, and records each view's event emitter.
|
|
116
|
+
*/
|
|
117
|
+
template <typename Descriptor>
|
|
118
|
+
class HostDescriptor final : public Descriptor {
|
|
119
|
+
public:
|
|
120
|
+
explicit HostDescriptor(const facebook::react::ComponentDescriptorParameters& parameters)
|
|
121
|
+
: Descriptor(parameters) {
|
|
122
|
+
installAndroidHost();
|
|
123
|
+
registerDescriptor(this->getComponentName(), this);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
~HostDescriptor() override { forgetDescriptor(this->getComponentName(), this); }
|
|
127
|
+
|
|
128
|
+
facebook::react::ShadowNodeFamily::Shared createFamily(
|
|
129
|
+
const facebook::react::ShadowNodeFamilyFragment& fragment) const override {
|
|
130
|
+
auto family = Descriptor::createFamily(fragment);
|
|
131
|
+
|
|
132
|
+
recordEmitter(fragment.surfaceId, fragment.tag, family->getEventEmitter());
|
|
133
|
+
|
|
134
|
+
return family;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
} // namespace lucent::views
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Boost Software License - Version 1.0 - August 17th, 2003
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person or organization
|
|
4
|
+
obtaining a copy of the software and accompanying documentation covered by
|
|
5
|
+
this license (the "Software") to use, reproduce, display, distribute,
|
|
6
|
+
execute, and transmit the Software, and to prepare derivative works of the
|
|
7
|
+
Software, and to permit third-parties to whom the Software is furnished to
|
|
8
|
+
do so, all subject to the following:
|
|
9
|
+
|
|
10
|
+
The copyright notices in the Software and this entire statement, including
|
|
11
|
+
the above license grant, this restriction and the following disclaimer,
|
|
12
|
+
must be included in all copies of the Software, in whole or in part, and
|
|
13
|
+
all derivative works of the Software, unless such copies or derivative
|
|
14
|
+
works are solely in the form of machine-executable object code generated by
|
|
15
|
+
a source language processor.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
20
|
+
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
21
|
+
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
22
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23
|
+
DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Dragonbox
|
|
2
|
+
|
|
3
|
+
`dragonbox.h` from [Dragonbox](https://github.com/jk-jeon/dragonbox) by
|
|
4
|
+
Junekey Jeon, unmodified: the shortest decimal digits that read back as a
|
|
5
|
+
given double, and the closest of those to it. Hermes uses the same header
|
|
6
|
+
for JavaScript's Number::toString.
|
|
7
|
+
|
|
8
|
+
- Commit: beeeef91cf6fef89a4d4ba5e95d47ca64ccb3a44
|
|
9
|
+
- License: Apache License 2.0 with LLVM Exceptions, or the Boost Software
|
|
10
|
+
License 1.0, at the user's choice. Lucent uses it under the Boost
|
|
11
|
+
Software License (LICENSE-Boost).
|
|
12
|
+
- Used by `lucent/number.cpp` (Number::toString, toExponential()).
|
|
13
|
+
|
|
14
|
+
To update, copy `include/dragonbox/dragonbox.h` from a newer commit and run
|
|
15
|
+
`packages/runtime/test/run.sh`, whose number corpus compares every form with
|
|
16
|
+
node.
|