| // -*- mode: ObjC -*- |
| |
| // This file is part of class-dump, a utility for examining the Objective-C segment of Mach-O files. |
| // Copyright (C) 1997-1998, 2000-2001, 2004-2009 Steve Nygard. |
| |
| #import "CDTextClassDumpVisitor.h" |
| |
| #include <mach-o/arch.h> |
| |
| #import "NSArray-Extensions.h" |
| #import "CDClassDump.h" |
| #import "CDObjectiveC1Processor.h" |
| #import "CDMachOFile.h" |
| #import "CDOCProtocol.h" |
| #import "CDLCDylib.h" |
| #import "CDOCClass.h" |
| #import "CDOCCategory.h" |
| #import "CDSymbolReferences.h" |
| #import "CDOCMethod.h" |
| #import "CDOCProperty.h" |
| #import "CDTypeFormatter.h" |
| #import "CDTypeController.h" |
| #import "CDVisitorPropertyState.h" |
| |
| static BOOL debug = NO; |
| |
| @implementation CDTextClassDumpVisitor |
| |
| - (id)init; |
| { |
| if ([super init] == nil) |
| return nil; |
| |
| resultString = [[NSMutableString alloc] init]; |
| symbolReferences = [[CDSymbolReferences alloc] init]; |
| |
| return self; |
| } |
| |
| - (void)dealloc; |
| { |
| [resultString release]; |
| [symbolReferences release]; |
| |
| [super dealloc]; |
| } |
| |
| - (void)writeResultToStandardOutput; |
| { |
| NSData *data; |
| |
| data = [resultString dataUsingEncoding:NSUTF8StringEncoding]; |
| [(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:data]; |
| } |
| |
| - (void)willVisitClass:(CDOCClass *)aClass; |
| { |
| NSArray *protocols; |
| |
| [resultString appendFormat:@"@interface %@", [aClass name]]; |
| if ([aClass superClassName] != nil) |
| [resultString appendFormat:@" : %@", [aClass superClassName]]; |
| |
| protocols = [aClass protocols]; |
| if ([protocols count] > 0) { |
| [resultString appendFormat:@" <%@>", [[protocols arrayByMappingSelector:@selector(name)] componentsJoinedByString:@", "]]; |
| [symbolReferences addProtocolNamesFromArray:[protocols arrayByMappingSelector:@selector(name)]]; |
| } |
| |
| [resultString appendString:@"\n"]; |
| } |
| |
| - (void)didVisitClass:(CDOCClass *)aClass; |
| { |
| if ([aClass hasMethods]) |
| [resultString appendString:@"\n"]; |
| |
| [resultString appendString:@"@end\n\n"]; |
| } |
| |
| - (void)willVisitIvarsOfClass:(CDOCClass *)aClass; |
| { |
| [resultString appendString:@"{\n"]; |
| } |
| |
| - (void)didVisitIvarsOfClass:(CDOCClass *)aClass; |
| { |
| [resultString appendString:@"}\n\n"]; |
| } |
| |
| - (void)willVisitCategory:(CDOCCategory *)aCategory; |
| { |
| NSArray *protocols; |
| |
| [resultString appendFormat:@"@interface %@ (%@)", [aCategory className], [aCategory name]]; |
| |
| protocols = [aCategory protocols]; |
| if ([protocols count] > 0) { |
| [resultString appendFormat:@" <%@>", [[protocols arrayByMappingSelector:@selector(name)] componentsJoinedByString:@", "]]; |
| [symbolReferences addProtocolNamesFromArray:[protocols arrayByMappingSelector:@selector(name)]]; |
| } |
| |
| [resultString appendString:@"\n"]; |
| } |
| |
| - (void)didVisitCategory:(CDOCCategory *)aCategory; |
| { |
| [resultString appendString:@"@end\n\n"]; |
| } |
| |
| - (void)willVisitProtocol:(CDOCProtocol *)aProtocol; |
| { |
| NSArray *protocols; |
| |
| [resultString appendFormat:@"@protocol %@", [aProtocol name]]; |
| |
| protocols = [aProtocol protocols]; |
| if ([protocols count] > 0) { |
| [resultString appendFormat:@" <%@>", [[protocols arrayByMappingSelector:@selector(name)] componentsJoinedByString:@", "]]; |
| [symbolReferences addProtocolNamesFromArray:[protocols arrayByMappingSelector:@selector(name)]]; |
| } |
| |
| [resultString appendString:@"\n"]; |
| } |
| |
| - (void)willVisitOptionalMethods; |
| { |
| [resultString appendString:@"\n@optional\n"]; |
| } |
| |
| - (void)didVisitProtocol:(CDOCProtocol *)aProtocol; |
| { |
| [resultString appendString:@"@end\n\n"]; |
| } |
| |
| - (void)visitClassMethod:(CDOCMethod *)aMethod; |
| { |
| [resultString appendString:@"+ "]; |
| [aMethod appendToString:resultString typeController:[classDump typeController] symbolReferences:symbolReferences]; |
| [resultString appendString:@"\n"]; |
| } |
| |
| - (void)visitInstanceMethod:(CDOCMethod *)aMethod propertyState:(CDVisitorPropertyState *)propertyState; |
| { |
| CDOCProperty *property; |
| |
| property = [propertyState propertyForAccessor:[aMethod name]]; |
| if (property == nil) { |
| //NSLog(@"No property for method: %@", [aMethod name]); |
| [resultString appendString:@"- "]; |
| [aMethod appendToString:resultString typeController:[classDump typeController] symbolReferences:symbolReferences]; |
| [resultString appendString:@"\n"]; |
| } else { |
| if ([propertyState hasUsedProperty:property] == NO) { |
| //NSLog(@"Emitting property %@ triggered by method %@", [property name], [aMethod name]); |
| [self visitProperty:property]; |
| [propertyState useProperty:property]; |
| } else { |
| //NSLog(@"Have already emitted property %@ triggered by method %@", [property name], [aMethod name]); |
| } |
| } |
| } |
| |
| - (void)visitIvar:(CDOCIvar *)anIvar; |
| { |
| [anIvar appendToString:resultString typeController:[classDump typeController] symbolReferences:symbolReferences]; |
| [resultString appendString:@"\n"]; |
| } |
| |
| - (void)_visitProperty:(CDOCProperty *)aProperty parsedType:(CDType *)parsedType attributes:(NSArray *)attrs; |
| { |
| NSMutableArray *alist, *unknownAttrs; |
| NSString *backingVar = nil; |
| NSString *formattedString; |
| BOOL isWeak = NO; |
| BOOL isDynamic = NO; |
| |
| alist = [[NSMutableArray alloc] init]; |
| unknownAttrs = [[NSMutableArray alloc] init]; |
| |
| // objc_v2_encode_prop_attr() in gcc/objc/objc-act.c |
| |
| for (NSString *attr in attrs) { |
| if ([attr hasPrefix:@"T"]) { |
| if (debug) NSLog(@"Warning: Property attribute 'T' should occur only occur at the beginning"); |
| } else if ([attr hasPrefix:@"R"]) { |
| [alist addObject:@"readonly"]; |
| } else if ([attr hasPrefix:@"C"]) { |
| [alist addObject:@"copy"]; |
| } else if ([attr hasPrefix:@"&"]) { |
| [alist addObject:@"retain"]; |
| } else if ([attr hasPrefix:@"G"]) { |
| [alist addObject:[NSString stringWithFormat:@"getter=%@", [attr substringFromIndex:1]]]; |
| } else if ([attr hasPrefix:@"S"]) { |
| [alist addObject:[NSString stringWithFormat:@"setter=%@", [attr substringFromIndex:1]]]; |
| } else if ([attr hasPrefix:@"V"]) { |
| backingVar = [attr substringFromIndex:1]; |
| } else if ([attr hasPrefix:@"N"]) { |
| [alist addObject:@"nonatomic"]; |
| } else if ([attr hasPrefix:@"W"]) { |
| // @property(assign) __weak NSObject *prop; |
| // Only appears with GC. |
| isWeak = YES; |
| } else if ([attr hasPrefix:@"P"]) { |
| // @property(assign) __strong NSObject *prop; |
| // Only appears with GC. |
| // This is the default. |
| isWeak = NO; |
| } else if ([attr hasPrefix:@"D"]) { |
| // Dynamic property. Implementation supplied at runtime. |
| // @property int prop; // @dynamic prop; |
| isDynamic = YES; |
| } else { |
| if (debug) NSLog(@"Warning: Unknown property attribute '%@'", attr); |
| [unknownAttrs addObject:attr]; |
| } |
| } |
| |
| if ([alist count] > 0) { |
| [resultString appendFormat:@"@property(%@) ", [alist componentsJoinedByString:@", "]]; |
| } else { |
| [resultString appendString:@"@property "]; |
| } |
| |
| if (isWeak) |
| [resultString appendString:@"__weak "]; |
| |
| formattedString = [[[classDump typeController] propertyTypeFormatter] formatVariable:[aProperty name] parsedType:parsedType symbolReferences:symbolReferences]; |
| [resultString appendFormat:@"%@;", formattedString]; |
| |
| if (isDynamic) { |
| [resultString appendFormat:@" // @dynamic %@;", [aProperty name]]; |
| } else if (backingVar != nil) { |
| if ([backingVar isEqualToString:[aProperty name]]) { |
| [resultString appendFormat:@" // @synthesize %@;", [aProperty name]]; |
| } else { |
| [resultString appendFormat:@" // @synthesize %@=%@;", [aProperty name], backingVar]; |
| } |
| } |
| |
| [resultString appendString:@"\n"]; |
| if ([unknownAttrs count] > 0) { |
| [resultString appendFormat:@"// Preceding property had unknown attributes: %@\n", [unknownAttrs componentsJoinedByString:@","]]; |
| if ([[aProperty attributeString] length] > 80) { |
| [resultString appendFormat:@"// Original attribute string (following type): %@\n\n", [aProperty attributeStringAfterType]]; |
| } else { |
| [resultString appendFormat:@"// Original attribute string: %@\n\n", [aProperty attributeString]]; |
| } |
| } |
| |
| [alist release]; |
| [unknownAttrs release]; |
| } |
| |
| - (void)visitProperty:(CDOCProperty *)aProperty; |
| { |
| CDType *parsedType; |
| |
| parsedType = [aProperty type]; |
| if (parsedType == nil) { |
| if ([[aProperty attributeString] hasPrefix:@"T"]) { |
| [resultString appendFormat:@"// Error parsing type for property %@:\n", [aProperty name]]; |
| [resultString appendFormat:@"// Property attributes: %@\n\n", [aProperty attributeString]]; |
| } else { |
| [resultString appendFormat:@"// Error: Property attributes should begin with the type ('T') attribute, property name: %@\n", [aProperty name]]; |
| [resultString appendFormat:@"// Property attributes: %@\n\n", [aProperty attributeString]]; |
| } |
| } else { |
| [self _visitProperty:aProperty parsedType:parsedType attributes:[aProperty attributes]]; |
| } |
| } |
| |
| #define ADD_SPACE |
| |
| - (void)didVisitPropertiesOfClass:(CDOCClass *)aClass; |
| { |
| #ifdef ADD_SPACE |
| if ([[aClass properties] count] > 0) |
| [resultString appendString:@"\n"]; |
| #endif |
| } |
| |
| - (void)willVisitPropertiesOfCategory:(CDOCCategory *)aCategory; |
| { |
| #ifdef ADD_SPACE |
| if ([[aCategory properties] count] > 0) |
| [resultString appendString:@"\n"]; |
| #endif |
| } |
| |
| - (void)didVisitPropertiesOfCategory:(CDOCCategory *)aCategory; |
| { |
| #ifdef ADD_SPACE |
| if ([[aCategory properties] count] > 0/* && [aCategory hasMethods]*/) |
| [resultString appendString:@"\n"]; |
| #endif |
| } |
| |
| - (void)willVisitPropertiesOfProtocol:(CDOCProtocol *)aProtocol; |
| { |
| #ifdef ADD_SPACE |
| if ([[aProtocol properties] count] > 0) |
| [resultString appendString:@"\n"]; |
| #endif |
| } |
| |
| - (void)didVisitPropertiesOfProtocol:(CDOCProtocol *)aProtocol; |
| { |
| #ifdef ADD_SPACE |
| if ([[aProtocol properties] count] > 0 /*&& [aProtocol hasMethods]*/) |
| [resultString appendString:@"\n"]; |
| #endif |
| } |
| |
| - (void)visitRemainingProperties:(CDVisitorPropertyState *)propertyState; |
| { |
| NSArray *remaining = [propertyState remainingProperties]; |
| |
| if ([remaining count] > 0) { |
| [resultString appendString:@"\n"]; |
| [resultString appendFormat:@"// Remaining properties\n"]; |
| //NSLog(@"Warning: remaining undeclared property count: %u", [remaining count]); |
| //NSLog(@"remaining: %@", remaining); |
| for (CDOCProperty *property in remaining) |
| [self visitProperty:property]; |
| } |
| } |
| |
| @end |