blob: d39dc3592e4d692a855b6df9f3b7d5b764b7d42a [file] [log] [blame]
// -*- mode: ObjC -*-
// This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files.
// Copyright (C) 1997-2019 Steve Nygard.
#import "CDOCProtocol.h"
#import "CDClassDump.h"
#import "CDOCMethod.h"
#import "CDVisitor.h"
#import "CDOCProperty.h"
#import "CDMethodType.h"
#import "CDType.h"
#import "CDTypeController.h"
#import "CDVisitorPropertyState.h"
@interface CDOCProtocol ()
@property (nonatomic, readonly) NSString *sortableName;
@end
#pragma mark -
@implementation CDOCProtocol
{
NSString *_name;
NSMutableArray *_protocols;
NSMutableArray *_classMethods;
NSMutableArray *_instanceMethods;
NSMutableArray *_optionalClassMethods;
NSMutableArray *_optionalInstanceMethods;
NSMutableArray *_properties;
NSMutableSet *_adoptedProtocolNames;
}
- (id)init;
{
if ((self = [super init])) {
_name = nil;
_protocols = [[NSMutableArray alloc] init];
_classMethods = [[NSMutableArray alloc] init];
_instanceMethods = [[NSMutableArray alloc] init];
_optionalClassMethods = [[NSMutableArray alloc] init];
_optionalInstanceMethods = [[NSMutableArray alloc] init];
_properties = [[NSMutableArray alloc] init];
_adoptedProtocolNames = [[NSMutableSet alloc] init];
}
return self;
}
#pragma mark - Debugging
- (NSString *)description;
{
return [NSString stringWithFormat:@"<%@:%p> name: %@, protocols: %ld, class methods: %ld, instance methods: %ld",
NSStringFromClass([self class]), self, self.name, [self.protocols count], [self.classMethods count], [self.instanceMethods count]];
}
#pragma mark -
// This assumes that the protocol name doesn't change after it's been added to this.
- (void)addProtocol:(CDOCProtocol *)protocol;
{
if ([_adoptedProtocolNames containsObject:protocol.name] == NO) {
[_protocols addObject:protocol];
[_adoptedProtocolNames addObject:protocol.name];
}
}
- (void)removeProtocol:(CDOCProtocol *)protocol;
{
[_adoptedProtocolNames removeObject:protocol.name];
[_protocols removeObject:protocol];
}
- (NSArray *)protocolNames;
{
NSMutableArray *names = [[NSMutableArray alloc] init];
[self.protocols enumerateObjectsUsingBlock:^(CDOCProtocol *protocol, NSUInteger index, BOOL *stop){
if (protocol.name != nil)
[names addObject:protocol.name];
}];
return [names copy];
}
- (NSString *)protocolsString;
{
NSArray *names = self.protocolNames;
if ([names count] == 0)
return @"";
return [names componentsJoinedByString:@", "];
}
- (void)addClassMethod:(CDOCMethod *)method;
{
[_classMethods addObject:method];
}
- (void)addInstanceMethod:(CDOCMethod *)method;
{
[_instanceMethods addObject:method];
}
- (void)addOptionalClassMethod:(CDOCMethod *)method;
{
[_optionalClassMethods addObject:method];
}
- (void)addOptionalInstanceMethod:(CDOCMethod *)method;
{
[_optionalInstanceMethods addObject:method];
}
- (void)addProperty:(CDOCProperty *)property;
{
[_properties addObject:property];
}
- (BOOL)hasMethods;
{
return [self.classMethods count] > 0 || [self.instanceMethods count] > 0 || [self.optionalClassMethods count] > 0 || [self.optionalInstanceMethods count] > 0;
}
- (void)registerTypesWithObject:(CDTypeController *)typeController phase:(NSUInteger)phase;
{
[self registerTypesFromMethods:self.classMethods withObject:typeController phase:phase];
[self registerTypesFromMethods:self.instanceMethods withObject:typeController phase:phase];
[self registerTypesFromMethods:self.optionalClassMethods withObject:typeController phase:phase];
[self registerTypesFromMethods:self.optionalInstanceMethods withObject:typeController phase:phase];
}
- (void)registerTypesFromMethods:(NSArray *)methods withObject:(CDTypeController *)typeController phase:(NSUInteger)phase;
{
for (CDOCMethod *method in methods) {
for (CDMethodType *methodType in method.parsedMethodTypes) {
[methodType.type phase:phase registerTypesWithObject:typeController usedInMethod:YES];
}
}
}
#pragma mark - Sorting
- (NSString *)sortableName;
{
return self.name;
}
- (NSComparisonResult)ascendingCompareByName:(CDOCProtocol *)other;
{
return [self.sortableName compare:other.sortableName];
}
#pragma mark -
- (NSString *)methodSearchContext;
{
NSMutableString *resultString = [NSMutableString string];
[resultString appendFormat:@"@protocol %@", self.name];
if ([self.protocols count] > 0)
[resultString appendFormat:@" <%@>", self.protocolsString];
return resultString;
}
- (void)recursivelyVisit:(CDVisitor *)visitor;
{
if ([visitor.classDump shouldShowName:self.name] && visitor.shouldShowProtocolSection) {
CDVisitorPropertyState *propertyState = [[CDVisitorPropertyState alloc] initWithProperties:self.properties];
[visitor willVisitProtocol:self];
//[aVisitor willVisitPropertiesOfProtocol:self];
//[self visitProperties:aVisitor];
//[aVisitor didVisitPropertiesOfProtocol:self];
[self visitMethods:visitor propertyState:propertyState];
// @optional properties will generate optional instance methods, and we'll emit @property in the @optional section.
[visitor visitRemainingProperties:propertyState];
[visitor didVisitProtocol:self];
}
}
- (void)visitMethods:(CDVisitor *)visitor propertyState:(CDVisitorPropertyState *)propertyState;
{
NSArray *methods = self.classMethods;
if (visitor.classDump.shouldSortMethods)
methods = [methods sortedArrayUsingSelector:@selector(ascendingCompareByName:)];
for (CDOCMethod *method in methods)
[visitor visitClassMethod:method];
methods = self.instanceMethods;
if (visitor.classDump.shouldSortMethods)
methods = [methods sortedArrayUsingSelector:@selector(ascendingCompareByName:)];
for (CDOCMethod *method in methods)
[visitor visitInstanceMethod:method propertyState:propertyState];
if ([self.optionalClassMethods count] > 0 || [self.optionalInstanceMethods count] > 0) {
[visitor willVisitOptionalMethods];
methods = self.optionalClassMethods;
if (visitor.classDump.shouldSortMethods)
methods = [methods sortedArrayUsingSelector:@selector(ascendingCompareByName:)];
for (CDOCMethod *method in methods)
[visitor visitClassMethod:method];
methods = self.optionalInstanceMethods;
if (visitor.classDump.shouldSortMethods)
methods = [methods sortedArrayUsingSelector:@selector(ascendingCompareByName:)];
for (CDOCMethod *method in methods)
[visitor visitInstanceMethod:method propertyState:propertyState];
[visitor didVisitOptionalMethods];
}
}
#if 0
- (void)visitProperties:(CDVisitor *)visitor;
{
NSArray *array = properties;
if (visitor.classDump.shouldSortMethods)
array = [array sortedArrayUsingSelector:@selector(ascendingCompareByName:)];
for (CDOCProperty *property in array)
[visitor visitProperty:property];
}
#endif
#pragma mark -
- (void)mergeMethodsFromProtocol:(CDOCProtocol *)other;
{
NSMutableDictionary *instanceMethodsByName = [NSMutableDictionary dictionary];
NSMutableDictionary *optionalInstanceMethodsByName = [NSMutableDictionary dictionary];
NSMutableDictionary *classMethodsByName = [NSMutableDictionary dictionary];
NSMutableDictionary *optionalClassMethodsByName = [NSMutableDictionary dictionary];
for (CDOCMethod *method in _instanceMethods)
instanceMethodsByName[method.name] = method;
for (CDOCMethod *method in _optionalInstanceMethods)
optionalInstanceMethodsByName[method.name] = method;
for (CDOCMethod *method in _classMethods)
classMethodsByName[method.name] = method;
for (CDOCMethod *method in _optionalClassMethods)
optionalClassMethodsByName[method.name] = method;
// Instance methods
for (CDOCMethod *method in other.instanceMethods) {
CDOCMethod *m2 = instanceMethodsByName[method.name];
if (m2 == nil) {
// Add if it is not an optional instance method.
if (optionalInstanceMethodsByName[method.name] == nil) {
[self addInstanceMethod:method];
instanceMethodsByName[method.name] = method;
}
}
}
for (CDOCMethod *method in other.optionalInstanceMethods) {
CDOCMethod *m2 = optionalInstanceMethodsByName[method.name];
if (m2 == nil) {
m2 = instanceMethodsByName[method.name];
if (m2 == nil) {
[self addOptionalInstanceMethod:method];
optionalInstanceMethodsByName[method.name] = method;
} else {
// Move to the optional instance methods.
[self addOptionalInstanceMethod:m2];
[_instanceMethods removeObject:m2];
optionalInstanceMethodsByName[m2.name] = m2;
[instanceMethodsByName removeObjectForKey:m2.name];
}
}
}
// Class methods
for (CDOCMethod *method in other.classMethods) {
CDOCMethod *m2 = classMethodsByName[method.name];
if (m2 == nil) {
// Add if it is not an optional class method.
if (optionalClassMethodsByName[method.name] == nil) {
[self addClassMethod:method];
classMethodsByName[method.name] = method;
}
}
}
for (CDOCMethod *method in other.optionalClassMethods) {
CDOCMethod *m2 = optionalClassMethodsByName[method.name];
if (m2 == nil) {
m2 = classMethodsByName[method.name];
if (m2 == nil) {
[self addOptionalClassMethod:method];
optionalClassMethodsByName[method.name] = method;
} else {
// Move to the optional class methods.
[self addOptionalClassMethod:m2];
[_classMethods removeObject:m2];
optionalClassMethodsByName[m2.name] = m2;
[classMethodsByName removeObjectForKey:m2.name];
}
}
}
}
- (void)mergePropertiesFromProtocol:(CDOCProtocol *)other;
{
NSMutableDictionary *propertiesByName = [NSMutableDictionary dictionary];
for (CDOCProperty *property in _properties)
propertiesByName[property.name] = property;
for (CDOCProperty *property in other.properties) {
CDOCProperty *p2 = propertiesByName[property.name];
if (p2 == nil) {
[self addProperty:property];
propertiesByName[property.name] = property;
}
}
}
@end