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 8ecbbd4

Browse files
committed
BridgeJS: Support optionals in associated value enum parameters + cleanup
1 parent 1e276d5 commit 8ecbbd4

File tree

24 files changed

+1153
-871
lines changed

24 files changed

+1153
-871
lines changed

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

Lines changed: 95 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public class ExportSwift {
142142
hint: "Only primitive types and types defined in the same module are allowed"
143143
)
144144
}
145-
145+
146146
private func diagnoseNestedOptional(node: some SyntaxProtocol, type: String) {
147147
diagnose(
148148
node: node,
@@ -192,30 +192,30 @@ public class ExportSwift {
192192
var parameters: [Parameter] = []
193193
for param in node.signature.parameterClause.parameters {
194194
let resolvedType = self.parent.lookupType(for: param.type)
195-
195+
196196
if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional {
197197
diagnoseNestedOptional(node: param.type, type: param.type.trimmedDescription)
198198
continue
199199
}
200-
200+
201201
guard let type = resolvedType else {
202202
diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription)
203203
continue
204204
}
205-
205+
206206
let name = param.secondName?.text ?? param.firstName.text
207207
let label = param.firstName.text
208208
parameters.append(Parameter(label: label, name: name, type: type))
209209
}
210210
let returnType: BridgeType
211211
if let returnClause = node.signature.returnClause {
212212
let resolvedType = self.parent.lookupType(for: returnClause.type)
213-
213+
214214
if let type = resolvedType, case .optional(let wrappedType) = type, wrappedType.isOptional {
215215
diagnoseNestedOptional(node: returnClause.type, type: returnClause.type.trimmedDescription)
216216
return nil
217217
}
218-
218+
219219
guard let type = resolvedType else {
220220
diagnoseUnsupportedType(node: returnClause.type, type: returnClause.type.trimmedDescription)
221221
return nil
@@ -561,12 +561,24 @@ public class ExportSwift {
561561
switch associatedValue.type {
562562
case .string, .int, .float, .double, .bool:
563563
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+
}
564576
default:
565577
diagnose(
566578
node: node,
567579
message: "Unsupported associated value type: \(associatedValue.type.swiftType)",
568580
hint:
569-
"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"
570582
)
571583
}
572584
}
@@ -744,22 +756,24 @@ public class ExportSwift {
744756
}
745757
// Optional<T>
746758
if let identifierType = type.as(IdentifierTypeSyntax.self),
747-
identifierType.name.text == "Optional",
748-
let genericArgs = identifierType.genericArgumentClause?.arguments,
749-
genericArgs.count == 1,
750-
let argType = genericArgs.first?.argument {
759+
identifierType.name.text == "Optional",
760+
let genericArgs = identifierType.genericArgumentClause?.arguments,
761+
genericArgs.count == 1,
762+
let argType = genericArgs.first?.argument
763+
{
751764
if let baseType = lookupType(for: argType) {
752765
return .optional(baseType)
753766
}
754767
}
755768
// Swift.Optional<T>
756769
if let memberType = type.as(MemberTypeSyntax.self),
757-
let baseType = memberType.baseType.as(IdentifierTypeSyntax.self),
758-
baseType.name.text == "Swift",
759-
memberType.name.text == "Optional",
760-
let genericArgs = memberType.genericArgumentClause?.arguments,
761-
genericArgs.count == 1,
762-
let argType = genericArgs.first?.argument {
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+
{
763777
if let wrappedType = lookupType(for: argType) {
764778
return .optional(wrappedType)
765779
}
@@ -771,7 +785,7 @@ public class ExportSwift {
771785
}
772786

773787
let typeName = type.trimmedDescription
774-
if let primitiveType = BridgeType.primitive(swiftType: typeName) {
788+
if let primitiveType = BridgeType(swiftType: typeName) {
775789
return primitiveType
776790
}
777791

@@ -890,15 +904,15 @@ public class ExportSwift {
890904
} else {
891905
argumentsToLift = liftingInfo.parameters.map { (name, _) in param.name + name.capitalizedFirstLetter }
892906
}
893-
907+
894908
let typeNameForIntrinsic: String
895909
switch param.type {
896910
case .optional(let wrappedType):
897911
typeNameForIntrinsic = "Optional<\(wrappedType.swiftType)>"
898912
default:
899913
typeNameForIntrinsic = param.type.swiftType
900914
}
901-
915+
902916
liftedParameterExprs.append(
903917
ExprSyntax(
904918
"\(raw: typeNameForIntrinsic).bridgeJSLiftParameter(\(raw: argumentsToLift.joined(separator: ", ")))"
@@ -996,11 +1010,6 @@ public class ExportSwift {
9961010
return
9971011
}
9981012

999-
if case .optional(_) = returnType {
1000-
append("return ret.bridgeJSLowerReturn()")
1001-
return
1002-
}
1003-
10041013
append("return ret.bridgeJSLowerReturn()")
10051014
}
10061015

@@ -1158,8 +1167,26 @@ public class ExportSwift {
11581167
return "\(paramName)Float.bridgeJSLiftParameter(_swift_js_pop_param_f32())"
11591168
case .double:
11601169
return "\(paramName)Double.bridgeJSLiftParameter(_swift_js_pop_param_f64())"
1161-
case .optional(_):
1162-
return "nil"
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+
}
11631190
default:
11641191
return "\(paramName)Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())"
11651192
}
@@ -1196,6 +1223,30 @@ public class ExportSwift {
11961223
bodyLines.append("_swift_js_push_f32(\(paramName))")
11971224
case .double:
11981225
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)")
11991250
default:
12001251
bodyLines.append(
12011252
"preconditionFailure(\"BridgeJS: unsupported associated value type in generated code\")"
@@ -1406,55 +1457,24 @@ extension AttributeListSyntax {
14061457
}
14071458

14081459
extension BridgeType {
1409-
// This initializer is primarily for string-based type descriptions,
1410-
// especially for internal and raw type lookups.
1411-
// For full SwiftSyntax based parsing, `ExportSwift.lookupType(for:)` should be used.
14121460
init?(swiftType: String) {
1413-
// Use SwiftSyntax to parse the string into a TypeSyntax for robust optional detection
1414-
let typeSyntax = TypeSyntax(stringLiteral: swiftType)
1415-
1416-
// 1. Handle T? syntax (OptionalTypeSyntax)
1417-
if let optionalType = typeSyntax.as(OptionalTypeSyntax.self) {
1418-
let wrappedTypeString = optionalType.wrappedType.trimmedDescription
1419-
if let baseType = BridgeType(swiftType: wrappedTypeString) {
1420-
self = .optional(baseType)
1421-
return
1422-
}
1423-
}
1424-
1425-
// 2. Handle Optional<T> syntax (IdentifierTypeSyntax with generic arguments)
1426-
if let identifierType = typeSyntax.as(IdentifierTypeSyntax.self),
1427-
identifierType.name.text == "Optional",
1428-
let genericArgs = identifierType.genericArgumentClause?.arguments,
1429-
genericArgs.count == 1,
1430-
let argType = genericArgs.first?.argument {
1431-
let innerTypeString = argType.trimmedDescription
1432-
if let baseType = BridgeType(swiftType: innerTypeString) {
1433-
self = .optional(baseType)
1434-
return
1435-
}
1436-
}
1437-
1438-
// Fallback to primitive type handling if not an optional
1439-
if let primitive = BridgeType.primitive(swiftType: swiftType) {
1440-
self = primitive
1441-
return
1442-
}
1443-
1444-
return nil
1445-
}
1446-
1447-
// Helper for direct primitive type mapping (used by init? and elsewhere)
1448-
fileprivate static func primitive(swiftType: String) -> BridgeType? {
14491461
switch swiftType {
1450-
case "Int": return .int
1451-
case "Float": return .float
1452-
case "Double": return .double
1453-
case "String": return .string
1454-
case "Bool": return .bool
1455-
case "Void": return .void
1456-
case "JSObject": return .jsObject(nil)
1457-
default: return nil
1462+
case "Int":
1463+
self = .int
1464+
case "Float":
1465+
self = .float
1466+
case "Double":
1467+
self = .double
1468+
case "String":
1469+
self = .string
1470+
case "Bool":
1471+
self = .bool
1472+
case "Void":
1473+
self = .void
1474+
case "JSObject":
1475+
self = .jsObject(nil)
1476+
default:
1477+
return nil
14581478
}
14591479
}
14601480
}
@@ -1507,7 +1527,6 @@ extension BridgeType {
15071527
("caseId", .i32)
15081528
])
15091529
}
1510-
15111530

15121531
func liftParameterInfo() throws -> LiftingIntrinsicInfo {
15131532
switch self {
@@ -1589,7 +1608,7 @@ extension BridgeType {
15891608
case .associatedValueEnum:
15901609
return .associatedValueEnum
15911610
case .namespaceEnum:
1592-
throw BridgeJSCoreError("Namespace enums are not supported as return types")
1611+
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
15931612
}
15941613
}
15951614
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class TypeDeclResolver {
6363
override func visitPost(_ node: EnumDeclSyntax) {
6464
visitPostNominalDecl()
6565
}
66-
66+
6767
override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind {
6868
let name = node.name.text
6969
let qualifiedName = scope.map(\.name.text) + [name]
@@ -139,7 +139,7 @@ class TypeDeclResolver {
139139
}
140140
return nil
141141
}
142-
142+
143143
/// Resolves a type usage node to a type alias declaration
144144
///
145145
/// - Parameter type: The SwiftSyntax node representing a type appearance in source code.

‎Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,9 @@ struct BridgeJSLink {
365365
}
366366
printer.write("} else {")
367367
printer.indent {
368-
printer.write("\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = Math.fround(value);")
368+
printer.write(
369+
"\(JSGlueVariableScope.reservedStorageToReturnOptionalFloat) = Math.fround(value);"
370+
)
369371
}
370372
printer.write("}")
371373
}
@@ -393,7 +395,7 @@ struct BridgeJSLink {
393395
printer.indent {
394396
printer.write(lines: [
395397
"const bytes = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, ptr, len);",
396-
"\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);"
398+
"\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedTextDecoder).decode(bytes);",
397399
])
398400
}
399401
printer.write("}")
@@ -407,7 +409,9 @@ struct BridgeJSLink {
407409
}
408410
printer.write("} else {")
409411
printer.indent {
410-
printer.write("\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedSwift).memory.getObject(objectId);")
412+
printer.write(
413+
"\(JSGlueVariableScope.reservedStorageToReturnString) = \(JSGlueVariableScope.reservedSwift).memory.getObject(objectId);"
414+
)
411415
}
412416
printer.write("}")
413417
}

0 commit comments

Comments
(0)

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