@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
|
@@ -16,27 +16,54 @@
|
|
|
16
16
|
|
|
17
17
|
namespace lucent {
|
|
18
18
|
|
|
19
|
-
/// How many platform objects Lucent holds (NativeRefs, not copies)
|
|
20
|
-
///
|
|
19
|
+
/// How many platform objects Lucent holds (NativeRefs, not copies), until
|
|
20
|
+
/// each is released.
|
|
21
21
|
long liveNativeRefs();
|
|
22
22
|
|
|
23
|
+
/// The part of liveNativeRefs() module code made: not the main context's
|
|
24
|
+
/// (views' setups and the callbacks they give the platform, which the
|
|
25
|
+
/// renderer and the platform's garbage collector release on their own
|
|
26
|
+
/// schedule), nor the ones kept for the process. Debug builds report what
|
|
27
|
+
/// is left when a JavaScript runtime's module goes.
|
|
28
|
+
long moduleNativeRefs();
|
|
29
|
+
|
|
23
30
|
namespace detail {
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
/// Counts a platform object Lucent now holds; whether module code made it.
|
|
32
|
+
bool countNativeRef();
|
|
33
|
+
|
|
34
|
+
/// Counts one released (or kept for the process: `held` then stays true).
|
|
35
|
+
void uncountNativeRef(bool module, bool held = false);
|
|
36
|
+
|
|
37
|
+
/// Releases `handle` on `on`'s thread: at once there, or with no context;
|
|
38
|
+
/// else posted to it (or here, if it takes no more work).
|
|
39
|
+
void releaseNative(void* handle, void (*release)(void*), ExecutionContext* on, bool module);
|
|
40
|
+
} // namespace detail
|
|
26
41
|
|
|
27
42
|
class NativeRef {
|
|
28
43
|
public:
|
|
29
44
|
NativeRef() = default;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
|
|
46
|
+
/// Takes ownership of `handle`; `release` runs when the last copy goes,
|
|
47
|
+
/// on the thread `releaseOn` runs on if the object requires one (UIKit
|
|
48
|
+
/// objects, the main context's), whichever thread dropped that copy.
|
|
49
|
+
NativeRef(void* handle, void (*release)(void*), bool (*same)(void*, void*), ExecutionContext* releaseOn = nullptr)
|
|
50
|
+
: p_(handle, Release{release, releaseOn, handle && detail::countNativeRef()}), same_(same) {}
|
|
35
51
|
|
|
36
52
|
void* get() const { return p_.get(); }
|
|
37
53
|
explicit operator bool() const { return p_ != nullptr; }
|
|
38
54
|
const void* identity() const { return p_.get(); }
|
|
39
55
|
|
|
56
|
+
/// The object lives as long as the process (the Android Application):
|
|
57
|
+
/// the teardown report leaves it out. Any copy; once.
|
|
58
|
+
void keepForProcess() {
|
|
59
|
+
auto* r = std::get_deleter<Release>(p_);
|
|
60
|
+
|
|
61
|
+
if (r && r->module) {
|
|
62
|
+
r->module = false;
|
|
63
|
+
detail::uncountNativeRef(true, true);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
/// The same platform object: pointer identity on iOS, IsSameObject on Android.
|
|
41
68
|
bool same(const NativeRef& o) const {
|
|
42
69
|
if (p_ == o.p_) return true;
|
|
@@ -47,10 +74,12 @@ class NativeRef {
|
|
|
47
74
|
private:
|
|
48
75
|
struct Release {
|
|
49
76
|
void (*release)(void*);
|
|
77
|
+
ExecutionContext* on;
|
|
78
|
+
/// Counted as module code's (moduleNativeRefs).
|
|
79
|
+
bool module;
|
|
80
|
+
|
|
50
81
|
void operator()(void* p) const {
|
|
51
|
-
if (
|
|
52
|
-
detail::countNativeRef(-1);
|
|
53
|
-
release(p);
|
|
82
|
+
if (p) detail::releaseNative(p, release, on, module);
|
|
54
83
|
}
|
|
55
84
|
};
|
|
56
85
|
std::shared_ptr<void> p_;
|
|
@@ -61,23 +90,24 @@ class NativeRef {
|
|
|
61
90
|
inline bool strictEquals(const NativeRef& a, const NativeRef& b) { return a.same(b); }
|
|
62
91
|
inline String toJsString(const NativeRef&) { return String::fromLatin1("[object NativeObject]"); }
|
|
63
92
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
// --- the legacy module context ----------------------------------------------
|
|
94
|
+
//
|
|
95
|
+
// What generated module code calls: each enters module code by taking the
|
|
96
|
+
// Lucent lock.
|
|
68
97
|
|
|
69
|
-
/// A platform callback into
|
|
70
|
-
/// the call:
|
|
71
|
-
/// Lucent code. `f` owns what it captured and
|
|
98
|
+
/// A platform callback into module code that returns nothing and outlives
|
|
99
|
+
/// the call: a turn of the legacy module context on the Lucent thread, so
|
|
100
|
+
/// the platform never waits for Lucent code. `f` owns what it captured and
|
|
101
|
+
/// is released after it runs.
|
|
72
102
|
template <class F>
|
|
73
103
|
void postCallback(F f) {
|
|
74
104
|
Scheduler::instance().post(std::move(f));
|
|
75
105
|
}
|
|
76
106
|
|
|
77
|
-
/// A platform callback the platform waits for (for its
|
|
78
|
-
/// it runs during the call or on the main thread): runs
|
|
79
|
-
/// thread, holding the Lucent lock. A Lucent error is
|
|
80
|
-
/// platform gets a default result.
|
|
107
|
+
/// A platform callback into module code the platform waits for (for its
|
|
108
|
+
/// result, or because it runs during the call or on the main thread): runs
|
|
109
|
+
/// on the calling thread, holding the Lucent lock. A Lucent error is
|
|
110
|
+
/// reported and the platform gets a default result.
|
|
81
111
|
template <class F>
|
|
82
112
|
auto callNow(F f) -> std::invoke_result_t<F&> {
|
|
83
113
|
using R = std::invoke_result_t<F&>;
|
|
@@ -90,9 +120,11 @@ auto callNow(F f) -> std::invoke_result_t<F&> {
|
|
|
90
120
|
}
|
|
91
121
|
}
|
|
92
122
|
|
|
93
|
-
/// `main(f)` from lucent:thread: runs `f` on the main
|
|
94
|
-
/// Lucent lock, and settles the promise with its result.
|
|
95
|
-
/// waits for the main thread, so the lock order stays
|
|
123
|
+
/// `main(f)` from lucent:thread, kept for module code: runs `f` on the main
|
|
124
|
+
/// thread holding the Lucent lock, and settles the promise with its result.
|
|
125
|
+
/// The caller never waits for the main thread, so the lock order stays
|
|
126
|
+
/// deadlock-free, but the main thread waits for any module job. UI work
|
|
127
|
+
/// uses the main context (runIn) instead.
|
|
96
128
|
template <class F>
|
|
97
129
|
auto runOnMain(F f) -> Promise<std::invoke_result_t<F>> {
|
|
98
130
|
using R = std::invoke_result_t<F>;
|
|
@@ -113,4 +145,62 @@ auto runOnMain(F f) -> Promise<std::invoke_result_t<F>> {
|
|
|
113
145
|
return p;
|
|
114
146
|
}
|
|
115
147
|
|
|
148
|
+
// --- any other context -------------------------------------------------------
|
|
149
|
+
//
|
|
150
|
+
// The same entries for another context, such as the main context: none
|
|
151
|
+
// takes the Lucent lock.
|
|
152
|
+
|
|
153
|
+
/// A platform callback into `context` that returns nothing and outlives the
|
|
154
|
+
/// call: a turn of that context. `f` owns what it captured and is released
|
|
155
|
+
/// on the context after it runs (or at once if the context has shut down).
|
|
156
|
+
template <class F>
|
|
157
|
+
void postTo(ExecutionContext& context, F f) {
|
|
158
|
+
context.post(std::move(f));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// A platform callback into `context` the platform waits for, made on the
|
|
162
|
+
/// context's own thread (the main thread, for the main context): runs now,
|
|
163
|
+
/// in the context, and the microtasks it queued run before it returns. A
|
|
164
|
+
/// Lucent error, or a call from any other thread, is reported and the
|
|
165
|
+
/// platform gets a default result.
|
|
166
|
+
template <class F>
|
|
167
|
+
auto callNowIn(ExecutionContext& context, F f) -> std::invoke_result_t<F&> {
|
|
168
|
+
using R = std::invoke_result_t<F&>;
|
|
169
|
+
try {
|
|
170
|
+
ContextEntry entry(context);
|
|
171
|
+
return f();
|
|
172
|
+
} catch (...) {
|
|
173
|
+
reportUncaught(std::current_exception(), "callback");
|
|
174
|
+
if constexpr (!std::is_void_v<R>) return R{};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// Runs `f` as a turn of `context` and settles the promise, which belongs to
|
|
179
|
+
/// the calling context, with its result there; a context that has shut down
|
|
180
|
+
/// rejects it. Neither side waits for the other. What `f` captures and
|
|
181
|
+
/// returns crosses threads, so it must not share mutable state with the
|
|
182
|
+
/// caller.
|
|
183
|
+
template <class F>
|
|
184
|
+
auto runIn(ExecutionContext& context, F f) -> Promise<std::invoke_result_t<F>> {
|
|
185
|
+
using R = std::invoke_result_t<F>;
|
|
186
|
+
Promise<R> p;
|
|
187
|
+
|
|
188
|
+
bool posted = context.post([p, f = std::move(f)]() mutable {
|
|
189
|
+
try {
|
|
190
|
+
if constexpr (std::is_void_v<R>) {
|
|
191
|
+
f();
|
|
192
|
+
p.resolve(undefined);
|
|
193
|
+
} else {
|
|
194
|
+
p.resolve(f());
|
|
195
|
+
}
|
|
196
|
+
} catch (...) {
|
|
197
|
+
p.reject(currentError(std::current_exception()));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (!posted) p.reject(makeError(String::fromLatin1("AbortError"), String::fromLatin1("The execution context has shut down")));
|
|
202
|
+
|
|
203
|
+
return p;
|
|
204
|
+
}
|
|
205
|
+
|
|
116
206
|
} // namespace lucent
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
#include <random>
|
|
9
9
|
#include <string>
|
|
10
10
|
|
|
11
|
+
// Dragonbox tests internal macros that may be undefined.
|
|
12
|
+
#pragma GCC diagnostic push
|
|
13
|
+
#pragma GCC diagnostic ignored "-Wundef"
|
|
14
|
+
#include "../third_party/dragonbox/dragonbox.h"
|
|
15
|
+
#pragma GCC diagnostic pop
|
|
16
|
+
|
|
11
17
|
namespace lucent {
|
|
12
18
|
|
|
13
19
|
namespace {
|
|
@@ -58,31 +64,19 @@ Decimal roundHalfUp(double a, int significant) {
|
|
|
58
64
|
return d;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
bool roundTrips(double v, int precision) {
|
|
68
|
-
char buf[64];
|
|
69
|
-
std::snprintf(buf, sizeof buf, "%.*e", precision - 1, v);
|
|
70
|
-
// strtod needs '.', which is what the C locale prints.
|
|
71
|
-
for (char* c = buf; *c; c++) {
|
|
72
|
-
if (*c == ',') *c = '.';
|
|
73
|
-
}
|
|
74
|
-
return std::strtod(buf, nullptr) == v;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Shortest digit string that round-trips (ECMA-262 Number::toString step 5).
|
|
67
|
+
/// The fewest digits that read back as `v` (finite, positive), the closest
|
|
68
|
+
/// to it of those (ECMA-262 Number::toString step 5). The correctly rounded
|
|
69
|
+
/// digits at that length are not always among them: below a power of two
|
|
70
|
+
/// the doubles are twice as dense, so only a farther string reads back.
|
|
78
71
|
Decimal shortest(double v) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
auto decimal = jkj::dragonbox::to_decimal(v);
|
|
73
|
+
char digits[24];
|
|
74
|
+
auto end = std::to_chars(digits, digits + sizeof digits, decimal.significand).ptr;
|
|
75
|
+
|
|
76
|
+
Decimal d;
|
|
77
|
+
d.digits.assign(digits, end);
|
|
78
|
+
d.n = decimal.exponent + static_cast<int>(d.digits.size());
|
|
79
|
+
return d;
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
std::string formatDecimal(const Decimal& d) {
|
|
@@ -226,18 +220,19 @@ String numberToString(double v, double radixValue) {
|
|
|
226
220
|
}
|
|
227
221
|
} while (fraction >= delta);
|
|
228
222
|
}
|
|
223
|
+
// Integer digits, least significant first. Those a double cannot tell
|
|
224
|
+
// apart (from 2^53 up) are zeros, as V8 prints them.
|
|
229
225
|
std::string intDigits;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
while (integer >= 1) {
|
|
234
|
-
double q = std::floor(integer / radix);
|
|
235
|
-
int digit = static_cast<int>(integer - q * radix);
|
|
236
|
-
intDigits.push_back(chars[digit]);
|
|
237
|
-
integer = q;
|
|
238
|
-
}
|
|
239
|
-
std::reverse(intDigits.begin(), intDigits.end());
|
|
226
|
+
while (integer / radix >= 9007199254740992.0) {
|
|
227
|
+
integer /= radix;
|
|
228
|
+
intDigits.push_back('0');
|
|
240
229
|
}
|
|
230
|
+
do {
|
|
231
|
+
double remainder = std::fmod(integer, radix);
|
|
232
|
+
intDigits.push_back(chars[static_cast<int>(remainder)]);
|
|
233
|
+
integer = (integer - remainder) / radix;
|
|
234
|
+
} while (integer > 0);
|
|
235
|
+
std::reverse(intDigits.begin(), intDigits.end());
|
|
241
236
|
std::string out = negative ? "-" : "";
|
|
242
237
|
out += intDigits;
|
|
243
238
|
if (!fracDigits.empty()) out += "." + fracDigits;
|
|
@@ -249,10 +244,10 @@ String numberToFixed(double v, double digitsValue) {
|
|
|
249
244
|
if (f < 0 || f > 100) throwRangeError("toFixed() digits argument must be between 0 and 100");
|
|
250
245
|
if (std::isnan(v)) return String::fromLatin1("NaN");
|
|
251
246
|
if (std::fabs(v) >= 1e21 || std::isinf(v)) return numberToString(v);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
std::string out =
|
|
255
|
-
return String::fromLatin1(out +
|
|
247
|
+
// A negative value keeps its sign even when it rounds to zero (-0.00);
|
|
248
|
+
// only -0 has none.
|
|
249
|
+
std::string out = v < 0 ? "-" : "";
|
|
250
|
+
return String::fromLatin1(out + fixedDigits(v, static_cast<int>(f)));
|
|
256
251
|
}
|
|
257
252
|
|
|
258
253
|
String numberToExponential(double v, double digitsValue) {
|
|
@@ -542,4 +537,9 @@ double max(double a, double b) {
|
|
|
542
537
|
|
|
543
538
|
} // namespace math
|
|
544
539
|
|
|
540
|
+
void throwInexactInteger(const std::string& value) {
|
|
541
|
+
std::string message = value + " is not an integer a number holds exactly (within +-(2^53 - 1))";
|
|
542
|
+
throwRangeError(message.c_str());
|
|
543
|
+
}
|
|
544
|
+
|
|
545
545
|
} // namespace lucent
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <cmath>
|
|
6
6
|
#include <cstdint>
|
|
7
7
|
#include <limits>
|
|
8
|
+
#include <string>
|
|
8
9
|
#include <type_traits>
|
|
9
10
|
|
|
10
11
|
#include "jsstring.h"
|
|
@@ -70,6 +71,38 @@ inline String toJsString(Null) { return String::fromLatin1("null"); }
|
|
|
70
71
|
inline bool isInteger(double v) { return std::isfinite(v) && std::trunc(v) == v; }
|
|
71
72
|
inline bool isSafeInteger(double v) { return isInteger(v) && std::fabs(v) <= 9007199254740991.0; }
|
|
72
73
|
|
|
74
|
+
[[noreturn]] void throwInexactInteger(const std::string& value);
|
|
75
|
+
|
|
76
|
+
/// A native integer (a 64-bit one: Int64, NSInteger, Java's long) as a
|
|
77
|
+
/// number, exactly: RangeError beyond +-(2^53 - 1), which a number cannot
|
|
78
|
+
/// tell apart from its neighbors. Never rounded.
|
|
79
|
+
template <class I>
|
|
80
|
+
requires std::is_integral_v<I>
|
|
81
|
+
double exactNumber(I v) {
|
|
82
|
+
constexpr uint64_t kMax = 9007199254740991; // 2^53 - 1
|
|
83
|
+
bool fits;
|
|
84
|
+
if constexpr (std::is_signed_v<I>)
|
|
85
|
+
fits = v >= -static_cast<int64_t>(kMax) && v <= static_cast<int64_t>(kMax);
|
|
86
|
+
else
|
|
87
|
+
fits = static_cast<uint64_t>(v) <= kMax;
|
|
88
|
+
|
|
89
|
+
if (!fits) throwInexactInteger(std::to_string(v));
|
|
90
|
+
return static_cast<double>(v);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// A number as a native 64-bit integer, as WebIDL's [EnforceRange] long
|
|
94
|
+
/// long converts it: truncated toward zero; RangeError when it is not
|
|
95
|
+
/// finite or beyond what a number holds exactly (below 0 for unsigned).
|
|
96
|
+
template <class I>
|
|
97
|
+
requires std::is_integral_v<I>
|
|
98
|
+
I toExactInteger(double v) {
|
|
99
|
+
double t = std::trunc(v);
|
|
100
|
+
double low = std::is_signed_v<I> ? -9007199254740991.0 : 0.0;
|
|
101
|
+
if (!(t >= low && t <= 9007199254740991.0)) throwInexactInteger(numberToString(v).toUtf8());
|
|
102
|
+
|
|
103
|
+
return static_cast<I>(t);
|
|
104
|
+
}
|
|
105
|
+
|
|
73
106
|
namespace math {
|
|
74
107
|
inline double abs(double v) { return std::fabs(v); }
|
|
75
108
|
inline double floor(double v) { return std::floor(v); }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Lucent runtime — native operations as promises.
|
|
2
|
+
//
|
|
3
|
+
// Native work that completes later, on any thread (a Kotlin coroutine, a
|
|
4
|
+
// platform callback), becomes a promise of the context that started it: an
|
|
5
|
+
// Operation (scope.h) under that context's root scope, whose outcome settles
|
|
6
|
+
// the promise there. Disposing the scope, or the AbortSignal the caller gave
|
|
7
|
+
// aborting, cancels it: the promise rejects at once, the registration's
|
|
8
|
+
// cleanup tells the native work to stop, and whatever it produces after that
|
|
9
|
+
// is released, never delivered.
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#include <functional>
|
|
13
|
+
#include <memory>
|
|
14
|
+
#include <utility>
|
|
15
|
+
|
|
16
|
+
#include "abort.h"
|
|
17
|
+
#include "async.h"
|
|
18
|
+
#include "execution.h"
|
|
19
|
+
#include "scope.h"
|
|
20
|
+
|
|
21
|
+
namespace lucent {
|
|
22
|
+
|
|
23
|
+
/// Starts native work for the calling context: `registration` begins it and
|
|
24
|
+
/// returns what ends it (see Operation::start). Already aborted, the work
|
|
25
|
+
/// never starts.
|
|
26
|
+
template <class T>
|
|
27
|
+
Promise<T> nativeOperation(typename Operation<T>::Registration registration, Opt<AbortSignal> signal = {}) {
|
|
28
|
+
ContextRef owner = ExecutionContext::currentRef();
|
|
29
|
+
std::shared_ptr<Scope> scope = ExecutionContext::of(owner).root();
|
|
30
|
+
AbortSignal abort = signal.has() ? signal.get() : nullptr;
|
|
31
|
+
|
|
32
|
+
Opt<Error> abortedBy;
|
|
33
|
+
if (abort && abort->aborted.load()) abortedBy = abort->reason;
|
|
34
|
+
|
|
35
|
+
Promise<T> promise;
|
|
36
|
+
auto operation = Operation<T>::start(scope, registration, abortedBy);
|
|
37
|
+
|
|
38
|
+
// The signal holds a listener only while the operation is pending.
|
|
39
|
+
if (abort && operation->state() == OperationState::Pending) {
|
|
40
|
+
std::weak_ptr<Operation<T>> pending = operation;
|
|
41
|
+
std::weak_ptr<AbortSignalObject> weak = abort;
|
|
42
|
+
|
|
43
|
+
uint64_t id = abort->add([pending, weak] {
|
|
44
|
+
auto op = pending.lock();
|
|
45
|
+
auto s = weak.lock();
|
|
46
|
+
if (op && s) op->cancel(s->reason);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
operation->onSettled([weak, id](const typename Operation<T>::Outcome&) {
|
|
50
|
+
if (auto s = weak.lock(); s && id) s->remove(id);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
operation->onSettled([promise](const typename Operation<T>::Outcome& outcome) {
|
|
55
|
+
if (outcome.state == OperationState::Succeeded) {
|
|
56
|
+
promise.resolve(*outcome.value);
|
|
57
|
+
} else {
|
|
58
|
+
promise.reject(outcome.error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return promise;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
} // namespace lucent
|
package/runtime/cpp/lucent/ops.h
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include "array.h"
|
|
9
9
|
#include "async.h"
|
|
10
|
+
#include "bigint.h"
|
|
10
11
|
#include "bytes.h"
|
|
11
12
|
#include "core.h"
|
|
12
13
|
#include "jserror.h"
|
|
@@ -21,6 +22,7 @@ namespace lucent {
|
|
|
21
22
|
inline bool truthy(bool v) { return v; }
|
|
22
23
|
inline bool truthy(double v) { return v != 0 && !std::isnan(v); }
|
|
23
24
|
inline bool truthy(const String& s) { return !s.empty(); }
|
|
25
|
+
inline bool truthy(const BigInt& v) { return !v.isZero(); }
|
|
24
26
|
inline bool truthy(Undefined) { return false; }
|
|
25
27
|
inline bool truthy(Null) { return false; }
|
|
26
28
|
template <class T>
|
|
@@ -59,6 +61,7 @@ String toJsString(const std::variant<Ts...>& v) {
|
|
|
59
61
|
inline String typeOf(double) { return String::fromLatin1("number"); }
|
|
60
62
|
inline String typeOf(bool) { return String::fromLatin1("boolean"); }
|
|
61
63
|
inline String typeOf(const String&) { return String::fromLatin1("string"); }
|
|
64
|
+
inline String typeOf(const BigInt&) { return String::fromLatin1("bigint"); }
|
|
62
65
|
inline String typeOf(Undefined) { return String::fromLatin1("undefined"); }
|
|
63
66
|
inline String typeOf(Null) { return String::fromLatin1("object"); }
|
|
64
67
|
template <class Sig>
|
|
@@ -81,11 +84,6 @@ String typeOf(const T&) {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
// --- unions -------------------------------------------------------------------
|
|
84
|
-
template <class T>
|
|
85
|
-
struct IsVariant : std::false_type {};
|
|
86
|
-
template <class... Ts>
|
|
87
|
-
struct IsVariant<std::variant<Ts...>> : std::true_type {};
|
|
88
|
-
|
|
89
87
|
template <class T, class V>
|
|
90
88
|
constexpr bool variantHolds() {
|
|
91
89
|
if constexpr (IsVariant<V>::value) {
|
|
@@ -109,14 +107,24 @@ To convertImpl(const From& from) {
|
|
|
109
107
|
return from;
|
|
110
108
|
} else if constexpr (IsOpt<To>::value) {
|
|
111
109
|
using Inner = typename To::ValueType;
|
|
110
|
+
// `null | undefined` (Opt<Undefined>) holds no value: narrowing proved
|
|
111
|
+
// any present one away, and throws here. Not in a helper returning To
|
|
112
|
+
// that only throws: clang 18 (Android NDK r27's) then returns garbage
|
|
113
|
+
// for the absent values beside it.
|
|
112
114
|
if constexpr (IsOpt<F>::value) {
|
|
113
115
|
if (from.isUndefined()) return To(undefined);
|
|
114
116
|
if (from.isNull()) return To(null);
|
|
115
|
-
|
|
117
|
+
if constexpr (IsAbsent<Inner>) {
|
|
118
|
+
throwTypeError("Value does not match the expected type");
|
|
119
|
+
} else {
|
|
120
|
+
return To(convert<Inner>(from.get()));
|
|
121
|
+
}
|
|
116
122
|
} else if constexpr (std::is_same_v<F, Undefined>) {
|
|
117
123
|
return To(undefined);
|
|
118
124
|
} else if constexpr (std::is_same_v<F, Null>) {
|
|
119
125
|
return To(null);
|
|
126
|
+
} else if constexpr (IsAbsent<Inner>) {
|
|
127
|
+
throwTypeError("Value does not match the expected type");
|
|
120
128
|
} else {
|
|
121
129
|
return To(convert<Inner>(from));
|
|
122
130
|
}
|