Refactor and improve Objective-C override checks (#2907)
This commit is contained in:
committed by
GitHub
parent
73d5ac8120
commit
14fb86d487
+26
-21
@@ -147,32 +147,37 @@ class ObjCOverridabilityCondition : ExternalOverridabilityCondition {
|
||||
subDescriptor: CallableDescriptor,
|
||||
subClassDescriptor: ClassDescriptor?
|
||||
): ExternalOverridabilityCondition.Result {
|
||||
|
||||
if (superDescriptor.name != subDescriptor.name) {
|
||||
return ExternalOverridabilityCondition.Result.UNKNOWN
|
||||
}
|
||||
|
||||
val superClass = superDescriptor.containingDeclaration as? ClassDescriptor
|
||||
val subClass = subDescriptor.containingDeclaration as? ClassDescriptor
|
||||
|
||||
if (superClass == null || !superClass.isObjCClass() || subClass == null) {
|
||||
return ExternalOverridabilityCondition.Result.UNKNOWN
|
||||
}
|
||||
|
||||
return if (areSelectorsEqual(superDescriptor, subDescriptor)) {
|
||||
// Also check the method signatures if the subclass is user-defined:
|
||||
if (subClass.isExternalObjCClass()) {
|
||||
ExternalOverridabilityCondition.Result.OVERRIDABLE
|
||||
} else {
|
||||
ExternalOverridabilityCondition.Result.UNKNOWN
|
||||
if (superDescriptor.name == subDescriptor.name) { // Slow path:
|
||||
if (superDescriptor is FunctionDescriptor && subDescriptor is FunctionDescriptor) {
|
||||
superDescriptor.getExternalObjCMethodInfo()?.let { superInfo ->
|
||||
val subInfo = subDescriptor.getExternalObjCMethodInfo()
|
||||
if (subInfo != null) {
|
||||
// Overriding Objective-C method by Objective-C method in interop stubs.
|
||||
// Don't even check method signatures:
|
||||
return if (superInfo.selector == subInfo.selector) {
|
||||
ExternalOverridabilityCondition.Result.OVERRIDABLE
|
||||
} else {
|
||||
ExternalOverridabilityCondition.Result.INCOMPATIBLE
|
||||
}
|
||||
} else {
|
||||
// Overriding Objective-C method by Kotlin method.
|
||||
if (!parameterNamesMatch(superDescriptor, subDescriptor)) {
|
||||
return ExternalOverridabilityCondition.Result.INCOMPATIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (superDescriptor.isExternalObjCClassProperty() && subDescriptor.isExternalObjCClassProperty()) {
|
||||
return ExternalOverridabilityCondition.Result.OVERRIDABLE
|
||||
}
|
||||
} else {
|
||||
ExternalOverridabilityCondition.Result.INCOMPATIBLE
|
||||
}
|
||||
|
||||
return ExternalOverridabilityCondition.Result.UNKNOWN
|
||||
}
|
||||
|
||||
private fun areSelectorsEqual(first: CallableDescriptor, second: CallableDescriptor): Boolean {
|
||||
private fun CallableDescriptor.isExternalObjCClassProperty() = this is PropertyDescriptor &&
|
||||
(this.containingDeclaration as? ClassDescriptor)?.isExternalObjCClass() == true
|
||||
|
||||
private fun parameterNamesMatch(first: FunctionDescriptor, second: FunctionDescriptor): Boolean {
|
||||
// The original Objective-C method selector is represented as
|
||||
// function name and parameter names (except first).
|
||||
|
||||
|
||||
@@ -150,12 +150,21 @@ NSObject* createNSObject() {
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
|
||||
@property NSString* formatted;
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
|
||||
@interface TestVarargs (TestVarargsExtension)
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
|
||||
@end;
|
||||
|
||||
@interface TestVarargsSubclass : TestVarargs
|
||||
// Test clashes:
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args;
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
|
||||
@end;
|
||||
|
||||
@interface TestOverrideInit : NSObject
|
||||
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
|
||||
+(instancetype)createWithValue:(int)value;
|
||||
|
||||
@@ -299,6 +299,14 @@ fun testVarargs() {
|
||||
format = "%d %d %d"
|
||||
).formatted
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"7",
|
||||
TestVarargsSubclass.stringWithFormat(
|
||||
"%d",
|
||||
7
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun testOverrideInit() {
|
||||
|
||||
@@ -134,7 +134,7 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
||||
}
|
||||
|
||||
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
|
||||
TestVarargs* result = [[TestVarargs alloc] init];
|
||||
TestVarargs* result = [[self alloc] init];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
@@ -144,6 +144,29 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ... {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
@end;
|
||||
|
||||
@implementation TestVarargsSubclass
|
||||
-(instancetype _Nonnull)initWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
|
||||
+(NSString* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args {
|
||||
abort();
|
||||
}
|
||||
@end;
|
||||
|
||||
@implementation TestOverrideInit
|
||||
|
||||
Reference in New Issue
Block a user