Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

BridgeJS: Swift closure support#463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
krodak wants to merge1 commit intoswiftwasm:main
base:main
Choose a base branch
Loading
fromPassiveLogic:feat/closure-support

Conversation

@krodak
Copy link
Member

@krodakkrodak commentedNov 5, 2025
edited
Loading

Overview

This PR implements typed closure support in BridgeJS, enabling Swift functions to accept closures as parameters and return closures as values with full type safety.

Example

Swift:

@JSclassTextProcessor{@JSinit(transform:@escaping(String)->String)@JSfunc processWithPerson(_ person:Person, formatter:(Person)->String)-> String@JS func makePersonCreator(defaultName:String)->(String)->Person}

#"auto" data-snippet-clipboard-copy-content="const processor = new exports.TextProcessor((text) => text.toUpperCase());const person = new exports.Person("Alice");const result = processor.processWithPerson(person, (p) => `${p.name}: ${p.greet()}`);const creator = processor.makePersonCreator("Default");const p1 = creator("Bob");">

constprocessor=newexports.TextProcessor((text)=>text.toUpperCase());constperson=newexports.Person("Alice");constresult=processor.processWithPerson(person,(p)=>`${p.name}:${p.greet()}`);constcreator=processor.makePersonCreator("Default");constp1=creator("Bob");

TypeScript:

exportinterfaceTextProcessorextendsSwiftHeapObject{processWithPerson(person:Person,formatter:(arg0:Person)=>string):string;makePersonCreator(defaultName:string):(arg0:string)=>Person;}

Technical Changes

  • Extended type system to supportBridgeType.closure(ClosureSignature)
  • Updated Swift parser to detect closure parameters in function declarations
  • Generate Swift helper enums (_BJS_Closure_*) for each unique closure signature
  • Generate JavaScript callback registry and invoke functions, needed to expose_exports and_bjs to support that
  • Map closure signatures to TypeScript function types
  • Automatic memory management in both directions, needed to addbridgeJSLowerParameterWithRetain to support optionalSwiftHeapObject

Supported Features

  • ✅ Closure parameters with any supported type
  • ✅ Closure return values
  • @escaping closures
  • ✅ Optional closures and optional parameters/returns
  • ✅ All existing BridgeJS types (primitives, String, enums, Swift classes)
  • ❌ Closure-typed properties (parameters/returns only)
  • ❌ Async/throwing closures

Testing

Added comprehensive snapshot and runtime tests for closure parameters, returns, optionals, and Swift heap objects in closures.

Documentation

Extended current documentation with newExporting-Swift-Closure.md

@krodakkrodak self-assigned thisNov 5, 2025
@krodakkrodak removed this fromWasmKitNov 5, 2025
}

forsignaturein closureSignatures.sorted(by:{ $0.mangleName< $1.mangleName}){
letinvokeFuncName="invoke_js_callback_\(signature.mangleName.lowercased())"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

To be conservative, it might be better not to force lowercase here to avoid conflicts with names having different casing

krodak reacted with thumbs up emoji
?"Void"
: parameters.map{ $0.mangleTypeName}.joined(separator:"_")
letreturnPart= returnType.mangleTypeName
self.mangleName="\(paramPart)_To_\(returnPart)"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Can we encode the module name into the mangled name to avoid conflicts with the same name types from other modules?

krodak reacted with thumbs up emoji
case.swiftHeapObject(let name):
return name
case.optional(let wrapped):
return"Optional\(wrapped.mangleTypeName)"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Suggested change
return"Optional\(wrapped.mangleTypeName)"
return"Optional<\(wrapped.mangleTypeName)>"

Let's include angle bracket not to conflict with user definedOptionalFoo nominal types

Copy link
MemberAuthor

@krodakkrodakNov 6, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

This is used for functions and classes name, so we need to avoid<> as this would be treat as generics - maybe we should settle for something like:return "Optional_\(wrapped.mangleTypeName)_"

That would also align with

case .closure(let signature):            return "Closure_\(signature.mangleName)"


if !closureSignatures.isEmpty{
printer.nextLine()
printer.write("bjs[\"release_js_callback\"] = function(id) {")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think we already haveswift_js_release, which does exactly the same thing

krodak reacted with thumbs up emoji
Comment on lines +57 to +72
@_spi(BridgeJS)publicfinalclass_JSCallbackOwner{
publicletcallbackId:Int32
privatevarisReleased:Bool=false

publicinit(callbackId:Int32){
self.callbackId= callbackId
}

deinit{
guard !isReleasedelse{return}
#if arch(wasm32)
_swift_js_release_callback(callbackId)
#endif
isReleased=true
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Can we consider replacing this class withJSObject? JS object handle deallocation is a bit tricky (e.g. we need to care about deallocation from other threads than it was created.), so I'd like to centralize the lifetime management inJSObject.

krodak reacted with thumbs up emoji
@krodakkrodak closed thisNov 6, 2025
@krodakkrodak reopened thisNov 6, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@kateinoigakukunkateinoigakukunkateinoigakukun left review comments

At least 1 approving review is required to merge this pull request.

Assignees

@krodakkrodak

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@krodak@kateinoigakukun

[8]ページ先頭

©2009-2025 Movatter.jp