Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 132 additions & 4 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,8 @@ struct StackCodegen {
return liftNullableExpression(wrappedType: wrappedType, kind: kind)
case .array(let elementType):
return liftArrayExpression(elementType: elementType)
case .dictionary(let valueType):
return liftDictionaryExpression(valueType: valueType)
case .closure:
return "JSObject.bridgeJSLiftParameter()"
case .void, .namespaceEnum:
Expand All @@ -849,7 +851,7 @@ struct StackCodegen {
return liftArrayExpressionInline(elementType: elementType)
case .swiftProtocol(let protocolName):
return "[Any\(raw: protocolName)].bridgeJSLiftParameter()"
case .nullable, .array, .closure:
case .nullable, .array, .closure, .dictionary:
return liftArrayExpressionInline(elementType: elementType)
case .void, .namespaceEnum:
fatalError("Invalid array element type: \(elementType)")
Expand All @@ -873,6 +875,51 @@ struct StackCodegen {
"""
}

func liftDictionaryExpression(valueType: BridgeType) -> ExprSyntax {
switch valueType {
case .int, .uint, .float, .double, .string, .bool, .jsValue,
.jsObject(nil), .swiftStruct, .caseEnum, .swiftHeapObject,
.unsafePointer, .rawValueEnum, .associatedValueEnum:
return "[String: \(raw: valueType.swiftType)].bridgeJSLiftParameter()"
case .jsObject(let className?):
return """
{
let __dict = [String: JSObject].bridgeJSLiftParameter()
return __dict.mapValues { \(raw: className)(unsafelyWrapping: $0) }
}()
"""
case .swiftProtocol(let protocolName):
return """
{
let __dict = [String: JSObject].bridgeJSLiftParameter()
return __dict.mapValues { $0 as! Any\(raw: protocolName) }
}()
"""
case .nullable, .array, .dictionary, .closure:
return liftDictionaryExpressionInline(valueType: valueType)
case .void, .namespaceEnum:
fatalError("Invalid dictionary value type: \(valueType)")
}
}

private func liftDictionaryExpressionInline(valueType: BridgeType) -> ExprSyntax {
let valueLift = liftExpression(for: valueType)
let swiftTypeName = valueType.swiftType
return """
{
let __count = Int(_swift_js_pop_i32())
var __result: [String: \(raw: swiftTypeName)] = [:]
__result.reserveCapacity(__count)
for _ in 0..<__count {
let __value = \(valueLift)
let __key = String.bridgeJSLiftParameter()
__result[__key] = __value
}
return __result
}()
"""
}

private func liftNullableExpression(wrappedType: BridgeType, kind: JSOptionalKind) -> ExprSyntax {
let typeName = kind == .null ? "Optional" : "JSUndefinedOr"
switch wrappedType {
Expand All @@ -897,6 +944,23 @@ struct StackCodegen {
}
}()
"""
case .dictionary(let valueType):
let dictionaryLift = liftDictionaryExpression(valueType: valueType)
let swiftTypeName = valueType.swiftType
let absentExpr =
kind == .null
? "\(typeName)<[String: \(swiftTypeName)]>.none"
: "\(typeName)<[String: \(swiftTypeName)]>.undefinedValue"
return """
{
let __isSome = _swift_js_pop_i32()
if __isSome == 0 {
return \(raw: absentExpr)
} else {
return \(dictionaryLift)
}
}()
"""
case .nullable, .void, .namespaceEnum, .closure, .unsafePointer, .swiftProtocol:
fatalError("Invalid nullable wrapped type: \(wrappedType)")
}
Expand Down Expand Up @@ -935,6 +999,8 @@ struct StackCodegen {
return []
case .array(let elementType):
return lowerArrayStatements(elementType: elementType, accessor: accessor, varPrefix: varPrefix)
case .dictionary(let valueType):
return lowerDictionaryStatements(valueType: valueType, accessor: accessor, varPrefix: varPrefix)
}
}

Expand All @@ -952,7 +1018,7 @@ struct StackCodegen {
return ["\(raw: accessor).map { $0.jsObject }.bridgeJSLowerReturn()"]
case .swiftProtocol(let protocolName):
return ["\(raw: accessor).map { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"]
case .nullable, .array, .closure:
case .nullable, .array, .closure, .dictionary:
return lowerArrayStatementsInline(
elementType: elementType,
accessor: accessor,
Expand Down Expand Up @@ -986,6 +1052,65 @@ struct StackCodegen {
return statements
}

private func lowerDictionaryStatements(
valueType: BridgeType,
accessor: String,
varPrefix: String
) -> [CodeBlockItemSyntax] {
switch valueType {
case .int, .uint, .float, .double, .string, .bool, .jsValue,
.jsObject(nil), .swiftStruct, .caseEnum, .swiftHeapObject,
.unsafePointer, .rawValueEnum, .associatedValueEnum:
return ["\(raw: accessor).bridgeJSLowerReturn()"]
case .jsObject(_?):
return ["\(raw: accessor).mapValues { $0.jsObject }.bridgeJSLowerReturn()"]
case .swiftProtocol(let protocolName):
return ["\(raw: accessor).mapValues { $0 as! Any\(raw: protocolName) }.bridgeJSLowerReturn()"]
case .nullable, .array, .dictionary, .closure:
return lowerDictionaryStatementsInline(
valueType: valueType,
accessor: accessor,
varPrefix: varPrefix
)
case .void, .namespaceEnum:
fatalError("Invalid dictionary value type: \(valueType)")
}
}

private func lowerDictionaryStatementsInline(
valueType: BridgeType,
accessor: String,
varPrefix: String
) -> [CodeBlockItemSyntax] {
var statements: [CodeBlockItemSyntax] = []
let pairVarName = "__bjs_kv_\(varPrefix)"
statements.append("for \(raw: pairVarName) in \(raw: accessor) {")
statements.append("let __bjs_key_\(raw: varPrefix) = \(raw: pairVarName).key")
statements.append("let __bjs_value_\(raw: varPrefix) = \(raw: pairVarName).value")

let keyStatements = lowerStatements(
for: .string,
accessor: "__bjs_key_\(varPrefix)",
varPrefix: "\(varPrefix)_key"
)
for stmt in keyStatements {
statements.append(stmt)
}

let valueStatements = lowerStatements(
for: valueType,
accessor: "__bjs_value_\(varPrefix)",
varPrefix: "\(varPrefix)_value"
)
for stmt in valueStatements {
statements.append(stmt)
}

statements.append("}")
statements.append("_swift_js_push_i32(Int32(\(raw: accessor).count))")
return statements
}

private func lowerOptionalStatements(
wrappedType: BridgeType,
accessor: String,
Expand Down Expand Up @@ -1033,6 +1158,8 @@ struct StackCodegen {
return ["\(raw: unwrappedVar).jsObject.bridgeJSLowerStackReturn()"]
case .array(let elementType):
return lowerArrayStatements(elementType: elementType, accessor: unwrappedVar, varPrefix: varPrefix)
case .dictionary(let valueType):
return lowerDictionaryStatements(valueType: valueType, accessor: unwrappedVar, varPrefix: varPrefix)
default:
return ["preconditionFailure(\"BridgeJS: unsupported optional wrapped type\")"]
}
Expand Down Expand Up @@ -1616,6 +1743,7 @@ extension BridgeType {
case .nullable(let wrappedType, let kind):
return kind == .null ? "Optional<\(wrappedType.swiftType)>" : "JSUndefinedOr<\(wrappedType.swiftType)>"
case .array(let elementType): return "[\(elementType.swiftType)]"
case .dictionary(let valueType): return "[String: \(valueType.swiftType)]"
case .caseEnum(let name): return name
case .rawValueEnum(let name, _): return name
case .associatedValueEnum(let name): return name
Expand Down Expand Up @@ -1675,7 +1803,7 @@ extension BridgeType {
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
case .closure:
return LiftingIntrinsicInfo(parameters: [("callbackId", .i32)])
case .array:
case .array, .dictionary:
return LiftingIntrinsicInfo(parameters: [])
}
}
Expand Down Expand Up @@ -1726,7 +1854,7 @@ extension BridgeType {
throw BridgeJSCoreError("Namespace enums are not supported to pass as parameters")
case .closure:
return .swiftHeapObject
case .array:
case .array, .dictionary:
return .array
}
}
Expand Down
4 changes: 2 additions & 2 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ extension BridgeType {
var params = [("isSome", WasmCoreType.i32)]
params.append(contentsOf: wrappedInfo.loweredParameters)
return LoweringParameterInfo(loweredParameters: params)
case .array:
case .array, .dictionary:
return LoweringParameterInfo(loweredParameters: [])
}
}
Expand Down Expand Up @@ -1059,7 +1059,7 @@ extension BridgeType {
case .nullable(let wrappedType, _):
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
case .array:
case .array, .dictionary:
return LiftingReturnInfo(valueToLift: nil)
}
}
Expand Down
39 changes: 39 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,45 @@ public final class SwiftToSkeleton {
return .array(elementType)
}
}
// [String: T]
if let dictType = type.as(DictionaryTypeSyntax.self) {
if let keyType = lookupType(for: dictType.key, errors: &errors),
keyType == .string,
let valueType = lookupType(for: dictType.value, errors: &errors)
{
return .dictionary(valueType)
}
}
// Dictionary<String, T>
if let identifierType = type.as(IdentifierTypeSyntax.self),
identifierType.name.text == "Dictionary",
let genericArgs = identifierType.genericArgumentClause?.arguments,
genericArgs.count == 2,
let keyArg = TypeSyntax(genericArgs.first?.argument),
let valueArg = TypeSyntax(genericArgs.last?.argument),
let keyType = lookupType(for: keyArg, errors: &errors),
keyType == .string
{
if let valueType = lookupType(for: valueArg, errors: &errors) {
return .dictionary(valueType)
}
}
// Swift.Dictionary<String, T>
if let memberType = type.as(MemberTypeSyntax.self),
let baseType = memberType.baseType.as(IdentifierTypeSyntax.self),
baseType.name.text == "Swift",
memberType.name.text == "Dictionary",
let genericArgs = memberType.genericArgumentClause?.arguments,
genericArgs.count == 2,
let keyArg = TypeSyntax(genericArgs.first?.argument),
let valueArg = TypeSyntax(genericArgs.last?.argument),
let keyType = lookupType(for: keyArg, errors: &errors),
keyType == .string
{
if let valueType = lookupType(for: valueArg, errors: &errors) {
return .dictionary(valueType)
}
}
if let aliasDecl = typeDeclResolver.resolveTypeAlias(type) {
if let resolvedType = lookupType(for: aliasDecl.initializer.value, errors: &errors) {
return resolvedType
Expand Down
5 changes: 5 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,9 @@ public struct BridgeJSLink {
return "(\(elementTypeStr))[]"
}
return "\(elementTypeStr)[]"
case .dictionary(let valueType):
let valueTypeStr = resolveTypeScriptType(valueType, exportedSkeletons: exportedSkeletons)
return "Record<string, \(valueTypeStr)>"
default:
return type.tsType
}
Expand Down Expand Up @@ -3564,6 +3567,8 @@ extension BridgeType {
return "(\(inner))[]"
}
return "\(inner)[]"
case .dictionary(let valueType):
return "Record<string, \(valueType.tsType)>"
}
}
}
Expand Down
Loading