Skip to content

Commit 598f09b

Browse files
committed
BridgeJS: Export types using a separate JS representation
1 parent 73a125d commit 598f09b

80 files changed

Lines changed: 8274 additions & 4429 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
@@ -153,7 +153,8 @@ public struct ClosureCodegen {
153153
let argNames = liftInfo.parameters.map { (argName, _) in
154154
liftInfo.parameters.count > 1 ? "\(paramName)\(argName.capitalizedFirstLetter)" : paramName
155155
}
156-
liftedParams.append("\(paramType.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))")
156+
let liftCall = "\(paramType.unaliased.swiftType).bridgeJSLiftParameter(\(argNames.joined(separator: ", ")))"
157+
liftedParams.append(paramType.liftAliases(expression: liftCall))
157158
}
158159

159160
let tryPrefix = signature.isThrows ? "try " : ""
@@ -197,7 +198,8 @@ public struct ClosureCodegen {
197198
}
198199
printer.write("}")
199200
default:
200-
printer.write("return result.bridgeJSLowerReturn()")
201+
let lowered = signature.returnType.lowerAliases(expression: "result")
202+
printer.write("return \(lowered).bridgeJSLowerReturn()")
201203
}
202204
}
203205
}

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,15 @@ public class ExportSwift {
227227
} else {
228228
optionalSwiftType = "JSUndefinedOr"
229229
}
230-
typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.swiftType)>"
231-
liftingExpr = ExprSyntax(
232-
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
233-
)
230+
typeNameForIntrinsic = "\(optionalSwiftType)<\(wrappedType.unaliased.swiftType)>"
231+
let liftCall =
232+
"\(typeNameForIntrinsic).bridgeJSLiftParameter(\(argumentsToLift.joined(separator: ", ")))"
233+
liftingExpr = "\(raw: param.type.liftAliases(expression: liftCall))"
234234
default:
235-
typeNameForIntrinsic = param.type.swiftType
236-
liftingExpr = ExprSyntax(
237-
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
238-
)
235+
typeNameForIntrinsic = param.type.unaliased.swiftType
236+
let liftCall =
237+
"\(typeNameForIntrinsic).bridgeJSLiftParameter(\(argumentsToLift.joined(separator: ", ")))"
238+
liftingExpr = "\(raw: param.type.liftAliases(expression: liftCall))"
239239
}
240240

241241
liftedParameterExprs.append(liftingExpr)
@@ -280,7 +280,8 @@ public class ExportSwift {
280280
}
281281

282282
if effects.isAsync, returnType != .void {
283-
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: callExpr)")))
283+
let lowered = returnType.lowerAliases(expression: callExpr.description)
284+
return CodeBlockItemSyntax(item: .init(StmtSyntax("return \(raw: lowered)")))
284285
}
285286

