@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.
Files changed (47) hide show
  1. package/dist/conversion/nodes.d.ts +31 -0
  2. package/dist/conversion/nodes.js +46 -2
  3. package/dist/conversion/record-view.d.ts +24 -0
  4. package/dist/conversion/record-view.js +54 -0
  5. package/dist/ir/certify.js +23 -1
  6. package/dist/ir/lower-operands.d.ts +10 -1
  7. package/dist/ir/lower-operands.js +58 -2
  8. package/dist/ir/lower.js +12 -3
  9. package/dist/ir/reflection-demand.js +5 -0
  10. package/dist/plugins/apple/host.d.ts +2 -0
  11. package/dist/plugins/apple/host.js +13 -6
  12. package/dist/plugins/gea/host.d.ts +2 -0
  13. package/dist/plugins/gea/host.js +11 -4
  14. package/dist/plugins/host-package.d.ts +37 -0
  15. package/dist/plugins/host-package.js +28 -0
  16. package/dist/plugins/load.js +15 -0
  17. package/dist/plugins/webgl/host.d.ts +9 -3
  18. package/dist/plugins/webgl/host.js +15 -6
  19. package/dist/plugins/webgl/plugin.js +4 -1
  20. package/dist/representation/embedded-carriers.d.ts +22 -0
  21. package/dist/representation/embedded-carriers.js +105 -0
  22. package/dist/semantics/model/operands.d.ts +17 -0
  23. package/dist/semantics/model/operations.d.ts +28 -1
  24. package/dist/semantics/normalize/producers/allocations.js +2 -0
  25. package/dist/semantics/normalize/producers/bindings.js +5 -1
  26. package/dist/semantics/normalize/producers/boundary.d.ts +1 -0
  27. package/dist/semantics/normalize/producers/boundary.js +6 -2
  28. package/dist/semantics/normalize/producers/class-lifecycle.js +3 -0
  29. package/dist/semantics/normalize/producers/control.js +2 -2
  30. package/dist/semantics/normalize/producers/erasure.d.ts +7 -0
  31. package/dist/semantics/normalize/producers/erasure.js +19 -0
  32. package/dist/semantics/normalize/producers/exact-arms.d.ts +12 -0
  33. package/dist/semantics/normalize/producers/exact-arms.js +12 -0
  34. package/dist/semantics/normalize/producers/invocations.js +33 -1
  35. package/dist/targets/cpp/class-properties/emit-class-properties.js +4 -4
  36. package/dist/targets/cpp/conversions.js +63 -1
  37. package/dist/targets/cpp/emit-callable.js +5 -5
  38. package/dist/targets/cpp/emit-context.d.ts +38 -1
  39. package/dist/targets/cpp/emit-context.js +28 -2
  40. package/dist/targets/cpp/emit-narrowing.d.ts +1 -1
  41. package/dist/targets/cpp/emit-narrowing.js +47 -1
  42. package/dist/targets/cpp/emit-record-view.js +31 -0
  43. package/dist/targets/cpp/emit.d.ts +3 -3
  44. package/dist/targets/cpp/emit.js +11 -5
  45. package/dist/targets/cpp/translation-unit.js +24 -15
  46. package/package.json +1 -1
  47. 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
- const auto target = node;
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
- const auto listenerId = gea::embedded::ui::Document::instance().body().addEventListener(
25147
- name.c_str(), [held, target, name](gea::framework::events::PointerEvent& event) mutable {
25148
- if (!gea::embedded::ui::Tree::instance().containsNode(target.id(), event.targetId)) return;
25149
- const auto previousTarget = event.currentTarget;
25150
- const auto previousTargetId = event.currentTargetId;
25151
- const auto previousPhase = event.eventPhase;
25152
- event.currentTarget = gea::framework::events::EventTarget(target.id());
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