blob: 494280f490c9cb20ec102d5fb7ca4a638e0814ca [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 "CDType.h"
#import "CDTypeController.h"
#import "CDTypeName.h"
#import "CDTypeLexer.h" // For T_NAMED_OBJECT
#import "CDTypeFormatter.h"
#import "CDTypeParser.h"
static BOOL debugMerge = NO;
@interface CDType ()
@property (nonatomic, readonly) NSString *formattedStringForSimpleType;
@end
#pragma mark -
// primitive types:
// * gets turned into ^c (i.e. char *)
// T_NAMED_OBJECT w/ _typeName as the name
// @ - id
// { - structure w/ _typeName, members
// ( - union w/ _typeName, members
// b - bitfield w/ _bitfieldSize - can these occur anywhere, or just in structures/unions?
// [ - array w/ _arraySize, _subtype
// ^ - poiner to _subtype
// C++ template type...
// Primitive types:
// c: char
// i: int
// s: short
// l: long
// q: long long
// C: unsigned char
// I: unsigned int
// S: unsigned short
// L: unsigned long
// Q: unsigned long long
// f: float
// d: double
// D: long double
// B: _Bool // C99 _Bool or C++ bool
// v: void
// #: Class
// :: SEL
// %: NXAtom
// ?: void
//case '?': return @"UNKNOWN"; // For easier regression testing.
// j: _Complex - is this a modifier or a primitive type?
//
// modifier (which?) w/ _subtype. Can we limit these to the top level of the type?
// - n - in
// - N - inout
// - o - out
// - O - bycopy
// - R - byref
// - V - oneway
// const is probably different from the previous modifiers. You can have const int * const foo, or something like that.
// - r - const
@implementation CDType
{
int _primitiveType;
NSArray *_protocols;
CDType *_subtype;
CDTypeName *_typeName;
NSMutableArray *_members;
NSString *_bitfieldSize;
NSString *_arraySize;
NSString *_variableName;
}
- (id)initSimpleType:(int)type;
{
if ((self = [self init])) {
if (type == '*') {
_primitiveType = '^';
_subtype = [[CDType alloc] initSimpleType:'c'];
} else {
_primitiveType = type;
}
}
return self;
}
- (id)initIDType:(CDTypeName *)name;
{
return [self initIDType:name withProtocols:nil];
}
- (id)initIDType:(CDTypeName *)name withProtocols:(NSArray *)protocols;
{
if ((self = [self init])) {
if (name != nil) {
_primitiveType = T_NAMED_OBJECT;
_typeName = name;
} else {
_primitiveType = '@';
}
_protocols = protocols;
}
return self;
}
- (id)initIDTypeWithProtocols:(NSArray *)protocols;
{
if ((self = [self init])) {
_primitiveType = '@';
_protocols = protocols;
}
return self;
}
- (id)initStructType:(CDTypeName *)name members:(NSArray *)members;
{
if ((self = [self init])) {
_primitiveType = '{';
_typeName = name;
_members = [[NSMutableArray alloc] initWithArray:members];
}
return self;
}
- (id)initUnionType:(CDTypeName *)name members:(NSArray *)members;
{
if ((self = [self init])) {
_primitiveType = '(';
_typeName = name;
_members = [[NSMutableArray alloc] initWithArray:members];
}
return self;
}
- (id)initBitfieldType:(NSString *)bitfieldSize;
{
if ((self = [self init])) {
_primitiveType = 'b';
_bitfieldSize = bitfieldSize;
}
return self;
}
- (id)initArrayType:(CDType *)type count:(NSString *)count;
{
if ((self = [self init])) {
_primitiveType = '[';
_arraySize = count;
_subtype = type;
}
return self;
}
- (id)initPointerType:(CDType *)type;
{
if ((self = [self init])) {
_primitiveType = '^';
_subtype = type;
}
return self;
}
- (id)initFunctionPointerType;
{
if ((self = [self init])) {
_primitiveType = T_FUNCTION_POINTER_TYPE;
}
return self;
}
- (id)initBlockTypeWithTypes:(NSArray *)types;
{
if ((self = [self init])) {
_primitiveType = T_BLOCK_TYPE;
_types = types;
}
return self;
}
- (id)initModifier:(int)modifier type:(CDType *)type;
{
if ((self = [self init])) {
_primitiveType = modifier;
_subtype = type;
}
return self;
}
#pragma mark - NSCopying
// An easy deep copy.
- (id)copyWithZone:(NSZone *)zone;
{
NSString *str = [self typeString];
NSParameterAssert(str != nil);
CDTypeParser *parser = [[CDTypeParser alloc] initWithString:str];
NSError *error = nil;
CDType *copiedType = [parser parseType:&error];
if (copiedType == nil)
NSLog(@"Warning: Parsing type in %s failed, %@", __PRETTY_FUNCTION__, str);
NSParameterAssert([str isEqualToString:copiedType.typeString]);
copiedType.variableName = _variableName;
return copiedType;
}
#pragma mark -
// TODO: (2009-08-26) Looks like this doesn't compare the variable name.
- (BOOL)isEqual:(id)object;
{
if ([object isKindOfClass:[self class]]) {
CDType *otherType = object;
return [self.typeString isEqual:otherType.typeString];
}
return NO;
}
#pragma mark - Debugging
- (NSString *)description;
{
return [NSString stringWithFormat:@"[%@] type: %d('%c'), name: %@, subtype: %@, bitfieldSize: %@, arraySize: %@, members: %@, variableName: %@",
NSStringFromClass([self class]), _primitiveType, _primitiveType, _typeName, _subtype, _bitfieldSize, _arraySize, _members, _variableName];
}
#pragma mark -
- (BOOL)isIDType;
{
return _primitiveType == '@' && _typeName == nil;
}
- (BOOL)isNamedObject;
{
return _primitiveType == T_NAMED_OBJECT;
}
- (BOOL)isTemplateType;
{
return _typeName.isTemplateType;
}
- (BOOL)isModifierType;
{
return _primitiveType == 'j' || _primitiveType == 'r' || _primitiveType == 'n' || _primitiveType == 'N' || _primitiveType == 'o' || _primitiveType == 'O' || _primitiveType == 'R' || _primitiveType == 'V' || _primitiveType == 'A';
}
- (int)typeIgnoringModifiers;
{
if (self.isModifierType && _subtype != nil)
return _subtype.typeIgnoringModifiers;
return _primitiveType;
}
- (NSUInteger)structureDepth;
{
if (_subtype != nil)
return _subtype.structureDepth;
if (_primitiveType == '{' || _primitiveType == '(') {
NSUInteger maxDepth = 0;
for (CDType *member in _members) {
if (maxDepth < member.structureDepth)
maxDepth = member.structureDepth;
}
return maxDepth + 1;
}
return 0;
}
- (NSString *)formattedString:(NSString *)previousName formatter:(CDTypeFormatter *)typeFormatter level:(NSUInteger)level;
{
NSString *result, *currentName;
NSString *baseType, *memberString;
assert(_variableName == nil || previousName == nil);
if (_variableName != nil)
currentName = _variableName;
else
currentName = previousName;
if ([_protocols count])
[typeFormatter formattingDidReferenceProtocolNames:_protocols];
switch (self.primitiveType) {
case T_NAMED_OBJECT: {
assert(self.typeName != nil);
[typeFormatter formattingDidReferenceClassName:_typeName.name];
NSString *typeName = nil;
if (_protocols == nil)
typeName = [NSString stringWithFormat:@"%@", _typeName];
else
typeName = [NSString stringWithFormat:@"%@<%@>", _typeName, [_protocols componentsJoinedByString:@", "]];
if (currentName == nil)
result = [NSString stringWithFormat:@"%@ *", typeName];
else
result = [NSString stringWithFormat:@"%@ *%@", typeName, currentName];
break;
}
case '@':
if (currentName == nil) {
if (_protocols == nil)
result = @"id";
else
result = [NSString stringWithFormat:@"id <%@>", [_protocols componentsJoinedByString:@", "]];
} else {
if (_protocols == nil)
result = [NSString stringWithFormat:@"id %@", currentName];
else
result = [NSString stringWithFormat:@"id <%@> %@", [_protocols componentsJoinedByString:@", "], currentName];
}
break;
case 'b':
if (currentName == nil) {
// This actually compiles!
result = [NSString stringWithFormat:@"unsigned int :%@", _bitfieldSize];
} else
result = [NSString stringWithFormat:@"unsigned int %@:%@", currentName, _bitfieldSize];
break;
case '[':
if (currentName == nil)
result = [NSString stringWithFormat:@"[%@]", _arraySize];
else
result = [NSString stringWithFormat:@"%@[%@]", currentName, _arraySize];
result = [_subtype formattedString:result formatter:typeFormatter level:level];
break;
case '(':
baseType = nil;
/*if (typeName == nil || [@"?" isEqual:[typeName description]])*/ {
NSString *typedefName = [typeFormatter typedefNameForStructure: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 typedefNameForStructure:self level:level];
if (typedefName != nil) {
baseType = typedefName;
}
}
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 = @"";
baseType = [baseType stringByAppendingString:memberString];
}
if (currentName == nil /*|| [currentName hasPrefix:@"?"]*/) // Not sure about this
result = baseType;
else
result = [NSString stringWithFormat:@"%@ %@", baseType, currentName];
break;
case '^':
if (currentName == nil)
result = @"*";
else
result = [@"*" stringByAppendingString:currentName];
if (_subtype != nil && _subtype.primitiveType == '[')
result = [NSString stringWithFormat:@"(%@)", result];
result = [_subtype formattedString:result formatter:typeFormatter level:level];
break;
case T_FUNCTION_POINTER_TYPE:
if (currentName == nil)
result = @"CDUnknownFunctionPointerType";
else
result = [NSString stringWithFormat:@"CDUnknownFunctionPointerType %@", currentName];
break;
case T_BLOCK_TYPE:
if (self.types) {
result = [self blockSignatureString];
} else {
if (currentName == nil)
result = @"CDUnknownBlockType";
else
result = [NSString stringWithFormat:@"CDUnknownBlockType %@", currentName];
}
break;
case 'j':
case 'r':
case 'n':
case 'N':
case 'o':
case 'O':
case 'R':
case 'V':
if (_subtype == nil) {
if (currentName == nil)
result = [self formattedStringForSimpleType];
else
result = [NSString stringWithFormat:@"%@ %@", self.formattedStringForSimpleType, currentName];
} else
result = [NSString stringWithFormat:@"%@ %@",
self.formattedStringForSimpleType, [_subtype formattedString:currentName formatter:typeFormatter level:level]];
break;
case 'A':
if (_subtype == nil) {
if (currentName == nil)
result = [self formattedStringForSimpleType];
else
result = [NSString stringWithFormat:@"%@ %@", self.formattedStringForSimpleType, currentName];
} else
result = [NSString stringWithFormat:@"%@ %@",
self.formattedStringForSimpleType, currentName];
break;
default:
if (currentName == nil)
result = self.formattedStringForSimpleType;
else
result = [NSString stringWithFormat:@"%@ %@", self.formattedStringForSimpleType, currentName];
break;
}
return result;
}
- (NSString *)formattedStringForMembersAtLevel:(NSUInteger)level formatter:(CDTypeFormatter *)typeFormatter;
{
NSParameterAssert(_primitiveType == '{' || _primitiveType == '(');
NSMutableString *str = [NSMutableString string];
for (CDType *member in _members) {
[str appendString:[NSString spacesIndentedToLevel:typeFormatter.baseLevel + level spacesPerLevel:4]];
[str appendString:[member formattedString:nil
formatter:typeFormatter
level:level]];
[str appendString:@";\n"];
}
return str;
}
- (NSString *)formattedStringForSimpleType;
{
// Ugly but simple:
switch (_primitiveType) {
case 'c': return @"char";
case 'i': return @"int";
case 's': return @"short";
case 'l': return @"long";
case 'q': return @"long long";
case 'C': return @"unsigned char";
case 'I': return @"unsigned int";
case 'S': return @"unsigned short";
case 'L': return @"unsigned long";
case 'Q': return @"unsigned long long";
case 'f': return @"float";
case 'd': return @"double";
case 'D': return @"long double";
case 'B': return @"_Bool"; // C99 _Bool or C++ bool
case 'v': return @"void";
case '*': return @"STR";
case '#': return @"Class";
case ':': return @"SEL";
case '%': return @"NXAtom";
case '?': return @"void";
//case '?': return @"UNKNOWN"; // For easier regression testing.
case 'j': return @"_Complex";
case 'r': return @"const";
case 'n': return @"in";
case 'N': return @"inout";
case 'o': return @"out";
case 'O': return @"bycopy";
case 'R': return @"byref";
case 'V': return @"oneway";
case 'A': return [NSString stringWithFormat:@"_Atomic(%@)", _subtype.formattedStringForSimpleType];
default:
break;
}
return nil;
}
- (NSString *)typeString;
{
return [self _typeStringWithVariableNamesToLevel:1e6 showObjectTypes:YES];
}
- (NSString *)bareTypeString;
{
return [self _typeStringWithVariableNamesToLevel:0 showObjectTypes:YES];
}
- (NSString *)reallyBareTypeString;
{
return [self _typeStringWithVariableNamesToLevel:0 showObjectTypes:NO];
}
- (NSString *)keyTypeString;
{
// use variable names at top level
return [self _typeStringWithVariableNamesToLevel:1 showObjectTypes:YES];
}
- (NSString *)_typeStringWithVariableNamesToLevel:(NSUInteger)level showObjectTypes:(BOOL)shouldShowObjectTypes;
{
NSString *result;
switch (_primitiveType) {
case T_NAMED_OBJECT:
assert(_typeName != nil);
if (shouldShowObjectTypes)
result = [NSString stringWithFormat:@"@\"%@\"", _typeName];
else
result = @"@";
break;
case '@':
result = @"@";
break;
case 'b':
result = [NSString stringWithFormat:@"b%@", _bitfieldSize];
break;
case '[':
result = [NSString stringWithFormat:@"[%@%@]", _arraySize, [_subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
break;
case '(':
if (_typeName == nil) {
return [NSString stringWithFormat:@"(%@)", [self _typeStringForMembersWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
} else if ([_members count] == 0) {
return [NSString stringWithFormat:@"(%@)", _typeName];
} else {
return [NSString stringWithFormat:@"(%@=%@)", _typeName, [self _typeStringForMembersWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
}
case '{':
if (_typeName == nil) {
return [NSString stringWithFormat:@"{%@}", [self _typeStringForMembersWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
} else if ([_members count] == 0) {
return [NSString stringWithFormat:@"{%@}", _typeName];
} else {
return [NSString stringWithFormat:@"{%@=%@}", _typeName, [self _typeStringForMembersWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
}
case '^':
result = [NSString stringWithFormat:@"^%@", [_subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
break;
case 'j':
case 'r':
case 'n':
case 'N':
case 'o':
case 'O':
case 'R':
case 'V':
case 'A':
result = [NSString stringWithFormat:@"%c%@", _primitiveType, [_subtype _typeStringWithVariableNamesToLevel:level showObjectTypes:shouldShowObjectTypes]];
break;
case T_FUNCTION_POINTER_TYPE:
result = @"^?";
break;
case T_BLOCK_TYPE:
result = @"@?";
break;
default:
result = [NSString stringWithFormat:@"%c", _primitiveType];
break;
}
return result;
}
- (NSString *)_typeStringForMembersWithVariableNamesToLevel:(NSInteger)level showObjectTypes:(BOOL)shouldShowObjectTypes;
{
NSParameterAssert(_primitiveType == '{' || _primitiveType == '(');
NSMutableString *str = [NSMutableString string];
for (CDType *member in _members) {
if (member.variableName != nil && level > 0)
[str appendFormat:@"\"%@\"", member.variableName];
[str appendString:[member _typeStringWithVariableNamesToLevel:level - 1 showObjectTypes:shouldShowObjectTypes]];
}
return str;
}
- (BOOL)canMergeWithType:(CDType *)otherType;
{
if (self.isIDType && otherType.isNamedObject)
return YES;
if (self.isNamedObject && otherType.isIDType) {
return YES;
}
if (_primitiveType != otherType.primitiveType) {
if (debugMerge) {
NSLog(@"--------------------");
NSLog(@"this: %@", self.typeString);
NSLog(@"other: %@", otherType.typeString);
NSLog(@"self isIDType? %u", self.isIDType);
NSLog(@"self isNamedObject? %u", self.isNamedObject);
NSLog(@"other isIDType? %u", otherType.isIDType);
NSLog(@"other isNamedObject? %u", otherType.isNamedObject);
}
if (debugMerge) NSLog(@"%s, Can't merge because of type... %@ vs %@", __cmd, self.typeString, otherType.typeString);
return NO;
}
if (_subtype != nil && [_subtype canMergeWithType:otherType.subtype] == NO) {
if (debugMerge) NSLog(@"%s, Can't merge subtype", __cmd);
return NO;
}
if (_subtype == nil && otherType.subtype != nil) {
if (debugMerge) NSLog(@"%s, This subtype is nil, other isn't.", __cmd);
return NO;
}
NSArray *otherMembers = otherType.members;
NSUInteger count = [_members count];
NSUInteger otherCount = [otherMembers count];
//NSLog(@"members: %p", members);
//NSLog(@"otherMembers: %p", otherMembers);
//NSLog(@"%s, count: %u, otherCount: %u", __cmd, count, otherCount);
if (count != 0 && otherCount == 0) {
if (debugMerge) NSLog(@"%s, count != 0 && otherCount is 0", __cmd);
return NO;
}
if (count != 0 && count != otherCount) {
if (debugMerge) NSLog(@"%s, count != 0 && count != otherCount", __cmd);
return NO;
}
// count == 0 is ok: we just have a name in that case.
if (count == otherCount) {
for (NSUInteger index = 0; index < count; index++) { // Oooh
CDType *thisMember = _members[index];
CDType *otherMember = otherMembers[index];
CDTypeName *thisTypeName = thisMember.typeName;
CDTypeName *otherTypeName = otherMember.typeName;
NSString *thisVariableName = thisMember.variableName;
NSString *otherVariableName = otherMember.variableName;
// It seems to be okay if one of them didn't have a name
if (thisTypeName != nil && otherTypeName != nil && [thisTypeName isEqual:otherTypeName] == NO) {
if (debugMerge) NSLog(@"%s, typeName mismatch on member %lu", __cmd, index);
return NO;
}
if (thisVariableName != nil && otherVariableName != nil && [thisVariableName isEqual:otherVariableName] == NO) {
if (debugMerge) NSLog(@"%s, variableName mismatch on member %lu", __cmd, index);
return NO;
}
if ([thisMember canMergeWithType:otherMember] == NO) {
if (debugMerge) NSLog(@"%s, Can't merge member %lu", __cmd, index);
return NO;
}
}
}
return YES;
}
// Merge struct/union member names. Should check using -canMergeWithType: first.
// Recursively merges, not just the top level.
- (void)mergeWithType:(CDType *)otherType;
{
NSString *before = self.typeString;
[self _recursivelyMergeWithType:otherType];
NSString *after = self.typeString;
if (debugMerge) {
NSLog(@"----------------------------------------");
NSLog(@"%s", __cmd);
NSLog(@"before: %@", before);
NSLog(@" after: %@", after);
NSLog(@"----------------------------------------");
}
}
- (void)_recursivelyMergeWithType:(CDType *)otherType;
{
if (self.isIDType && otherType.isNamedObject) {
//NSLog(@"thisType: %@", [self typeString]);
//NSLog(@"otherType: %@", [otherType typeString]);
_primitiveType = T_NAMED_OBJECT;
_typeName = [otherType.typeName copy];
return;
}
if (self.isNamedObject && otherType.isIDType) {
return;
}
if (_primitiveType != otherType.primitiveType) {
NSLog(@"Warning: Trying to merge different types in %s", __cmd);
return;
}
[_subtype _recursivelyMergeWithType:otherType.subtype];
NSArray *otherMembers = otherType.members;
NSUInteger count = [_members count];
NSUInteger otherCount = [otherMembers count];
// The counts can be zero when we register structures that just have a name. That happened while I was working on the
// structure registration.
if (otherCount == 0) {
return;
} else if (count == 0 && otherCount != 0) {
NSParameterAssert(_members != nil);
[_members removeAllObjects];
[_members addObjectsFromArray:otherMembers];
//[self setMembers:otherMembers];
} else if (count != otherCount) {
// Not so bad after all. Even kind of common. Consider _flags.
NSLog(@"Warning: Types have different number of members. This is bad. (%lu vs %lu)", count, otherCount);
NSLog(@"%@ vs %@", self.typeString, otherType.typeString);
return;
}
//NSLog(@"****************************************");
for (NSUInteger index = 0; index < count; index++) {
CDType *thisMember = _members[index];
CDType *otherMember = otherMembers[index];
CDTypeName *thisTypeName = thisMember.typeName;
CDTypeName *otherTypeName = otherMember.typeName;
NSString *thisVariableName = thisMember.variableName;
NSString *otherVariableName = otherMember.variableName;
//NSLog(@"%d: type: %@ vs %@", index, thisTypeName, otherTypeName);
//NSLog(@"%d: vari: %@ vs %@", index, thisVariableName, otherVariableName);
if ((thisTypeName == nil && otherTypeName != nil) || (thisTypeName != nil && otherTypeName == nil)) {
; // It seems to be okay if one of them didn't have a name
//NSLog(@"Warning: (1) type names don't match, %@ vs %@", thisTypeName, otherTypeName);
} else if (thisTypeName != nil && [thisTypeName isEqual:otherTypeName] == NO) {
NSLog(@"Warning: (2) type names don't match:\n\t%@ vs \n\t%@.", thisTypeName, otherTypeName);
// In this case, we should skip the merge.
}
if (otherVariableName != nil) {
if (thisVariableName == nil)
thisMember.variableName = otherVariableName;
else if ([thisVariableName isEqual:otherVariableName] == NO)
NSLog(@"Warning: Different variable names for same member...");
}
[thisMember _recursivelyMergeWithType:otherMember];
}
}
- (NSArray *)memberVariableNames;
{
NSMutableArray *names = [[NSMutableArray alloc] init];
[_members enumerateObjectsUsingBlock:^(CDType *memberType, NSUInteger index, BOOL *stop){
if (memberType.variableName != nil)
[names addObject:memberType.variableName];
}];
return [names copy];
}
- (void)generateMemberNames;
{
if (_primitiveType == '{' || _primitiveType == '(') {
NSSet *usedNames = [[NSSet alloc] initWithArray:self.memberVariableNames];
NSUInteger number = 1;
for (CDType *member in _members) {
[member generateMemberNames];
// Bitfields don't need a name.
if (member.variableName == nil && member.primitiveType != 'b') {
NSString *name;
do {
name = [NSString stringWithFormat:@"_field%lu", number++];
} while ([usedNames containsObject:name]);
member.variableName = name;
}
}
}
[_subtype generateMemberNames];
}
- (NSString *)blockSignatureString;
{
NSMutableString *blockSignatureString = [[NSMutableString alloc] init];
CDTypeFormatter *blockSignatureTypeFormatter = [[CDTypeFormatter alloc] init];
blockSignatureTypeFormatter.shouldExpand = NO;
blockSignatureTypeFormatter.shouldAutoExpand = NO;
blockSignatureTypeFormatter.baseLevel = 0;
[self.types enumerateObjectsUsingBlock:^(CDType *type, NSUInteger idx, BOOL *stop) {
if (idx != 1)
[blockSignatureString appendString:[blockSignatureTypeFormatter formatVariable:nil type:type]];
else
[blockSignatureString appendString:@"(^)"];
BOOL isLastType = idx == [self.types count] - 1;
if (idx == 0)
[blockSignatureString appendString:@" "];
else if (idx == 1)
[blockSignatureString appendString:@"("];
else if (idx >= 2 && !isLastType)
[blockSignatureString appendString:@", "];
if (isLastType) {
if ([self.types count] == 2) {
[blockSignatureString appendString:@"void"];
}
[blockSignatureString appendString:@")"];
}
}];
return blockSignatureString;
}
#pragma mark - Phase 0
- (void)phase:(NSUInteger)phase registerTypesWithObject:(CDTypeController *)typeController usedInMethod:(BOOL)isUsedInMethod;
{
if (phase == 0) {
[self phase0RegisterStructuresWithObject:typeController usedInMethod:isUsedInMethod];
}
}
// Just top level structures
- (void)phase0RegisterStructuresWithObject:(CDTypeController *)typeController usedInMethod:(BOOL)isUsedInMethod;
{
// ^{ComponentInstanceRecord=}
if (_subtype != nil)
[_subtype phase0RegisterStructuresWithObject:typeController usedInMethod:isUsedInMethod];
if ((_primitiveType == '{' || _primitiveType == '(') && [_members count] > 0) {
[typeController phase0RegisterStructure:self usedInMethod:isUsedInMethod];
} else if (self.primitiveType == T_FUNCTION_POINTER_TYPE && self.types == nil) {
typeController.hasUnknownFunctionPointers = YES;
} else if (self.primitiveType == T_BLOCK_TYPE && self.types == nil) {
typeController.hasUnknownBlocks = YES;
}
}
- (void)phase0RecursivelyFixStructureNames:(BOOL)flag;
{
[_subtype phase0RecursivelyFixStructureNames:flag];
if ([_typeName.name hasPrefix:@"$"]) {
if (flag) NSLog(@"%s, changing type name %@ to ?", __cmd, _typeName.name);
_typeName.name = @"?";
}
for (CDType *member in _members)
[member phase0RecursivelyFixStructureNames:flag];
}
#pragma mark - Phase 1
// Recursively go through type, registering structs/unions.
- (void)phase1RegisterStructuresWithObject:(CDTypeController *)typeController;
{
// ^{ComponentInstanceRecord=}
if (_subtype != nil)
[_subtype phase1RegisterStructuresWithObject:typeController];
if ((_primitiveType == '{' || _primitiveType == '(') && [_members count] > 0) {
[typeController phase1RegisterStructure:self];
for (CDType *member in _members)
[member phase1RegisterStructuresWithObject:typeController];
}
}
#pragma mark - Phase 2
// This wraps the recursive method, optionally logging if anything changed.
- (void)phase2MergeWithTypeController:(CDTypeController *)typeController debug:(BOOL)phase2Debug;
{
NSString *before = self.typeString;
[self _phase2MergeWithTypeController:typeController debug:phase2Debug];
NSString *after = self.typeString;
if (phase2Debug && [before isEqualToString:after] == NO) {
NSLog(@"----------------------------------------");
NSLog(@"%s, merge changed type", __cmd);
NSLog(@"before: %@", before);
NSLog(@" after: %@", after);
}
}
// Recursive, bottom-up
- (void)_phase2MergeWithTypeController:(CDTypeController *)typeController debug:(BOOL)phase2Debug;
{
[_subtype _phase2MergeWithTypeController:typeController debug:phase2Debug];
for (CDType *member in _members)
[member _phase2MergeWithTypeController:typeController debug:phase2Debug];
if ((_primitiveType == '{' || _primitiveType == '(') && [_members count] > 0) {
CDType *phase2Type = [typeController phase2ReplacementForType:self];
if (phase2Type != nil) {
// >0 members so we don't try replacing things like... {_xmlNode=^{_xmlNode}}
if ([_members count] > 0 && [self canMergeWithType:phase2Type]) {
[self mergeWithType:phase2Type];
} else {
if (phase2Debug) {
NSLog(@"Found phase2 type, but can't merge with it.");
NSLog(@"this: %@", [self typeString]);
NSLog(@"that: %@", [phase2Type typeString]);
}
}
}
}
}
#pragma mark - Phase 3
- (void)phase3RegisterWithTypeController:(CDTypeController *)typeController;
{
[_subtype phase3RegisterWithTypeController:typeController];
if (_primitiveType == '{' || _primitiveType == '(') {
[typeController phase3RegisterStructure:self /*count:1 usedInMethod:NO*/];
}
}
- (void)phase3RegisterMembersWithTypeController:(CDTypeController *)typeController;
{
//NSLog(@" > %s %@", __cmd, [self typeString]);
for (CDType *member in _members) {
[member phase3RegisterWithTypeController:typeController];
}
//NSLog(@"< %s", __cmd);
}
// Bottom-up
- (void)phase3MergeWithTypeController:(CDTypeController *)typeController;
{
[_subtype phase3MergeWithTypeController:typeController];
for (CDType *member in _members)
[member phase3MergeWithTypeController:typeController];
if ((_primitiveType == '{' || _primitiveType == '(') && [_members count] > 0) {
CDType *phase3Type = [typeController phase3ReplacementForType:self];
if (phase3Type != nil) {
// >0 members so we don't try replacing things like... {_xmlNode=^{_xmlNode}}
if ([_members count] > 0 && [self canMergeWithType:phase3Type]) {
[self mergeWithType:phase3Type];
} else {
#if 0
// This can happen in AU Lab, that struct has no members...
NSLog(@"Found phase3 type, but can't merge with it.");
NSLog(@"this: %@", self.typeString);
NSLog(@"that: %@", phase3Type.typeString);
#endif
}
}
}
}
@end