From 1814af213c2852ef70ffa399f220915e70e8d6d8 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 24 May 2012 16:06:19 -0700 Subject: [PATCH 01/15] Fix the #import names of protocols in -H mode. Protocol declarations (@protocol ... @end) are generated in files named according to the scheme "MyProtocolName-Protocol.h". But sometime between September 2011 and May 2012, class-dump started emitting the corresponding #import directives using the scheme "MyProtocolName.h" --- missing the "-Protocol" part. With this revision, I restore the old correct behavior. I think this probably broke in revision 2ccd217b, judging by that revision's commit message. :) --- Source/CDMultiFileVisitor.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/CDMultiFileVisitor.m b/Source/CDMultiFileVisitor.m index 2e6fbdd5..d7a17267 100644 --- a/Source/CDMultiFileVisitor.m +++ b/Source/CDMultiFileVisitor.m @@ -235,9 +235,9 @@ - (NSString *)importStringForProtocolName:(NSString *)name; if (name != nil) { NSString *framework = [self frameworkForProtocolName:name]; if (framework == nil) - return [NSString stringWithFormat:@"#import \"%@.h\"\n", name]; + return [NSString stringWithFormat:@"#import \"%@-Protocol.h\"\n", name]; else - return [NSString stringWithFormat:@"#import <%@/%@.h>\n", framework, name]; + return [NSString stringWithFormat:@"#import <%@/%@-Protocol.h>\n", framework, name]; } return nil; From 62669950d1017f6cb4f08cae5656e5bc4dc941c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Tue, 29 May 2012 22:32:32 +0200 Subject: [PATCH 02/15] Use __attribute__((visibility("hidden"))) instead of // Not exported --- Source/CDTextClassDumpVisitor.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/CDTextClassDumpVisitor.m b/Source/CDTextClassDumpVisitor.m index 909d9885..039eef8f 100644 --- a/Source/CDTextClassDumpVisitor.m +++ b/Source/CDTextClassDumpVisitor.m @@ -45,7 +45,7 @@ - (id)init; - (void)willVisitClass:(CDOCClass *)aClass; { if (aClass.isExported == NO) - [self.resultString appendString:@"// Not exported\n"]; + [self.resultString appendString:@"__attribute__((visibility(\"hidden\")))\n"]; [self.resultString appendFormat:@"@interface %@", aClass.name]; if (aClass.superClassName != nil) From dd854e99f3d7c3e27b30b2d329bd200c49f7d146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Tue, 29 May 2012 22:33:50 +0200 Subject: [PATCH 03/15] Don't assume modern runtime == 64 bits --- Source/CDLCSymbolTable.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/CDLCSymbolTable.m b/Source/CDLCSymbolTable.m index 080890be..8a0cb18b 100644 --- a/Source/CDLCSymbolTable.m +++ b/Source/CDLCSymbolTable.m @@ -127,6 +127,11 @@ - (void)loadSymbols; CDSymbol *symbol = [[CDSymbol alloc] initWithName:str machOFile:self.machOFile nlist32:nlist]; [_symbols addObject:symbol]; + + if ([str hasPrefix:ObjCClassSymbolPrefix] && symbol.value != 0) { + NSString *className = [str substringFromIndex:[ObjCClassSymbolPrefix length]]; + [_classSymbols setObject:symbol forKey:className]; + } } //NSLog(@"Loaded %lu 32-bit symbols", [symbols count]); @@ -151,7 +156,7 @@ - (void)loadSymbols; CDSymbol *symbol = [[CDSymbol alloc] initWithName:str machOFile:self.machOFile nlist64:nlist]; [_symbols addObject:symbol]; - if ([str hasPrefix:ObjCClassSymbolPrefix] && [symbol value] != 0) { + if ([str hasPrefix:ObjCClassSymbolPrefix] && symbol.value != 0) { NSString *className = [str substringFromIndex:[ObjCClassSymbolPrefix length]]; [_classSymbols setObject:symbol forKey:className]; } From a9aa2d12c3db3390aa3d2a84b8da8a831fa32516 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 24 May 2012 21:18:52 -0700 Subject: [PATCH 04/15] Fix parsing of the type-encodings for blocks and function pointers. The type-encoding "@?" means "block", and "^?" means "function pointer". Unfortunately there is no way to determine the original parameter types or return type of the function or block, so I arbitrarily pick (void(*)()) and (void(^)()) as the type displayed by class-dump. --- Source/CDType.h | 2 ++ Source/CDType.m | 32 ++++++++++++++++++++++++++++++++ Source/CDTypeLexer.h | 2 ++ Source/CDTypeParser.m | 6 ++++++ 4 files changed, 42 insertions(+) diff --git a/Source/CDType.h b/Source/CDType.h index 79462338..eb52f060 100644 --- a/Source/CDType.h +++ b/Source/CDType.h @@ -18,6 +18,8 @@ - (id)initBitfieldType:(NSString *)bitfieldSize; - (id)initArrayType:(CDType *)type count:(NSString *)count; - (id)initPointerType:(CDType *)type; +- (id)initFunctionPointerType; +- (id)initBlockType; - (id)initModifier:(int)modifier type:(CDType *)type; @property (strong) NSString *variableName; diff --git a/Source/CDType.m b/Source/CDType.m index d460219b..a16b2d59 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -151,6 +151,24 @@ - (id)initPointerType:(CDType *)aType; return self; } +- (id)initFunctionPointerType +{ + if ((self = [self init])) { + type = T_FUNCTION_POINTER_TYPE; + } + + return self; +} + +- (id)initBlockType +{ + if ((self = [self init])) { + type = T_BLOCK_TYPE; + } + + return self; +} + - (id)initModifier:(int)aModifier type:(CDType *)aType; { if ((self = [self init])) { @@ -397,6 +415,20 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte result = [subtype formattedString:result formatter:typeFormatter level:level]; break; + case T_FUNCTION_POINTER_TYPE: + if (currentName == nil) + result = @"void(*)()"; + else + result = [NSString stringWithFormat:@"void(*%@)()", currentName]; + break; + + case T_BLOCK_TYPE: + if (currentName == nil) + result = @"void(^)()"; + else + result = [NSString stringWithFormat:@"void(^%@)()", currentName]; + break; + case 'j': case 'r': case 'n': diff --git a/Source/CDTypeLexer.h b/Source/CDTypeLexer.h index 8dcd76ac..1cffe865 100644 --- a/Source/CDTypeLexer.h +++ b/Source/CDTypeLexer.h @@ -11,6 +11,8 @@ #define T_NAMED_OBJECT 259 #define TK_QUOTED_STRING 260 #define TK_TEMPLATE_TYPE TK_IDENTIFIER +#define T_FUNCTION_POINTER_TYPE 1001 +#define T_BLOCK_TYPE 1002 enum { CDTypeLexerState_Normal = 0, diff --git a/Source/CDTypeParser.m b/Source/CDTypeParser.m index 4907f461..aa6eb2fa 100644 --- a/Source/CDTypeParser.m +++ b/Source/CDTypeParser.m @@ -247,6 +247,9 @@ - (CDType *)_parseTypeInStruct:(BOOL)isInStruct; type = [[CDType alloc] initSimpleType:'v']; // Safari on 10.5 has: "m_function"{?="__pfn"^"__delta"i} result = [[CDType alloc] initPointerType:type]; + } else if (lookahead == '?') { + [self match:'?']; + result = [[CDType alloc] initFunctionPointerType]; } else { type = [self _parseTypeInStruct:isInStruct]; result = [[CDType alloc] initPointerType:type]; @@ -277,6 +280,9 @@ - (CDType *)_parseTypeInStruct:(BOOL)isInStruct; } [self match:TK_QUOTED_STRING]; + } else if (lookahead == '?') { + [self match:'?']; + result = [[CDType alloc] initBlockType]; } else { result = [[CDType alloc] initIDType:nil]; } From 7056ff2a8f4e5980dcdaed9d9307d4ee129829b2 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 1 Jun 2012 12:13:54 -0700 Subject: [PATCH 05/15] Fix an assert-fail if a block or function pointer appears inside a struct. My revision 3a1c177e wasn't quite complete enough; we also need to include cases for the two new types in [CDType typeString], or we'll get assertion failures on code like struct FaceCoreAPI { void (^field1)(); }; @interface A -(struct FaceCoreAPI*)api; @end This is now fixed. --- Source/CDType.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/CDType.m b/Source/CDType.m index a16b2d59..ea605b5f 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -595,6 +595,14 @@ - (NSString *)_typeStringWithVariableNamesToLevel:(NSUInteger)level showObjectTy result = [NSString stringWithFormat:@"%c%@", type, [subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]]; break; + case T_FUNCTION_POINTER_TYPE: + result = @"^?"; + break; + + case T_BLOCK_TYPE: + result = @"@?"; + break; + default: result = [NSString stringWithFormat:@"%c", type]; break; From 0a2b5dcff10b957d9a87a8ae7c043b9a9d1b5add Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 31 May 2012 16:10:23 -0700 Subject: [PATCH 06/15] Fix https://github.com/nygard/class-dump/issues/13. If a property is listed as having the modifier "retain" or "copy", but is not a garbage-collected type, then we need to decorate it with __attribute__((NSObject)) to keep the compiler quiet. Garbage-collected types, as far as I can tell, are only "Class", "id", block types, and named types such as "InterfaceType *". Despite the explicit pointer syntax in "InterfaceType *", the Objective-C encoding of that type is @"myInterfaceType" If it were a pointer to a C struct type instead, it would be encoded as ^{myStructType=} and would not be considered a garbage-collected type. --- Source/CDTextClassDumpVisitor.m | 5 +++++ Source/CDType.h | 1 + Source/CDType.m | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/Source/CDTextClassDumpVisitor.m b/Source/CDTextClassDumpVisitor.m index 909d9885..b255141d 100644 --- a/Source/CDTextClassDumpVisitor.m +++ b/Source/CDTextClassDumpVisitor.m @@ -10,6 +10,7 @@ #import "CDOCCategory.h" #import "CDOCMethod.h" #import "CDOCProperty.h" +#import "CDType.h" #import "CDTypeController.h" #import "CDTypeFormatter.h" #import "CDVisitorPropertyState.h" @@ -284,6 +285,10 @@ - (void)_visitProperty:(CDOCProperty *)property parsedType:(CDType *)parsedType if (isWeak) [self.resultString appendString:@"__weak "]; + if ([alist containsObject:@"retain"] || [alist containsObject:@"copy"]) + if (!parsedType.isGarbageCollectedType) + [self.resultString appendString:@"__attribute__((NSObject)) "]; + NSString *formattedString = [self.classDump.typeController.propertyTypeFormatter formatVariable:property.name parsedType:parsedType]; [self.resultString appendFormat:@"%@;", formattedString]; diff --git a/Source/CDType.h b/Source/CDType.h index eb52f060..1bfc3c0b 100644 --- a/Source/CDType.h +++ b/Source/CDType.h @@ -28,6 +28,7 @@ @property (readonly) BOOL isIDType; @property (readonly) BOOL isNamedObject; @property (readonly) BOOL isTemplateType; +@property (readonly) BOOL isGarbageCollectedType; @property (nonatomic, readonly) CDType *subtype; @property (nonatomic, readonly) CDTypeName *typeName; diff --git a/Source/CDType.m b/Source/CDType.m index ea605b5f..9f2d464f 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -241,6 +241,12 @@ - (BOOL)isTemplateType; return typeName.isTemplateType; } +- (BOOL)isGarbageCollectedType; +{ + return self.isIDType || self.isNamedObject || + self.type == '#' || self.type == T_BLOCK_TYPE; +} + - (CDType *)subtype; { return subtype; From 2f0d5ad4361edf12e1c1576fae5d8c96cc2c2130 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 29 Jun 2012 14:15:51 -0700 Subject: [PATCH 07/15] Correctly pass through the argument to -[NSCopying copyWithZone:]. This is pretty much just a cosmetic change, but the whole point of the "zone" parameter is that you're supposed to pass it to allocWithZone:, so let's do that. -[CDType copyWithZone:] should also use the "zone" parameter, but in fact it doesn't directly allocate the copied type; it simply passes along the return value of -[parser parseType:], which is allocated in an unknown zone. --- Source/CDOCMethod.m | 2 +- Source/CDStructureInfo.m | 2 +- Source/CDTypeName.m | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/CDOCMethod.m b/Source/CDOCMethod.m index 32d12374..2166b836 100644 --- a/Source/CDOCMethod.m +++ b/Source/CDOCMethod.m @@ -53,7 +53,7 @@ - (id)initWithName:(NSString *)aName type:(NSString *)aType; - (id)copyWithZone:(NSZone *)zone; { - return [[CDOCMethod alloc] initWithName:name type:type imp:imp]; + return [[CDOCMethod allocWithZone:zone] initWithName:name type:type imp:imp]; } #pragma mark - Debugging diff --git a/Source/CDStructureInfo.m b/Source/CDStructureInfo.m index 82815a0a..c7062cdb 100644 --- a/Source/CDStructureInfo.m +++ b/Source/CDStructureInfo.m @@ -34,7 +34,7 @@ - (id)initWithType:(CDType *)type; - (id)copyWithZone:(NSZone *)zone; { - CDStructureInfo *copy = [[CDStructureInfo alloc] initWithType:self.type]; // type gets copied + CDStructureInfo *copy = [[CDStructureInfo allocWithZone:zone] initWithType:self.type]; // type gets copied copy.referenceCount = self.referenceCount; copy.isUsedInMethod = self.isUsedInMethod; copy.typedefName = self.typedefName; diff --git a/Source/CDTypeName.m b/Source/CDTypeName.m index a17ff66f..66c14a01 100644 --- a/Source/CDTypeName.m +++ b/Source/CDTypeName.m @@ -27,12 +27,12 @@ - (id)init; - (id)copyWithZone:(NSZone *)zone; { - CDTypeName *copy = [[CDTypeName alloc] init]; + CDTypeName *copy = [[CDTypeName allocWithZone:zone] init]; copy.name = self.name; copy.suffix = self.suffix; for (CDTypeName *subtype in self.templateTypes) { - CDTypeName *subcopy = [subtype copy]; + CDTypeName *subcopy = [subtype copyWithZone:zone]; [copy.templateTypes addObject:subcopy]; } From 3a9be2162ee5c7fad1fedb489c577825128320b0 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Wed, 6 Jun 2012 18:52:51 -0700 Subject: [PATCH 08/15] Don't duplicate struct definitions for Objective-C++ templated types. The "// Template types" section of the generated CDStructures.h contains typedef declarations such as typedef struct S { int _field1; } S_9abcdef0; This causes compiler errors even *after* flattening "S" into the Objective-C-friendly "S_int_", because the S type was already defined earlier in the file. What we really want to see here is simply typedef struct S S_9abcdef0; I'm not at all confident that this change is the best way to guarantee this output, but it does seem to work on my test case. --- Source/CDStructureTable.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/CDStructureTable.m b/Source/CDStructureTable.m index dae1bbdc..9479fe2f 100644 --- a/Source/CDStructureTable.m +++ b/Source/CDStructureTable.m @@ -825,6 +825,7 @@ - (void)appendTypedefsToString:(NSMutableString *)resultString [resultString appendFormat:@"// depth: %u, ref: %u, used in method? %u\n", info.type.structureDepth, info.referenceCount, info.isUsedInMethod]; } + typeFormatter.shouldExpand = NO; NSString *formattedString = [typeFormatter formatVariable:nil parsedType:info.type]; if (formattedString != nil) { //[resultString appendFormat:@"%@;\n\n", formattedString]; From d7ade3e7aff96094517b359032e4b5058f92f22e Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 5 Jul 2012 19:17:40 -0700 Subject: [PATCH 09/15] Add a poorly tested --mangle-template-types option. This option is designed to do inside class-dump what I've been doing for a long time outside it with a sed script (that has finally met its match in a deeply nested template argument). The output of "class-dump --mangle-template-types" should be identical to the output without "--mangle-template-types", except that each type-identifier will have all its non-alphanumeric characters replaced with underscore (_) characters. This will allow the output of "class-dump random-objective-c++-binary" to compile as Objective-C with fewer hacks. (Some hacks are still needed: for example, Apple's QuartzCore binary has a struct with a field named "_vptr$GenericContext". I don't know how to reproduce this; it might be a quirk of Apple's original source code rather than a quirk of the Objective-C++ implementation.) Right now the output of "--mangle-template-types" is not quite identical to the old output; it's missing some lines of the form typedef struct vector vector_5a588309; I don't understand why this should be. --- Source/CDTypeName.h | 2 ++ Source/CDTypeName.m | 26 +++++++++++++++++++++----- class-dump.m | 10 ++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Source/CDTypeName.h b/Source/CDTypeName.h index ba312c64..86429305 100644 --- a/Source/CDTypeName.h +++ b/Source/CDTypeName.h @@ -13,3 +13,5 @@ @property (readonly) BOOL isTemplateType; @end + +extern BOOL global_shouldMangleTemplateTypes; \ No newline at end of file diff --git a/Source/CDTypeName.m b/Source/CDTypeName.m index a17ff66f..d86a1593 100644 --- a/Source/CDTypeName.m +++ b/Source/CDTypeName.m @@ -5,6 +5,8 @@ #import "CDTypeName.h" +BOOL global_shouldMangleTemplateTypes = NO; + @implementation CDTypeName { NSString *name; @@ -53,13 +55,27 @@ - (BOOL)isEqual:(id)otherObject; - (NSString *)description; { - if ([self.templateTypes count] == 0) - return name; - + if ([self.name isEqualToString:@"?"]) { + return @"?"; + } + + NSMutableString *result = [NSMutableString string]; + + [result setString:self.name]; + if ([self.templateTypes count] != 0) + [result appendFormat:@"<%@>", [self.templateTypes componentsJoinedByString:@", "]]; if (self.suffix != nil) - return [NSString stringWithFormat:@"%@<%@>%@", self.name, [self.templateTypes componentsJoinedByString:@", "], self.suffix]; + [result appendFormat:@"%@", self.suffix]; - return [NSString stringWithFormat:@"%@<%@>", self.name, [self.templateTypes componentsJoinedByString:@", "]]; + if (global_shouldMangleTemplateTypes) { + NSString *legitCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" "0123456789"; + + NSCharacterSet *illegitCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:legitCharacters] invertedSet]; + return [[result componentsSeparatedByCharactersInSet:illegitCharacterSet] componentsJoinedByString:@"_"]; + } else { + return [result description]; + } } #pragma mark - diff --git a/class-dump.m b/class-dump.m index 174fcf65..b4ded1e2 100644 --- a/class-dump.m +++ b/class-dump.m @@ -22,6 +22,8 @@ #import "CDFatArch.h" #import "CDSearchPathState.h" +extern BOOL global_shouldMangleTemplateTypes; + void print_usage(void) { fprintf(stderr, @@ -45,6 +47,7 @@ void print_usage(void) " --sdk-ios specify iOS SDK version (will look in /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk\n" " --sdk-mac specify Mac OS X version (will look in /Developer/SDKs/MacOSX.sdk\n" " --sdk-root specify the full SDK root path (or use --sdk-ios/--sdk-mac for a shortcut)\n" + " --mangle-template-types mangle non-alnum characters in type names\n" , CLASS_DUMP_VERSION ); @@ -56,6 +59,7 @@ void print_usage(void) #define CD_OPT_SDK_IOS 4 #define CD_OPT_SDK_MAC 5 #define CD_OPT_SDK_ROOT 6 +#define CD_OPT_MANGLE_TEMPLATE_TYPES 7 int main(int argc, char *argv[]) { @@ -89,6 +93,7 @@ int main(int argc, char *argv[]) { "sdk-ios", required_argument, NULL, CD_OPT_SDK_IOS }, { "sdk-mac", required_argument, NULL, CD_OPT_SDK_MAC }, { "sdk-root", required_argument, NULL, CD_OPT_SDK_ROOT }, + { "mangle-template-types", no_argument, NULL, CD_OPT_MANGLE_TEMPLATE_TYPES }, { NULL, 0, NULL, 0 }, }; @@ -146,6 +151,11 @@ int main(int argc, char *argv[]) break; } + + case CD_OPT_MANGLE_TEMPLATE_TYPES: { + global_shouldMangleTemplateTypes = YES; + break; + } case 'a': classDump.shouldShowIvarOffsets = YES; From 5adbfb66fe304b198cfdfdc1a8bffa119eaac7fc Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 28 Jun 2012 04:22:21 -0700 Subject: [PATCH 10/15] Fix https://github.com/nygard/class-dump/issues/16. The encoding of an empty struct type is sometimes "{?=}" and sometimes "{?}", depending on its context. Either way, class-dump internally represents it as a type whose "members" array has zero elements. For some reason I don't fully understand (but have made an educated guess at in my code comments), non-empty struct types also sometimes show up with zero elements in their "members" array. This normally allows us to print struct Foo { int i; } on first reference, and struct Foo thereafter; but in the case of an empty struct type, because the "members" array is already empty, we end up with struct on first and all subsequent references, and this generally doesn't compile. In the long run, we need to represent "my members have already been printed" in a different way (e.g., set "members" to nil) from the way we represent "I have no members at all" (i.e., an empty "members" array). However, that's a bigger change than I feel comfortable making right now. --- Source/CDType.m | 82 +++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/Source/CDType.m b/Source/CDType.m index 9f2d464f..b24fcd5f 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -297,7 +297,6 @@ - (NSUInteger)structureDepth; - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatter *)typeFormatter level:(NSUInteger)level; { NSString *result, *currentName; - NSString *baseType, *memberString; assert(variableName == nil || previousName == nil); if (variableName != nil) @@ -346,61 +345,36 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte result = [subtype formattedString:result formatter:typeFormatter level:level]; break; - case '(': - baseType = nil; - /*if (typeName == nil || [@"?" isEqual:[typeName description]])*/ { - NSString *typedefName = [typeFormatter typedefNameForStruct:self level:level]; - if (typedefName != nil) { - baseType = typedefName; - } - } - - if (baseType == nil) { - if (typeName == nil || [@"?" isEqual:[typeName description]]) - baseType = @"union"; - else - baseType = [NSString stringWithFormat:@"union %@", typeName]; - - if ((typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self] && [members count] > 0) - || (level == 0 && typeFormatter.shouldExpand && [members count] > 0)) - memberString = [NSString stringWithFormat:@" {\n%@%@}", - [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], - [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; - else - memberString = @""; - - baseType = [baseType stringByAppendingString:memberString]; - } - - if (currentName == nil /*|| [currentName hasPrefix:@"?"]*/) // Not sure about this - result = baseType; - else - result = [NSString stringWithFormat:@"%@ %@", baseType, currentName]; - break; - case '{': - baseType = nil; - /*if (typeName == nil || [@"?" isEqual:[typeName description]])*/ { - NSString *typedefName = [typeFormatter typedefNameForStruct:self level:level]; - if (typedefName != nil) { - baseType = typedefName; - } - } + case '(': { + NSString *struct_or_union = (type == '{' ? @"struct" : @"union"); + NSString *baseType = [typeFormatter typedefNameForStruct:self level:level]; + if (baseType == nil) { - if (typeName == nil || [@"?" isEqual:[typeName description]]) - baseType = @"struct"; - else - baseType = [NSString stringWithFormat:@"struct %@", typeName]; - - if ((typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self] && [members count] > 0) - || (level == 0 && typeFormatter.shouldExpand && [members count] > 0)) - memberString = [NSString stringWithFormat:@" {\n%@%@}", - [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], - [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; - else - memberString = @""; + BOOL print_members = NO; + + if (typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self]) { + /* In these cases, "members" may be empty to indicate that + * this type is already defined and must not be repeated. */ + print_members = ([members count] > 0); + } else if (level == 0 && typeFormatter.shouldExpand) { + print_members = ([members count] > 0); + } + + if (typeName == nil || [@"?" isEqual:[typeName description]]) { + /* If the type has no name, we MUST print its members. */ + baseType = struct_or_union; + print_members = YES; + } else { + baseType = [NSString stringWithFormat:@"%@ %@", struct_or_union, typeName]; + } - baseType = [baseType stringByAppendingString:memberString]; + if (print_members) { + NSString *memberString = [NSString stringWithFormat:@" {\n%@%@}", + [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], + [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; + baseType = [baseType stringByAppendingString:memberString]; + } } if (currentName == nil /*|| [currentName hasPrefix:@"?"]*/) // Not sure about this @@ -408,7 +382,7 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte else result = [NSString stringWithFormat:@"%@ %@", baseType, currentName]; break; - + } case '^': if (currentName == nil) result = @"*"; From 4b09fe0ae53bc8d9e601ce1f6aa19137e61c076b Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 29 Jun 2012 13:21:18 -0700 Subject: [PATCH 11/15] Use formatVariable:parsedType: to avoid making a copy of the CDType. This is not merely efficient, but also necessary, because the CDType may hold more information than is directly visible in the type-encoding represented by "type". Right now the only such information is the variable name (which is irrelevant), but soon we'll also be tracking bitfield underlying types. --- Source/CDOCIvar.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/CDOCIvar.m b/Source/CDOCIvar.m index 41e82a40..c89a4bb7 100644 --- a/Source/CDOCIvar.m +++ b/Source/CDOCIvar.m @@ -74,7 +74,7 @@ - (CDType *)parsedType; - (void)appendToString:(NSMutableString *)resultString typeController:(CDTypeController *)typeController; { - NSString *formattedString = [[typeController ivarTypeFormatter] formatVariable:name type:type]; + NSString *formattedString = [[typeController ivarTypeFormatter] formatVariable:name parsedType:[self parsedType]]; if (formattedString != nil) { [resultString appendString:formattedString]; [resultString appendString:@";"]; From 2c518c304386d05af0fd36d9d58c57fc8b2bc6d8 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 29 Jun 2012 13:17:35 -0700 Subject: [PATCH 12/15] Track alignment and size of CDOCIvars; output sane bitfield types. In Objective-C 2.0, ivars have not only a type-encoding but also an offset, alignment, and size. We can use the "size" field to make an educated guess at the proper underlying type for bitfield types; for example, if a bitfield has encoding "b7" and size 1, we should output it as "unsigned char field:7;" in preference to "unsigned int field:7;". (It might originally have been signed char, or BOOL; we have no way of knowing.) Furthermore, even in Objective-C 1.0 (or inside a C struct), bitfields wider than "int" should be printed as "unsigned long long" instead, to avoid compiler errors. Fixes https://github.com/nygard/class-dump/issues/18. --- Source/CDOCIvar.h | 4 +++- Source/CDOCIvar.m | 12 +++++++++- Source/CDObjectiveC1Processor.m | 2 +- Source/CDObjectiveC2Processor.m | 2 +- Source/CDType.h | 3 +++ Source/CDType.m | 42 ++++++++++++++++++++++++++++----- 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/Source/CDOCIvar.h b/Source/CDOCIvar.h index d565c13f..4545bfda 100644 --- a/Source/CDOCIvar.h +++ b/Source/CDOCIvar.h @@ -9,11 +9,13 @@ @interface CDOCIvar : NSObject -- (id)initWithName:(NSString *)name type:(NSString *)aType offset:(NSUInteger)offset; +- (id)initWithName:(NSString *)name type:(NSString *)aType offset:(NSUInteger)offset alignment:(NSUInteger)alignment size:(NSUInteger)size; @property (readonly) NSString *name; @property (readonly) NSString *type; @property (readonly) NSUInteger offset; +@property (readonly) NSUInteger alignment; +@property (readonly) NSUInteger size; @property (nonatomic, readonly) CDType *parsedType; diff --git a/Source/CDOCIvar.m b/Source/CDOCIvar.m index c89a4bb7..ec4e8108 100644 --- a/Source/CDOCIvar.m +++ b/Source/CDOCIvar.m @@ -22,17 +22,21 @@ @implementation CDOCIvar NSString *name; NSString *type; NSUInteger offset; + NSUInteger alignment; + NSUInteger size; BOOL hasParsedType; CDType *parsedType; } -- (id)initWithName:(NSString *)aName type:(NSString *)aType offset:(NSUInteger)anOffset; +- (id)initWithName:(NSString *)aName type:(NSString *)aType offset:(NSUInteger)anOffset alignment:(NSUInteger)anAlignment size:(NSUInteger)aSize; { if ((self = [super init])) { name = aName; type = aType; offset = anOffset; + alignment = anAlignment; + size = aSize; hasParsedType = NO; parsedType = nil; @@ -54,6 +58,8 @@ - (NSString *)description; @synthesize name; @synthesize type; @synthesize offset; +@synthesize alignment; +@synthesize size; @synthesize hasParsedType; - (CDType *)parsedType; @@ -66,6 +72,10 @@ - (CDType *)parsedType; if (parsedType == nil) NSLog(@"Warning: Parsing ivar type failed, %@", name); + if (parsedType.isBitfieldType) { + [parsedType setUnderlyingType:size]; + } + self.hasParsedType = YES; } diff --git a/Source/CDObjectiveC1Processor.m b/Source/CDObjectiveC1Processor.m index d82d016c..c292cd03 100644 --- a/Source/CDObjectiveC1Processor.m +++ b/Source/CDObjectiveC1Processor.m @@ -288,7 +288,7 @@ - (CDOCClass *)processClassDefinitionAtAddress:(uint32_t)address; // bitfields don't need names. // NSIconRefBitmapImageRep in AppKit on 10.5 has a single-bit bitfield, plus an unnamed 31-bit field. if (type != nil) { - CDOCIvar *anIvar = [[CDOCIvar alloc] initWithName:name type:type offset:objcIvar.offset]; + CDOCIvar *anIvar = [[CDOCIvar alloc] initWithName:name type:type offset:objcIvar.offset alignment:0 size:0]; [ivars addObject:anIvar]; } } diff --git a/Source/CDObjectiveC2Processor.m b/Source/CDObjectiveC2Processor.m index 0af6e295..19f1d311 100644 --- a/Source/CDObjectiveC2Processor.m +++ b/Source/CDObjectiveC2Processor.m @@ -411,7 +411,7 @@ - (NSArray *)loadIvarsAtAddress:(uint64_t)address; NSString *name = [self.machOFile stringAtAddress:objc2Ivar.name]; NSString *type = [self.machOFile stringAtAddress:objc2Ivar.type]; - CDOCIvar *ivar = [[CDOCIvar alloc] initWithName:name type:type offset:objc2Ivar.offset]; + CDOCIvar *ivar = [[CDOCIvar alloc] initWithName:name type:type offset:objc2Ivar.offset alignment:objc2Ivar.alignment size:objc2Ivar.size]; [ivars addObject:ivar]; } else { //NSLog(@"%016lx %016lx %016lx %08x %08x", objc2Ivar.offset, objc2Ivar.name, objc2Ivar.type, objc2Ivar.alignment, objc2Ivar.size); diff --git a/Source/CDType.h b/Source/CDType.h index 1bfc3c0b..ba74c55f 100644 --- a/Source/CDType.h +++ b/Source/CDType.h @@ -22,6 +22,8 @@ - (id)initBlockType; - (id)initModifier:(int)modifier type:(CDType *)type; +- (void)setUnderlyingType:(NSUInteger)underlyingWidth; + @property (strong) NSString *variableName; @property (nonatomic, readonly) int type; @@ -29,6 +31,7 @@ @property (readonly) BOOL isNamedObject; @property (readonly) BOOL isTemplateType; @property (readonly) BOOL isGarbageCollectedType; +@property (readonly) BOOL isBitfieldType; @property (nonatomic, readonly) CDType *subtype; @property (nonatomic, readonly) CDTypeName *typeName; diff --git a/Source/CDType.m b/Source/CDType.m index 9f2d464f..3b757739 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -39,6 +39,7 @@ @implementation CDType CDTypeName *typeName; NSMutableArray *members; NSString *bitfieldSize; + NSUInteger widthOfUnderlyingType; NSString *arraySize; NSString *variableName; @@ -125,6 +126,7 @@ - (id)initBitfieldType:(NSString *)aBitfieldSize; if ((self = [self init])) { type = 'b'; bitfieldSize = aBitfieldSize; + widthOfUnderlyingType = 0; /* to be set later */ } return self; @@ -179,6 +181,12 @@ - (id)initModifier:(int)aModifier type:(CDType *)aType; return self; } +- (void)setUnderlyingType:(NSUInteger)aWidth; +{ + assert(type == 'b'); + widthOfUnderlyingType = aWidth; +} + #pragma mark - NSCopying // An easy deep copy. @@ -197,6 +205,7 @@ - (id)copyWithZone:(NSZone *)zone; NSParameterAssert([str isEqualToString:copiedType.typeString]); [copiedType setVariableName:variableName]; + copiedType->widthOfUnderlyingType = widthOfUnderlyingType; return copiedType; } @@ -247,6 +256,11 @@ - (BOOL)isGarbageCollectedType; self.type == '#' || self.type == T_BLOCK_TYPE; } +- (BOOL)isBitfieldType; +{ + return self.type == 'b'; +} + - (CDType *)subtype; { return subtype; @@ -329,13 +343,29 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte } break; - case 'b': - if (currentName == nil) { - // This actually compiles! - result = [NSString stringWithFormat:@"unsigned int :%@", bitfieldSize]; - } else - result = [NSString stringWithFormat:@"unsigned int %@:%@", currentName, bitfieldSize]; + case 'b': { + switch (widthOfUnderlyingType) { + case 1: result = @"unsigned char "; break; + case 2: result = @"unsigned short "; break; + case 4: result = @"unsigned int "; break; + case 8: result = @"unsigned long long "; break; + default: + /* This path is reachable in ObjC 1.0, where ivars don't + * specify their sizes. It's also reachable if the bitfield + * is a member of a C struct. */ + if ([bitfieldSize intValue] > 32) { + result = @"unsigned long long "; + } else { + result = @"unsigned int "; + } + break; + } + if (currentName != nil) { + result = [result stringByAppendingString:currentName]; + } + result = [NSString stringWithFormat:@"%@:%@", result, bitfieldSize]; break; + } case '[': if (currentName == nil) From f7144a76869eee1bac050c98c3d56ffd59a7ff75 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 29 Jun 2012 13:59:34 -0700 Subject: [PATCH 13/15] Replace "NSString" with "int" for arraySize and bitfieldSize. --- Source/CDType.h | 4 ++-- Source/CDType.m | 31 ++++++++++++++++--------------- Source/CDTypeParser.m | 8 ++++---- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Source/CDType.h b/Source/CDType.h index ba74c55f..973b1ade 100644 --- a/Source/CDType.h +++ b/Source/CDType.h @@ -15,8 +15,8 @@ - (id)initIDTypeWithProtocols:(NSArray *)protocols; - (id)initStructType:(CDTypeName *)name members:(NSArray *)members; - (id)initUnionType:(CDTypeName *)name members:(NSArray *)members; -- (id)initBitfieldType:(NSString *)bitfieldSize; -- (id)initArrayType:(CDType *)type count:(NSString *)count; +- (id)initBitfieldType:(int)bitfieldSize; +- (id)initArrayType:(CDType *)type count:(int)count; - (id)initPointerType:(CDType *)type; - (id)initFunctionPointerType; - (id)initBlockType; diff --git a/Source/CDType.m b/Source/CDType.m index 3b757739..bbc594ce 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -38,9 +38,9 @@ @implementation CDType CDType *subtype; CDTypeName *typeName; NSMutableArray *members; - NSString *bitfieldSize; - NSUInteger widthOfUnderlyingType; - NSString *arraySize; + int bitfieldSize; + int widthOfUnderlyingType; + int arraySize; NSString *variableName; } @@ -54,8 +54,9 @@ - (id)init; typeName = nil; members = nil; variableName = nil; - bitfieldSize = nil; - arraySize = nil; + bitfieldSize = -1; + widthOfUnderlyingType = -1; + arraySize = -1; } return self; @@ -121,7 +122,7 @@ - (id)initUnionType:(CDTypeName *)aName members:(NSArray *)someMembers; return self; } -- (id)initBitfieldType:(NSString *)aBitfieldSize; +- (id)initBitfieldType:(int)aBitfieldSize; { if ((self = [self init])) { type = 'b'; @@ -132,7 +133,7 @@ - (id)initBitfieldType:(NSString *)aBitfieldSize; return self; } -- (id)initArrayType:(CDType *)aType count:(NSString *)aCount; +- (id)initArrayType:(CDType *)aType count:(int)aCount; { if ((self = [self init])) { type = '['; @@ -181,7 +182,7 @@ - (id)initModifier:(int)aModifier type:(CDType *)aType; return self; } -- (void)setUnderlyingType:(NSUInteger)aWidth; +- (void)setUnderlyingType:(int)aWidth; { assert(type == 'b'); widthOfUnderlyingType = aWidth; @@ -222,7 +223,7 @@ - (BOOL)isEqual:(CDType *)otherType; - (NSString *)description; { - return [NSString stringWithFormat:@"[%@] type: %d('%c'), name: %@, subtype: %@, bitfieldSize: %@, arraySize: %@, members: %@, variableName: %@", + return [NSString stringWithFormat:@"[%@] type: %d('%c'), name: %@, subtype: %@, bitfieldSize: %d, arraySize: %d, members: %@, variableName: %@", NSStringFromClass([self class]), type, type, typeName, subtype, bitfieldSize, arraySize, members, variableName]; } @@ -353,7 +354,7 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte /* This path is reachable in ObjC 1.0, where ivars don't * specify their sizes. It's also reachable if the bitfield * is a member of a C struct. */ - if ([bitfieldSize intValue] > 32) { + if (bitfieldSize > 32) { result = @"unsigned long long "; } else { result = @"unsigned int "; @@ -363,15 +364,15 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte if (currentName != nil) { result = [result stringByAppendingString:currentName]; } - result = [NSString stringWithFormat:@"%@:%@", result, bitfieldSize]; + result = [NSString stringWithFormat:@"%@:%d", result, bitfieldSize]; break; } case '[': if (currentName == nil) - result = [NSString stringWithFormat:@"[%@]", arraySize]; + result = [NSString stringWithFormat:@"[%d]", arraySize]; else - result = [NSString stringWithFormat:@"%@[%@]", currentName, arraySize]; + result = [NSString stringWithFormat:@"%@[%d]", currentName, arraySize]; result = [subtype formattedString:result formatter:typeFormatter level:level]; break; @@ -589,11 +590,11 @@ - (NSString *)_typeStringWithVariableNamesToLevel:(NSUInteger)level showObjectTy break; case 'b': - result = [NSString stringWithFormat:@"b%@", bitfieldSize]; + result = [NSString stringWithFormat:@"b%d", bitfieldSize]; break; case '[': - result = [NSString stringWithFormat:@"[%@%@]", arraySize, [subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]]; + result = [NSString stringWithFormat:@"[%d%@]", arraySize, [subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]]; break; case '(': diff --git a/Source/CDTypeParser.m b/Source/CDTypeParser.m index aa6eb2fa..72dbc060 100644 --- a/Source/CDTypeParser.m +++ b/Source/CDTypeParser.m @@ -256,8 +256,8 @@ - (CDType *)_parseTypeInStruct:(BOOL)isInStruct; } } else if (lookahead == 'b') { // bitfield [self match:'b']; - NSString *number = [self parseNumber]; - result = [[CDType alloc] initBitfieldType:number]; + int width = [[self parseNumber] intValue]; + result = [[CDType alloc] initBitfieldType:width]; } else if (lookahead == '@') { // id [self match:'@']; #if 0 @@ -311,11 +311,11 @@ - (CDType *)_parseTypeInStruct:(BOOL)isInStruct; } } else if (lookahead == '[') { // array [self match:'[']; - NSString *number = [self parseNumber]; + int elements = [[self parseNumber] intValue]; CDType *type = [self _parseType]; [self match:']']; - result = [[CDType alloc] initArrayType:type count:number]; + result = [[CDType alloc] initArrayType:type count:elements]; } else if ([self isTokenInSimpleTypeSet:lookahead]) { // simple type int simpleType = lookahead; [self match:simpleType]; From d937756877ac35f9a7a032d2e5f8226df1d9f373 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 28 Jun 2012 04:22:21 -0700 Subject: [PATCH 14/15] Fix https://github.com/nygard/class-dump/issues/16. The encoding of an empty struct type is sometimes "{?=}" and sometimes "{?}", depending on its context. Either way, class-dump internally represents it as a type whose "members" array has zero elements. For some reason I don't fully understand (but have made an educated guess at in my code comments), non-empty struct types also sometimes show up with zero elements in their "members" array. This normally allows us to print struct Foo { int i; } on first reference, and struct Foo thereafter; but in the case of an empty struct type, because the "members" array is already empty, we end up with struct on first and all subsequent references, and this generally doesn't compile. In the long run, we need to represent "my members have already been printed" in a different way (e.g., set "members" to nil) from the way we represent "I have no members at all" (i.e., an empty "members" array). However, that's a bigger change than I feel comfortable making right now. --- Source/CDType.m | 82 +++++++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 54 deletions(-) diff --git a/Source/CDType.m b/Source/CDType.m index 9f2d464f..b24fcd5f 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -297,7 +297,6 @@ - (NSUInteger)structureDepth; - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatter *)typeFormatter level:(NSUInteger)level; { NSString *result, *currentName; - NSString *baseType, *memberString; assert(variableName == nil || previousName == nil); if (variableName != nil) @@ -346,61 +345,36 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte result = [subtype formattedString:result formatter:typeFormatter level:level]; break; - case '(': - baseType = nil; - /*if (typeName == nil || [@"?" isEqual:[typeName description]])*/ { - NSString *typedefName = [typeFormatter typedefNameForStruct:self level:level]; - if (typedefName != nil) { - baseType = typedefName; - } - } - - if (baseType == nil) { - if (typeName == nil || [@"?" isEqual:[typeName description]]) - baseType = @"union"; - else - baseType = [NSString stringWithFormat:@"union %@", typeName]; - - if ((typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self] && [members count] > 0) - || (level == 0 && typeFormatter.shouldExpand && [members count] > 0)) - memberString = [NSString stringWithFormat:@" {\n%@%@}", - [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], - [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; - else - memberString = @""; - - baseType = [baseType stringByAppendingString:memberString]; - } - - if (currentName == nil /*|| [currentName hasPrefix:@"?"]*/) // Not sure about this - result = baseType; - else - result = [NSString stringWithFormat:@"%@ %@", baseType, currentName]; - break; - case '{': - baseType = nil; - /*if (typeName == nil || [@"?" isEqual:[typeName description]])*/ { - NSString *typedefName = [typeFormatter typedefNameForStruct:self level:level]; - if (typedefName != nil) { - baseType = typedefName; - } - } + case '(': { + NSString *struct_or_union = (type == '{' ? @"struct" : @"union"); + NSString *baseType = [typeFormatter typedefNameForStruct:self level:level]; + if (baseType == nil) { - if (typeName == nil || [@"?" isEqual:[typeName description]]) - baseType = @"struct"; - else - baseType = [NSString stringWithFormat:@"struct %@", typeName]; - - if ((typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self] && [members count] > 0) - || (level == 0 && typeFormatter.shouldExpand && [members count] > 0)) - memberString = [NSString stringWithFormat:@" {\n%@%@}", - [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], - [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; - else - memberString = @""; + BOOL print_members = NO; + + if (typeFormatter.shouldAutoExpand && [typeFormatter.typeController shouldExpandType:self]) { + /* In these cases, "members" may be empty to indicate that + * this type is already defined and must not be repeated. */ + print_members = ([members count] > 0); + } else if (level == 0 && typeFormatter.shouldExpand) { + print_members = ([members count] > 0); + } + + if (typeName == nil || [@"?" isEqual:[typeName description]]) { + /* If the type has no name, we MUST print its members. */ + baseType = struct_or_union; + print_members = YES; + } else { + baseType = [NSString stringWithFormat:@"%@ %@", struct_or_union, typeName]; + } - baseType = [baseType stringByAppendingString:memberString]; + if (print_members) { + NSString *memberString = [NSString stringWithFormat:@" {\n%@%@}", + [self formattedStringForMembersAtLevel:level + 1 formatter:typeFormatter], + [NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]]; + baseType = [baseType stringByAppendingString:memberString]; + } } if (currentName == nil /*|| [currentName hasPrefix:@"?"]*/) // Not sure about this @@ -408,7 +382,7 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte else result = [NSString stringWithFormat:@"%@ %@", baseType, currentName]; break; - + } case '^': if (currentName == nil) result = @"*"; From 996327886c9b9ceeeb50c782c1665e702bfb38a5 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Thu, 5 Jul 2012 19:17:40 -0700 Subject: [PATCH 15/15] Add a poorly tested --mangle-template-types option. This option is designed to do inside class-dump what I've been doing for a long time outside it with a sed script (that has finally met its match in a deeply nested template argument). The output of "class-dump --mangle-template-types" should be identical to the output without "--mangle-template-types", except that each type-identifier will have all its non-alphanumeric characters replaced with underscore (_) characters. This will allow the output of "class-dump random-objective-c++-binary" to compile as Objective-C with fewer hacks. (Some hacks are still needed: for example, Apple's QuartzCore binary has a struct with a field named "_vptr$GenericContext". I don't know how to reproduce this; it might be a quirk of Apple's original source code rather than a quirk of the Objective-C++ implementation.) Right now the output of "--mangle-template-types" is not quite identical to the old output; it's missing some lines of the form typedef struct vector vector_5a588309; I don't understand why this should be. --- Source/CDType.h | 2 ++ Source/CDType.m | 18 ++++++++++++++++-- class-dump.m | 10 ++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/CDType.h b/Source/CDType.h index 1bfc3c0b..3c99c131 100644 --- a/Source/CDType.h +++ b/Source/CDType.h @@ -67,3 +67,5 @@ - (void)phase3MergeWithTypeController:(CDTypeController *)typeController; @end + +extern BOOL global_shouldMangleTemplateTypes; diff --git a/Source/CDType.m b/Source/CDType.m index b24fcd5f..89897edb 100644 --- a/Source/CDType.m +++ b/Source/CDType.m @@ -11,6 +11,8 @@ #import "CDTypeFormatter.h" #import "CDTypeParser.h" +BOOL global_shouldMangleTemplateTypes = NO; + static BOOL debugMerge = NO; @interface CDType () @@ -349,6 +351,18 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte case '(': { NSString *struct_or_union = (type == '{' ? @"struct" : @"union"); NSString *baseType = [typeFormatter typedefNameForStruct:self level:level]; + NSString *type_name = [self.typeName description]; + + if ([@"?" isEqual:type_name]) + type_name = nil; + + if (global_shouldMangleTemplateTypes && (type_name != nil)) { + NSString *legitCharacters = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" "0123456789"; + + NSCharacterSet *illegitCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:legitCharacters] invertedSet]; + type_name = [[type_name componentsSeparatedByCharactersInSet:illegitCharacterSet] componentsJoinedByString:@"_"]; + } if (baseType == nil) { BOOL print_members = NO; @@ -361,12 +375,12 @@ - (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatte print_members = ([members count] > 0); } - if (typeName == nil || [@"?" isEqual:[typeName description]]) { + if (type_name == nil) { /* If the type has no name, we MUST print its members. */ baseType = struct_or_union; print_members = YES; } else { - baseType = [NSString stringWithFormat:@"%@ %@", struct_or_union, typeName]; + baseType = [NSString stringWithFormat:@"%@ %@", struct_or_union, type_name]; } if (print_members) { diff --git a/class-dump.m b/class-dump.m index 174fcf65..b4ded1e2 100644 --- a/class-dump.m +++ b/class-dump.m @@ -22,6 +22,8 @@ #import "CDFatArch.h" #import "CDSearchPathState.h" +extern BOOL global_shouldMangleTemplateTypes; + void print_usage(void) { fprintf(stderr, @@ -45,6 +47,7 @@ void print_usage(void) " --sdk-ios specify iOS SDK version (will look in /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk\n" " --sdk-mac specify Mac OS X version (will look in /Developer/SDKs/MacOSX.sdk\n" " --sdk-root specify the full SDK root path (or use --sdk-ios/--sdk-mac for a shortcut)\n" + " --mangle-template-types mangle non-alnum characters in type names\n" , CLASS_DUMP_VERSION ); @@ -56,6 +59,7 @@ void print_usage(void) #define CD_OPT_SDK_IOS 4 #define CD_OPT_SDK_MAC 5 #define CD_OPT_SDK_ROOT 6 +#define CD_OPT_MANGLE_TEMPLATE_TYPES 7 int main(int argc, char *argv[]) { @@ -89,6 +93,7 @@ int main(int argc, char *argv[]) { "sdk-ios", required_argument, NULL, CD_OPT_SDK_IOS }, { "sdk-mac", required_argument, NULL, CD_OPT_SDK_MAC }, { "sdk-root", required_argument, NULL, CD_OPT_SDK_ROOT }, + { "mangle-template-types", no_argument, NULL, CD_OPT_MANGLE_TEMPLATE_TYPES }, { NULL, 0, NULL, 0 }, }; @@ -146,6 +151,11 @@ int main(int argc, char *argv[]) break; } + + case CD_OPT_MANGLE_TEMPLATE_TYPES: { + global_shouldMangleTemplateTypes = YES; + break; + } case 'a': classDump.shouldShowIvarOffsets = YES;