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

Commitbf5f4d9

Browse files
BridgeJS: Add async function support with Promise-based interop
Implements comprehensive async Swift-to-JavaScript interoperability using JSPromise.async wrapper.Includes TypeScript Promise<T> type generation and full integration with module name architecture.
1 parent48720bb commitbf5f4d9

File tree

43 files changed

+1407
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1407
-136
lines changed

‎Package.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ let package = Package(
155155
),
156156
.testTarget(
157157
name:"BridgeJSRuntimeTests",
158-
dependencies:["JavaScriptKit"],
158+
dependencies:["JavaScriptKit","JavaScriptEventLoop"],
159159
exclude:[
160160
"bridge-js.config.json",
161161
"bridge-js.d.ts",

‎Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift‎

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,9 @@ public class ExportSwift {
453453
varcallExpr:ExprSyntax=
454454
"\(raw: callee)(\(raw: abiParameterForwardings.map{ $0.description}.joined(separator:",")))"
455455
if effects.isAsync{
456-
callExpr=ExprSyntax(AwaitExprSyntax(awaitKeyword:.keyword(.await), expression: callExpr))
456+
callExpr=ExprSyntax(
457+
AwaitExprSyntax(awaitKeyword:.keyword(.await).with(\.trailingTrivia,.space), expression: callExpr)
458+
)
457459
}
458460
if effects.isThrows{
459461
callExpr=ExprSyntax(
@@ -463,6 +465,11 @@ public class ExportSwift {
463465
)
464466
)
465467
}
468+
469+
if effects.isAsync, returnType!=.void{
470+
returnCodeBlockItemSyntax(item:.init(StmtSyntax("return\(raw: callExpr).jsValue")))
471+
}
472+
466473
letretMutability= returnType==.string?"var":"let"
467474
if returnType==.void{
468475
returnCodeBlockItemSyntax(item:.init(ExpressionStmtSyntax(expression: callExpr)))
@@ -486,7 +493,40 @@ public class ExportSwift {
486493
}
487494

488495
func lowerReturnValue(returnType:BridgeType){
489-
abiReturnType= returnType.abiReturnType
496+
if effects.isAsync{
497+
// Async functions always return a Promise, which is a JSObject
498+
_lowerReturnValue(returnType:.jsObject(nil))
499+
}else{
500+
_lowerReturnValue(returnType: returnType)
501+
}
502+
}
503+
504+
privatefunc _lowerReturnValue(returnType:BridgeType){
505+
switch returnType{
506+
case.void:
507+
abiReturnType=nil
508+
case.bool:
509+
abiReturnType=.i32
510+
case.int:
511+
abiReturnType=.i32
512+
case.float:
513+
abiReturnType=.f32
514+
case.double:
515+
abiReturnType=.f64
516+
case.string:
517+
abiReturnType=nil
518+
case.jsObject:
519+
abiReturnType=.i32
520+
case.swiftHeapObject:
521+
// UnsafeMutableRawPointer is returned as an i32 pointer
522+
abiReturnType=.pointer
523+
}
524+
525+
if effects.isAsync{
526+
// The return value of async function (T of `(...) async -> T`) is
527+
// handled by the JSPromise.async, so we don't need to do anything here.
528+
return
529+
}
490530

491531
switch returnType{
492532
case.void:break
@@ -527,7 +567,14 @@ public class ExportSwift {
527567

528568
func render(abiName:String)->DeclSyntax{
529569
letbody:CodeBlockItemListSyntax
530-
if effects.isThrows{
570+
if effects.isAsync{
571+
body="""
572+
let ret = JSPromise.async {
573+
\(CodeBlockItemListSyntax(self.body))
574+
}.jsObject
575+
return _swift_js_retain(Int32(bitPattern: ret.id))
576+
"""
577+
}elseif effects.isThrows{
531578
body="""
532579
do {
533580
\(CodeBlockItemListSyntax(self.body))

‎Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift‎

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,13 @@ struct BridgeJSLink {
172172
let tmpRetBytes;
173173
let tmpRetException;
174174
return {
175-
/** @param {WebAssembly.Imports} importObject */
176-
addImports: (importObject) => {
175+
/**
176+
* @param {WebAssembly.Imports} importObject
177+
*/
178+
addImports: (importObject, importsContext) => {
177179
const bjs = {};
178180
importObject["bjs"] = bjs;
181+
const imports = options.getImports(importsContext);
179182
bjs["swift_js_return_string"] = function(ptr, len) {
180183
const bytes = new Uint8Array(memory.buffer, ptr, len)\(sharedMemory?".slice()":"");
181184
tmpRetString = textDecoder.decode(bytes);
@@ -294,7 +297,7 @@ struct BridgeJSLink {
294297
// Add methods
295298
formethodin type.methods{
296299
letmethodSignature=
297-
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
300+
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects:Effects(isAsync:false, isThrows:false)));"
298301
typeDefinitions.append(methodSignature.indent(count:4))
299302
}
300303

@@ -368,7 +371,7 @@ struct BridgeJSLink {
368371

369372
formethodin klass.methods{
370373
letmethodSignature=
371-
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
374+
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));"
372375
dtsLines.append("\(methodSignature)".indent(count: identBaseSize*(parts.count+2)))
373376
}
374377

@@ -394,7 +397,7 @@ struct BridgeJSLink {
394397

395398
forfunctionin functions{
396399
letsignature=
397-
"function\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
400+
"function\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));"
398401
dtsLines.append("\(signature)".indent(count: identBaseSize*(parts.count+1)))
399402
}
400403

@@ -446,6 +449,14 @@ struct BridgeJSLink {
446449
}
447450

448451
func call(abiName:String, returnType:BridgeType)->String?{
452+
if effects.isAsync{
453+
return_call(abiName: abiName, returnType:.jsObject(nil))
454+
}else{
455+
return_call(abiName: abiName, returnType: returnType)
456+
}
457+
}
458+
459+
privatefunc _call(abiName:String, returnType:BridgeType)->String?{
449460
letcall="instance.exports.\(abiName)(\(parameterForwardings.joined(separator:",")))"
450461
varreturnExpr:String?
451462

@@ -519,8 +530,15 @@ struct BridgeJSLink {
519530
}
520531
}
521532

522-
privatefunc renderTSSignature(parameters:[Parameter], returnType:BridgeType)->String{
523-
return"(\(parameters.map{"\($0.name):\($0.type.tsType)"}.joined(separator:","))):\(returnType.tsType)"
533+
privatefunc renderTSSignature(parameters:[Parameter], returnType:BridgeType, effects:Effects)->String{
534+
letreturnTypeWithEffect:String
535+
if effects.isAsync{
536+
returnTypeWithEffect="Promise<\(returnType.tsType)>"
537+
}else{
538+
returnTypeWithEffect= returnType.tsType
539+
}
540+
return
541+
"(\(parameters.map{"\($0.name):\($0.type.tsType)"}.joined(separator:","))):\(returnTypeWithEffect)"
524542
}
525543

526544
func renderExportedFunction(function:ExportedFunction)->(js:[String], dts:[String]){
@@ -538,7 +556,7 @@ struct BridgeJSLink {
538556
)
539557
vardtsLines:[String]=[]
540558
dtsLines.append(
541-
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
559+
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));"
542560
)
543561

544562
return(funcLines, dtsLines)
@@ -581,7 +599,7 @@ struct BridgeJSLink {
581599
jsLines.append(contentsOf: funcLines.map{ $0.indent(count:4)})
582600

583601
dtsExportEntryLines.append(
584-
"constructor\(renderTSSignature(parameters: constructor.parameters, returnType:.swiftHeapObject(klass.name)));"
602+
"constructor\(renderTSSignature(parameters: constructor.parameters, returnType:.swiftHeapObject(klass.name), effects: constructor.effects));"
585603
.indent(count:4)
586604
)
587605
}
@@ -603,7 +621,7 @@ struct BridgeJSLink {
603621
).map{ $0.indent(count:4)}
604622
)
605623
dtsTypeLines.append(
606-
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
624+
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));"
607625
.indent(count:4)
608626
)
609627
}
@@ -712,7 +730,7 @@ struct BridgeJSLink {
712730
}
713731

714732
func call(name:String, returnType:BridgeType){
715-
letcall="options.imports.\(name)(\(parameterForwardings.joined(separator:",")))"
733+
letcall="imports.\(name)(\(parameterForwardings.joined(separator:",")))"
716734
if returnType==.void{
717735
bodyLines.append("\(call);")
718736
}else{
@@ -721,7 +739,7 @@ struct BridgeJSLink {
721739
}
722740

723741
func callConstructor(name:String){
724-
letcall="newoptions.imports.\(name)(\(parameterForwardings.joined(separator:",")))"
742+
letcall="new imports.\(name)(\(parameterForwardings.joined(separator:",")))"
725743
bodyLines.append("let ret =\(call);")
726744
}
727745

@@ -801,9 +819,10 @@ struct BridgeJSLink {
801819
returnExpr: returnExpr,
802820
returnType: function.returnType
803821
)
822+
leteffects=Effects(isAsync:false, isThrows:false)
804823
importObjectBuilder.appendDts(
805824
[
806-
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
825+
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: effects));"
807826
]
808827
)
809828
importObjectBuilder.assignToImportObject(name: function.abiName(context:nil), function: funcLines)
@@ -878,7 +897,8 @@ struct BridgeJSLink {
878897
importObjectBuilder.assignToImportObject(name: abiName, function: funcLines)
879898
importObjectBuilder.appendDts([
880899
"\(type.name): {",
881-
"new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType));".indent(count:4),
900+
"new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType, effects:Effects(isAsync:false, isThrows:false)));"
901+
.indent(count:4),
882902
"}",
883903
])
884904
}

‎Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/index.d.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ export type Parameter = {
1212
type:BridgeType;
1313
}
1414

15+
exporttypeEffects={
16+
isAsync:boolean;
17+
}
18+
1519
exporttypeImportFunctionSkeleton={
1620
name:string;
1721
parameters:Parameter[];
1822
returnType:BridgeType;
23+
effects:Effects;
1924
documentation:string|undefined;
2025
}
2126

‎Plugins/BridgeJS/Sources/TS2Skeleton/JavaScript/src/processor.js‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export class TypeProcessor {
162162
parameters,
163163
returnType:bridgeReturnType,
164164
documentation,
165+
effects:{isAsync:false},
165166
};
166167
}
167168

@@ -341,6 +342,10 @@ export class TypeProcessor {
341342
*@private
342343
*/
343344
visitType(type,node){
345+
// Treat A<B> and A<C> as the same type
346+
if(isTypeReference(type)){
347+
type=type.target;
348+
}
344349
constmaybeProcessed=this.processedTypes.get(type);
345350
if(maybeProcessed){
346351
returnmaybeProcessed;
@@ -364,8 +369,13 @@ export class TypeProcessor {
364369
"object":{"jsObject":{}},
365370
"symbol":{"jsObject":{}},
366371
"never":{"void":{}},
372+
"Promise":{
373+
"jsObject":{
374+
"_0":"JSPromise"
375+
}
376+
},
367377
};
368-
consttypeString=this.checker.typeToString(type);
378+
consttypeString=type.getSymbol()?.name??this.checker.typeToString(type);
369379
if(typeMap[typeString]){
370380
returntypeMap[typeString];
371381
}
@@ -377,7 +387,7 @@ export class TypeProcessor {
377387
if(this.checker.isTypeAssignableTo(type,this.checker.getStringType())){
378388
return{"string":{}};
379389
}
380-
if(type.getFlags()&ts.TypeFlags.TypeParameter){
390+
if(type.isTypeParameter()){
381391
return{"jsObject":{}};
382392
}
383393

@@ -412,3 +422,24 @@ export class TypeProcessor {
412422
returnundefined;
413423
}
414424
}
425+
426+
/**
427+
*@param {ts.Type} type
428+
*@returns {type is ts.ObjectType}
429+
*/
430+
functionisObjectType(type){
431+
//@ts-ignore
432+
returntypeoftype.objectFlags==="number";
433+
}
434+
435+
/**
436+
*
437+
*@param {ts.Type} type
438+
*@returns {type is ts.TypeReference}
439+
*/
440+
functionisTypeReference(type){
441+
return(
442+
isObjectType(type)&&
443+
(type.objectFlags&ts.ObjectFlags.Reference)!==0
444+
);
445+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
exportfunctionasyncReturnVoid():Promise<void>;
2+
exportfunctionasyncRoundTripInt(v:number):Promise<number>;
3+
exportfunctionasyncRoundTripString(v:string):Promise<string>;
4+
exportfunctionasyncRoundTripBool(v:boolean):Promise<boolean>;
5+
exportfunctionasyncRoundTripFloat(v:number):Promise<number>;
6+
exportfunctionasyncRoundTripDouble(v:number):Promise<number>;
7+
exportfunctionasyncRoundTripJSObject(v:any):Promise<any>;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@JSfunc asyncReturnVoid()async{}
2+
@JSfunc asyncRoundTripInt(_ v:Int)async->Int{
3+
return v
4+
}
5+
@JSfunc asyncRoundTripString(_ v:String)async->String{
6+
return v
7+
}
8+
@JSfunc asyncRoundTripBool(_ v:Bool)async->Bool{
9+
return v
10+
}
11+
@JSfunc asyncRoundTripFloat(_ v:Float)async->Float{
12+
return v
13+
}
14+
@JSfunc asyncRoundTripDouble(_ v:Double)async->Double{
15+
return v
16+
}
17+
@JSfunc asyncRoundTripJSObject(_ v:JSObject)async->JSObject{
18+
return v
19+
}

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ export async function createInstantiator(options, swift) {
1515
lettmpRetBytes;
1616
lettmpRetException;
1717
return{
18-
/**@param {WebAssembly.Imports} importObject */
19-
addImports:(importObject)=>{
18+
/**
19+
*@param {WebAssembly.Imports} importObject
20+
*/
21+
addImports:(importObject,importsContext)=>{
2022
constbjs={};
2123
importObject["bjs"]=bjs;
24+
constimports=options.getImports(importsContext);
2225
bjs["swift_js_return_string"]=function(ptr,len){
2326
constbytes=newUint8Array(memory.buffer,ptr,len);
2427
tmpRetString=textDecoder.decode(bytes);
@@ -50,21 +53,21 @@ export async function createInstantiator(options, swift) {
5053
constTestModule=importObject["TestModule"]=importObject["TestModule"]||{};
5154
TestModule["bjs_checkArray"]=functionbjs_checkArray(a){
5255
try{
53-
options.imports.checkArray(swift.memory.getObject(a));
56+
imports.checkArray(swift.memory.getObject(a));
5457
}catch(error){
5558
setException(error);
5659
}
5760
}
5861
TestModule["bjs_checkArrayWithLength"]=functionbjs_checkArrayWithLength(a,b){
5962
try{
60-
options.imports.checkArrayWithLength(swift.memory.getObject(a),b);
63+
imports.checkArrayWithLength(swift.memory.getObject(a),b);
6164
}catch(error){
6265
setException(error);
6366
}
6467
}
6568
TestModule["bjs_checkArray"]=functionbjs_checkArray(a){
6669
try{
67-
options.imports.checkArray(swift.memory.getObject(a));
70+
imports.checkArray(swift.memory.getObject(a));
6871
}catch(error){
6972
setException(error);
7073
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp