Refactor and improve Objective-C override checks (#2907)

This commit is contained in:
SvyatoslavScherbina
2019-04-24 15:01:47 +03:00
committed by GitHub
parent 73d5ac8120
commit 14fb86d487
4 changed files with 67 additions and 22 deletions
@@ -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() {
+24 -1
View File
@@ -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