Skip to content

Commit 43a05d9

Browse files
committed
BridgeJS: Few more array import tests
BridgeJS: Array import
1 parent 26dfded commit 43a05d9

File tree

26 files changed

+2011
-125
lines changed

26 files changed

+2011
-125
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ public struct ImportTS {
8383
var abiReturnType: WasmCoreType?
8484
// Track destructured variable names for multiple lowered parameters
8585
var destructuredVarNames: [String] = []
86+
// Stack-lowered parameters should be evaluated in reverse order to match LIFO stacks
87+
var stackLoweringStmts: [CodeBlockItemSyntax] = []
8688

8789
init(moduleName: String, abiName: String, context: BridgeContext = .importTS) {
8890
self.moduleName = moduleName
@@ -93,11 +95,6 @@ public struct ImportTS {
9395
func lowerParameter(param: Parameter) throws {
9496
let loweringInfo = try param.type.loweringParameterInfo(context: context)
9597

96-
// Generate destructured variable names for all lowered parameters
97-
let destructuredNames = loweringInfo.loweredParameters.map {
98-
"\(param.name)\($0.name.capitalizedFirstLetter)"
99-
}
100-
10198
let initializerExpr: ExprSyntax
10299
switch param.type {
103100
case .closure(let signature):
@@ -108,6 +105,33 @@ public struct ImportTS {
108105
initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()")
109106
}
110107

108+
if loweringInfo.loweredParameters.isEmpty {
109+
let stmt = CodeBlockItemSyntax(
110+
item: .decl(
111+
DeclSyntax(
112+
VariableDeclSyntax(
113+
bindingSpecifier: .keyword(.let),
114+
bindings: PatternBindingListSyntax {
115+
PatternBindingSyntax(
116+
pattern: PatternSyntax(
117+
IdentifierPatternSyntax(identifier: .wildcardToken())
118+
),
119+
initializer: InitializerClauseSyntax(value: initializerExpr)
120+
)
121+
}
122+
)
123+
)
124+
)
125+
)
126+
stackLoweringStmts.insert(stmt, at: 0)
127+
return
128+
}
129+
130+
// Generate destructured variable names for all lowered parameters
131+
let destructuredNames = loweringInfo.loweredParameters.map {
132+
"\(param.name)\($0.name.capitalizedFirstLetter)"
133+
}
134+
111135
// Always add destructuring statement to body (unified for single and multiple)
112136
let pattern: PatternSyntax
113137
if destructuredNames.count == 1 {
@@ -166,7 +190,9 @@ public struct ImportTS {
166190
}
167191

168192
func call(returnType: BridgeType) throws {
169-
// Build function call expression
193+
let liftingInfo = try returnType.liftingReturnInfo(context: context)
194+
body.append(contentsOf: stackLoweringStmts)
195+
170196
let callExpr = FunctionCallExprSyntax(
171197
calledExpression: ExprSyntax("\(raw: abiName)"),
172198
leftParen: .leftParenToken(),
@@ -178,19 +204,15 @@ public struct ImportTS {
178204
rightParen: .rightParenToken()
179205
)
180206

181-
let needsRetBinding: Bool
182-
let liftingInfo = try returnType.liftingReturnInfo(context: context)
183-
if liftingInfo.valueToLift == nil || returnType.usesSideChannelForOptionalReturn() {
184-
// Void and side-channel returns don't need "let ret ="
185-
needsRetBinding = false
207+
if returnType == .void {
208+
body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr)))))
209+
} else if returnType.usesSideChannelForOptionalReturn() {
210+
// Side channel returns don't need "let ret ="
211+
body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr)))))
212+
} else if liftingInfo.valueToLift == nil {
213+
body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr)))))
186214
} else {
187-
needsRetBinding = true
188-
}
189-
190-
if needsRetBinding {
191215
body.append("let ret = \(raw: callExpr)")
192-
} else {
193-
body.append(CodeBlockItemSyntax(item: .stmt(StmtSyntax(ExpressionStmtSyntax(expression: callExpr)))))
194216
}
195217

196218
// Add exception check for ImportTS context
@@ -934,13 +956,8 @@ extension BridgeType {
934956
return LoweringParameterInfo(loweredParameters: [("value", .i32)])
935957
}
936958
case .rawValueEnum(_, let rawType):
937-
switch context {
938-
case .importTS:
939-
return LoweringParameterInfo(loweredParameters: [("value", rawType.wasmCoreType ?? .i32)])
940-
case .exportSwift:
941-
// For protocol export we return .i32 for String raw value type instead of nil
942-
return LoweringParameterInfo(loweredParameters: [("value", rawType.wasmCoreType ?? .i32)])
943-
}
959+
let wasmType = rawType.wasmCoreType ?? .i32
960+
return LoweringParameterInfo(loweredParameters: [("value", wasmType)])
944961
case .associatedValueEnum:
945962
switch context {
946963
case .importTS:
@@ -964,12 +981,7 @@ extension BridgeType {
964981
params.append(contentsOf: wrappedInfo.loweredParameters)
965982
return LoweringParameterInfo(loweredParameters: params)
966983
case .array:
967-
switch context {
968-
case .importTS:
969-
throw BridgeJSCoreError("Array types are not yet supported in TypeScript imports")
970-
case .exportSwift:
971-
return LoweringParameterInfo(loweredParameters: [])
972-
}
984+
return LoweringParameterInfo(loweredParameters: [])
973985
}
974986
}
975987

@@ -1025,13 +1037,8 @@ extension BridgeType {
10251037
return LiftingReturnInfo(valueToLift: .i32)
10261038
}
10271039
case .rawValueEnum(_, let rawType):
1028-
switch context {
1029-
case .importTS:
1030-
return LiftingReturnInfo(valueToLift: rawType.wasmCoreType ?? .i32)
1031-
case .exportSwift:
1032-
// For protocol export we return .i32 for String raw value type instead of nil
1033-
return LiftingReturnInfo(valueToLift: rawType.wasmCoreType ?? .i32)
1034-
}
1040+
let wasmType = rawType.wasmCoreType ?? .i32
1041+
return LiftingReturnInfo(valueToLift: wasmType)
10351042
case .associatedValueEnum:
10361043
switch context {
10371044
case .importTS:
@@ -1053,12 +1060,7 @@ extension BridgeType {
10531060
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
10541061
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10551062
case .array:
1056-
switch context {
1057-
case .importTS:
1058-
throw BridgeJSCoreError("Array types are not yet supported in TypeScript imports")
1059-
case .exportSwift:
1060-
return LiftingReturnInfo(valueToLift: nil)
1061-
}
1063+
return LiftingReturnInfo(valueToLift: nil)
10621064
}
10631065
}
10641066
}

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,12 +2212,10 @@ extension BridgeJSLink {
22122212

22132213
func liftParameter(param: Parameter) throws {
22142214
let liftingFragment = try IntrinsicJSFragment.liftParameter(type: param.type, context: context)
2215-
assert(
2216-
liftingFragment.parameters.count >= 1,
2217-
"Lifting fragment should have at least one parameter to lift"
2218-
)
22192215
let valuesToLift: [String]
2220-
if liftingFragment.parameters.count == 1 {
2216+
if liftingFragment.parameters.count == 0 {
2217+
valuesToLift = []
2218+
} else if liftingFragment.parameters.count == 1 {
22212219
parameterNames.append(param.name)
22222220
valuesToLift = [scope.variable(param.name)]
22232221
} else {

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,14 +1895,7 @@ struct IntrinsicJSFragment: Sendable {
18951895
"Namespace enums are not supported to be passed as parameters to imported JS functions: \(string)"
18961896
)
18971897
case .array(let elementType):
1898-
switch context {
1899-
case .importTS:
1900-
throw BridgeJSLinkError(
1901-
message: "Arrays are not yet supported to be passed as parameters to imported JS functions"
1902-
)
1903-
case .exportSwift:
1904-
return try arrayLift(elementType: elementType)
1905-
}
1898+
return try arrayLift(elementType: elementType)
19061899
}
19071900
}
19081901

@@ -1971,14 +1964,7 @@ struct IntrinsicJSFragment: Sendable {
19711964
message: "Namespace enums are not supported to be returned from imported JS functions: \(string)"
19721965
)
19731966
case .array(let elementType):
1974-
switch context {
1975-
case .importTS:
1976-
throw BridgeJSLinkError(
1977-
message: "Arrays are not yet supported to be returned from imported JS functions"
1978-
)
1979-
case .exportSwift:
1980-
return try arrayLower(elementType: elementType)
1981-
}
1967+
return try arrayLower(elementType: elementType)
19821968
}
19831969
}
19841970

@@ -3215,11 +3201,10 @@ struct IntrinsicJSFragment: Sendable {
32153201
// Lift return value if needed
32163202
if method.returnType != .void {
32173203
let liftFragment = try! IntrinsicJSFragment.liftReturn(type: method.returnType)
3218-
if !liftFragment.parameters.isEmpty {
3219-
let lifted = liftFragment.printCode(["ret"], methodScope, printer, methodCleanup)
3220-
if let liftedValue = lifted.first {
3221-
printer.write("return \(liftedValue);")
3222-
}
3204+
let liftArgs = liftFragment.parameters.isEmpty ? [] : ["ret"]
3205+
let lifted = liftFragment.printCode(liftArgs, methodScope, printer, methodCleanup)
3206+
if let liftedValue = lifted.first {
3207+
printer.write("return \(liftedValue);")
32233208
}
32243209
}
32253210
}
@@ -3508,9 +3493,36 @@ struct IntrinsicJSFragment: Sendable {
35083493
),
35093494
allStructs: allStructs
35103495
)
3496+
let guardedPrinter = CodeFragmentPrinter()
3497+
let guardedCleanup = CodeFragmentPrinter()
3498+
_ = wrappedFragment.printCode([value], scope, guardedPrinter, guardedCleanup)
3499+
var loweredLines = guardedPrinter.lines
3500+
var hoistedCleanupVar: String?
3501+
if let first = loweredLines.first {
3502+
let trimmed = first.trimmingCharacters(in: .whitespaces)
3503+
if trimmed.hasPrefix("const "),
3504+
let namePart = trimmed.split(separator: " ").dropFirst().first,
3505+
trimmed.contains("= []")
3506+
{
3507+
hoistedCleanupVar = String(namePart)
3508+
loweredLines[0] = "\(hoistedCleanupVar!) = [];"
3509+
}
3510+
}
3511+
if let hoistedName = hoistedCleanupVar {
3512+
printer.write("let \(hoistedName);")
3513+
}
35113514
printer.write("if (\(isSomeVar)) {")
35123515
printer.indent {
3513-
_ = wrappedFragment.printCode([value], scope, printer, cleanup)
3516+
for line in loweredLines {
3517+
printer.write(line)
3518+
}
3519+
if !guardedCleanup.lines.isEmpty {
3520+
cleanup.write("if (\(isSomeVar)) {")
3521+
cleanup.indent {
3522+
cleanup.write(contentsOf: guardedCleanup)
3523+
}
3524+
cleanup.write("}")
3525+
}
35143526
}
35153527
printer.write("}")
35163528
printer.write("\(JSGlueVariableScope.reservedTmpParamInts).push(\(isSomeVar) ? 1 : 0);")

Plugins/BridgeJS/Sources/TS2Swift/JavaScript/src/processor.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,15 @@ export class TypeProcessor {
663663
* @private
664664
*/
665665
visitType(type, node) {
666+
if (this.checker.isArrayType(type)) {
667+
const typeArgs = this.checker.getTypeArguments(type);
668+
if (typeArgs && typeArgs.length > 0) {
669+
const elementType = this.visitType(typeArgs[0], node);
670+
return `[${elementType}]`;
671+
}
672+
return "[JSObject]";
673+
}
674+
666675
// Treat A<B> and A<C> as the same type
667676
if (isTypeReference(type)) {
668677
type = type.target;
@@ -727,7 +736,7 @@ export class TypeProcessor {
727736
return this.renderTypeIdentifier(typeName);
728737
}
729738

730-
if (this.checker.isArrayType(type) || this.checker.isTupleType(type) || type.getCallSignatures().length > 0) {
739+
if (this.checker.isTupleType(type) || type.getCallSignatures().length > 0) {
731740
return "JSObject";
732741
}
733742
// "a" | "b" -> string

Plugins/BridgeJS/Sources/TS2Swift/JavaScript/test/__snapshots__/ts2swift.test.js.snap

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ exports[`ts2swift > snapshots Swift output for ArrayParameter.d.ts > ArrayParame
99
1010
@_spi(Experimental) @_spi(BridgeJS) import JavaScriptKit
1111
12-
@JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void
12+
@JSFunction func processNumbers(_ values: [Double]) throws(JSException) -> Void
1313
14-
@JSFunction func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void
14+
@JSFunction func getNumbers() throws(JSException) -> [Double]
1515
16-
@JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void
16+
@JSFunction func transformNumbers(_ values: [Double]) throws(JSException) -> [Double]
17+
18+
@JSFunction func processStrings(_ values: [String]) throws(JSException) -> [String]
19+
20+
@JSFunction func processBooleans(_ values: [Bool]) throws(JSException) -> [Bool]
21+
22+
@JSFunction func processArraySyntax(_ values: [Double]) throws(JSException) -> [Double]
1723
"
1824
`;
1925

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1-
export function checkArray(a: number[]): void;
2-
export function checkArrayWithLength(a: number[], b: number): void;
3-
export function checkArray(a: Array<number>): void;
1+
// Array as parameter
2+
export function processNumbers(values: number[]): void;
3+
4+
// Array as return value
5+
export function getNumbers(): number[];
6+
7+
// Array as both parameter and return value
8+
export function transformNumbers(values: number[]): number[];
9+
10+
// String arrays
11+
export function processStrings(values: string[]): string[];
12+
13+
// Boolean arrays
14+
export function processBooleans(values: boolean[]): boolean[];
15+
16+
// Using Array<T> syntax
17+
export function processArraySyntax(values: Array<number>): Array<number>;

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/ArrayTypes.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@
6161

6262
@JSFunction func checkArray(_ a: JSObject) throws(JSException) -> Void
6363
@JSFunction func checkArrayWithLength(_ a: JSObject, _ b: Double) throws(JSException) -> Void
64+
65+
@JSFunction func importProcessNumbers(_ values: [Double]) throws(JSException) -> Void
66+
@JSFunction func importGetNumbers() throws(JSException) -> [Double]
67+
@JSFunction func importTransformNumbers(_ values: [Double]) throws(JSException) -> [Double]
68+
@JSFunction func importProcessStrings(_ values: [String]) throws(JSException) -> [String]
69+
@JSFunction func importProcessBooleans(_ values: [Bool]) throws(JSException) -> [Bool]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@JSFunction func roundtrip(_ items: [Int]) throws(JSException) -> [Int]
2+
@JSFunction func logStrings(_ items: [String]) throws(JSException)

0 commit comments

Comments
 (0)