Skip to content

Commit 83b0e1a

Browse files
committed
Java2Swift: Always use the nearest imported superclass for the superclass
When we import a class from Java into Swift, we check whether its superclass was also imported before generating a reference to that superclass. If it isn't there, we fell back to JavaObject. That's too conservative, because there might be a superclass in between that we could use. Instead, walk up the superclass chain until we find the most-specific superclass that *is* mapped into Swift, and use that as the generated superclass. Additionally, use this as the basis for determining when we need the "override" keyword when in the class-generating mode.
1 parent 6440a3c commit 83b0e1a

File tree

2 files changed

+92
-7
lines changed

2 files changed

+92
-7
lines changed

Sources/Java2SwiftLib/JavaClassTranslator.swift

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ struct JavaClassTranslator {
3737
/// class.
3838
let swiftTypeName: String
3939

40+
/// The effective Java superclass object, which is the nearest
41+
/// superclass that has been mapped into Swift.
42+
let effectiveJavaSuperclass: JavaClass<JavaObject>?
43+
4044
/// The Swift name of the superclass.
4145
let swiftSuperclass: String?
4246

@@ -114,14 +118,24 @@ struct JavaClassTranslator {
114118
self.nestedClasses = translator.nestedClasses[fullName] ?? []
115119

116120
// Superclass.
117-
if !javaClass.isInterface(), let javaSuperclass = javaClass.getSuperclass() {
118-
do {
119-
self.swiftSuperclass = try translator.getSwiftTypeName(javaSuperclass, preferValueTypes: false).swiftName
120-
} catch {
121-
translator.logUntranslated("Unable to translate '\(fullName)' superclass: \(error)")
122-
self.swiftSuperclass = nil
121+
if !javaClass.isInterface() {
122+
var javaSuperclass = javaClass.getSuperclass()
123+
var swiftSuperclass: String? = nil
124+
while let javaSuperclassNonOpt = javaSuperclass {
125+
do {
126+
swiftSuperclass = try translator.getSwiftTypeName(javaSuperclassNonOpt, preferValueTypes: false).swiftName
127+
break
128+
} catch {
129+
translator.logUntranslated("Unable to translate '\(fullName)' superclass: \(error)")
130+
}
131+
132+
javaSuperclass = javaSuperclassNonOpt.getSuperclass()
123133
}
134+
135+
self.effectiveJavaSuperclass = javaSuperclass
136+
self.swiftSuperclass = swiftSuperclass
124137
} else {
138+
self.effectiveJavaSuperclass = nil
125139
self.swiftSuperclass = nil
126140
}
127141

@@ -646,7 +660,7 @@ extension JavaClassTranslator {
646660
/// Determine whether this method is an override of another Java
647661
/// method.
648662
func isOverride(_ method: Method) -> Bool {
649-
guard let javaSuperclass = javaClass.getSuperclass() else {
663+
guard let javaSuperclass = effectiveJavaSuperclass else {
650664
return false
651665
}
652666

Tests/Java2SwiftTests/Java2SwiftTests.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,79 @@ class Java2SwiftTests: XCTestCase {
327327
]
328328
)
329329
}
330+
331+
func testURLLoaderSkipMappingAsClass() throws {
332+
// URLClassLoader actually inherits from SecureClassLoader. However,
333+
// that type wasn't mapped into Swift, so we find the nearest
334+
// superclass that was mapped into Swift.
335+
try assertTranslatedClass(
336+
URLClassLoader.self,
337+
swiftTypeName: "URLClassLoader",
338+
asClass: true,
339+
translatedClasses: [
340+
"java.lang.Object" : ("JavaObject", "JavaKit"),
341+
"java.lang.ClassLoader" : ("ClassLoader", "JavaKit"),
342+
"java.net.URL" : ("URL", "JavaKitNetwork"),
343+
],
344+
expectedChunks: [
345+
"import JavaKit",
346+
"""
347+
@JavaClass("java.net.URLClassLoader")
348+
open class URLClassLoader: ClassLoader {
349+
""",
350+
"""
351+
@JavaMethod
352+
open func close() throws
353+
""",
354+
"""
355+
@JavaMethod
356+
open override func findResource(_ arg0: String) -> URL!
357+
""",
358+
]
359+
)
360+
}
361+
362+
func testURLLoaderSkipTwiceMappingAsClass() throws {
363+
// URLClassLoader actually inherits from SecureClassLoader. However,
364+
// that type wasn't mapped into Swift here, nor is ClassLoader,
365+
// so we fall back to JavaObject.
366+
try assertTranslatedClass(
367+
URLClassLoader.self,
368+
swiftTypeName: "URLClassLoader",
369+
asClass: true,
370+
translatedClasses: [
371+
"java.lang.Object" : ("JavaObject", "JavaKit"),
372+
"java.net.URL" : ("URL", "JavaKitNetwork"),
373+
],
374+
expectedChunks: [
375+
"import JavaKit",
376+
"""
377+
@JavaClass("java.net.URLClassLoader")
378+
open class URLClassLoader: JavaObject {
379+
""",
380+
"""
381+
@JavaMethod
382+
open func close() throws
383+
""",
384+
"""
385+
@JavaMethod
386+
open func findResource(_ arg0: String) -> URL!
387+
""",
388+
]
389+
)
390+
}
330391
}
331392

393+
@JavaClass("java.lang.ClassLoader")
394+
public struct ClassLoader { }
395+
396+
@JavaClass("java.security.SecureClassLoader")
397+
public struct SecureClassLoader { }
398+
399+
@JavaClass("java.net.URLClassLoader")
400+
public struct URLClassLoader { }
401+
402+
332403
@JavaClass("java.util.ArrayList")
333404
public struct MyArrayList<E: AnyJavaObject> {
334405
}

0 commit comments

Comments
 (0)