286287
if returnType == .void {
@@ -393,17 +394,18 @@ public class ExportSwift {
393394
return
394395
}
395396

397+
let returnAccessor = returnType.lowerAliases(expression: "ret")
396398
switch returnType {
397399
case .closure(_, useJSTypedClosure: false):
398400
append("return JSTypedClosure(ret).bridgeJSLowerReturn()")
399401
case .array, .nullable(.array, _):
400402
let stackCodegen = StackCodegen()
401-
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: "ret", varPrefix: "ret") {
403+
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: returnAccessor, varPrefix: "ret") {
402404
append(stmt)
403405
}
404406
case .dictionary(.swiftProtocol):
405407
let stackCodegen = StackCodegen()
406-
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: "ret", varPrefix: "ret") {
408+
for stmt in stackCodegen.lowerStatements(for: returnType, accessor: returnAccessor, varPrefix: "ret") {
407409
append(stmt)
408410
}
409411
case .swiftProtocol:
@@ -419,7 +421,7 @@ public class ExportSwift {
419421
"""
420422
)
421423
default:
422-
append("return ret.bridgeJSLowerReturn()")
424+
append("return \(raw: returnAccessor).bridgeJSLowerReturn()")
423425
}
424426
}
425427

@@ -877,8 +879,8 @@ struct StackCodegen {
877879
switch type {
878880
case .string, .integer, .bool, .float, .double,
879881
.jsObject(nil), .jsValue, .swiftStruct, .swiftHeapObject, .unsafePointer,
880-
.swiftProtocol, .caseEnum, .associatedValueEnum, .rawValueEnum, .array, .dictionary:
881-
return "\(raw: type.swiftType).bridgeJSStackPop()"
882+
.swiftProtocol, .caseEnum, .associatedValueEnum, .rawValueEnum, .array, .dictionary, .alias:
883+
return "\(raw: type.liftAliases(expression: "\(type.unaliased.swiftType).bridgeJSStackPop()"))"
882884
case .jsObject(let className?):
883885
return "\(raw: className)(unsafelyWrapping: JSObject.bridgeJSStackPop())"
884886
case .nullable(let wrappedType, let kind):
@@ -895,8 +897,10 @@ struct StackCodegen {
895897
switch wrappedType {
896898
case .string, .integer, .bool, .float, .double, .jsObject(nil), .jsValue,
897899
.swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum,
898-
.array, .dictionary:
899-
return "\(raw: typeName)<\(raw: wrappedType.swiftType)>.bridgeJSStackPop()"
900+
.array, .dictionary, .alias:
901+
let popCall = "\(typeName)<\(wrappedType.unaliased.swiftType)>.bridgeJSStackPop()"
902+
let nullableType = BridgeType.nullable(wrappedType, kind)
903+
return "\(raw: nullableType.liftAliases(expression: popCall))"
900904
case .jsObject(let className?):
901905
return "\(raw: typeName)<JSObject>.bridgeJSStackPop().map { \(raw: className)(unsafelyWrapping: $0) }"
902906
case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol:
@@ -918,7 +922,7 @@ struct StackCodegen {
918922
switch type {
919923
case .string, .integer, .bool, .float, .double, .jsValue,
920924
.jsObject(nil), .swiftHeapObject, .unsafePointer, .closure,
921-
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct, .nullable:
925+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct, .nullable, .alias:
922926
return ["\(raw: accessor).bridgeJSStackPush()"]
923927
case .jsObject(_?):
924928
return ["\(raw: accessor).jsObject.bridgeJSStackPush()"]
@@ -1204,9 +1208,10 @@ struct EnumCodegen {
12041208
) {
12051209
for (index, associatedValue) in associatedValues.enumerated() {
12061210
let paramName = associatedValue.label ?? "param\(index)"
1211+
let accessor = associatedValue.type.lowerAliases(expression: paramName)
12071212
let statements = stackCodegen.lowerStatements(
12081213
for: associatedValue.type,
1209-
accessor: paramName,
1214+
accessor: accessor,
12101215
varPrefix: paramName
12111216
)
12121217
for statement in statements {
@@ -1339,7 +1344,7 @@ struct StructCodegen {
13391344
let instanceProps = structDef.properties.filter { !$0.isStatic }
13401345

13411346
for property in instanceProps {
1342-
let accessor = "self.\(property.name)"
1347+
let accessor = property.type.lowerAliases(expression: "self.\(property.name)")
13431348
let statements = stackCodegen.lowerStatements(
13441349
for: property.type,
13451350
accessor: accessor,
@@ -1565,6 +1570,64 @@ extension UnsafePointerType {
15651570
}
15661571

15671572
extension BridgeType {
1573+
var unaliased: BridgeType {
1574+
switch self {
1575+
case .alias(_, let underlying): return underlying.unaliased
1576+
case .nullable(let wrapped, let kind): return .nullable(wrapped.unaliased, kind)
1577+
case .array(let element): return .array(element.unaliased)
1578+
case .dictionary(let value): return .dictionary(value.unaliased)
1579+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1580+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1581+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1582+
.namespaceEnum, .closure:
1583+
return self
1584+
}
1585+
}
1586+
1587+
/// If this type contains an alias, convert the expression with a type of the alias to the underlying type.
1588+
func liftAliases(expression: String) -> String {
1589+
switch self {
1590+
case .alias(let name, _):
1591+
return "\(name).bridgeFromJS(\(expression))"
1592+
case .nullable(let wrapped, _):
1593+
let lifted = wrapped.liftAliases(expression: "$0")
1594+
return lifted == "$0" ? expression : "\(expression).map { \(lifted) }"
1595+
case .array(let element):
1596+
let lifted = element.liftAliases(expression: "$0")
1597+
return lifted == "$0" ? expression : "\(expression).map { \(lifted) }"
1598+
case .dictionary(let value):
1599+
let lifted = value.liftAliases(expression: "$0")
1600+
return lifted == "$0" ? expression : "\(expression).mapValues { \(lifted) }"
1601+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1602+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1603+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1604+
.namespaceEnum, .closure:
1605+
return expression
1606+
}
1607+
}
1608+
1609+
/// Opposite of `liftAliases`: if this type contains an alias, convert the expression with a type of the underlying to the alias type.
1610+
func lowerAliases(expression: String) -> String {
1611+
switch self {
1612+
case .alias:
1613+
return "\(expression).bridgeToJS()"
1614+
case .nullable(let wrapped, _):
1615+
let lowered = wrapped.lowerAliases(expression: "$0")
1616+
return lowered == "$0" ? expression : "\(expression).map { \(lowered) }"
1617+
case .array(let element):
1618+
let lowered = element.lowerAliases(expression: "$0")
1619+
return lowered == "$0" ? expression : "\(expression).map { \(lowered) }"
1620+
case .dictionary(let value):
1621+
let lowered = value.lowerAliases(expression: "$0")
1622+
return lowered == "$0" ? expression : "\(expression).mapValues { \(lowered) }"
1623+
case .bool, .integer, .float, .double, .string, .jsValue, .jsObject,
1624+
.swiftHeapObject, .unsafePointer, .swiftProtocol, .void,
1625+
.caseEnum, .rawValueEnum, .associatedValueEnum, .swiftStruct,
1626+
.namespaceEnum, .closure:
1627+
return expression
1628+
}
1629+
}
1630+
15681631
var swiftType: String {
15691632
switch self {
15701633
case .bool: return "Bool"
@@ -1593,6 +1656,7 @@ extension BridgeType {
15931656
let effectsStr = (signature.isAsync ? " async" : "") + (signature.isThrows ? " throws" : "")
15941657
let closureType = "(\(paramTypes))\(effectsStr) -> \(signature.returnType.swiftType)"
15951658
return useJSTypedClosure ? "JSTypedClosure<\(closureType)>" : closureType
1659+
case .alias(let name, _): return name
15961660
}
15971661
}
15981662

@@ -1617,6 +1681,8 @@ extension BridgeType {
16171681
return true
16181682
case .nullable(let wrapped, _):
16191683
return wrapped.isStackUsingParameter
1684+
case .alias(_, let underlying):
1685+
return underlying.isStackUsingParameter
16201686
default:
16211687
return false
16221688
}
@@ -1675,6 +1741,8 @@ extension BridgeType {
16751741
return LiftingIntrinsicInfo(parameters: [("callbackId", .i32)])
16761742
case .array, .dictionary:
16771743
return LiftingIntrinsicInfo(parameters: [])
1744+
case .alias(_, let underlying):
1745+
return try underlying.liftParameterInfo()
16781746
}
16791747
}
16801748

@@ -1726,6 +1794,8 @@ extension BridgeType {
17261794
return .jsObject
17271795
case .array, .dictionary:
17281796
return .array
1797+
case .alias(_, let underlying):
1798+
return try underlying.loweringReturnInfo()
17291799
}
17301800
}
17311801
}

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 {
@@ -294,18 +296,21 @@ public struct ImportTS {
294296

295297
if returnType.usesSideChannelForOptionalReturn() {
296298
// Side channel returns: extern function returns Void, value is retrieved via side channel
297-
body.write("return \(returnType.swiftType).bridgeJSLiftReturnFromSideChannel()")
299+
let liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturnFromSideChannel()"
300+
body.write("return \(returnType.liftAliases(expression: liftCall))")
298301
} else {
299302
let liftExpr: String
300303
switch returnType {
301304
case .closure(let signature, _):
302305
liftExpr = "_BJS_Closure_\(signature.mangleName).bridgeJSLift(ret)"
303306
default:
307+
let liftCall: String
304308
if liftingInfo.valueToLift != nil {
305-
liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn(ret)"
309+
liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturn(ret)"
306310
} else {
307-
liftExpr = "\(returnType.swiftType).bridgeJSLiftReturn()"
311+
liftCall = "\(returnType.unaliased.swiftType).bridgeJSLiftReturn()"
308312
}
313+
liftExpr = returnType.liftAliases(expression: liftCall)
309314
}
310315
body.write("return \(liftExpr)")
311316
}
@@ -957,6 +962,8 @@ extension BridgeType {
957962
return LoweringParameterInfo(loweredParameters: params, useBorrowing: wrappedInfo.useBorrowing)
958963
case .array, .dictionary:
959964
return LoweringParameterInfo(loweredParameters: [])
965+
case .alias(_, let underlying):
966+
return try underlying.loweringParameterInfo(context: context)
960967
}
961968
}
962969

@@ -1019,7 +1026,7 @@ extension BridgeType {
10191026
case .nullable(let wrappedType, _):
10201027
// jsObject and `@JS struct` use the stack ABI for optionals — the thunk returns
10211028
// void and the value (plus isSome discriminator) flows through the stacks.
1022-
if case .jsObject = wrappedType {
1029+
if case .jsObject = wrappedType.unaliased {
10231030
return LiftingReturnInfo(valueToLift: nil)
10241031
}
10251032
if case .swiftStruct = wrappedType, context == .importTS {
@@ -1029,6 +1036,8 @@ extension BridgeType {
10291036
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10301037
case .array, .dictionary:
10311038
return LiftingReturnInfo(valueToLift: nil)
1039+
case .alias(_, let underlying):
1040+
return try underlying.liftingReturnInfo(context: context)
10321041
}
10331042
}
10341043
}

0 commit comments

Comments
 (0)