OLD | NEW |
(Empty) | |
| 1 // -*- mode: ObjC -*- |
| 2 |
| 3 // This file is part of class-dump, a utility for examining the Objective-C seg
ment of Mach-O files. |
| 4 // Copyright (C) 1997-1998, 2000-2001, 2004-2010 Steve Nygard. |
| 5 |
| 6 #import "CDObjectiveCProcessor.h" |
| 7 |
| 8 #import "CDClassDump.h" |
| 9 #import "CDMachOFile.h" |
| 10 #import "CDVisitor.h" |
| 11 #import "NSArray-Extensions.h" |
| 12 #import "CDLCSegment.h" |
| 13 #import "CDLCDynamicSymbolTable.h" |
| 14 #import "CDLCSymbolTable.h" |
| 15 #import "CDOCProtocol.h" |
| 16 #import "CDTypeController.h" |
| 17 |
| 18 // Note: sizeof(long long) == 8 on both 32-bit and 64-bit. sizeof(uint64_t) ==
8. So use [NSNumber numberWithUnsignedLongLong:]. |
| 19 |
| 20 @implementation CDObjectiveCProcessor |
| 21 |
| 22 - (id)initWithMachOFile:(CDMachOFile *)aMachOFile; |
| 23 { |
| 24 if ([super init] == nil) |
| 25 return nil; |
| 26 |
| 27 machOFile = [aMachOFile retain]; |
| 28 classes = [[NSMutableArray alloc] init]; |
| 29 classesByAddress = [[NSMutableDictionary alloc] init]; |
| 30 categories = [[NSMutableArray alloc] init]; |
| 31 protocolsByName = [[NSMutableDictionary alloc] init]; |
| 32 protocolsByAddress = [[NSMutableDictionary alloc] init]; |
| 33 |
| 34 return self; |
| 35 } |
| 36 |
| 37 - (void)dealloc; |
| 38 { |
| 39 [machOFile release]; |
| 40 [classes release]; |
| 41 [classesByAddress release]; |
| 42 [categories release]; |
| 43 [protocolsByName release]; |
| 44 [protocolsByAddress release]; |
| 45 |
| 46 [super dealloc]; |
| 47 } |
| 48 |
| 49 - (CDMachOFile *)machOFile; |
| 50 { |
| 51 return machOFile; |
| 52 } |
| 53 |
| 54 - (BOOL)hasObjectiveCData; |
| 55 { |
| 56 return [machOFile hasObjectiveC1Data] || [machOFile hasObjectiveC2Data]; |
| 57 } |
| 58 |
| 59 - (void)process; |
| 60 { |
| 61 if ([machOFile isEncrypted] == NO && [machOFile canDecryptAllSegments]) { |
| 62 [[machOFile symbolTable] loadSymbols]; |
| 63 [[machOFile dynamicSymbolTable] loadSymbols]; |
| 64 |
| 65 [self loadProtocols]; |
| 66 |
| 67 // Load classes before categories, so we can get a dictionary of classes
by address. |
| 68 [self loadClasses]; |
| 69 [self loadCategories]; |
| 70 } |
| 71 } |
| 72 |
| 73 - (void)loadProtocols; |
| 74 { |
| 75 // Implement in subclasses. |
| 76 } |
| 77 |
| 78 - (void)loadClasses; |
| 79 { |
| 80 // Implement in subclasses. |
| 81 } |
| 82 |
| 83 - (void)loadCategories; |
| 84 { |
| 85 // Implement in subclasses. |
| 86 } |
| 87 |
| 88 |
| 89 - (void)registerTypesWithObject:(CDTypeController *)typeController phase:(NSUInt
eger)phase; |
| 90 { |
| 91 for (CDOCClass *aClass in classes) |
| 92 [aClass registerTypesWithObject:typeController phase:phase]; |
| 93 |
| 94 for (CDOCCategory *category in categories) |
| 95 [category registerTypesWithObject:typeController phase:phase]; |
| 96 |
| 97 for (NSString *name in [[protocolsByName allKeys] sortedArrayUsingSelector:@
selector(compare:)]) |
| 98 [[protocolsByName objectForKey:name] registerTypesWithObject:typeControl
ler phase:phase]; |
| 99 } |
| 100 |
| 101 - (NSString *)description; |
| 102 { |
| 103 return [NSString stringWithFormat:@"<%@:%p> machOFile: %@", |
| 104 NSStringFromClass([self class]), self, |
| 105 [machOFile filename]]; |
| 106 } |
| 107 |
| 108 - (void)recursivelyVisit:(CDVisitor *)aVisitor; |
| 109 { |
| 110 NSMutableArray *classesAndCategories; |
| 111 NSArray *protocolNames; |
| 112 |
| 113 classesAndCategories = [[NSMutableArray alloc] init]; |
| 114 [classesAndCategories addObjectsFromArray:classes]; |
| 115 [classesAndCategories addObjectsFromArray:categories]; |
| 116 |
| 117 // TODO: Sort protocols by dependency |
| 118 // TODO (2004-01-30): It looks like protocols might be defined in more than
one file. i.e. NSObject. |
| 119 // TODO (2004-02-02): Looks like we need to record the order the protocols w
ere encountered, or just always sort protocols |
| 120 protocolNames = [[protocolsByName allKeys] sortedArrayUsingSelector:@selecto
r(compare:)]; |
| 121 |
| 122 [aVisitor willVisitObjectiveCProcessor:self]; |
| 123 |
| 124 // Skip if there are no protocols, classes, or categories to print. |
| 125 // But don't skip if the file is encrypted or has segments that can't be dec
rypted. |
| 126 if ([protocolNames count] > 0 || [classesAndCategories count] > 0 || [machOF
ile isEncrypted] || [machOFile canDecryptAllSegments] == NO) { |
| 127 [aVisitor visitObjectiveCProcessor:self]; |
| 128 } |
| 129 |
| 130 for (NSString *protocolName in protocolNames) { |
| 131 [[protocolsByName objectForKey:protocolName] recursivelyVisit:aVisitor]; |
| 132 } |
| 133 |
| 134 if ([[aVisitor classDump] shouldSortClassesByInheritance]) { |
| 135 [classesAndCategories sortTopologically]; |
| 136 } else if ([[aVisitor classDump] shouldSortClasses]) |
| 137 [classesAndCategories sortUsingSelector:@selector(ascendingCompareByName
:)]; |
| 138 |
| 139 for (id aClassOrCategory in classesAndCategories) |
| 140 [aClassOrCategory recursivelyVisit:aVisitor]; |
| 141 |
| 142 [classesAndCategories release]; |
| 143 |
| 144 [aVisitor didVisitObjectiveCProcessor:self]; |
| 145 } |
| 146 |
| 147 - (void)createUniquedProtocols; |
| 148 { |
| 149 // Now unique the protocols by name and store in protocolsByName |
| 150 |
| 151 for (NSNumber *key in [[protocolsByAddress allKeys] sortedArrayUsingSelector
:@selector(compare:)]) { |
| 152 CDOCProtocol *p1, *p2; |
| 153 |
| 154 p1 = [protocolsByAddress objectForKey:key]; |
| 155 p2 = [protocolsByName objectForKey:[p1 name]]; |
| 156 if (p2 == nil) { |
| 157 p2 = [[CDOCProtocol alloc] init]; |
| 158 [p2 setName:[p1 name]]; |
| 159 [protocolsByName setObject:p2 forKey:[p2 name]]; |
| 160 // adopted protocols still not set, will want uniqued instances |
| 161 [p2 release]; |
| 162 } else { |
| 163 } |
| 164 } |
| 165 |
| 166 //NSLog(@"uniqued protocol names: %@", [[[protocolsByName allKeys] sortedArr
ayUsingSelector:@selector(compare:)] componentsJoinedByString:@", "]); |
| 167 |
| 168 // And finally fill in adopted protocols, instance and class methods. And p
roperties. |
| 169 for (NSNumber *key in [[protocolsByAddress allKeys] sortedArrayUsingSelector
:@selector(compare:)]) { |
| 170 CDOCProtocol *p1, *uniqueProtocol; |
| 171 |
| 172 p1 = [protocolsByAddress objectForKey:key]; |
| 173 uniqueProtocol = [protocolsByName objectForKey:[p1 name]]; |
| 174 for (CDOCProtocol *p2 in [p1 protocols]) |
| 175 [uniqueProtocol addProtocol:[protocolsByName objectForKey:[p2 name]]
]; |
| 176 |
| 177 if ([[uniqueProtocol classMethods] count] == 0) { |
| 178 for (CDOCMethod *method in [p1 classMethods]) |
| 179 [uniqueProtocol addClassMethod:method]; |
| 180 } else { |
| 181 NSParameterAssert([[p1 classMethods] count] == 0 || [[uniqueProtocol
classMethods] count] == [[p1 classMethods] count]); |
| 182 } |
| 183 |
| 184 if ([[uniqueProtocol instanceMethods] count] == 0) { |
| 185 for (CDOCMethod *method in [p1 instanceMethods]) |
| 186 [uniqueProtocol addInstanceMethod:method]; |
| 187 } else { |
| 188 NSParameterAssert([[p1 instanceMethods] count] == 0 || [[uniqueProto
col instanceMethods] count] == [[p1 instanceMethods] count]); |
| 189 } |
| 190 |
| 191 if ([[uniqueProtocol optionalClassMethods] count] == 0) { |
| 192 for (CDOCMethod *method in [p1 optionalClassMethods]) |
| 193 [uniqueProtocol addOptionalClassMethod:method]; |
| 194 } else { |
| 195 NSParameterAssert([[p1 optionalClassMethods] count] == 0 || [[unique
Protocol optionalClassMethods] count] == [[p1 optionalClassMethods] count]); |
| 196 } |
| 197 |
| 198 if ([[uniqueProtocol optionalInstanceMethods] count] == 0) { |
| 199 for (CDOCMethod *method in [p1 optionalInstanceMethods]) |
| 200 [uniqueProtocol addOptionalInstanceMethod:method]; |
| 201 } else { |
| 202 NSParameterAssert([[p1 optionalInstanceMethods] count] == 0 || [[uni
queProtocol optionalInstanceMethods] count] == [[p1 optionalInstanceMethods] cou
nt]); |
| 203 } |
| 204 |
| 205 if ([[uniqueProtocol properties] count] == 0) { |
| 206 for (CDOCProperty *property in [p1 properties]) |
| 207 [uniqueProtocol addProperty:property]; |
| 208 } else { |
| 209 NSParameterAssert([[p1 properties] count] == 0 || [[uniqueProtocol p
roperties] count] == [[p1 properties] count]); |
| 210 } |
| 211 } |
| 212 |
| 213 //NSLog(@"protocolsByName: %@", protocolsByName); |
| 214 } |
| 215 |
| 216 - (NSData *)objcImageInfoData; |
| 217 { |
| 218 // Good for objc2. Use __OBJC segment for objc1. |
| 219 return [[[machOFile segmentWithName:@"__DATA"] sectionWithName:@"__objc_imag
einfo"] data]; |
| 220 } |
| 221 |
| 222 - (NSString *)garbageCollectionStatus; |
| 223 { |
| 224 NSData *sectionData; |
| 225 CDDataCursor *cursor; |
| 226 uint32_t v1, v2; |
| 227 |
| 228 sectionData = [self objcImageInfoData]; |
| 229 if ([sectionData length] < 8) |
| 230 return @"Unknown"; |
| 231 |
| 232 cursor = [[CDDataCursor alloc] initWithData:sectionData]; |
| 233 [cursor setByteOrder:[machOFile byteOrder]]; |
| 234 |
| 235 v1 = [cursor readInt32]; |
| 236 v2 = [cursor readInt32]; |
| 237 //NSLog(@"%s: %08x %08x", _cmd, v1, v2); |
| 238 // v2 == 0 -> Objective-C Garbage Collection: Unsupported |
| 239 // v2 == 2 -> Supported |
| 240 // v2 == 6 -> Required |
| 241 NSParameterAssert(v2 == 0 || v2 == 2 || v2 == 6); |
| 242 |
| 243 [cursor release]; |
| 244 |
| 245 // These are probably bitfields that should be tested/masked... |
| 246 switch (v2) { |
| 247 case 0: return @"Unsupported"; |
| 248 case 2: return @"Supported"; |
| 249 case 6: return @"Required"; |
| 250 } |
| 251 |
| 252 return [NSString stringWithFormat:@"Unknown (0x%08x)", v2]; |
| 253 } |
| 254 |
| 255 @end |
OLD | NEW |