BridgeJS: Support generic functions on imported JS APIs#18
Draft
krodak wants to merge 3 commits into
Draft
Conversation
The non-generic bridging paths cloned each container's stack ABI (array loops with the typed-array fast path, dictionary key/value ordering, the optional presence-flag protocol) inline into every thunk, while the generic paths already described those shapes once via the codec combinators. Route the non-generic array/dictionary/optional emission through the same combinators so each container's stack ABI is described exactly once and instantiated with an element codec: - __bjs_primitiveCodecs becomes a token-keyed table (Bool, Int, ..., String, JSValue) referenced by both combinator instantiations and the generic type-handle registration hooks (same canonical order). - @js struct elements compose directly with structHelpers.<T> (already codec-shaped); associated-value enums adapt their helper through the new __bjs_enumCodec combinator; other element shapes (case enums, JSObject, heap objects, ...) get a local codec literal built from the single element stack fragment description. - __bjs_optionalCodec gains an isUndefinedOr parameter so the JSUndefinedOr flavor instantiates the same combinator instead of a bespoke inline form; stack-convention optional parameters and returns now route through it too. Nested compositions recurse (e.g. __bjs_arrayCodec(__bjs_optionalCodec(__bjs_primitiveCodecs.Int))). - Emission gating: the combinators and the primitive codec table are emitted through the intrinsic registry whenever any linked module uses container bridging (generic or not); builds bridging no containers pay nothing. The generic runtime (codecByTypeId, registration hooks, afterInitialize) stays gated on generics, independently. - Paths that cannot use the combinators keep inline code with comments: side-channel/sentinel optional returns, and optional parameters whose presence flag arrives as a wasm parameter rather than on the i32 stack. Deletes the inline array/dictionary loop fragments, stackOptionalLower, and optionalLiftReturnStruct from JSGlueGen; snapshots re-recorded.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Import-side half of BridgeJS generic function support (split out of the upstream generics PR per review), reworked around the review feedback on the shared ABI. Covers the import scenarios from the upstream generics-support request.
What this enables
One declaration serves every bridged type — primitives,
String,@JSstructs/enums/final classes, andT?/[T]/[String: T]compositions — instead of a wrapper per deserialized type (OpenAPI clients,JSON.parse, storage reads, message payloads). Also supported on@JSClassmethods and statics. Return-only generics work (Tinferred from the return type). Generic imports are Embedded-compatible and exercised byExamples/Embedded.Design (incorporates the upstream review feedback)
BridgeJSTypeHandle;typeIDis the handle's pointer. No name-based identity anywhere on the wire — cross-module name conflicts are structurally impossible. Under Embedded the handle is a pure identity token (thetypeexistential property is compiled out; imports are statically specialized and never need it).bjs_<Module>_register_type_handles, pairing handle IDs with JS codecs index-by-index in a canonical order shared by Swift codegen and the link layer. Trigger is hybrid: eager via an optionalafterInitialize()instantiator hook called afterwasi.initialize()(registration executes Swift, so it cannot run during glue setup), with a lazy first-generic-call fallback for worker threads and hand-rolled hosts.BridgedSwiftGenericBridgeableconformances are emitted for all@JStypes regardless of whether the defining module declares generics — a type from module A works as a generic argument in module B. The JS-side generic runtime stays link-gated (the linker sees all modules), so fully non-generic builds pay nothing in the bundle.__bjs_arrayCodec(elem),__bjs_optionalCodec(elem),__bjs_dictCodec(value)— and generic paths compose them. (Routing the non-generic inline emission through the same combinators is proposed as a follow-up formalization step.)The constraint protocol is
BridgedSwiftGenericBridgeable(public spelling, since users write it; conformances are runtime/codegen-owned — the sole requirement is SPI-gated). Generic parameters on exported@JSfunctions are rejected with a clear diagnostic in this PR; the export side (type reification via handle recovery,BridgeType<T>tokens,@JSprotocol constraints) lands separately on top of this ABI.Testing
ImportGenericAPITests(Swift Testing) round-trips scalars,String, structs, enums, classes, compositions through real JS implementations (includingJSON.parse).Examples/Embeddedbuilds and runs a generic import round-trip.check:bridgejs-dts, WASM E2E (make unittest), formatter.