Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 79a0a4e

Browse files
Merge pull request #444 from PassiveLogic/feat/optionals-support
2 parents 250af4d + 29064b9 commit 79a0a4e

File tree

65 files changed

+7225
-128
lines changed

Some content is hidden

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

65 files changed

+7225
-128
lines changed

‎Benchmarks/Sources/Generated/BridgeJS.ExportSwift.swift‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
@_spi(BridgeJS) import JavaScriptKit
88

9-
privateextension APIResult {
10-
static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult {
9+
extension APIResult:_BridgedSwiftAssociatedValueEnum {
10+
@_spi(BridgeJS)@_transparentpublicstatic func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult {
1111
switch caseId {
1212
case 0:
1313
return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()))
@@ -26,7 +26,7 @@ private extension APIResult {
2626
}
2727
}
2828

29-
func bridgeJSLowerReturn() {
29+
@_spi(BridgeJS)@_transparentpublicconsumingfunc bridgeJSLowerReturn() {
3030
switch self {
3131
case .success(let param0):
3232
_swift_js_push_tag(Int32(0))
@@ -52,8 +52,8 @@ private extension APIResult {
5252
}
5353
}
5454

55-
privateextension ComplexResult {
56-
static func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult {
55+
extension ComplexResult:_BridgedSwiftAssociatedValueEnum {
56+
@_spi(BridgeJS)@_transparentpublicstatic func bridgeJSLiftParameter(_ caseId: Int32) -> ComplexResult {
5757
switch caseId {
5858
case 0:
5959
return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()))
@@ -74,7 +74,7 @@ private extension ComplexResult {
7474
}
7575
}
7676

77-
func bridgeJSLowerReturn() {
77+
@_spi(BridgeJS)@_transparentpublicconsumingfunc bridgeJSLowerReturn() {
7878
switch self {
7979
case .success(let param0):
8080
_swift_js_push_tag(Int32(0))

‎Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift‎

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ public class ExportSwift {
143143
)
144144
}
145145

146+
private func diagnoseNestedOptional(node: some SyntaxProtocol, type: String) {
147+
diagnose(
148+
node: node,
149+
message: "Nested optional types are not supported: \(type)",
150+
hint: "Use a single optional like String? instead of String?? or Optional<Optional<T>>"
151+
)
152+
}
153+
146154
override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
147155
switch state {
148156
case .topLevel:
@@ -183,17 +191,32 @@ public class ExportSwift {
183191

184192
var parameters: [Parameter] = []
185193
for param in node.signature.parameterClause.parameters {
186-
guard let type = self.parent.lookupType(for: param.type) else {
194+
let resolvedType = self.parent.lookupType(for: param.type)
195+
196+
if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional {
197+
diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription)
198+
continue
199+
}
200+
201+
guard let type = resolvedType else {
187202
diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription)
188203
continue
189204
}
205+
190206
let name = param.secondName?.text ?? param.firstName.text
191207
let label = param.firstName.text
192208
parameters.append(Parameter(label: label, name: name, type: type))
193209
}
194210
let returnType: BridgeType
195211
if let returnClause = node.signature.returnClause {
196-
guard let type = self.parent.lookupType(for: returnClause.type) else {
212+
let resolvedType = self.parent.lookupType(for: returnClause.type)
213+
214+
if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional {
215+
diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription)
216+
return nil
217+
}
218+
219+
guard let type = resolvedType else {
197220
diagnoseUnsupportedType(node: returnClause.type, type: returnClause.type.trimmedDescription)
198221
return nil
199222
}
@@ -538,12 +561,24 @@ public class ExportSwift {
538561
switch associatedValue.type {
539562
case .string, .int, .float, .double, .bool:
540563
break
564+
case .optional(let wrappedType):
565+
switch wrappedType {
566+
case .string, .int, .float, .double, .bool:
567+
break
568+
default:
569+
diagnose(
570+
node: node,
571+
message: "Unsupported associated value type: \(associatedValue.type.swiftType)",
572+
hint:
573+
"Only primitive types and optional primitives (String?, Int?, Float?, Double?, Bool?) are supported in associated-value enums"
574+
)
575+
}
541576
default:
542577
diagnose(
543578
node: node,
544579
message: "Unsupported associated value type: \(associatedValue.type.swiftType)",
545580
hint:
546-
"Only primitive types (String, Int, Float, Double, Bool) are supported in associated-value enums"
581+
"Only primitive types and optional primitives (String?, Int?, Float?, Double?, Bool?) are supported in associated-value enums"
547582
)
548583
}
549584
}
@@ -712,8 +747,46 @@ public class ExportSwift {
712747
}
713748

714749
func lookupType(for type: TypeSyntax) -> BridgeType? {
715-
if let primitive = BridgeType(swiftType: type.trimmedDescription) {
716-
return primitive
750+
// T?
751+
if let optionalType = type.as(OptionalTypeSyntax.self) {
752+
let wrappedType = optionalType.wrappedType
753+
if let baseType = lookupType(for: wrappedType) {
754+
return .optional(baseType)
755+
}
756+
}
757+
// Optional<T>
758+
if let identifierType = type.as(IdentifierTypeSyntax.self),
759+
identifierType.name.text == "Optional",
760+
let genericArgs = identifierType.genericArgumentClause?.arguments,
761+
genericArgs.count == 1,
762+
let argType = genericArgs.first?.argument
763+
{
764+
if let baseType = lookupType(for: argType) {
765+
return .optional(baseType)
766+
}
767+
}
768+
// Swift.Optional<T>
769+
if let memberType = type.as(MemberTypeSyntax.self),
770+
let baseType = memberType.baseType.as(IdentifierTypeSyntax.self),
771+
baseType.name.text == "Swift",
772+
memberType.name.text == "Optional",
773+
let genericArgs = memberType.genericArgumentClause?.arguments,
774+
genericArgs.count == 1,
775+
let argType = genericArgs.first?.argument
776+
{
777+
if let wrappedType = lookupType(for: argType) {
778+
return .optional(wrappedType)
779+
}
780+
}
781+
if let aliasDecl = typeDeclResolver.resolveTypeAlias(type) {
782+
if let resolvedType = lookupType(for: aliasDecl.initializer.value) {
783+
return resolvedType
784+
}
785+
}
786+
787+
let typeName = type.trimmedDescription
788+
if let primitiveType = BridgeType(swiftType: typeName) {
789+
return primitiveType
717790
}
718791

719792
guard let typeDecl = typeDeclResolver.resolve(type) else {
@@ -831,9 +904,18 @@ public class ExportSwift {
831904
} else {
832905
argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter }
833906
}
907+
908+
let typeNameForIntrinsic: String
909+
switch param.type {
910+
case .optional(let wrappedType):
911+
typeNameForIntrinsic = "Optional<\(wrappedType.swiftType)>"
912+
default:
913+
typeNameForIntrinsic = param.type.swiftType
914+
}
915+
834916
liftedParameterExprs.append(
835917
ExprSyntax(
836-
"\(raw: param.type.swiftType).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
918+
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
837919
)
838920
)
839921
for (name, type) in zip(argumentsToLift, liftingInfo.parameters.map { 0ドル.type }) {
@@ -1013,7 +1095,7 @@ public class ExportSwift {
10131095
let valueSwitch = (["switch self {"] + valueCases + ["}"]).joined(separator: "\n")
10141096

10151097
return """
1016-
extension \(raw: typeName) {
1098+
extension \(raw: typeName): _BridgedSwiftCaseEnum {
10171099
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
10181100
return bridgeJSRawValue
10191101
}
@@ -1041,15 +1123,15 @@ public class ExportSwift {
10411123
func renderAssociatedValueEnumHelpers(_ enumDef: ExportedEnum) -> DeclSyntax {
10421124
let typeName = enumDef.swiftCallName
10431125
return """
1044-
private extension \(raw: typeName) {
1045-
static func bridgeJSLiftParameter(_ caseId: Int32) -> \(raw: typeName) {
1126+
extension \(raw: typeName): _BridgedSwiftAssociatedValueEnum {
1127+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> \(raw: typeName) {
10461128
switch caseId {
10471129
\(raw: generateStackLiftSwitchCases(enumDef: enumDef).joined(separator: "\n"))
10481130
default: fatalError("Unknown \(raw: typeName) case ID: \\(caseId)")
10491131
}
10501132
}
10511133
1052-
func bridgeJSLowerReturn() {
1134+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() {
10531135
switch self {
10541136
\(raw: generateReturnSwitchCases(enumDef: enumDef).joined(separator: "\n"))
10551137
}
@@ -1085,6 +1167,26 @@ public class ExportSwift {
10851167
return "\(paramName)Float.bridgeJSLiftParameter(_swift_js_pop_param_f32())"
10861168
case .double:
10871169
return "\(paramName)Double.bridgeJSLiftParameter(_swift_js_pop_param_f64())"
1170+
case .optional(let wrappedType):
1171+
switch wrappedType {
1172+
case .string:
1173+
return
1174+
"\(paramName)Optional<String>.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32(), _swift_js_pop_param_int32())"
1175+
case .int:
1176+
return
1177+
"\(paramName)Optional<Int>.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())"
1178+
case .bool:
1179+
return
1180+
"\(paramName)Optional<Bool>.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())"
1181+
case .float:
1182+
return
1183+
"\(paramName)Optional<Float>.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_f32())"
1184+
case .double:
1185+
return
1186+
"\(paramName)Optional<Double>.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_f64())"
1187+
default:
1188+
return ""
1189+
}
10881190
default:
10891191
return "\(paramName)Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())"
10901192
}
@@ -1121,6 +1223,30 @@ public class ExportSwift {
11211223
bodyLines.append("_swift_js_push_f32(\(paramName))")
11221224
case .double:
11231225
bodyLines.append("_swift_js_push_f64(\(paramName))")
1226+
case .optional(let wrappedType):
1227+
bodyLines.append("let __bjs_isSome_\(paramName) = \(paramName) != nil")
1228+
bodyLines.append("if let __bjs_unwrapped_\(paramName) = \(paramName) {")
1229+
switch wrappedType {
1230+
case .string:
1231+
bodyLines.append("var __bjs_str_\(paramName) = __bjs_unwrapped_\(paramName)")
1232+
bodyLines.append("__bjs_str_\(paramName).withUTF8 { ptr in")
1233+
bodyLines.append("_swift_js_push_string(ptr.baseAddress, Int32(ptr.count))")
1234+
bodyLines.append("}")
1235+
case .int:
1236+
bodyLines.append("_swift_js_push_int(Int32(__bjs_unwrapped_\(paramName)))")
1237+
case .bool:
1238+
bodyLines.append("_swift_js_push_int(__bjs_unwrapped_\(paramName) ? 1 : 0)")
1239+
case .float:
1240+
bodyLines.append("_swift_js_push_f32(__bjs_unwrapped_\(paramName))")
1241+
case .double:
1242+
bodyLines.append("_swift_js_push_f64(__bjs_unwrapped_\(paramName))")
1243+
default:
1244+
bodyLines.append(
1245+
"preconditionFailure(\"BridgeJS: unsupported optional wrapped type in generated code\")"
1246+
)
1247+
}
1248+
bodyLines.append("}")
1249+
bodyLines.append("_swift_js_push_int(__bjs_isSome_\(paramName) ? 1 : 0)")
11241250
default:
11251251
bodyLines.append(
11261252
"preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")"
@@ -1377,6 +1503,7 @@ extension BridgeType {
13771503
case .jsObject(let name?): return name
13781504
case .swiftHeapObject(let name): return name
13791505
case .void: return "Void"
1506+
case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>"
13801507
case .caseEnum(let name): return name
13811508
case .rawValueEnum(let name, _): return name
13821509
case .associatedValueEnum(let name): return name
@@ -1411,6 +1538,10 @@ extension BridgeType {
14111538
case .jsObject: return .jsObject
14121539
case .swiftHeapObject: return .swiftHeapObject
14131540
case .void: return .void
1541+
case .optional(let wrappedType):
1542+
var optionalParams: [(name: String, type: WasmCoreType)] = [("isSome", .i32)]
1543+
optionalParams.append(contentsOf: try wrappedType.liftParameterInfo().parameters)
1544+
return LiftingIntrinsicInfo(parameters: optionalParams)
14141545
case .caseEnum: return .caseEnum
14151546
case .rawValueEnum(_, let rawType):
14161547
switch rawType {
@@ -1446,6 +1577,7 @@ extension BridgeType {
14461577
static let caseEnum = LoweringIntrinsicInfo(returnType: .i32)
14471578
static let rawValueEnum = LoweringIntrinsicInfo(returnType: .i32)
14481579
static let associatedValueEnum = LoweringIntrinsicInfo(returnType: nil)
1580+
static let optional = LoweringIntrinsicInfo(returnType: nil)
14491581
}
14501582

14511583
func loweringReturnInfo() throws -> LoweringIntrinsicInfo {
@@ -1458,6 +1590,7 @@ extension BridgeType {
14581590
case .jsObject: return .jsObject
14591591
case .swiftHeapObject: return .swiftHeapObject
14601592
case .void: return .void
1593+
case .optional: return .optional
14611594
case .caseEnum: return .caseEnum
14621595
case .rawValueEnum(_, let rawType):
14631596
switch rawType {

‎Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,8 @@ extension BridgeType {
437437
throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures")
438438
case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum:
439439
throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports")
440+
case .optional:
441+
throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports")
440442
}
441443
}
442444

@@ -465,6 +467,8 @@ extension BridgeType {
465467
throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures")
466468
case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum:
467469
throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports")
470+
case .optional:
471+
throw BridgeJSCoreError("Optional types are not yet supported in TypeScript imports")
468472
}
469473
}
470474
}

‎Plugins/BridgeJS/Sources/BridgeJSCore/TypeDeclResolver.swift‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class TypeDeclResolver {
88
/// `Outer.Inner` type declaration is represented as ["Outer", "Inner"]
99
typealias QualifiedName = [String]
1010
private var typeDeclByQualifiedName: [QualifiedName: TypeDecl] = [:]
11+
private var typeAliasByQualifiedName: [QualifiedName: TypeAliasDeclSyntax] = [:]
1112

1213
enum Error: Swift.Error {
1314
case typeNotFound(QualifiedName)
@@ -62,6 +63,13 @@ class TypeDeclResolver {
6263
override func visitPost(_ node: EnumDeclSyntax) {
6364
visitPostNominalDecl()
6465
}
66+
67+
override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind {
68+
let name = node.name.text
69+
let qualifiedName = scope.map(\.name.text) + [name]
70+
resolver.typeAliasByQualifiedName[qualifiedName] = node
71+
return .skipChildren
72+
}
6573
}
6674

6775
/// Collects type declarations from a parsed Swift source file
@@ -132,6 +140,21 @@ class TypeDeclResolver {
132140
return nil
133141
}
134142

143+
/// Resolves a type usage node to a type alias declaration
144+
///
145+
/// - Parameter type: The SwiftSyntax node representing a type appearance in source code.
146+
/// - Returns: The type alias declaration if found, otherwise nil.
147+
func resolveTypeAlias(_ type: TypeSyntax) -> TypeAliasDeclSyntax? {
148+
if let id = type.as(IdentifierTypeSyntax.self) {
149+
let qualifiedName = tryQualify(type: id)
150+
return typeAliasByQualifiedName[qualifiedName]
151+
}
152+
if let components = qualifiedComponents(from: type) {
153+
return typeAliasByQualifiedName[components]
154+
}
155+
return nil
156+
}
157+
135158
private func qualifiedComponents(from type: TypeSyntax) -> QualifiedName? {
136159
if let m = type.as(MemberTypeSyntax.self) {
137160
guard let base = qualifiedComponents(from: TypeSyntax(m.baseType)) else { return nil }

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /