Skip to content

Commit 2ae6bbf

Browse files
committed
improved duplicated name (overloads) "mangling" for cdecls
1 parent f742aba commit 2ae6bbf

File tree

10 files changed

+119
-34
lines changed

10 files changed

+119
-34
lines changed

Sources/JExtractSwift/ImportedDecls.swift

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ public enum NominalTypeKind {
6868
}
6969

7070
public struct ImportedParam {
71-
let param: FunctionParameterSyntax
71+
let syntax: FunctionParameterSyntax
7272

7373
var firstName: String? {
74-
let text = param.firstName.trimmed.text
74+
let text = syntax.firstName.trimmed.text
7575
guard text != "_" else {
7676
return nil
7777
}
@@ -80,7 +80,7 @@ public struct ImportedParam {
8080
}
8181

8282
var secondName: String? {
83-
let text = param.secondName?.trimmed.text
83+
let text = syntax.secondName?.trimmed.text
8484
guard text != "_" else {
8585
return nil
8686
}
@@ -94,7 +94,7 @@ public struct ImportedParam {
9494

9595
// The Swift type as-is from the swift interface
9696
var swiftType: String {
97-
param.type.trimmed.description
97+
syntax.type.trimmed.description
9898
}
9999

100100
// The mapped-to Java type of the above Java type, collections and optionals may be replaced with Java ones etc.
@@ -182,15 +182,15 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible {
182182
case .pointer:
183183
let selfParam: FunctionParameterSyntax = "self$: $swift_pointer"
184184
params.append(
185-
ImportedParam(param: selfParam, type: parent)
185+
ImportedParam(syntax: selfParam, type: parent)
186186
)
187187

188188
case .memorySegment:
189189
let selfParam: FunctionParameterSyntax = "self$: $java_lang_foreign_MemorySegment"
190190
var parentForSelf = parent
191191
parentForSelf.javaType = .javaForeignMemorySegment
192192
params.append(
193-
ImportedParam(param: selfParam, type: parentForSelf)
193+
ImportedParam(syntax: selfParam, type: parentForSelf)
194194
)
195195

196196
case .swiftThunkSelf:
@@ -205,10 +205,6 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible {
205205
}
206206
}
207207

208-
public var accessorThunkName: String {
209-
SwiftKitPrinting.Names.functionThunk(module: self.module, function: self)
210-
}
211-
212208
public var swiftDecl: any DeclSyntaxProtocol
213209

214210
public var syntax: String? {
@@ -236,7 +232,6 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible {
236232
public var description: String {
237233
"""
238234
ImportedFunc {
239-
accessorThunkName: \(self.accessorThunkName)
240235
identifier: \(identifier)
241236
returnType: \(returnType)
242237
parameters: \(parameters)
@@ -312,7 +307,7 @@ public struct ImportedVariable: ImportedDecl, CustomStringConvertible {
312307
parent: self.parentName,
313308
identifier: self.identifier,
314309
returnType: TranslatedType.void,
315-
parameters: [.init(param: newValueParam, type: self.returnType)])
310+
parameters: [.init(syntax: newValueParam, type: self.returnType)])
316311
return funcDecl
317312

318313
case .get:
@@ -337,7 +332,7 @@ public struct ImportedVariable: ImportedDecl, CustomStringConvertible {
337332
"_ newValue: \(raw: self.returnType.swiftTypeName)"
338333
params.append(
339334
ImportedParam(
340-
param: newValueParam,
335+
syntax: newValueParam,
341336
type: self.returnType)
342337
)
343338
}
@@ -351,7 +346,7 @@ public struct ImportedVariable: ImportedDecl, CustomStringConvertible {
351346
let selfParam: FunctionParameterSyntax = "self$: $swift_pointer"
352347
params.append(
353348
ImportedParam(
354-
param: selfParam,
349+
syntax: selfParam,
355350
type: parentName
356351
)
357352
)
@@ -362,7 +357,7 @@ public struct ImportedVariable: ImportedDecl, CustomStringConvertible {
362357
parentForSelf.javaType = .javaForeignMemorySegment
363358
params.append(
364359
ImportedParam(
365-
param: selfParam,
360+
syntax: selfParam,
366361
type: parentForSelf
367362
)
368363
)

Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,10 +648,14 @@ extension Swift2JavaTranslator {
648648
_ printer: inout CodePrinter, _ decl: ImportedFunc,
649649
accessorKind: VariableAccessorKind? = nil
650650
) {
651+
var thunkName = SwiftKitPrinting.Names.functionThunk(
652+
thunkNameRegistry: &self.thunkNameRegistry,
653+
module: self.swiftModuleName, function: decl)
654+
thunkName = thunkNameRegistry.deduplicate(name: thunkName)
651655
printer.print(
652656
"""
653657
public static final MemorySegment \(accessorKind.renderAddrFieldName) =
654-
\(self.swiftModuleName).findOrThrow("\(SwiftKitPrinting.Names.functionThunk(module: self.swiftModuleName, function: decl))");
658+
\(self.swiftModuleName).findOrThrow("\(thunkName)");
655659
"""
656660
)
657661
}

Sources/JExtractSwift/Swift2JavaTranslator.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ public final class Swift2JavaTranslator {
3636

3737
// ==== Output state
3838

39-
// TODO: consider how/if we need to store those etc
40-
public var importedGlobalFuncs: [ImportedFunc] = []
39+
package var importedGlobalFuncs: [ImportedFunc] = []
4140

4241
/// A mapping from Swift type names (e.g., A.B) over to the imported nominal
4342
/// type representation.
44-
public var importedTypes: [String: ImportedNominalType] = [:]
43+
package var importedTypes: [String: ImportedNominalType] = [:]
4544

4645
let nominalResolution: NominalTypeResolution = NominalTypeResolution()
4746

47+
var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry()
48+
4849
public init(
4950
javaPackage: String,
5051
swiftModuleName: String

Sources/JExtractSwift/Swift2JavaVisitor.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ final class Swift2JavaVisitor: SyntaxVisitor {
9595
// TODO: more robust parameter handling
9696
// TODO: More robust type handling
9797
return ImportedParam(
98-
param: param,
98+
syntax: param,
9999
type: try cCompatibleType(for: param.type)
100100
)
101101
}
@@ -193,7 +193,7 @@ final class Swift2JavaVisitor: SyntaxVisitor {
193193
// TODO: more robust parameter handling
194194
// TODO: More robust type handling
195195
return ImportedParam(
196-
param: param,
196+
syntax: param,
197197
type: try cCompatibleType(for: param.type)
198198
)
199199
}

Sources/JExtractSwift/SwiftKit+Printing.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,33 @@ package struct SwiftKitPrinting {
3232
// Helpers to form names of "well known" SwiftKit generated functions
3333

3434
extension SwiftKitPrinting {
35-
enum Names {}
35+
enum Names {
36+
}
3637
}
3738

3839
extension SwiftKitPrinting.Names {
3940
static func getType(module: String, nominal: ImportedNominalType) -> String {
4041
"swiftjava_getType_\(module)_\(nominal.swiftTypeName)"
4142
}
4243

43-
static func functionThunk(module: String, function: ImportedFunc) -> String {
44-
if let parent = function.parent {
45-
"swiftjava_\(module)_\(parent.swiftTypeName)_\(function.baseIdentifier)_"
46-
} else {
47-
"swiftjava_\(module)_\(function.baseIdentifier)"
44+
static func functionThunk(
45+
thunkNameRegistry: inout ThunkNameRegistry,
46+
module: String, function: ImportedFunc) -> String {
47+
let params = function.effectiveParameters(paramPassingStyle: .swiftThunkSelf)
48+
var paramsPart = ""
49+
if !params.isEmpty {
50+
paramsPart = "_" + params.map { param in
51+
param.firstName ?? "_"
52+
}.joined(separator: "_")
4853
}
54+
55+
let name =
56+
if let parent = function.parent {
57+
"swiftjava_\(module)_\(parent.swiftTypeName)_\(function.baseIdentifier)\(paramsPart)"
58+
} else {
59+
"swiftjava_\(module)_\(function.baseIdentifier)\(paramsPart)"
60+
}
61+
62+
return thunkNameRegistry.deduplicate(name: name)
4963
}
5064
}

Sources/JExtractSwift/SwiftThunkTranslator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ struct SwiftThunkTranslator {
6666
"""
6767
@_cdecl("\(raw: funcName)")
6868
public func \(raw: funcName)() -> Any /* Any.Type */ {
69-
print("[swift] get type \\(\(raw: nominal.swiftTypeName).self)")
70-
print("[swift] type object identifier ObjectIdentifier(\(raw: nominal.swiftTypeName).self) = \\(ObjectIdentifier(\(raw: nominal.swiftTypeName).self))")
7169
return \(raw: nominal.swiftTypeName).self
7270
}
7371
"""
@@ -79,6 +77,7 @@ struct SwiftThunkTranslator {
7977
}
8078

8179
let funcName = SwiftKitPrinting.Names.functionThunk(
80+
thunkNameRegistry: &self.st.thunkNameRegistry,
8281
module: st.swiftModuleName,
8382
function: function)
8483

@@ -100,6 +99,7 @@ struct SwiftThunkTranslator {
10099
func render(forFunc decl: ImportedFunc) -> [DeclSyntax] {
101100
st.log.trace("Rendering thunks for: \(decl.baseIdentifier)")
102101
let funcName = SwiftKitPrinting.Names.functionThunk(
102+
thunkNameRegistry: &st.thunkNameRegistry,
103103
module: st.swiftModuleName,
104104
function: decl)
105105

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// Registry of names we've already emitted as @_cdecl and must be kept unique.
16+
/// In order to avoid duplicate symbols, the registry can append some unique identifier to duplicated names
17+
package struct ThunkNameRegistry {
18+
/// Maps base names such as "swiftjava_Module_Type_method_a_b_c" to the number of times we've seen them.
19+
/// This is used to de-duplicate symbols as we emit them.
20+
private var baseNames: [String: Int] = [:]
21+
22+
package init() {}
23+
24+
package mutating func deduplicate(name: String) -> String {
25+
var emittedCount = self.baseNames[name, default: 0]
26+
self.baseNames[name] = emittedCount + 1
27+
28+
if emittedCount == 0 {
29+
return name // first occurrence of a name we keep as-is
30+
} else {
31+
return "\(name)$\(emittedCount)"
32+
}
33+
}
34+
}

Tests/JExtractSwiftTests/Asserts/TextAssertions.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ func assertOutput(
5757
let textLinesAtOffset = gotLines[offset..<offset+detectChunkByInitialLines]
5858
.map({$0.trimmingCharacters(in: .whitespacesAndNewlines)})
5959
.joined(separator: "\n")
60-
6160
if textLinesAtOffset == expectedInitialMatchingLines {
6261
matchingOutputOffset = offset
6362
break

Tests/JExtractSwiftTests/MethodThunkTests.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,26 @@ final class MethodThunkTests {
2828
func thunk_overloads() throws {
2929
let st = Swift2JavaTranslator(
3030
javaPackage: "com.example.swift",
31-
swiftModuleName: "__FakeModule"
31+
swiftModuleName: "FakeModule"
3232
)
33-
st.log.logLevel = .trace
33+
st.log.logLevel = .error
3434

3535
try assertOutput(
3636
st, input: input, .swift,
37+
detectChunkByInitialLines: 1,
3738
expectedChunks:
3839
[
3940
"""
40-
@_cdecl()
41-
func kappa()
41+
@_cdecl("swiftjava_FakeModule_globalFunc_a_b")
42+
public func swiftjava_FakeModule_globalFunc_a_b(a: Int32, b: Int64) -> Swift.Void /* Void */ {
43+
globalFunc(a: a, b: b)
44+
}
45+
""",
46+
"""
47+
@_cdecl("swiftjava_FakeModule_globalFunc_a_b$1")
48+
public func swiftjava_FakeModule_globalFunc_a_b$1(a: Double, b: Int64) -> Swift.Void /* Void */ {
49+
globalFunc(a: a, b: b)
50+
}
4251
"""
4352
]
4453
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import JExtractSwift
16+
import Testing
17+
18+
final class ThunkNameRegistryTests {
19+
@Test("Thunk names: deduplicate names")
20+
func deduplicate() throws {
21+
var registry = ThunkNameRegistry()
22+
#expect(registry.deduplicate(name: "swiftjava_hello") == "swiftjava_hello")
23+
#expect(registry.deduplicate(name: "swiftjava_hello") == "swiftjava_hello$1")
24+
#expect(registry.deduplicate(name: "swiftjava_hello") == "swiftjava_hello$2")
25+
#expect(registry.deduplicate(name: "swiftjava_hello") == "swiftjava_hello$3")
26+
#expect(registry.deduplicate(name: "swiftjava_other") == "swiftjava_other")
27+
#expect(registry.deduplicate(name: "swiftjava_other") == "swiftjava_other$1")
28+
}
29+
}

0 commit comments

Comments
 (0)