@geastack/compiler 1.0.16 → 1.0.17
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/dist/conversion/nodes.d.ts +31 -0
- package/dist/conversion/nodes.js +46 -2
- package/dist/conversion/record-view.d.ts +24 -0
- package/dist/conversion/record-view.js +54 -0
- package/dist/ir/certify.js +23 -1
- package/dist/ir/lower-operands.d.ts +10 -1
- package/dist/ir/lower-operands.js +58 -2
- package/dist/ir/lower.js +12 -3
- package/dist/ir/reflection-demand.js +5 -0
- package/dist/plugins/apple/host.d.ts +2 -0
- package/dist/plugins/apple/host.js +13 -6
- package/dist/plugins/gea/host.d.ts +2 -0
- package/dist/plugins/gea/host.js +11 -4
- package/dist/plugins/host-package.d.ts +37 -0
- package/dist/plugins/host-package.js +28 -0
- package/dist/plugins/load.js +15 -0
- package/dist/plugins/webgl/host.d.ts +9 -3
- package/dist/plugins/webgl/host.js +15 -6
- package/dist/plugins/webgl/plugin.js +4 -1
- package/dist/representation/embedded-carriers.d.ts +22 -0
- package/dist/representation/embedded-carriers.js +105 -0
- package/dist/semantics/model/operands.d.ts +17 -0
- package/dist/semantics/model/operations.d.ts +28 -1
- package/dist/semantics/normalize/producers/allocations.js +2 -0
- package/dist/semantics/normalize/producers/bindings.js +5 -1
- package/dist/semantics/normalize/producers/boundary.d.ts +1 -0
- package/dist/semantics/normalize/producers/boundary.js +6 -2
- package/dist/semantics/normalize/producers/class-lifecycle.js +3 -0
- package/dist/semantics/normalize/producers/control.js +2 -2
- package/dist/semantics/normalize/producers/erasure.d.ts +7 -0
- package/dist/semantics/normalize/producers/erasure.js +19 -0
- package/dist/semantics/normalize/producers/exact-arms.d.ts +12 -0
- package/dist/semantics/normalize/producers/exact-arms.js +12 -0
- package/dist/semantics/normalize/producers/invocations.js +33 -1
- package/dist/targets/cpp/class-properties/emit-class-properties.js +4 -4
- package/dist/targets/cpp/conversions.js +63 -1
- package/dist/targets/cpp/emit-callable.js +5 -5
- package/dist/targets/cpp/emit-context.d.ts +38 -1
- package/dist/targets/cpp/emit-context.js +28 -2
- package/dist/targets/cpp/emit-narrowing.d.ts +1 -1
- package/dist/targets/cpp/emit-narrowing.js +47 -1
- package/dist/targets/cpp/emit-record-view.js +31 -0
- package/dist/targets/cpp/emit.d.ts +3 -3
- package/dist/targets/cpp/emit.js +11 -5
- package/dist/targets/cpp/translation-unit.js +24 -15
- package/package.json +1 -1
- package/src/targets/cpp/runtime/gea_runtime.h +123 -44
|
@@ -3170,6 +3170,25 @@ struct CallableObject<Result(Arguments...)> {
|
|
|
3170
3170
|
return true;
|
|
3171
3171
|
}
|
|
3172
3172
|
|
|
3173
|
+
// The same registration, performed where a function OBJECT is minted from
|
|
3174
|
+
// the thunk rather than by a namespace-scope initializer beside the thunk.
|
|
3175
|
+
// A namespace-scope registration is a static initializer that takes the
|
|
3176
|
+
// thunk's address, and a static initializer with a side effect is a GC
|
|
3177
|
+
// root: neither `--gc-sections` nor LTO may drop it, so it kept every
|
|
3178
|
+
// emitted function -- and its source text -- in the binary whether or not
|
|
3179
|
+
// anything reached it. One `EventEmitter` boxing a listener turned the
|
|
3180
|
+
// registry on for a whole program, and that pinned all 384 functions of a
|
|
3181
|
+
// raw HTTP server. Here the registration is reachable only from a site
|
|
3182
|
+
// that creates the function object, which is the only site that can later
|
|
3183
|
+
// observe its `name`/`length`/source. The function-local static is one
|
|
3184
|
+
// per `Entry`, so several minting sites of one declaration share a single
|
|
3185
|
+
// registration, and a site costs one guard-variable load per mint.
|
|
3186
|
+
template <Invoke Entry>
|
|
3187
|
+
static Invoke entryWithFacts(std::string_view name, std::size_t length, std::string_view text) {
|
|
3188
|
+
static const SourceRegistration registration{Entry, name, length, text, nullptr};
|
|
3189
|
+
return Entry;
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3173
3192
|
// An adapter (`dropArguments` and its siblings below) shares ONE thunk
|
|
3174
3193
|
// pointer across every closure it wraps, so the facts it reports cannot
|
|
3175
3194
|
// live in this registration -- they belong to whichever `Source` closure
|
|
@@ -18330,6 +18349,31 @@ template <typename T>
|
|
|
18330
18349
|
throw gea::Value::box(gea::Value::Tag::String, std::string("InternalError: entered a branch the compiler proved unreachable"));
|
|
18331
18350
|
}
|
|
18332
18351
|
|
|
18352
|
+
/**
|
|
18353
|
+
* The exact-arm projection of a tagged union (`conversion/nodes.ts`'s
|
|
18354
|
+
* `exactArmFor`): the payload of arm `Index`, or a `TypeError` when the value
|
|
18355
|
+
* holds another arm.
|
|
18356
|
+
*
|
|
18357
|
+
* Emitted inside a body whose declaration states `@gea-exact-arms`, and at a
|
|
18358
|
+
* value the program's own `as T` states the arm of where the census has no
|
|
18359
|
+
* sound per-arm answer (`lower-operands.ts`'s `assertedArmEntry`). The
|
|
18360
|
+
* alternative the compiler would otherwise render is a per-arm dispatch that
|
|
18361
|
+
* CONVERTS every arm into the target -- for a callable target, an adapter that
|
|
18362
|
+
* boxes the typed arm's parameters to feed a generic listener -- and the tag
|
|
18363
|
+
* is the author stating that the guard around the cast already excluded those
|
|
18364
|
+
* arms. A `TypeError` rather than an abort because a wrong arm here is the
|
|
18365
|
+
* program's contract violated (`server.on('request', genericHandler)`), not
|
|
18366
|
+
* the compiler's, and the program may want to catch it.
|
|
18367
|
+
*/
|
|
18368
|
+
template <std::size_t Index, typename... Arms>
|
|
18369
|
+
typename gea::TaggedUnion<Arms...>::template ArmType<Index> exactArm(const gea::TaggedUnion<Arms...>& value) {
|
|
18370
|
+
if (!value.template is<Index>()) {
|
|
18371
|
+
throwRuntimeError("TypeError", "Value is not the union arm this operation requires (arm " + std::to_string(Index) +
|
|
18372
|
+
" expected, arm " + std::to_string(value.index()) + " held)");
|
|
18373
|
+
}
|
|
18374
|
+
return value.template get<Index>();
|
|
18375
|
+
}
|
|
18376
|
+
|
|
18333
18377
|
} // namespace gea::host
|
|
18334
18378
|
|
|
18335
18379
|
namespace gea::runtime::string {
|
|
@@ -25050,6 +25094,77 @@ inline void recordNodeSubscription(int nodeId, std::function<void()> release);
|
|
|
25050
25094
|
template <typename Node>
|
|
25051
25095
|
gea::Ref<bool> nodeLiveToken(const Node& node);
|
|
25052
25096
|
|
|
25097
|
+
/**
|
|
25098
|
+
* Whether the engine dispatches `name` off-tree. `rotary` (the encoder dial)
|
|
25099
|
+
* always: it has no element target, the runtime hands each detent to the one
|
|
25100
|
+
* document-level listener chain (`dispatchDocumentRotary`), and
|
|
25101
|
+
* `Tree::setEventListener` drops "rotary" as a type it never walks the tree
|
|
25102
|
+
* for -- so an `onRotary` delegated to the body was registered nowhere and the
|
|
25103
|
+
* dial did nothing, silently. `keydown` is off-tree on anything but an
|
|
25104
|
+
* `<input>`: the runtime hands a key to the focused input's own listener (which
|
|
25105
|
+
* bubbles to the body) and then to the document-level chain. The same rule the
|
|
25106
|
+
* gea plugin's `cpp-mounted-lowering.ts` applies to its direct-root listeners.
|
|
25107
|
+
*/
|
|
25108
|
+
template <typename Node>
|
|
25109
|
+
bool isDocumentLevelEvent(const Node& node, const std::string& name) {
|
|
25110
|
+
if (name == "rotary") return true;
|
|
25111
|
+
if (name != "keydown") return false;
|
|
25112
|
+
const char* tag = node.tagName();
|
|
25113
|
+
if (!tag) return true;
|
|
25114
|
+
std::string lowered(tag);
|
|
25115
|
+
for (char& character : lowered) character = static_cast<char>(std::tolower(static_cast<unsigned char>(character)));
|
|
25116
|
+
return lowered != "input";
|
|
25117
|
+
}
|
|
25118
|
+
|
|
25119
|
+
/**
|
|
25120
|
+
* Register `invoke` for `name` on behalf of `node`: the one place both engine
|
|
25121
|
+
* `addNodeListener` overloads bind through.
|
|
25122
|
+
*
|
|
25123
|
+
* A document-level event goes straight to `Document::addEventListener`, which
|
|
25124
|
+
* chains it onto the off-tree singleton: no `containsNode` gate, since there is
|
|
25125
|
+
* no target to contain. The chain has no per-listener removal, so the handler
|
|
25126
|
+
* is gated on the node's live token instead -- a rebuilt subtree leaves an
|
|
25127
|
+
* inert closure behind rather than a second generation of the handler.
|
|
25128
|
+
*
|
|
25129
|
+
* Everything else is delegated to the BODY and gated by `containsNode`; see
|
|
25130
|
+
* the no-argument `addNodeListener` for why, and `recordNodeSubscription` for
|
|
25131
|
+
* how it is released with the node.
|
|
25132
|
+
*/
|
|
25133
|
+
template <typename Node, typename Invoke>
|
|
25134
|
+
void bindNodeListener(Node& node, const std::string& name, Invoke invoke) {
|
|
25135
|
+
const auto target = node;
|
|
25136
|
+
auto run = [target, invoke](gea::framework::events::PointerEvent& event) mutable {
|
|
25137
|
+
const auto previousTarget = event.currentTarget;
|
|
25138
|
+
const auto previousTargetId = event.currentTargetId;
|
|
25139
|
+
const auto previousPhase = event.eventPhase;
|
|
25140
|
+
event.currentTarget = gea::framework::events::EventTarget(target.id());
|
|
25141
|
+
event.currentTargetId = target.id();
|
|
25142
|
+
event.eventPhase = event.targetId == target.id() ? gea::framework::events::EventPhase::AtTarget
|
|
25143
|
+
: gea::framework::events::EventPhase::Bubbling;
|
|
25144
|
+
invoke(event);
|
|
25145
|
+
event.currentTarget = previousTarget;
|
|
25146
|
+
event.currentTargetId = previousTargetId;
|
|
25147
|
+
event.eventPhase = previousPhase;
|
|
25148
|
+
};
|
|
25149
|
+
if (isDocumentLevelEvent(node, name)) {
|
|
25150
|
+
auto alive = nodeLiveToken(node);
|
|
25151
|
+
gea::embedded::ui::Document::instance().addEventListener(
|
|
25152
|
+
name.c_str(), [alive, run](gea::framework::events::PointerEvent& event) mutable {
|
|
25153
|
+
if (!*alive) return;
|
|
25154
|
+
run(event);
|
|
25155
|
+
});
|
|
25156
|
+
return;
|
|
25157
|
+
}
|
|
25158
|
+
const auto listenerId = gea::embedded::ui::Document::instance().body().addEventListener(
|
|
25159
|
+
name.c_str(), [target, run](gea::framework::events::PointerEvent& event) mutable {
|
|
25160
|
+
if (!gea::embedded::ui::Tree::instance().containsNode(target.id(), event.targetId)) return;
|
|
25161
|
+
run(event);
|
|
25162
|
+
});
|
|
25163
|
+
recordNodeSubscription(target.id(), [name, listenerId]() {
|
|
25164
|
+
gea::embedded::ui::Document::instance().body().removeEventListener(name.c_str(), listenerId);
|
|
25165
|
+
});
|
|
25166
|
+
}
|
|
25167
|
+
|
|
25053
25168
|
/**
|
|
25054
25169
|
* `onClick={() => ...}` on the real engine: the handler is copied into the
|
|
25055
25170
|
* listener the engine holds, and the event it hands back is dropped -- a
|
|
@@ -25092,26 +25207,7 @@ void addNodeListener(Node& node, const std::string& type, const gea::CallableObj
|
|
|
25092
25207
|
// flipped the unit twice and the whole UI stayed in Celsius, with nothing
|
|
25093
25208
|
// logged and every node still rendering correctly.
|
|
25094
25209
|
const gea::CallableObject<Result()> held = handler;
|
|
25095
|
-
|
|
25096
|
-
const std::string name = eventTypeOf(type);
|
|
25097
|
-
const auto listenerId = gea::embedded::ui::Document::instance().body().addEventListener(
|
|
25098
|
-
name.c_str(), [held, target](gea::framework::events::PointerEvent& event) mutable {
|
|
25099
|
-
if (!gea::embedded::ui::Tree::instance().containsNode(target.id(), event.targetId)) return;
|
|
25100
|
-
const auto previousTarget = event.currentTarget;
|
|
25101
|
-
const auto previousTargetId = event.currentTargetId;
|
|
25102
|
-
const auto previousPhase = event.eventPhase;
|
|
25103
|
-
event.currentTarget = gea::framework::events::EventTarget(target.id());
|
|
25104
|
-
event.currentTargetId = target.id();
|
|
25105
|
-
event.eventPhase = event.targetId == target.id() ? gea::framework::events::EventPhase::AtTarget
|
|
25106
|
-
: gea::framework::events::EventPhase::Bubbling;
|
|
25107
|
-
held.call();
|
|
25108
|
-
event.currentTarget = previousTarget;
|
|
25109
|
-
event.currentTargetId = previousTargetId;
|
|
25110
|
-
event.eventPhase = previousPhase;
|
|
25111
|
-
});
|
|
25112
|
-
recordNodeSubscription(target.id(), [name, listenerId]() {
|
|
25113
|
-
gea::embedded::ui::Document::instance().body().removeEventListener(name.c_str(), listenerId);
|
|
25114
|
-
});
|
|
25210
|
+
bindNodeListener(node, eventTypeOf(type), [held](gea::framework::events::PointerEvent&) mutable { held.call(); });
|
|
25115
25211
|
}
|
|
25116
25212
|
|
|
25117
25213
|
/**
|
|
@@ -25141,31 +25237,14 @@ void addNodeListener(Node& node, const std::string& type, const gea::CallableObj
|
|
|
25141
25237
|
template <typename Node, typename Result, typename Argument>
|
|
25142
25238
|
void addNodeListener(Node& node, const std::string& type, const gea::CallableObject<Result(Argument)>& handler) {
|
|
25143
25239
|
const gea::CallableObject<Result(Argument)> held = handler;
|
|
25144
|
-
const auto target = node;
|
|
25145
25240
|
const std::string name = eventTypeOf(type);
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
event.currentTargetId = target.id();
|
|
25154
|
-
event.eventPhase = event.targetId == target.id() ? gea::framework::events::EventPhase::AtTarget
|
|
25155
|
-
: gea::framework::events::EventPhase::Bubbling;
|
|
25156
|
-
if constexpr (std::is_constructible_v<std::decay_t<Argument>, const gea::framework::events::PointerEvent&>) {
|
|
25157
|
-
held.call(static_cast<std::decay_t<Argument>>(event));
|
|
25158
|
-
} else {
|
|
25159
|
-
std::fprintf(stderr, "gea: the handler for \"%s\" declares an event parameter, which no host table states a carrier for\n", name.c_str());
|
|
25160
|
-
gea::detail::abortAfterFlush();
|
|
25161
|
-
}
|
|
25162
|
-
event.currentTarget = previousTarget;
|
|
25163
|
-
event.currentTargetId = previousTargetId;
|
|
25164
|
-
event.eventPhase = previousPhase;
|
|
25165
|
-
});
|
|
25166
|
-
// Owned by `node`, for the reason the no-argument overload above states.
|
|
25167
|
-
recordNodeSubscription(target.id(), [name, listenerId]() {
|
|
25168
|
-
gea::embedded::ui::Document::instance().body().removeEventListener(name.c_str(), listenerId);
|
|
25241
|
+
bindNodeListener(node, name, [held, name](gea::framework::events::PointerEvent& event) mutable {
|
|
25242
|
+
if constexpr (std::is_constructible_v<std::decay_t<Argument>, const gea::framework::events::PointerEvent&>) {
|
|
25243
|
+
held.call(static_cast<std::decay_t<Argument>>(event));
|
|
25244
|
+
} else {
|
|
25245
|
+
std::fprintf(stderr, "gea: the handler for \"%s\" declares an event parameter, which no host table states a carrier for\n", name.c_str());
|
|
25246
|
+
gea::detail::abortAfterFlush();
|
|
25247
|
+
}
|
|
25169
25248
|
});
|
|
25170
25249
|
}
|
|
25171
25250
|
|