Skip to content

Commit aa82380

Browse files
BridgeJS: Add Dictionary support
1 parent 63b7b72 commit aa82380

File tree

17 files changed

+1978
-7
lines changed

17 files changed

+1978
-7
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ struct StackCodegen {
832832
return liftNullableExpression(wrappedType: wrappedType, kind: kind)
833833
case .array(let elementType):
834834
return liftArrayExpression(elementType: elementType)
835+
case .dictionary(let valueType):
836+
return liftDictionaryExpression(valueType: valueType)
835837
case .closure:
836838
return "JSObject.bridgeJSLiftParameter()"
837839
case .void, .namespaceEnum:
@@ -849,7 +851,7 @@ struct StackCodegen {
849851
return liftArrayExpressionInline(elementType: elementType)
850852
case .swiftProtocol(let protocolName):
851853
return "[Any\(raw: protocolName)].bridgeJSLiftParameter()"
852-
case .nullable, .array, .closure:
854+
case .nullable, .array, .closure, .dictionary:
853855
return liftArrayExpressionInline(elementType: elementType)
854856
case .void, .namespaceEnum:
855857
fatalError("Invalid array element type: \(elementType)")
@@ -873,6 +875,51 @@ struct StackCodegen {
873875
"""
874876
}
875877

878+
func liftDictionaryExpression(valueType: BridgeType) -> ExprSyntax {
879+
switch valueType {
880+
case .int, .uint, .float, .double, .string, .bool, .jsValue,
881+
.jsObject(nil), .swiftStruct, .caseEnum, .swiftHeapObject,
882+
.unsafePointer, .rawValueEnum, .associatedValueEnum:
883+
return "[String: \(raw: valueType.swiftType)].bridgeJSLiftParameter()"
884+
case .jsObject(let className?):
885+
return """
886+
{
887+
let __dict = [String: JSObject].bridgeJSLiftParameter()
888+
return __dict.mapValues { \(raw: className)(unsafelyWrapping: $0) }
889+
}()
890+
"""
891+
case .swiftProtocol(let protocolName):
892+
return """
893+
{
894+
let __dict = [String: JSObject].bridgeJSLiftParameter()
895+
return __dict.mapValues { $0 as! Any\(raw: protocolName) }
896+
}()
897+
"""
898+
case .nullable, .array, .dictionary, .closure:
899+
return liftDictionaryExpressionInline(valueType: valueType)
900+
case .void, .namespaceEnum:
901+
fatalError("Invalid dictionary value type: \(valueType)")
902+
}
903+
}
904+
905+
private func liftDictionaryExpressionInline(valueType: BridgeType) -> ExprSyntax {
906+
let valueLift = liftExpression(for: valueType)
907+
let swiftTypeName = valueType.swiftType
908+
return """
909+
{
910+
let __count = Int(_swift_js_pop_i32())
911+
var __result: [String: \(raw: swiftTypeName)] = [:]
912+
__result.reserveCapacity(__count)
913+
for _ in 0..<__count {
914+
let __value = \(valueLift)
915+
let __key = String.bridgeJSLiftParameter()
916+
__result[__key] = __value
917+
}
918+
return __result
919+
}()
920+
"""
921+
}
922+
876923
private func liftNullableExpression(wrappedType: BridgeType, kind: JSOptionalKind) -> ExprSyntax {
877924
let typeName = kind == .null ? "Optional" : "JSUndefinedOr"
878925
switch wrappedType {
@@ -897,6 +944,23 @@ struct StackCodegen {
897944
}
898945
}()
899946
"""
947+
case .dictionary(let valueType):
948+
let dictionaryLift = liftDictionaryExpression(valueType: valueType)
949+
let swiftTypeName = valueType.swiftType
950+
let absentExpr =
951+
kind == .null
952+
? "\(typeName)<[String: \(swiftTypeName)]>.none"
953+
: "\(typeName)<[String: \(swiftTypeName)]>.undefinedValue"
954+
return """
955+
{
956+
let __isSome = _swift_js_pop_i32()
957+
if __isSome == 0 {
958+
return \(raw: absentExpr)
959+
} else {
960+
return \(dictionaryLift)
961+
}
962+
}()
963+
"""
900964
case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol:
901965
fatalError("Invalid nullable wrapped type: \(wrappedType)")
902966
}
@@ -935,6 +999,8 @@ struct StackCodegen {
935999
return []
9361000
case .array(let elementType):
9371001
return lowerArrayStatements(elementType: elementType, accessor: accessor, varPrefix: varPrefix)
1002+
case .dictionary(let valueType):
1003+
return lowerDictionaryStatements(valueType: valueType, accessor: accessor, varPrefix: varPrefix)
9381004
}
9391005
}
9401006

@@ -952,7 +1018,7 @@ struct StackCodegen {
9521018
return ["\(raw: accessor).map { $0.jsObject }.bridgeJSLowerReturn()"]
9531019
case .swiftProtocol(let protocolName):
9541020
return ["\(raw: accessor).map { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"]
955-
case .nullable, .array, .closure:
1021+
case .nullable, .array, .closure, .dictionary:
9561022
return lowerArrayStatementsInline(
9571023
elementType: elementType,
9581024
accessor: accessor,
@@ -986,6 +1052,65 @@ struct StackCodegen {
9861052
return statements
9871053
}
9881054

1055+
private func lowerDictionaryStatements(
1056+
valueType: BridgeType,
1057+
accessor: String,
1058+
varPrefix: String
1059+
) -> [CodeBlockItemSyntax] {
1060+
switch valueType {
1061+
case .int, .uint, .float, .double, .string, .bool, .jsValue,
1062+
.jsObject(nil), .swiftStruct, .caseEnum, .swiftHeapObject,
1063+
.unsafePointer, .rawValueEnum, .associatedValueEnum:
1064+
return ["\(raw: accessor).bridgeJSLowerReturn()"]
1065+
case .jsObject(_?):
1066+
return ["\(raw: accessor).mapValues { $0.jsObject }.bridgeJSLowerReturn()"]
1067+
case .swiftProtocol(let protocolName):
1068+
return ["\(raw: accessor).mapValues { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"]
1069+
case .nullable, .array, .dictionary, .closure:
1070+
return lowerDictionaryStatementsInline(
1071+
valueType: valueType,
1072+
accessor: accessor,
1073+
varPrefix: varPrefix
1074+
)
1075+
case .void, .namespaceEnum:
1076+
fatalError("Invalid dictionary value type: \(valueType)")
1077+
}
1078+
}
1079+
1080+
private func lowerDictionaryStatementsInline(
1081+
valueType: BridgeType,
1082+
accessor: String,
1083+
varPrefix: String
1084+
) -> [CodeBlockItemSyntax] {
1085+
var statements: [CodeBlockItemSyntax] = []
1086+
let pairVarName = "__bjs_kv_\(varPrefix)"
1087+
statements.append("for \(raw: pairVarName) in \(raw: accessor) {")
1088+
statements.append("let __bjs_key_\(raw: varPrefix) = \(raw: pairVarName).key")
1089+
statements.append("let __bjs_value_\(raw: varPrefix) = \(raw: pairVarName).value")
1090+
1091+
let keyStatements = lowerStatements(
1092+
for: .string,
1093+
accessor: "__bjs_key_\(varPrefix)",
1094+
varPrefix: "\(varPrefix)_key"
1095+
)
1096+
for stmt in keyStatements {
1097+
statements.append(stmt)
1098+
}
1099+
1100+
let valueStatements = lowerStatements(
1101+
for: valueType,
1102+
accessor: "__bjs_value_\(varPrefix)",
1103+
varPrefix: "\(varPrefix)_value"
1104+
)
1105+
for stmt in valueStatements {
1106+
statements.append(stmt)
1107+
}
1108+
1109+
statements.append("}")
1110+
statements.append("_swift_js_push_i32(Int32(\(raw: accessor).count))")
1111+
return statements
1112+
}
1113+
9891114
private func lowerOptionalStatements(
9901115
wrappedType: BridgeType,
9911116
accessor: String,
@@ -1033,6 +1158,8 @@ struct StackCodegen {
10331158
return ["\(raw: unwrappedVar).jsObject.bridgeJSLowerStackReturn()"]
10341159
case .array(let elementType):
10351160
return lowerArrayStatements(elementType: elementType, accessor: unwrappedVar, varPrefix: varPrefix)
1161+
case .dictionary(let valueType):
1162+
return lowerDictionaryStatements(valueType: valueType, accessor: unwrappedVar, varPrefix: varPrefix)
10361163
default:
10371164
return ["preconditionFailure(\"BridgeJS: unsupported optional wrapped type\")"]
10381165
}
@@ -1616,6 +1743,7 @@ extension BridgeType {
16161743
case .nullable(let wrappedType, let kind):
16171744
return kind == .null ? "Optional<\(wrappedType.swiftType)>" : "JSUndefinedOr<\(wrappedType.swiftType)>"
16181745
case .array(let elementType): return "[\(elementType.swiftType)]"
1746+
case .dictionary(let valueType): return "[String: \(valueType.swiftType)]"
16191747
case .caseEnum(let name): return name
16201748
case .rawValueEnum(let name, _): return name
16211749
case .associatedValueEnum(let name): return name
@@ -1675,7 +1803,7 @@ extension BridgeType {
16751803
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
16761804
case .closure:
16771805
return LiftingIntrinsicInfo(parameters: [("callbackId", .i32)])
1678-
case .array:
1806+
case .array, .dictionary:
16791807
return LiftingIntrinsicInfo(parameters: [])
16801808
}
16811809
}
@@ -1726,7 +1854,7 @@ extension BridgeType {
17261854
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
17271855
case .closure:
17281856
return .swiftHeapObject
1729-
case .array:
1857+
case .array, .dictionary:
17301858
return .array
17311859
}
17321860
}

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ extension BridgeType {
980980
var params = [("isSome", WasmCoreType.i32)]
981981
params.append(contentsOf: wrappedInfo.loweredParameters)
982982
return LoweringParameterInfo(loweredParameters: params)
983-
case .array:
983+
case .array, .dictionary:
984984
return LoweringParameterInfo(loweredParameters: [])
985985
}
986986
}
@@ -1059,7 +1059,7 @@ extension BridgeType {
10591059
case .nullable(let wrappedType, _):
10601060
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
10611061
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
1062-
case .array:
1062+
case .array, .dictionary:
10631063
return LiftingReturnInfo(valueToLift: nil)
10641064
}
10651065
}

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,45 @@ public final class SwiftToSkeleton {
213213
return .array(elementType)
214214
}
215215
}
216+
// [String: T]
217+
if let dictType = type.as(DictionaryTypeSyntax.self) {
218+
if let keyType = lookupType(for: dictType.key, errors: &errors),
219+
keyType == .string,
220+
let valueType = lookupType(for: dictType.value, errors: &errors)
221+
{
222+
return .dictionary(valueType)
223+
}
224+
}
225+
// Dictionary<String, T>
226+
if let identifierType = type.as(IdentifierTypeSyntax.self),
227+
identifierType.name.text == "Dictionary",
228+
let genericArgs = identifierType.genericArgumentClause?.arguments,
229+
genericArgs.count == 2,
230+
let keyArg = TypeSyntax(genericArgs.first?.argument),
231+
let valueArg = TypeSyntax(genericArgs.last?.argument),
232+
let keyType = lookupType(for: keyArg, errors: &errors),
233+
keyType == .string
234+
{
235+
if let valueType = lookupType(for: valueArg, errors: &errors) {
236+
return .dictionary(valueType)
237+
}
238+
}
239+
// Swift.Dictionary<String, T>
240+
if let memberType = type.as(MemberTypeSyntax.self),
241+
let baseType = memberType.baseType.as(IdentifierTypeSyntax.self),
242+
baseType.name.text == "Swift",
243+
memberType.name.text == "Dictionary",
244+
let genericArgs = memberType.genericArgumentClause?.arguments,
245+
genericArgs.count == 2,
246+
let keyArg = TypeSyntax(genericArgs.first?.argument),
247+
let valueArg = TypeSyntax(genericArgs.last?.argument),
248+
let keyType = lookupType(for: keyArg, errors: &errors),
249+
keyType == .string
250+
{
251+
if let valueType = lookupType(for: valueArg, errors: &errors) {
252+
return .dictionary(valueType)
253+
}
254+
}
216255
if let aliasDecl = typeDeclResolver.resolveTypeAlias(type) {
217256
if let resolvedType = lookupType(for: aliasDecl.initializer.value, errors: &errors) {
218257
return resolvedType

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,9 @@ public struct BridgeJSLink {
14621462
return "(\(elementTypeStr))[]"
14631463
}
14641464
return "\(elementTypeStr)[]"
1465+
case .dictionary(let valueType):
1466+
let valueTypeStr = resolveTypeScriptType(valueType, exportedSkeletons: exportedSkeletons)
1467+
return "Record<string, \(valueTypeStr)>"
14651468
default:
14661469
return type.tsType
14671470
}
@@ -3564,6 +3567,8 @@ extension BridgeType {
35643567
return "(\(inner))[]"
35653568
}
35663569
return "\(inner)[]"
3570+
case .dictionary(let valueType):
3571+
return "Record<string, \(valueType.tsType)>"
35673572
}
35683573
}
35693574
}

0 commit comments

Comments
 (0)