Skip to content

Commit 48c84e3

Browse files
WIP
1 parent d62db09 commit 48c84e3

File tree

24 files changed

+1248
-77
lines changed

24 files changed

+1248
-77
lines changed

Package.swift

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

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,9 @@ class ExportSwift {
400400
var callExpr: ExprSyntax =
401401
"\(raw: callee)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))"
402402
if effects.isAsync {
403-
callExpr = ExprSyntax(AwaitExprSyntax(awaitKeyword: .keyword(.await), expression: callExpr))
403+
callExpr = ExprSyntax(
404+
AwaitExprSyntax(awaitKeyword: .keyword(.await).with(\.trailingTrivia, .space), expression: callExpr)
405+
)
404406
}
405407
if effects.isThrows {
406408
callExpr = ExprSyntax(
@@ -410,6 +412,12 @@ class ExportSwift {
410412
)
411413
)
412414
}
415+
416+
417+
if effects.isAsync, returnType != .void {
418+
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: callExpr).jsValue")))
419+
}
420+
413421
let retMutability = returnType == .string ? "var" : "let"
414422
if returnType == .void {
415423
return CodeBlockItemSyntax(item: .init(ExpressionStmtSyntax(expression: callExpr)))
@@ -433,7 +441,40 @@ class ExportSwift {
433441
}
434442

435443
func lowerReturnValue(returnType: BridgeType) {
436-
abiReturnType = returnType.abiReturnType
444+
if effects.isAsync {
445+
// Async functions always return a Promise, which is a JSObject
446+
_lowerReturnValue(returnType: .jsObject(nil))
447+
} else {
448+
_lowerReturnValue(returnType: returnType)
449+
}
450+
}
451+
452+
private func _lowerReturnValue(returnType: BridgeType) {
453+
switch returnType {
454+
case .void:
455+
abiReturnType = nil
456+
case .bool:
457+
abiReturnType = .i32
458+
case .int:
459+
abiReturnType = .i32
460+
case .float:
461+
abiReturnType = .f32
462+
case .double:
463+
abiReturnType = .f64
464+
case .string:
465+
abiReturnType = nil
466+
case .jsObject:
467+
abiReturnType = .i32
468+
case .swiftHeapObject:
469+
// UnsafeMutableRawPointer is returned as an i32 pointer
470+
abiReturnType = .pointer
471+
}
472+
473+
if effects.isAsync {
474+
// The return value of async function (T of `(...) async -> T`) is
475+
// handled by the JSPromise.async, so we don't need to do anything here.
476+
return
477+
}
437478

438479
switch returnType {
439480
case .void: break
@@ -474,7 +515,14 @@ class ExportSwift {
474515

475516
func render(abiName: String) -> DeclSyntax {
476517
let body: CodeBlockItemListSyntax
477-
if effects.isThrows {
518+
if effects.isAsync {
519+
body = """
520+
let ret = JSPromise.async {
521+
\(CodeBlockItemListSyntax(self.body))
522+
}.jsObject
523+
return _swift_js_retain(Int32(bitPattern: ret.id))
524+
"""
525+
} else if effects.isThrows {
478526
body = """
479527
do {
480528
\(CodeBlockItemListSyntax(self.body))

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ struct BridgeJSLink {
126126
let tmpRetBytes;
127127
let tmpRetException;
128128
return {
129-
/** @param {WebAssembly.Imports} importObject */
130-
addImports: (importObject) => {
129+
/**
130+
* @param {WebAssembly.Imports} importObject
131+
*/
132+
addImports: (importObject, importsContext) => {
131133
const bjs = {};
132134
importObject["bjs"] = bjs;
135+
const imports = options.getImports(importsContext);
133136
bjs["swift_js_return_string"] = function(ptr, len) {
134137
const bytes = new Uint8Array(memory.buffer, ptr, len)\(sharedMemory ? ".slice()" : "");
135138
tmpRetString = textDecoder.decode(bytes);
@@ -239,6 +242,14 @@ struct BridgeJSLink {
239242
}
240243

241244
func call(abiName: String, returnType: BridgeType) -> String? {
245+
if effects.isAsync {
246+
return _call(abiName: abiName, returnType: .jsObject(nil))
247+
} else {
248+
return _call(abiName: abiName, returnType: returnType)
249+
}
250+
}
251+
252+
private func _call(abiName: String, returnType: BridgeType) -> String? {
242253
let call = "instance.exports.\(abiName)(\(parameterForwardings.joined(separator: ", ")))"
243254
var returnExpr: String?
244255

@@ -312,8 +323,14 @@ struct BridgeJSLink {
312323
}
313324
}
314325

315-
private func renderTSSignature(parameters: [Parameter], returnType: BridgeType) -> String {
316-
return "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnType.tsType)"
326+
private func renderTSSignature(parameters: [Parameter], returnType: BridgeType, effects: Effects) -> String {
327+
let returnTypeWithEffect: String
328+
if effects.isAsync {
329+
returnTypeWithEffect = "Promise<\(returnType.tsType)>"
330+
} else {
331+
returnTypeWithEffect = returnType.tsType
332+
}
333+
return "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnTypeWithEffect)"
317334
}
318335

319336
func renderExportedFunction(function: ExportedFunction) -> (js: [String], dts: [String]) {
@@ -331,7 +348,7 @@ struct BridgeJSLink {
331348
)
332349
var dtsLines: [String] = []
333350
dtsLines.append(
334-
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
351+
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));"
335352
)
336353

337354
return (funcLines, dtsLines)
@@ -362,7 +379,7 @@ struct BridgeJSLink {
362379
jsLines.append(contentsOf: funcLines.map { $0.indent(count: 4) })
363380

364381
dtsExportEntryLines.append(
365-
"new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
382+
"new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name), effects: constructor.effects));"
366383
.indent(count: 4)
367384
)
368385
}
@@ -384,7 +401,7 @@ struct BridgeJSLink {
384401
).map { $0.indent(count: 4) }
385402
)
386403
dtsTypeLines.append(
387-
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
404+
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));"
388405
.indent(count: 4)
389406
)
390407
}
@@ -446,7 +463,7 @@ struct BridgeJSLink {
446463
}
447464

448465
func call(name: String, returnType: BridgeType) {
449-
let call = "options.imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
466+
let call = "imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
450467
if returnType == .void {
451468
bodyLines.append("\(call);")
452469
} else {
@@ -455,7 +472,7 @@ struct BridgeJSLink {
455472
}
456473

457474
func callConstructor(name: String) {
458-
let call = "new options.imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
475+
let call = "new imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
459476
bodyLines.append("let ret = \(call);")
460477
}
461478

@@ -533,9 +550,10 @@ struct BridgeJSLink {
533550
returnExpr: returnExpr,
534551
returnType: function.returnType
535552
)
553+
let effects = Effects(isAsync: false, isThrows: false)
536554
importObjectBuilder.appendDts(
537555
[
538-
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
556+
"\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: effects));"
539557
]
540558
)
541559
importObjectBuilder.assignToImportObject(name: function.abiName(context: nil), function: funcLines)
@@ -610,7 +628,8 @@ struct BridgeJSLink {
610628
importObjectBuilder.assignToImportObject(name: abiName, function: funcLines)
611629
importObjectBuilder.appendDts([
612630
"\(type.name): {",
613-
"new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType));".indent(count: 4),
631+
"new\(renderTSSignature(parameters: constructor.parameters, returnType: returnType, effects: Effects(isAsync: false, isThrows: false)));"
632+
.indent(count: 4),
614633
"}",
615634
])
616635
}

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+
export type Effects = {
16+
isAsync: boolean;
17+
}
18+
1519
export type ImportFunctionSkeleton = {
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
const maybeProcessed = this.processedTypes.get(type);
345350
if (maybeProcessed) {
346351
return maybeProcessed;
@@ -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-
const typeString = this.checker.typeToString(type);
378+
const typeString = type.getSymbol()?.name ?? this.checker.typeToString(type);
369379
if (typeMap[typeString]) {
370380
return typeMap[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
return undefined;
413423
}
414424
}
425+
426+
/**
427+
* @param {ts.Type} type
428+
* @returns {type is ts.ObjectType}
429+
*/
430+
function isObjectType(type) {
431+
// @ts-ignore
432+
return typeof type.objectFlags === "number";
433+
}
434+
435+
/**
436+
*
437+
* @param {ts.Type} type
438+
* @returns {type is ts.TypeReference}
439+
*/
440+
function isTypeReference(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+
export function asyncReturnVoid(): Promise<void>;
2+
export function asyncRoundTripInt(v: number): Promise<number>;
3+
export function asyncRoundTripString(v: string): Promise<string>;
4+
export function asyncRoundTripBool(v: boolean): Promise<boolean>;
5+
export function asyncRoundTripFloat(v: number): Promise<number>;
6+
export function asyncRoundTripDouble(v: number): Promise<number>;
7+
export function asyncRoundTripJSObject(v: any): Promise<any>;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@JS func asyncReturnVoid() async {}
2+
@JS func asyncRoundTripInt(_ v: Int) async -> Int {
3+
return v
4+
}
5+
@JS func asyncRoundTripString(_ v: String) async -> String {
6+
return v
7+
}
8+
@JS func asyncRoundTripBool(_ v: Bool) async -> Bool {
9+
return v
10+
}
11+
@JS func asyncRoundTripFloat(_ v: Float) async -> Float {
12+
return v
13+
}
14+
@JS func asyncRoundTripDouble(_ v: Double) async -> Double {
15+
return v
16+
}
17+
@JS func asyncRoundTripJSObject(_ v: JSObject) async -> JSObject {
18+
return v
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
2+
// DO NOT EDIT.
3+
//
4+
// To update this file, just rebuild your project or run
5+
// `swift package bridge-js`.
6+
7+
export type Exports = {
8+
asyncReturnVoid(): Promise<void>;
9+
asyncRoundTripInt(v: number): Promise<number>;
10+
asyncRoundTripString(v: string): Promise<string>;
11+
asyncRoundTripBool(v: boolean): Promise<boolean>;
12+
asyncRoundTripFloat(v: number): Promise<number>;
13+
asyncRoundTripDouble(v: number): Promise<number>;
14+
asyncRoundTripJSObject(v: any): Promise<any>;
15+
}
16+
export type Imports = {
17+
}
18+
export function createInstantiator(options: {
19+
imports: Imports;
20+
}, swift: any): Promise<{
21+
addImports: (importObject: WebAssembly.Imports) => void;
22+
setInstance: (instance: WebAssembly.Instance) => void;
23+
createExports: (instance: WebAssembly.Instance) => Exports;
24+
}>;

0 commit comments

Comments
 (0)