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, subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor? subClassDescriptor: ClassDescriptor?
): ExternalOverridabilityCondition.Result { ): ExternalOverridabilityCondition.Result {
if (superDescriptor.name == subDescriptor.name) { // Slow path:
if (superDescriptor.name != subDescriptor.name) { if (superDescriptor is FunctionDescriptor && subDescriptor is FunctionDescriptor) {
return ExternalOverridabilityCondition.Result.UNKNOWN superDescriptor.getExternalObjCMethodInfo()?.let { superInfo ->
} val subInfo = subDescriptor.getExternalObjCMethodInfo()
if (subInfo != null) {
val superClass = superDescriptor.containingDeclaration as? ClassDescriptor // Overriding Objective-C method by Objective-C method in interop stubs.
val subClass = subDescriptor.containingDeclaration as? ClassDescriptor // Don't even check method signatures:
return if (superInfo.selector == subInfo.selector) {
if (superClass == null || !superClass.isObjCClass() || subClass == null) { ExternalOverridabilityCondition.Result.OVERRIDABLE
return ExternalOverridabilityCondition.Result.UNKNOWN } else {
} ExternalOverridabilityCondition.Result.INCOMPATIBLE
}
return if (areSelectorsEqual(superDescriptor, subDescriptor)) { } else {
// Also check the method signatures if the subclass is user-defined: // Overriding Objective-C method by Kotlin method.
if (subClass.isExternalObjCClass()) { if (!parameterNamesMatch(superDescriptor, subDescriptor)) {
ExternalOverridabilityCondition.Result.OVERRIDABLE return ExternalOverridabilityCondition.Result.INCOMPATIBLE
} else { }
ExternalOverridabilityCondition.Result.UNKNOWN }
}
} 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 // The original Objective-C method selector is represented as
// function name and parameter names (except first). // function name and parameter names (except first).
@@ -150,12 +150,21 @@ NSObject* createNSObject() {
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...; -(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...; +(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ...;
@property NSString* formatted; @property NSString* formatted;
+(NSString* _Nonnull)stringWithFormat:(NSString*)format, ...;
+(NSObject* _Nonnull)stringWithFormat:(NSString*)format args:(void*)args;
@end; @end;
@interface TestVarargs (TestVarargsExtension) @interface TestVarargs (TestVarargsExtension)
-(instancetype _Nonnull)initWithFormat:(NSString*)format, ...; -(instancetype _Nonnull)initWithFormat:(NSString*)format, ...;
@end; @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 @interface TestOverrideInit : NSObject
-(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER; -(instancetype)initWithValue:(int)value NS_DESIGNATED_INITIALIZER;
+(instancetype)createWithValue:(int)value; +(instancetype)createWithValue:(int)value;
@@ -299,6 +299,14 @@ fun testVarargs() {
format = "%d %d %d" format = "%d %d %d"
).formatted ).formatted
) )
assertEquals(
"7",
TestVarargsSubclass.stringWithFormat(
"%d",
7
)
)
} }
fun testOverrideInit() { fun testOverrideInit() {
+24 -1
View File
@@ -134,7 +134,7 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
} }
+(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... { +(instancetype _Nonnull)testVarargsWithFormat:(NSString*)format, ... {
TestVarargs* result = [[TestVarargs alloc] init]; TestVarargs* result = [[self alloc] init];
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@@ -144,6 +144,29 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
return result; 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; @end;
@implementation TestOverrideInit @implementation TestOverrideInit