Skip to content

Zero-glue platform classes: pattern doc, parity pins, accessor-safe A2 probe - #117

Merged
lann merged 1 commit into
mainfrom
platform-class-parity
Aug 12, 2026
Merged

Zero-glue platform classes: pattern doc, parity pins, accessor-safe A2 probe#117
lann merged 1 commit into
mainfrom
platform-class-parity

Conversation

@lannbot

@lannbot lannbot commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Track 3 from the #686 evaluation (watchlist: #115): claim the draft web embedding's best ergonomic idea — satisfying a WIT resource import by passing a native platform class directly — which deltic's existing semantics already support for method-shaped interfaces, and pin its limits as executable characterization.

Doc (contracts/embedder-api.md, non-normative subsection under Resources): the pattern, the executable reference, and the three limits each with a one-line extends Native bridge:

  1. getter-backed properties are not methods → per-call Trap ("has no method");
  2. platform null ≠ WIT none → conversion-layer TypeError naming the import (not a trap, never none);
  3. platform exceptions are unbranded → trap even from result-typed imports; only throw new ComponentException(payload) crosses as err.

Test (runtime/tests/embedder/platform_class_test.ts + platform-class.wat): native URLSearchParams/TextDecoder bound with zero glue — happy path proves ctor args, kebab→camel (to-stringtoString), string/bool/list<u8>/record conversions and BufferSource compatibility; then each limit's exact failure class and pinned wording; then the wrapper recipe (subclass try/catch → ComponentException) flipping the same native failure into the guest's err case. Wat-authoring note: wasmparser's all_valtypes_named_in_func requires records referenced by imported functions to be named type exports of the importing instance.

Runtime fix (instantiate.ts): the A2 wrap-time suspending probe now walks descriptors and reads only data properties. The previous raw prototype[member] read invoked platform getters with this = prototype, so binding URLSearchParams failed at instantiation with Deno's brand-check TypeError — even for guests that never call the getter-backed member. Now accessor-backed members are a per-call concern with the documented named Trap, and an A2 suspending mark cannot ride an accessor (documented).

Characterization delta worth a look: a malformed host return value (limit 2) surfaces as a per-call TypeError without poisoning the instance, while an unbranded host throw traps and poisons. Defensible asymmetry, but if we ever want conversion failures to trap like host throws, that's a contract question — flagging, not changing.

Gates: full just gates green (runtime 489/0 incl. the 5 new pins, conformance 1254/0, seeds, sm/node/bun shells, chromium+firefox, websocket-conformance 55/0, smoke legs).

…2 probe

The #686-inspired pattern — satisfy a WIT resource import by passing a
native platform class directly — already works here for method-shaped
interfaces; claim it and pin its limits (deltic#115 tracks the upstream
draft):

- contracts/embedder-api.md: non-normative "binding platform classes
  directly" subsection under Resources — the pattern, the executable
  reference, and the three limits with their one-line bridges (getter-
  backed properties trap per-call; platform null is not WIT none, the
  conversion TypeError names the import; platform exceptions are
  unbranded and trap even from result-typed imports, only
  ComponentException crosses as err).

- runtime/tests/embedder/platform_class_test.ts + platform-class.wat:
  characterization pins against native URLSearchParams/TextDecoder with
  no wrapper — happy path (ctor args, kebab->camel toString, string/
  bool/list<u8>/record conversions, BufferSource compatibility), each
  limit's exact failure class + wording, and the wrapper recipe turning
  a native throw into a WIT err. Fixture note: wasmparser requires
  records referenced by imported functions to be NAMED type exports of
  the importing instance (all_valtypes_named_in_func).

- runtime/src/embedder/instantiate.ts: the A2 wrap-time suspending probe
  now reads only DATA properties (descriptor walk, dataMember) — the old
  raw prototype read invoked platform getters with this = prototype,
  so binding URLSearchParams exploded at INSTANTIATION with Deno's
  brand-check TypeError, even for guests that never touch the getter-
  backed member. Accessor-backed members are now a per-call concern with
  the documented named Trap; an A2 mark cannot ride an accessor (doc'd).
@lann
lann merged commit b297834 into main Aug 12, 2026
4 checks passed
@lannbot
lannbot deleted the platform-class-parity branch August 23, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants