BridgeJS: Support generic functions at the Swift and JavaScript boundary#14
Closed
krodak wants to merge 2 commits into
Closed
BridgeJS: Support generic functions at the Swift and JavaScript boundary#14krodak wants to merge 2 commits into
krodak wants to merge 2 commits into
Conversation
c6c78a4 to
4ad8122
Compare
765ecd2 to
f19341a
Compare
42c2670 to
6e3da66
Compare
6e3da66 to
50bcb2c
Compare
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.
Overview
Adds support for generic functions and methods to BridgeJS in both directions, constrained to a new bridgeable bound
_BridgedSwiftGenericBridgeable. Values cross the boundary using each type's existing stack ABI, and a runtime type-ID registry selects the correct per-type codec, so the per-function glue stays type-agnostic. The@JS/@JSFunctionmacros are unchanged.The bound is
_BridgedSwiftStackTyperefined withStackLiftResult == Self(so a generic thunk can pop aT, which also structurally excludesJSObject) plus a singlebridgeJSTypeID: Int32requirement. That ID is the runtime identity handshake: each type's token string is interned through one wasm import exactly once (a lazystatic let), after which both sides exchange plaini32s; name-based interning is what keeps IDs consistent across independently compiled modules.Tmay be a supported primitive (Bool, any fixed-width integer,Float,Double,String, orJSValue), or any@JSstruct,final @JS class, or@JS enumdefined in the same module. It may be used bare (T) or wrapped as[T],T?, or[String: T].JSObjectcannot be used asT; useJSValueinstead.What's included
1. Import direction. Generic
@JSFunctionthunks (top-level or@JSClassmembers) are generic and monomorphized by the Swift compiler at the call site. One type-agnostic wasm import carries a trailing type-ID per generic parameter, and JS dispatches through a shared codec table. Supports a generic used in one or many parameters, multiple distinct generic parameters, and return-only generics (make<T>() -> T).2. Export direction. Generics work on top-level functions, instance and static methods of
@JSclasses and structs, and static methods of@JSenums. The wasm entry point is a concrete@_expose/@_cdeclthunk taking a trailing type-ID per generic parameter; the generic value crosses on the stack, andselfis threaded through the existential open-chain for instance methods. The thunk looks the type-ID up in a codegen-emitted registry and reifiesTthrough an opened existential (nested opening chain for multiple parameters). Concrete parameters of any supported bridged type may be mixed in with correct stack ordering. JavaScript callers pass a generatedBridgeType<T>token, exported as aBridgeTypesmap. Because this relies on opened existentials, the exported thunk is emitted as afatalErrorstub under#if hasFeature(Embedded); the import side stays Embedded-compatible.3. Codecs and wrapped generics. Each codec (
{ lower, lift }) is generated from the canonical per-type stack fragments (stackLowerFragment/stackLiftFragment), the same path the non-generic glue uses, so there is no duplicated lowering to drift.[T],T?, and[String: T]bridge through the existingArray/Optional/Dictionaryconditional conformances on the Swift side, exactly like concrete types; in particular, numeric arrays keep the bulk typed-array fast path (the JS composition helper honors the existing-1bulk discriminator). This builds on the optional stack encoding unification (previous PR in this series), which made the optional stack form uniform across all types.4. Diagnostics. Build-time, source-located diagnostics for unsupported forms: missing or incorrect constraint,
whereclauses,asyncorthrowsgenerics, generics in@JSClassor static members, unsupported wrappings ([[T]],[T?],[Int: T]), and an export return type that is neither a declared generic (optionally wrapped) norVoid. The linker also fails the build when two linked modules define same-named@JStypes while generics are in use, since generic tokens are unqualified type names.5. Documentation. DocC articles (exporting/importing functions, supported types, unsupported features, internals rationale) and the BridgeJS README bridged-type table.
Testing
Tvst), and generic methods on each owning construct (class, struct, enum, namespace enum,@JSClass).Tin both directions, including the[T]/T?/[String: T]wrappers with empty collections andnil, bulk numeric arrays, heap-object reference semantics, consecutive calls with different types, and generic instance/static methods on classes, structs, and enums.Open questions
BridgeTypes, resolver, type registry) is only emitted for modules that actually declare generics. I considered dropping the gating to streamline the codegen, but the gates themselves are a handful of trivial conditionals, while removing them would regenerate every snapshot fixture and grow the generated output of every module that never uses generics. If you would rather have unconditional emission for simplicity, I am happy to remove the gating.Addresses swiftwasm#398