Skip to content

Commit fc7e237

Browse files
BridgeJS: Support T | null and T | undefined
1 parent 89ed56e commit fc7e237

File tree

17 files changed

+1250
-83
lines changed

17 files changed

+1250
-83
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,8 @@ struct StackCodegen {
820820
return "\(raw: type.swiftType).bridgeJSLiftParameter(_swift_js_pop_i32())"
821821
case .optional(let wrappedType):
822822
return liftOptionalExpression(wrappedType: wrappedType)
823+
case .undefinedOr(let wrappedType):
824+
return liftUndefinedExpression(wrappedType: wrappedType)
823825
case .array(let elementType):
824826
return liftArrayExpression(elementType: elementType)
825827
case .closure:
@@ -839,7 +841,7 @@ struct StackCodegen {
839841
return liftArrayExpressionInline(elementType: elementType)
840842
case .swiftProtocol(let protocolName):
841843
return "[Any\(raw: protocolName)].bridgeJSLiftParameter()"
842-
case .optional, .array, .closure:
844+
case .optional, .undefinedOr, .array, .closure:
843845
return liftArrayExpressionInline(elementType: elementType)
844846
case .void, .namespaceEnum:
845847
fatalError("Invalid array element type: \(elementType)")
@@ -883,11 +885,34 @@ struct StackCodegen {
883885
}
884886
}()
885887
"""
886-
case .void, .namespaceEnum, .closure, .optional, .unsafePointer, .swiftProtocol:
888+
case .undefinedOr, .void, .namespaceEnum, .closure, .optional, .unsafePointer, .swiftProtocol:
887889
fatalError("Invalid optional wrapped type: \(wrappedType)")
888890
}
889891
}
890892

893+
private func liftUndefinedExpression(wrappedType: BridgeType) -> ExprSyntax {
894+
switch wrappedType {
895+
case .string, .int, .uint, .bool, .float, .double, .jsObject,
896+
.swiftStruct, .swiftHeapObject, .caseEnum, .associatedValueEnum, .rawValueEnum:
897+
return "JSUndefinedOr<\(raw: wrappedType.swiftType)>.bridgeJSLiftParameter()"
898+
case .array(let elementType):
899+
let arrayLift = liftArrayExpression(elementType: elementType)
900+
let swiftTypeName = elementType.swiftType
901+
return """
902+
{
903+
let __isDefined = _swift_js_pop_i32()
904+
if __isDefined == 0 {
905+
return JSUndefinedOr<\(raw: swiftTypeName)>.undefined
906+
} else {
907+
return JSUndefinedOr<\(raw: swiftTypeName)>(optional: \(arrayLift))
908+
}
909+
}()
910+
"""
911+
case .void, .namespaceEnum, .closure, .optional, .undefinedOr, .unsafePointer, .swiftProtocol:
912+
fatalError("Invalid undefinedOr wrapped type: \(wrappedType)")
913+
}
914+
}
915+
891916
/// Generates statements to lower/push a value onto the stack.
892917
/// - Parameters:
893918
/// - type: The BridgeType to lower
@@ -917,6 +942,8 @@ struct StackCodegen {
917942
return ["\(raw: accessor).bridgeJSLowerReturn()"]
918943
case .optional(let wrappedType):
919944
return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix)
945+
case .undefinedOr(let wrappedType):
946+
return lowerOptionalStatements(wrappedType: wrappedType, accessor: accessor, varPrefix: varPrefix)
920947
case .void, .namespaceEnum:
921948
return []
922949
case .array(let elementType):
@@ -938,7 +965,7 @@ struct StackCodegen {
938965
return ["\(raw: accessor).map { $0.jsObject }.bridgeJSLowerReturn()"]
939966
case .swiftProtocol(let protocolName):
940967
return ["\(raw: accessor).map { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"]
941-
case .optional, .array, .closure:
968+
case .optional, .undefinedOr, .array, .closure:
942969
return lowerArrayStatementsInline(
943970
elementType: elementType,
944971
accessor: accessor,
@@ -1597,6 +1624,7 @@ extension BridgeType {
15971624
case .swiftProtocol(let name): return "Any\(name)"
15981625
case .void: return "Void"
15991626
case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>"
1627+
case .undefinedOr(let wrappedType): return "JSUndefinedOr<\(wrappedType.swiftType)>"
16001628
case .array(let elementType): return "[\(elementType.swiftType)]"
16011629
case .caseEnum(let name): return name
16021630
case .rawValueEnum(let name, _): return name
@@ -1644,6 +1672,10 @@ extension BridgeType {
16441672
var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)]
16451673
optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters)
16461674
return LiftingIntrinsicInfo(parameters: optionalParams)
1675+
case .undefinedOr(let wrappedType):
1676+
var params: [(name: String, type: WasmCoreType)] = [("isDefined", .i32)]
1677+
params.append(contentsOf: try wrappedType.liftParameterInfo().parameters)
1678+
return LiftingIntrinsicInfo(parameters: params)
16471679
case .caseEnum: return .caseEnum
16481680
case .rawValueEnum(_, let rawType):
16491681
return rawType.liftingIntrinsicInfo
@@ -1693,6 +1725,7 @@ extension BridgeType {
16931725
case .swiftProtocol: return .jsObject
16941726
case .void: return .void
16951727
case .optional: return .optional
1728+
case .undefinedOr: return .optional
16961729
case .caseEnum: return .caseEnum
16971730
case .rawValueEnum(_, let rawType):
16981731
return rawType.loweringIntrinsicInfo

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -925,15 +925,15 @@ extension BridgeType {
925925
case .namespaceEnum:
926926
throw BridgeJSCoreError("Namespace enums cannot be used as parameters")
927927
case .optional(let wrappedType):
928-
switch context {
929-
case .importTS:
930-
throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports")
931-
case .exportSwift:
932-
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
933-
var params = [("isSome", WasmCoreType.i32)]
934-
params.append(contentsOf: wrappedInfo.loweredParameters)
935-
return LoweringParameterInfo(loweredParameters: params)
936-
}
928+
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
929+
var params = [("isSome", WasmCoreType.i32)]
930+
params.append(contentsOf: wrappedInfo.loweredParameters)
931+
return LoweringParameterInfo(loweredParameters: params)
932+
case .undefinedOr(let wrappedType):
933+
let wrappedInfo = try wrappedType.loweringParameterInfo(context: context)
934+
var params = [("isDefined", WasmCoreType.i32)]
935+
params.append(contentsOf: wrappedInfo.loweredParameters)
936+
return LoweringParameterInfo(loweredParameters: params)
937937
case .array:
938938
switch context {
939939
case .importTS:
@@ -1019,13 +1019,11 @@ extension BridgeType {
10191019
case .namespaceEnum:
10201020
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
10211021
case .optional(let wrappedType):
1022-
switch context {
1023-
case .importTS:
1024-
throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports")
1025-
case .exportSwift:
1026-
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
1027-
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
1028-
}
1022+
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
1023+
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
1024+
case .undefinedOr(let wrappedType):
1025+
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
1026+
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10291027
case .array:
10301028
switch context {
10311029
case .importTS:

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,30 @@ public final class SwiftToSkeleton {
133133
return .optional(baseType)
134134
}
135135
}
136+
// JSUndefinedOr<T>
137+
if let identifierType = type.as(IdentifierTypeSyntax.self),
138+
identifierType.name.text == "JSUndefinedOr",
139+
let genericArgs = identifierType.genericArgumentClause?.arguments,
140+
genericArgs.count == 1,
141+
let argType = TypeSyntax(genericArgs.first?.argument)
142+
{
143+
if let baseType = lookupType(for: argType, errors: &errors) {
144+
return .undefinedOr(baseType)
145+
}
146+
}
147+
// JavaScriptKit.JSUndefinedOr<T>
148+
if let memberType = type.as(MemberTypeSyntax.self),
149+
let baseType = memberType.baseType.as(IdentifierTypeSyntax.self),
150+
baseType.name.text == "JavaScriptKit",
151+
memberType.name.text == "JSUndefinedOr",
152+
let genericArgs = memberType.genericArgumentClause?.arguments,
153+
genericArgs.count == 1,
154+
let argType = TypeSyntax(genericArgs.first?.argument)
155+
{
156+
if let wrappedType = lookupType(for: argType, errors: &errors) {
157+
return .undefinedOr(wrappedType)
158+
}
159+
}
136160
// Optional<T>
137161
if let identifierType = type.as(IdentifierTypeSyntax.self),
138162
identifierType.name.text == "Optional",

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3405,6 +3405,8 @@ extension BridgeType {
34053405
return "number"
34063406
case .optional(let wrappedType):
34073407
return "\(wrappedType.tsType) | null"
3408+
case .undefinedOr(let wrappedType):
3409+
return "\(wrappedType.tsType) | undefined"
34083410
case .caseEnum(let name):
34093411
return "\(name)Tag"
34103412
case .rawValueEnum(let name, _):

0 commit comments

Comments
 (0)