Skip to content

Commit 7346359

Browse files
committed
Java2Swift: Account for differences in covariant overrides between Java and Swift
Java allows more subtyping relationships for the result types in a covariant method override than Swift does, such as covariant arrays and wildcards. Take the Swift semantics into account when determining whether to apply the `override` keyword.
1 parent 374c847 commit 7346359

File tree

5 files changed

+309
-90
lines changed

5 files changed

+309
-90
lines changed

Sources/Java2Swift/JavaToSwift.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ struct JavaToSwift: ParsableCommand {
183183
) throws {
184184
let translator = JavaTranslator(
185185
swiftModuleName: moduleName,
186-
environment: environment
186+
environment: environment,
187+
translateAsClass: false
187188
)
188189

189190
// Keep track of all of the Java classes that will have

Sources/Java2SwiftLib/JavaClassTranslator.swift

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,9 +685,21 @@ extension JavaClassTranslator {
685685

686686
// If this superclass declares a method with the same parameter types,
687687
// we have an override.
688-
let overriddenMethod = try currentSuperclassNonOpt
689-
.getDeclaredMethod(method.getName(), method.getParameterTypes())
690-
if overriddenMethod != nil {
688+
guard let overriddenMethod = try currentSuperclassNonOpt
689+
.getDeclaredMethod(method.getName(), method.getParameterTypes()) else {
690+
continue
691+
}
692+
693+
// Ignore non-public, non-protected methods because they would not
694+
// have been render into the Swift superclass.
695+
if !overriddenMethod.isPublic && !overriddenMethod.isProtected {
696+
continue
697+
}
698+
699+
// We know that Java considers this method an override. However, it is
700+
// possible that Swift will not consider it an override, because Java
701+
// has subtyping relations that Swift does not.
702+
if method.getGenericReturnType().isEqualToOrSubtypeOf(overriddenMethod.getGenericReturnType()) {
691703
return true
692704
}
693705
} catch {
@@ -697,3 +709,126 @@ extension JavaClassTranslator {
697709
return false
698710
}
699711
}
712+
713+
extension [Type?] {
714+
/// Determine whether the types in the array match the other array.
715+
func allTypesEqual(_ other: [Type?]) -> Bool {
716+
if self.count != other.count {
717+
return false
718+
}
719+
720+
for (selfType, otherType) in zip(self, other) {
721+
if !selfType!.isEqualTo(otherType!) {
722+
return false
723+
}
724+
}
725+
726+
return true
727+
}
728+
}
729+
730+
extension Type {
731+
/// Adjust the given type to use its bounds, mirroring what we do in
732+
/// mapping Java types into Swift.
733+
func adjustToJavaBounds(adjusted: inout Bool) -> Type {
734+
if let typeVariable = self.as(TypeVariable<GenericDeclaration>.self),
735+
typeVariable.getBounds().count == 1,
736+
let bound = typeVariable.getBounds()[0] {
737+
adjusted = true
738+
return bound
739+
}
740+
741+
if let wildcardType = self.as(WildcardType.self),
742+
wildcardType.getUpperBounds().count == 1,
743+
let bound = wildcardType.getUpperBounds()[0] {
744+
adjusted = true
745+
return bound
746+
}
747+
748+
return self
749+
}
750+
751+
/// Determine whether this type is equivalent to or a subtype of the other
752+
/// type.
753+
func isEqualTo(_ other: Type) -> Bool {
754+
// First, adjust types to their bounds, if we need to.
755+
var anyAdjusted: Bool = false
756+
let adjustedSelf = self.adjustToJavaBounds(adjusted: &anyAdjusted)
757+
let adjustedOther = other.adjustToJavaBounds(adjusted: &anyAdjusted)
758+
if anyAdjusted {
759+
return adjustedSelf.isEqualTo(adjustedOther)
760+
}
761+
762+
// If both are classes, check for equivalence.
763+
if let selfClass = self.as(JavaClass<JavaObject>.self),
764+
let otherClass = other.as(JavaClass<JavaObject>.self) {
765+
return selfClass.equals(otherClass.as(JavaObject.self))
766+
}
767+
768+
// If both are arrays, check that their component types are equivalent.
769+
if let selfArray = self.as(GenericArrayType.self),
770+
let otherArray = other.as(GenericArrayType.self) {
771+
return selfArray.getGenericComponentType().isEqualTo(otherArray.getGenericComponentType())
772+
}
773+
774+
// If both are parameterized types, check their raw type and type
775+
// arguments for equivalence.
776+
if let selfParameterizedType = self.as(ParameterizedType.self),
777+
let otherParameterizedType = other.as(ParameterizedType.self) {
778+
if !selfParameterizedType.getRawType().isEqualTo(otherParameterizedType.getRawType()) {
779+
return false
780+
}
781+
782+
return selfParameterizedType.getActualTypeArguments()
783+
.allTypesEqual(otherParameterizedType.getActualTypeArguments())
784+
}
785+
786+
// If both are type variables, compare their bounds.
787+
// FIXME: This is a hack.
788+
if let selfTypeVariable = self.as(TypeVariable<GenericDeclaration>.self),
789+
let otherTypeVariable = other.as(TypeVariable<GenericDeclaration>.self) {
790+
return selfTypeVariable.getBounds().allTypesEqual(otherTypeVariable.getBounds())
791+
}
792+
793+
// If both are wildcards, compare their upper and lower bounds.
794+
if let selfWildcard = self.as(WildcardType.self),
795+
let otherWildcard = other.as(WildcardType.self) {
796+
return selfWildcard.getUpperBounds().allTypesEqual(otherWildcard.getUpperBounds())
797+
&& selfWildcard.getLowerBounds().allTypesEqual(otherWildcard.getLowerBounds())
798+
}
799+
800+
return false
801+
}
802+
803+
/// Determine whether this type is equivalent to or a subtype of the
804+
/// other type.
805+
func isEqualToOrSubtypeOf(_ other: Type) -> Bool {
806+
// First, adjust types to their bounds, if we need to.
807+
var anyAdjusted: Bool = false
808+
let adjustedSelf = self.adjustToJavaBounds(adjusted: &anyAdjusted)
809+
let adjustedOther = other.adjustToJavaBounds(adjusted: &anyAdjusted)
810+
if anyAdjusted {
811+
return adjustedSelf.isEqualToOrSubtypeOf(adjustedOther)
812+
}
813+
814+
if isEqualTo(other) {
815+
return true
816+
}
817+
818+
// If both are classes, check for subclassing.
819+
if let selfClass = self.as(JavaClass<JavaObject>.self),
820+
let otherClass = other.as(JavaClass<JavaObject>.self) {
821+
return selfClass.isSubclass(of: otherClass)
822+
}
823+
824+
// Anything object-like is a subclass of java.lang.Object
825+
if let otherClass = other.as(JavaClass<JavaObject>.self),
826+
otherClass.getName() == "java.lang.Object" {
827+
if self.is(GenericArrayType.self) || self.is(ParameterizedType.self) ||
828+
self.is(WildcardType.self) || self.is(TypeVariable<GenericDeclaration>.self) {
829+
return true
830+
}
831+
}
832+
return false
833+
}
834+
}

Sources/JavaKitCollection/generated/HashMap.swift

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -88,92 +88,6 @@ public struct HashMap<K: AnyJavaObject, V: AnyJavaObject> {
8888
@JavaMethod
8989
public func wait() throws
9090
}
91-
extension HashMap {
92-
@JavaClass("java.util.AbstractMap$SimpleEntry", extends: JavaObject.self)
93-
public struct SimpleEntry<K: AnyJavaObject, V: AnyJavaObject> {
94-
@JavaMethod
95-
public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil)
96-
97-
@JavaMethod
98-
public func equals(_ arg0: JavaObject?) -> Bool
99-
100-
@JavaMethod
101-
public func toString() -> String
102-
103-
@JavaMethod
104-
public func hashCode() -> Int32
105-
106-
@JavaMethod
107-
public func getValue() -> JavaObject!
108-
109-
@JavaMethod
110-
public func getKey() -> JavaObject!
111-
112-
@JavaMethod
113-
public func setValue(_ arg0: JavaObject?) -> JavaObject!
114-
115-
@JavaMethod
116-
public func getClass() -> JavaClass<JavaObject>!
117-
118-
@JavaMethod
119-
public func notify()
120-
121-
@JavaMethod
122-
public func notifyAll()
123-
124-
@JavaMethod
125-
public func wait(_ arg0: Int64) throws
126-
127-
@JavaMethod
128-
public func wait(_ arg0: Int64, _ arg1: Int32) throws
129-
130-
@JavaMethod
131-
public func wait() throws
132-
}
133-
}
134-
extension HashMap {
135-
@JavaClass("java.util.AbstractMap$SimpleImmutableEntry", extends: JavaObject.self)
136-
public struct SimpleImmutableEntry<K: AnyJavaObject, V: AnyJavaObject> {
137-
@JavaMethod
138-
public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil)
139-
140-
@JavaMethod
141-
public func equals(_ arg0: JavaObject?) -> Bool
142-
143-
@JavaMethod
144-
public func toString() -> String
145-
146-
@JavaMethod
147-
public func hashCode() -> Int32
148-
149-
@JavaMethod
150-
public func getValue() -> JavaObject!
151-
152-
@JavaMethod
153-
public func getKey() -> JavaObject!
154-
155-
@JavaMethod
156-
public func setValue(_ arg0: JavaObject?) -> JavaObject!
157-
158-
@JavaMethod
159-
public func getClass() -> JavaClass<JavaObject>!
160-
161-
@JavaMethod
162-
public func notify()
163-
164-
@JavaMethod
165-
public func notifyAll()
166-
167-
@JavaMethod
168-
public func wait(_ arg0: Int64) throws
169-
170-
@JavaMethod
171-
public func wait(_ arg0: Int64, _ arg1: Int32) throws
172-
173-
@JavaMethod
174-
public func wait() throws
175-
}
176-
}
17791
extension JavaClass {
17892
@JavaStaticMethod
17993
public func newHashMap<K: AnyJavaObject, V: AnyJavaObject>(_ arg0: Int32) -> HashMap<JavaObject, JavaObject>! where ObjectType == HashMap<K, V>

Sources/JavaKitCollection/generated/TreeMap.swift

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,89 @@ public struct TreeMap<K: AnyJavaObject, V: AnyJavaObject> {
106106
@JavaMethod
107107
public func getOrDefault(_ arg0: JavaObject?, _ arg1: JavaObject?) -> JavaObject!
108108
}
109+
extension TreeMap {
110+
@JavaClass("java.util.AbstractMap$SimpleEntry", extends: JavaObject.self)
111+
public struct SimpleEntry<K: AnyJavaObject, V: AnyJavaObject> {
112+
@JavaMethod
113+
public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil)
114+
115+
@JavaMethod
116+
public func equals(_ arg0: JavaObject?) -> Bool
117+
118+
@JavaMethod
119+
public func toString() -> String
120+
121+
@JavaMethod
122+
public func hashCode() -> Int32
123+
124+
@JavaMethod
125+
public func getValue() -> JavaObject!
126+
127+
@JavaMethod
128+
public func getKey() -> JavaObject!
129+
130+
@JavaMethod
131+
public func setValue(_ arg0: JavaObject?) -> JavaObject!
132+
133+
@JavaMethod
134+
public func getClass() -> JavaClass<JavaObject>!
135+
136+
@JavaMethod
137+
public func notify()
138+
139+
@JavaMethod
140+
public func notifyAll()
141+
142+
@JavaMethod
143+
public func wait(_ arg0: Int64) throws
144+
145+
@JavaMethod
146+
public func wait(_ arg0: Int64, _ arg1: Int32) throws
147+
148+
@JavaMethod
149+
public func wait() throws
150+
}
151+
}
152+
extension TreeMap {
153+
@JavaClass("java.util.AbstractMap$SimpleImmutableEntry", extends: JavaObject.self)
154+
public struct SimpleImmutableEntry<K: AnyJavaObject, V: AnyJavaObject> {
155+
@JavaMethod
156+
public init(_ arg0: JavaObject?, _ arg1: JavaObject?, environment: JNIEnvironment? = nil)
157+
158+
@JavaMethod
159+
public func equals(_ arg0: JavaObject?) -> Bool
160+
161+
@JavaMethod
162+
public func toString() -> String
163+
164+
@JavaMethod
165+
public func hashCode() -> Int32
166+
167+
@JavaMethod
168+
public func getValue() -> JavaObject!
169+
170+
@JavaMethod
171+
public func getKey() -> JavaObject!
172+
173+
@JavaMethod
174+
public func setValue(_ arg0: JavaObject?) -> JavaObject!
175+
176+
@JavaMethod
177+
public func getClass() -> JavaClass<JavaObject>!
178+
179+
@JavaMethod
180+
public func notify()
181+
182+
@JavaMethod
183+
public func notifyAll()
184+
185+
@JavaMethod
186+
public func wait(_ arg0: Int64) throws
187+
188+
@JavaMethod
189+
public func wait(_ arg0: Int64, _ arg1: Int32) throws
190+
191+
@JavaMethod
192+
public func wait() throws
193+
}
194+
}

0 commit comments

Comments
 (0)