Skip to content

Commit 09c44ac

Browse files
committed
BridgeJS: Export types using a separate JS representation
1 parent 403ae95 commit 09c44ac

80 files changed

Lines changed: 14066 additions & 6618 deletions

File tree

Some content is hidden

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

Benchmarks/Sources/Generated/JavaScript/BridgeJS.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"exported" : {
3+
"aliases" : [
4+
5+
],
36
"classes" : [
47
{
58
"constructor" : {

Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"exported" : {
3+
"aliases" : [
4+
5+
],
36
"classes" : [
47
{
58
"constructor" : {

Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ public struct ClosureCodegen {
143143
let argNames = liftInfo.parameters.map { (argName, _) in
144144
liftInfo.parameters.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName
145145
}
146-
liftedParams.append("\(paramType.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))")
146+
let liftCall = "\(paramType.unaliased.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))"
147+
liftedParams.append(paramType.liftAliases(expression: liftCall))
147148
}
148149

149150
let closureCallExpr = ExprSyntax("closure(\(raw: liftedParams.joined(separator: ", ")))")
@@ -184,7 +185,8 @@ public struct ClosureCodegen {
184185
}
185186
printer.write("}")
186187
default:
187-
printer.write("return result.bridgeJSLowerReturn()")
188+
let lowered = signature.returnType.lowerAliases(expression: "result")
189+
printer.write("return \(lowered).bridgeJSLowerReturn()")
188190
}
189191
}
190192
}

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,15 @@ public class ExportSwift {
147147
} else {
148148
optionalSwiftType = "JSUndefinedOr"
149149
}
150-
typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.swiftType)>"
151-
liftingExpr = ExprSyntax(
152-
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
153-
)
150+
typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.unaliased.swiftType)>"
151+
let liftCall =
152+
"\(typeNameForIntrinsic).bridgeJSLiftParameter(\(argumentsToLift.joined(separator: ", ")))"
153+
liftingExpr = "\(raw: param.type.liftAliases(expression: liftCall))"
154154
default:
155-
typeNameForIntrinsic = param.type.swiftType
156-
liftingExpr = ExprSyntax(
157-
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
158-
)
155+
typeNameForIntrinsic = param.type.unaliased.swiftType
156+
let liftCall =
157+
"\(typeNameForIntrinsic).bridgeJSLiftParameter(\(argumentsToLift.joined(separator: ", ")))"
158+
liftingExpr = "\(raw: param.type.liftAliases(expression: liftCall))"
159159
}
160160

161161
liftedParameterExprs.append(liftingExpr)
@@ -200,7 +200,8 @@ public class ExportSwift {
200200
}
201201

202202
if effects.isAsync, returnType != .void {
203-
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: callExpr).jsValue")))
203+
let lowered = returnType.lowerAliases(expression: callExpr.description)
204+
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: lowered).jsValue")))
204205
}
205206

