Skip to content

Commit e7278d6

Browse files
committed
BridgeJS: Generic function review fixes and simplifications
1 parent ef81857 commit e7278d6

22 files changed

Lines changed: 986 additions & 618 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -748,21 +748,9 @@ public class ExportSwift {
748748
printer.indent {
749749
printer.write("guard !_bridgeJSExportTypeRegistryInitialized else { return }")
750750
printer.write("_bridgeJSExportTypeRegistryInitialized = true")
751-
printer.write("_bjs_registerGenericExportType(Bool.self)")
752-
printer.write("_bjs_registerGenericExportType(Int.self)")
753-
printer.write("_bjs_registerGenericExportType(Int8.self)")
754-
printer.write("_bjs_registerGenericExportType(UInt8.self)")
755-
printer.write("_bjs_registerGenericExportType(Int16.self)")
756-
printer.write("_bjs_registerGenericExportType(UInt16.self)")
757-
printer.write("_bjs_registerGenericExportType(Int32.self)")
758-
printer.write("_bjs_registerGenericExportType(UInt32.self)")
759-
printer.write("_bjs_registerGenericExportType(UInt.self)")
760-
printer.write("_bjs_registerGenericExportType(Int64.self)")
761-
printer.write("_bjs_registerGenericExportType(UInt64.self)")
762-
printer.write("_bjs_registerGenericExportType(Float.self)")
763-
printer.write("_bjs_registerGenericExportType(Double.self)")
764-
printer.write("_bjs_registerGenericExportType(String.self)")
765-
printer.write("_bjs_registerGenericExportType(JSValue.self)")
751+
for primitive in BridgeType.genericBridgeablePrimitives {
752+
printer.write("_bjs_registerGenericExportType(\(primitive.token).self)")
753+
}
766754
for structDef in skeleton.structs {
767755
printer.write("_bjs_registerGenericExportType(\(structDef.swiftCallName).self)")
768756
}
@@ -857,8 +845,9 @@ public class ExportSwift {
857845
let concreteLiftedExprs = builder.liftedParameterExprs
858846
let concreteABINames = concreteABIParameters.map { $0.name }
859847

860-
func metatypeName(_ genericName: String) -> String { "\(genericName.lowercased())Type" }
861-
func typeIdName(_ genericName: String) -> String { "\(genericName.lowercased())TypeId" }
848+
func genericIndex(_ genericName: String) -> Int { genericNames.firstIndex(of: genericName) ?? 0 }
849+
func metatypeName(_ genericName: String) -> String { "_generic\(genericIndex(genericName))Type" }
850+
func typeIdName(_ genericName: String) -> String { "_generic\(genericIndex(genericName))TypeId" }
862851

863852
func argument(label: String?, expression: String) -> String {
864853
if let label, label != "_" {
@@ -1687,12 +1676,10 @@ struct StructCodegen {
16871676
returnType: .i32
16881677
)
16891678

1690-
var decls: [DeclSyntax] = [
1679+
return [
16911680
bridgedStructExtension, "\(raw: lowerExternDeclPrinter.lines.joined(separator: "\n"))",
16921681
"\(raw: liftExternDeclPrinter.lines.joined(separator: "\n"))",
16931682
]
1694-
1695-
return decls
16961683
}
16971684

16981685
private func generateStructLiftCode(structDef: ExportedStruct) -> [String] {
@@ -1943,22 +1930,22 @@ extension UnsafePointerType {
19431930

19441931
extension BridgeType {
19451932
var genericStackPopExpression: String? {
1946-
switch self {
1947-
case .generic(let name): return "\(name).bridgeJSStackPop()"
1948-
case .array(.generic(let name)): return "_bridgeJSStackPopArrayGeneric(\(name).self)"
1949-
case .nullable(.generic(let name), _): return "_bridgeJSStackPopOptionalGeneric(\(name).self)"
1950-
case .dictionary(.generic(let name)): return "_bridgeJSStackPopDictGeneric(\(name).self)"
1951-
default: return nil
1933+
guard let wrapper = genericWrapper else { return nil }
1934+
switch wrapper.kind {
1935+
case .bare: return "\(wrapper.name).bridgeJSStackPop()"
1936+
case .array: return "_bridgeJSStackPopArrayGeneric(\(wrapper.name).self)"
1937+
case .optional: return "_bridgeJSStackPopOptionalGeneric(\(wrapper.name).self)"
1938+
case .dictionary: return "_bridgeJSStackPopDictGeneric(\(wrapper.name).self)"
19521939
}
19531940
}
19541941

19551942
func genericStackPushStatement(value: String) -> String? {
1956-
switch self {
1957-
case .generic: return "\(value).bridgeJSStackPush()"
1958-
case .array(.generic): return "_bridgeJSStackPushArrayGeneric(\(value))"
1959-
case .nullable(.generic, _): return "_bridgeJSStackPushOptionalGeneric(\(value))"
1960-
case .dictionary(.generic): return "_bridgeJSStackPushDictGeneric(\(value))"
1961-
default: return nil
1943+
guard let wrapper = genericWrapper else { return nil }
1944+
switch wrapper.kind {
1945+
case .bare: return "\(value).bridgeJSStackPush()"
1946+
case .array: return "_bridgeJSStackPushArrayGeneric(\(value))"
1947+
case .optional: return "_bridgeJSStackPushOptionalGeneric(\(value))"
1948+
case .dictionary: return "_bridgeJSStackPushDictGeneric(\(value))"
19621949
}
19631950
}
19641951

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ public struct ImportTS {
246246
abiParameterForwardings.insert(contentsOf: ["resolveRef", "rejectRef"], at: 0)
247247
}
248248

249-
func appendTypeIDParameter(genericParameterName: String) {
250-
let abiParamName = "\(genericParameterName.lowercased())TypeId"
249+
func appendTypeIDParameter(index: Int, genericParameterName: String) {
250+
let abiParamName = "_generic\(index)TypeId"
251251
abiParameterSignatures.append((abiParamName, .i32))
252252
abiParameterForwardings.append("\(genericParameterName).bridgeJSTypeID")
253253
}
@@ -453,8 +453,8 @@ public struct ImportTS {
453453
for param in function.parameters {
454454
try builder.lowerParameter(param: param)
455455
}
456-
for genericParam in function.genericParameters ?? [] {
457-
builder.appendTypeIDParameter(genericParameterName: genericParam)
456+
for (index, genericParam) in (function.genericParameters ?? []).enumerated() {
457+
builder.appendTypeIDParameter(index: index, genericParameterName: genericParam)
458458
}
459459
try builder.call()
460460
try builder.liftReturnValue()

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,13 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
12991299
)
13001300
return nil
13011301
}
1302+
if node.signature.effectSpecifiers?.throwsClause != nil {
1303+
diagnose(
1304+
node: node,
1305+
message: "Generic @JS functions cannot be 'throws' yet."
1306+
)
1307+
return nil
1308+
}
13021309
}
13031310

13041311
let name = node.name.text

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 64 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,15 @@ public struct BridgeJSLink {
424424
private func genericWrapperHelperDeclarations() -> [String] {
425425
let i32 = JSGlueVariableScope.reservedI32Stack
426426
let codecs = JSGlueVariableScope.reservedCodecs
427+
let codecsById = JSGlueVariableScope.reservedCodecsById
427428
return [
429+
"function __bjs_resolveGenericType(token) {",
430+
" const codec = \(codecs)[token];",
431+
" if (!codec) { throw new Error(\"BridgeJS: no generic codec registered for type '\" + token + \"'\"); }",
432+
" let id = \(codecsById).indexOf(codec);",
433+
" if (id === -1) { id = \(codecsById).push(codec) - 1; }",
434+
" return id;",
435+
"}",
428436
"function __bjs_lowerArrayGeneric(value, codec) {",
429437
" for (let i = 0; i < value.length; i++) { codec.lower(value[i]); }",
430438
" \(i32).push(value.length);",
@@ -475,23 +483,7 @@ public struct BridgeJSLink {
475483
allClasses: [ExportedClass],
476484
allEnums: [ExportedEnum]
477485
) -> [(token: String, type: BridgeType)] {
478-
var entries: [(token: String, type: BridgeType)] = [
479-
("Bool", .bool),
480-
("Int", .integer(.int)),
481-
("Int8", .integer(.int8)),
482-
("UInt8", .integer(.uint8)),
483-
("Int16", .integer(.int16)),
484-
("UInt16", .integer(.uint16)),
485-
("Int32", .integer(.int32)),
486-
("UInt32", .integer(.uint32)),
487-
("UInt", .integer(.uint)),
488-
("Int64", .integer(.int64)),
489-
("UInt64", .integer(.uint64)),
490-
("Float", .float),
491-
("Double", .double),
492-
("String", .string),
493-
("JSValue", .jsValue),
494-
]
486+
var entries = BridgeType.genericBridgeablePrimitives
495487
for structDef in allStructs {
496488
entries.append((structDef.abiName, .swiftStruct(structDef.abiName)))
497489
}
@@ -550,57 +542,39 @@ public struct BridgeJSLink {
550542
printer.write("};")
551543
printer.write("bjs[\"swift_js_resolve_type_id\"] = function(ptr, len) {")
552544
printer.indent {
553-
printer.write("const name = \(decodeString)(ptr, len);")
554-
printer.write("const codec = \(JSGlueVariableScope.reservedCodecs)[name];")
555-
printer.write("if (!codec) {")
556-
printer.indent {
557-
printer.write(
558-
"throw new Error(\"BridgeJS: no generic codec registered for type '\" + name + \"'\");"
559-
)
560-
}
561-
printer.write("}")
562-
printer.write("let id = \(JSGlueVariableScope.reservedCodecsById).indexOf(codec);")
563-
printer.write("if (id === -1) {")
564-
printer.indent {
565-
printer.write("id = \(JSGlueVariableScope.reservedCodecsById).push(codec) - 1;")
566-
}
567-
printer.write("}")
568-
printer.write("return id;")
545+
printer.write("return __bjs_resolveGenericType(\(decodeString)(ptr, len));")
569546
}
570547
printer.write("}")
571548
}
572549

573550
private func generateBridgeTypeTokens() -> (js: [String], dts: [String]) {
574-
let primitives: [(token: String, tsType: String)] = [
575-
("Bool", BridgeType.bool.tsType),
576-
("Int", BridgeType.integer(.int).tsType),
577-
("Int8", BridgeType.integer(.int8).tsType),
578-
("UInt8", BridgeType.integer(.uint8).tsType),
579-
("Int16", BridgeType.integer(.int16).tsType),
580-
("UInt16", BridgeType.integer(.uint16).tsType),
581-
("Int32", BridgeType.integer(.int32).tsType),
582-
("UInt32", BridgeType.integer(.uint32).tsType),
583-
("UInt", BridgeType.integer(.uint).tsType),
584-
("Int64", BridgeType.integer(.int64).tsType),
585-
("UInt64", BridgeType.integer(.uint64).tsType),
586-
("Float", BridgeType.float.tsType),
587-
("Double", BridgeType.double.tsType),
588-
("String", BridgeType.string.tsType),
589-
("JSValue", BridgeType.jsValue.tsType),
590-
]
551+
let primitives: [(token: String, tsType: String)] = BridgeType.genericBridgeablePrimitives.map {
552+
(token: $0.token, tsType: $0.type.tsType)
553+
}
554+
func tsQualifiedName(name: String, namespace: [String]?) -> String {
555+
((namespace ?? []) + [name]).joined(separator: ".")
556+
}
591557
let structs = skeletons.compactMap { $0.exported?.structs }.flatMap { $0 }
592558
let classes = skeletons.compactMap { $0.exported?.classes }.flatMap { $0 }
593559
let enums = skeletons.compactMap { $0.exported?.enums }.flatMap { $0 }
594560
var tokens: [(token: String, tsType: String)] = primitives
595561
for structDef in structs {
596-
tokens.append((token: structDef.abiName, tsType: structDef.name))
562+
tokens.append(
563+
(
564+
token: structDef.abiName,
565+
tsType: tsQualifiedName(name: structDef.name, namespace: structDef.namespace)
566+
)
567+
)
597568
}
598569
for klass in classes where klass.isFinal == true {
599-
tokens.append((token: klass.abiName, tsType: klass.name))
570+
tokens.append(
571+
(token: klass.abiName, tsType: tsQualifiedName(name: klass.name, namespace: klass.namespace))
572+
)
600573
}
601574
for enumDef in enums {
602-
guard let bridgeType = genericEnumBridgeType(enumDef) else { continue }
603-
tokens.append((token: enumDef.abiName, tsType: bridgeType.tsType))
575+
guard genericEnumBridgeType(enumDef) != nil else { continue }
576+
let enumTypeName = enumDef.emitStyle == .tsEnum ? enumDef.tsFullPath : "\(enumDef.tsFullPath)Tag"
577+
tokens.append((token: enumDef.abiName, tsType: enumTypeName))
604578
}
605579

606580
let jsEntries = tokens.map { "\($0.token): \"\($0.token)\"" }
@@ -2132,22 +2106,22 @@ extension BridgeJSLink {
21322106
}
21332107

21342108
static func genericCodecLowerStatement(type: BridgeType, codec: String, value: String) -> String? {
2135-
switch type {
2136-
case .generic: return "\(codec).lower(\(value));"
2137-
case .array(.generic): return "__bjs_lowerArrayGeneric(\(value), \(codec));"
2138-
case .nullable(.generic, _): return "__bjs_lowerOptionalGeneric(\(value), \(codec));"
2139-
case .dictionary(.generic): return "__bjs_lowerDictGeneric(\(value), \(codec));"
2140-
default: return nil
2109+
guard let wrapper = type.genericWrapper else { return nil }
2110+
switch wrapper.kind {
2111+
case .bare: return "\(codec).lower(\(value));"
2112+
case .array: return "__bjs_lowerArrayGeneric(\(value), \(codec));"
2113+
case .optional: return "__bjs_lowerOptionalGeneric(\(value), \(codec));"
2114+
case .dictionary: return "__bjs_lowerDictGeneric(\(value), \(codec));"
21412115
}
21422116
}
21432117

21442118
static func genericCodecLiftExpression(type: BridgeType, codec: String) -> String? {
2145-
switch type {
2146-
case .generic: return "\(codec).lift()"
2147-
case .array(.generic): return "__bjs_liftArrayGeneric(\(codec))"
2148-
case .nullable(.generic, _): return "__bjs_liftOptionalGeneric(\(codec))"
2149-
case .dictionary(.generic): return "__bjs_liftDictGeneric(\(codec))"
2150-
default: return nil
2119+
guard let wrapper = type.genericWrapper else { return nil }
2120+
switch wrapper.kind {
2121+
case .bare: return "\(codec).lift()"
2122+
case .array: return "__bjs_liftArrayGeneric(\(codec))"
2123+
case .optional: return "__bjs_liftOptionalGeneric(\(codec))"
2124+
case .dictionary: return "__bjs_liftDictGeneric(\(codec))"
21512125
}
21522126
}
21532127

@@ -2168,7 +2142,10 @@ extension BridgeJSLink {
21682142
let genericNames = function.genericParameters ?? []
21692143

21702144
func genericNameOf(_ param: Parameter) -> String {
2171-
param.type.referencedGenericName ?? genericNames[0]
2145+
guard let name = param.type.referencedGenericName else {
2146+
preconditionFailure("Generic value parameter '\(param.name)' has no referenced generic name")
2147+
}
2148+
return name
21722149
}
21732150

21742151
let genericValueParameters = function.parameters.filter { $0.type.usesBareGeneric }
@@ -2188,9 +2165,20 @@ extension BridgeJSLink {
21882165
}
21892166
let concreteForwardings = thunkBuilder.parameterForwardings
21902167

2191-
func tokenName(_ genericName: String) -> String { "type\(genericName)" }
2192-
func codecVariable(_ genericName: String) -> String { "codec\(genericName)" }
2193-
func typeIdVariable(_ genericName: String) -> String { "\(genericName.lowercased())TypeId" }
2168+
let nameScope = JSGlueVariableScope(intrinsicRegistry: intrinsicRegistry)
2169+
for parameter in function.parameters {
2170+
_ = nameScope.variable(parameter.name)
2171+
}
2172+
let tokenNames = Dictionary(uniqueKeysWithValues: genericNames.map { ($0, nameScope.variable("type\($0)")) })
2173+
let codecNames = Dictionary(uniqueKeysWithValues: genericNames.map { ($0, nameScope.variable("codec\($0)")) })
2174+
let typeIdNames = Dictionary(
2175+
uniqueKeysWithValues: genericNames.map { ($0, nameScope.variable("\($0.lowercased())TypeId")) }
2176+
)
2177+
func tokenName(_ genericName: String) -> String { tokenNames[genericName] ?? "type\(genericName)" }
2178+
func codecVariable(_ genericName: String) -> String { codecNames[genericName] ?? "codec\(genericName)" }
2179+
func typeIdVariable(_ genericName: String) -> String {
2180+
typeIdNames[genericName] ?? "\(genericName.lowercased())TypeId"
2181+
}
21942182

21952183
let returnGenericName = function.returnType.referencedGenericName
21962184

@@ -2200,16 +2188,10 @@ extension BridgeJSLink {
22002188
printer.indent {
22012189
for genericName in genericNames {
22022190
printer.write(
2203-
"const \(codecVariable(genericName)) = \(JSGlueVariableScope.reservedCodecs)[\(tokenName(genericName))];"
2204-
)
2205-
printer.write(
2206-
"if (!\(codecVariable(genericName))) { throw new Error(\"BridgeJS: no generic codec registered for type '\" + \(tokenName(genericName)) + \"'\"); }"
2207-
)
2208-
printer.write(
2209-
"let \(typeIdVariable(genericName)) = \(JSGlueVariableScope.reservedCodecsById).indexOf(\(codecVariable(genericName)));"
2191+
"const \(typeIdVariable(genericName)) = __bjs_resolveGenericType(\(tokenName(genericName)));"
22102192
)
22112193
printer.write(
2212-
"if (\(typeIdVariable(genericName)) === -1) { \(typeIdVariable(genericName)) = \(JSGlueVariableScope.reservedCodecsById).push(\(codecVariable(genericName))) - 1; }"
2194+
"const \(codecVariable(genericName)) = \(JSGlueVariableScope.reservedCodecsById)[\(typeIdVariable(genericName))];"
22132195
)
22142196
}
22152197
printer.write(contentsOf: thunkBuilder.body)
@@ -2652,6 +2634,7 @@ extension BridgeJSLink {
26522634
var parameterForwardings: [String] = []
26532635
var returnExpr: String?
26542636
var genericCodecVariables: [String: String] = [:]
2637+
var genericTypeIdParameters: [String: String] = [:]
26552638
let printContext: IntrinsicJSFragment.PrintCodeContext
26562639

26572640
init(
@@ -2679,10 +2662,11 @@ extension BridgeJSLink {
26792662

26802663
func declareGenericCodecs(genericParameters: [String]) {
26812664
for genericParam in genericParameters {
2682-
let typeIdParam = "\(genericParam.lowercased())TypeId"
2665+
let typeIdParam = scope.variable("\(genericParam.lowercased())TypeId")
26832666
let codecVar = scope.variable("codec\(genericParam)")
26842667
body.write("const \(codecVar) = \(JSGlueVariableScope.reservedCodecsById)[\(typeIdParam)];")
26852668
genericCodecVariables[genericParam] = codecVar
2669+
genericTypeIdParameters[genericParam] = typeIdParam
26862670
}
26872671
}
26882672

@@ -3729,7 +3713,7 @@ extension BridgeJSLink {
37293713
try thunkBuilder.liftParameter(param: param)
37303714
}
37313715
for genericParam in genericParameters {
3732-
thunkBuilder.parameterNames.append("\(genericParam.lowercased())TypeId")
3716+
thunkBuilder.parameterNames.append(thunkBuilder.genericTypeIdParameters[genericParam] ?? genericParam)
37333717
}
37343718
let jsName = function.jsName ?? function.name
37353719
let importRootExpr = function.from == .global ? "globalThis" : "imports"

0 commit comments

Comments
 (0)