206207
if returnType == .void {
@@ -298,17 +299,18 @@ public class ExportSwift {
298299
return
299300
}
300301

302+
let returnAccessor = returnType.lowerAliases(expression: "ret")
301303
switch returnType {
302304
case .closure(_, useJSTypedClosure: false):
303305
append("return JSTypedClosure(ret).bridgeJSLowerReturn()")
304306
case .array, .nullable(.array, _):
305307
let stackCodegen = StackCodegen()
306-
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: "ret", varPrefix: "ret") {
308+
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: returnAccessor, varPrefix: "ret") {
307309
append(stmt)
308310
}
309311
case .dictionary(.swiftProtocol):
310312
let stackCodegen = StackCodegen()
311-
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: "ret", varPrefix: "ret") {
313+
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: returnAccessor, varPrefix: "ret") {
312314
append(stmt)
313315
}
314316
case .swiftProtocol:
@@ -324,7 +326,7 @@ public class ExportSwift {
324326
"""
325327
)
326328
default:
327-
append("return ret.bridgeJSLowerReturn()")
329+
append("return \(raw: returnAccessor).bridgeJSLowerReturn()")
328330
}
329331
}
330332

@@ -754,8 +756,8 @@ struct StackCodegen {
754756
switch type {
755757
case .string, .integer, .bool, .float, .double,
756758
.jsObject(nil), .jsValue, .swiftStruct, .swiftHeapObject, .unsafePointer,
757-
.swiftProtocol, .caseEnum, .associatedValueEnum, .rawValueEnum, .array, .dictionary:
758-
return "\(raw: type.swiftType).bridgeJSStackPop()"
759+
.swiftProtocol, .caseEnum, .associatedValueEnum, .rawValueEnum, .array, .dictionary, .alias:
760+
return "\(raw: type.liftAliases(expression: "\(type.unaliased.swiftType).bridgeJSStackPop()"))"
759761
case .jsObject(let className?):
760762
return "\(raw: className)(unsafelyWrapping: JSObject.bridgeJSStackPop())"
761763
case .nullable(let wrappedType, let kind):
@@ -772,8 +774,10 @@ struct StackCodegen {
772774
switch wrappedType {
773775
case .string, .integer, .bool, .float, .double, .jsObject(nil), .jsValue,
774776
.swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum,
775-
.array, .dictionary:
776-
return "\(raw: typeName)<\(raw: wrappedType.swiftType)>.bridgeJSStackPop()"
777+
.array, .dictionary, .alias:
778+
let popCall = "\(typeName)<\(wrappedType.unaliased.swiftType)>.bridgeJSStackPop()"
779+
let nullableType = BridgeType.nullable(wrappedType, kind)
780+
return "\(raw: nullableType.liftAliases(expression: popCall))"
777781
case .jsObject(let className?):
778782
return "\(raw: typeName)<JSObject>.bridgeJSStackPop().map { \(raw: className)(unsafelyWrapping: $0) }"
779783
case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol:
@@ -795,7 +799,7 @@ struct StackCodegen {
795799
switch type {
796800
case .string, .integer, .bool, .float, .double, .jsValue,
797801
.jsObject(nil), .swiftHeapObject, .unsafePointer, .closure,
798-
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct, .nullable:
802+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct, .nullable, .alias:
799803
return ["\(raw: accessor).bridgeJSStackPush()"]
800804
case .jsObject(_?):
801805
return ["\(raw: accessor).jsObject.bridgeJSStackPush()"]
@@ -1081,9 +1085,10 @@ struct EnumCodegen {
10811085
) {
10821086
for (index, associatedValue) in associatedValues.enumerated() {
10831087
let paramName = associatedValue.label ?? "param\(index)"
1088+
let accessor = associatedValue.type.lowerAliases(expression: paramName)
10841089
let statements = stackCodegen.lowerStatements(
10851090
for: associatedValue.type,
1086-
accessor: paramName,
1091+
accessor: accessor,
10871092
varPrefix: paramName
10881093
)
10891094
for statement in statements {
@@ -1216,7 +1221,7 @@ struct StructCodegen {
12161221
let instanceProps = structDef.properties.filter { !$0.isStatic }
12171222

12181223
for property in instanceProps {
1219-
let accessor = "self.\(property.name)"
1224+
let accessor = property.type.lowerAliases(expression: "self.\(property.name)")
12201225
let statements = stackCodegen.lowerStatements(
12211226
for: property.type,
12221227
accessor: accessor,
@@ -1442,6 +1447,64 @@ extension UnsafePointerType {
14421447
}
14431448

14441449
extension BridgeType {
1450+
var unaliased: BridgeType {
1451+
switch self {
1452+
case .alias(_, let underlying): return underlying.unaliased
1453+
case .nullable(let wrapped, let kind): return .nullable(wrapped.unaliased, kind)
1454+
case .array(let element): return .array(element.unaliased)
1455+
case .dictionary(let value): return .dictionary(value.unaliased)
1456+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1457+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1458+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1459+
.namespaceEnum, .closure:
1460+
return self
1461+
}
1462+
}
1463+
1464+
/// If this type contains an alias, convert the expression with a type of the alias to the underlying type.
1465+
func liftAliases(expression: String) -> String {
1466+
switch self {
1467+
case .alias(let name, _):
1468+
return "\(name).bridgeFromJS(\(expression))"
1469+
case .nullable(let wrapped, _):
1470+
let lifted = wrapped.liftAliases(expression: "$0")
1471+
return lifted == "$0" ? expression : "\(expression).map { \(lifted) }"
1472+
case .array(let element):
1473+
let lifted = element.liftAliases(expression: "$0")
1474+
return lifted == "$0" ? expression : "\(expression).map { \(lifted) }"
1475+
case .dictionary(let value):
1476+
let lifted = value.liftAliases(expression: "$0")
1477+
return lifted == "$0" ? expression : "\(expression).mapValues { \(lifted) }"
1478+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1479+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1480+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1481+
.namespaceEnum, .closure:
1482+
return expression
1483+
}
1484+
}
1485+
1486+
/// Opposite of `liftAliases`: if this type contains an alias, convert the expression with a type of the underlying to the alias type.
1487+
func lowerAliases(expression: String) -> String {
1488+
switch self {
1489+
case .alias:
1490+
return "\(expression).bridgeToJS()"
1491+
case .nullable(let wrapped, _):
1492+
let lowered = wrapped.lowerAliases(expression: "$0")
1493+
return lowered == "$0" ? expression : "\(expression).map { \(lowered) }"
1494+
case .array(let element):
1495+
let lowered = element.lowerAliases(expression: "$0")
1496+
return lowered == "$0" ? expression : "\(expression).map { \(lowered) }"
1497+
case .dictionary(let value):
1498+
let lowered = value.lowerAliases(expression: "$0")
1499+
return lowered == "$0" ? expression : "\(expression).mapValues { \(lowered) }"
1500+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1501+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1502+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1503+
.namespaceEnum, .closure:
1504+
return expression
1505+
}
1506+
}
1507+
14451508
var swiftType: String {
14461509
switch self {
14471510
case .bool: return "Bool"
@@ -1470,6 +1533,7 @@ extension BridgeType {
14701533
let effectsStr = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "")
14711534
let closureType = "(\(paramTypes))\(effectsStr) -> \(signature.returnType.swiftType)"
14721535
return useJSTypedClosure ? "JSTypedClosure<\(closureType)>" : closureType
1536+
case .alias(let name, _): return name
14731537
}
14741538
}
14751539

@@ -1494,6 +1558,8 @@ extension BridgeType {
14941558
return true
14951559
case .nullable(let wrapped, _):
14961560
return wrapped.isStackUsingParameter
1561+
case .alias(_, let underlying):
1562+
return underlying.isStackUsingParameter
14971563
default:
14981564
return false
14991565
}
@@ -1552,6 +1618,8 @@ extension BridgeType {
15521618
return LiftingIntrinsicInfo(parameters: [("callbackId", .i32)])
15531619
case .array, .dictionary:
15541620
return LiftingIntrinsicInfo(parameters: [])
1621+
case .alias(_, let underlying):
1622+
return try underlying.liftParameterInfo()
15551623
}
15561624
}
15571625

@@ -1603,6 +1671,8 @@ extension BridgeType {
16031671
return .jsObject
16041672
case .array, .dictionary:
16051673
return .array
1674+
case .alias(_, let underlying):
1675+
return try underlying.loweringReturnInfo()
16061676
}
16071677
}
16081678
}

Plugins/BridgeJS/Sources/BridgeJSCore/ExternalModuleIndex.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public struct ExternalModuleIndex {
6363
for proto in exported.protocols {
6464
register(dotPath: proto.name, bridgeType: .swiftProtocol(proto.name))
6565
}
66+
for alias in exported.aliases {
67+
register(
68+
dotPath: alias.swiftCallName,
69+
bridgeType: .alias(name: alias.swiftCallName, underlying: alias.underlying)
70+
)
71+
}
6672

6773
entriesByModule[moduleName] = moduleEntries
6874
}

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ public struct ImportTS {
172172
if loweringInfo.useBorrowing {
173173
let returnVariableName = "ret\(borrowedArguments.count)"
174174
let assign = needsReturnVariable ? "let \(returnVariableName) = " : ""
175-
body.write("\(assign)\(param.name).bridgeJSWithLoweredParameter { \(pattern) in")
175+
let loweredAlias = param.type.lowerAliases(expression: param.name)
176+
body.write("\(assign)\(loweredAlias).bridgeJSWithLoweredParameter { \(pattern) in")
176177
body.indent()
177178
borrowedArguments.append(
178179
BorrowedArgument(
@@ -203,7 +204,8 @@ public struct ImportTS {
203204
"(\(raw: param.name) as! _BridgedSwiftProtocolExportable).bridgeJSLowerAsProtocolReturn()"
204205
)
205206
} else {
206-
initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()")
207+
let loweredAlias = param.type.lowerAliases(expression: param.name)
208+
initializerExpr = ExprSyntax("\(raw: loweredAlias).bridgeJSLowerParameter()")
207209
}
208210

209211
if loweringInfo.loweredParameters.isEmpty {
@@ -296,18 +298,21 @@ public struct ImportTS {
296298

297299
if returnType.usesSideChannelForOptionalReturn() {
298300
// Side channel returns: extern function returns Void, value is retrieved via side channel
299-
body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()")
301+
let liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturnFromSideChannel()"
302+
body.write("return \(returnType.liftAliases(expression: liftCall))")
300303
} else {
301304
let liftExpr: String
302305
switch returnType {
303306
case .closure(let signature, _):
304307
liftExpr = "_BJS_Closure_\(signature.mangleName).bridgeJSLift(ret)"
305308
default:
309+
let liftCall: String
306310
if liftingInfo.valueToLift != nil {
307-
liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn(ret)"
311+
liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturn(ret)"
308312
} else {
309-
liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn()"
313+
liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturn()"
310314
}
315+
liftExpr = returnType.liftAliases(expression: liftCall)
311316
}
312317
body.write("return \(liftExpr)")
313318
}
@@ -964,6 +969,8 @@ extension BridgeType {
964969
return LoweringParameterInfo(loweredParameters: params, useBorrowing: wrappedInfo.useBorrowing)
965970
case .array, .dictionary:
966971
return LoweringParameterInfo(loweredParameters: [])
972+
case .alias(_, let underlying):
973+
return try underlying.loweringParameterInfo(context: context)
967974
}
968975
}
969976

@@ -1035,13 +1042,15 @@ extension BridgeType {
10351042
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
10361043
case .nullable(let wrappedType, _):
10371044
// jsObject uses stack ABI for optionals — returns void, value goes through stacks
1038-
if case .jsObject = wrappedType {
1045+
if case .jsObject = wrappedType.unaliased {
10391046
return LiftingReturnInfo(valueToLift: nil)
10401047
}
10411048
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
10421049
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10431050
case .array, .dictionary:
10441051
return LiftingReturnInfo(valueToLift: nil)
1052+
case .alias(_, let underlying):
1053+
return try underlying.liftingReturnInfo(context: context)
10451054
}
10461055
}
10471056
}

0 commit comments

Comments
 (0)