Created
February 26, 2015 00:34
-
-
Save rsms/eccb008ddad25284f9c7 to your computer and use it in GitHub Desktop.
class-dump is awesome, but sometimes it crashes (i.e. when trying to dump WebKit.dylib) and sometimes you need some programmatic action. Really hacky and incomplete "class dump"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <objc/runtime.h> | |
static NSString* ObjcTypeToCode(const char* t) { | |
switch (*t) { | |
case 'c': return @"BOOL"; // A char | |
case 'i': return @"int"; // An int | |
case 's': return @"short"; // A short | |
case 'l': return @"int32_t"; // A long (l is treated as a 32-bit quantity on 64-bit programs) | |
case 'q': return @"int64_t"; // A long long | |
case 'C': return @"unsigned char"; // An unsigned char | |
case 'I': return @"unsigned int"; // An unsigned int | |
case 'S': return @"unsigned short"; // An unsigned short | |
case 'L': return @"uint32_t"; // An unsigned long | |
case 'Q': return @"uint64_t"; // An unsigned long long | |
case 'f': return @"float"; // A float | |
case 'd': return @"double"; // A double | |
case 'B': return @"bool"; // A C++ bool or a C99 _Bool | |
case 'v': return @"void"; // A void | |
case '*': return @"char*"; // A character string (char *) | |
case '@': return @"id"; // An object (whether statically typed or typed id) | |
case '#': return @"Class"; // A class object (Class) | |
case ':': return @"SEL"; // A method selector (SEL) | |
//case '[': //return @"[array type]"; // An array | |
//case '{': //return @"{name=type...}"; // A structure | |
//case '(': //return @"(name=type...)"; // A union | |
//case 'b': //return @"bnum" // A bit field of num bits | |
//case '^': return @"^type"; // A pointer to type | |
case '?': return @"?"; // An unknown type (among other things, this code is used for function pointers) | |
default: return [NSString stringWithUTF8String:t]; | |
} | |
} | |
static NSString* ObjcAttrToCode(const char* t, BOOL isPropertyDelc) { | |
switch (*t) { | |
case 'R': return @"readonly"; // The property is read-only (readonly). | |
case 'C': return @"copy"; // The property is a copy of the value last assigned (copy). | |
case '&': return @"retain"; // The property is a reference to the value last assigned (retain). | |
case 'N': return @"nonatomic"; // The property is non-atomic (nonatomic). | |
case 'G': // The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,). | |
return [NSString stringWithFormat:@"getter=%s", &t[1]]; | |
case 'S': // The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,). | |
return [NSString stringWithFormat:@"setter=%s", &t[1]]; | |
case 'D': return isPropertyDelc ? @"dynamic" : @"@dynamic"; // The property is dynamic (@dynamic). | |
case 'W': return isPropertyDelc ? @"weak" : @"__weak"; // The property is a weak reference (__weak). | |
case 'P': return @"<meta:gc-enabled>"; // The property is eligible for garbage collection. | |
case 't': // Specifies the type using old-style encoding. | |
return [NSString stringWithFormat:@"<old-style encoding=%s>", &t[1]]; | |
default: | |
return [NSString stringWithUTF8String:t]; | |
} | |
} | |
static void ghetto_class_dump(Class cls) { | |
@autoreleasepool { | |
const char* clsName = class_getName(cls); | |
Class superCls = class_getSuperclass(cls); | |
if (superCls == NULL) { | |
printf("@interface %s", clsName); | |
} else { | |
printf("@interface %s : %s", clsName, class_getName(superCls)); | |
} | |
// Instance variables | |
unsigned int ic = 0; | |
Ivar* iv = class_copyIvarList(cls, &ic); | |
if (ic == 0) { | |
printf("\n"); | |
} else { | |
printf(" {\n"); | |
for (unsigned int i = 0; i != ic; i++) { | |
auto name = ivar_getName(iv[i]); | |
auto offs = ivar_getOffset(iv[i]); | |
auto t = ivar_getTypeEncoding(iv[i]); | |
printf(" %s %s; // offset=%lu\n", ObjcTypeToCode(t).UTF8String, name, offs); | |
} | |
printf("}\n"); | |
} | |
free(iv); | |
// Properties | |
unsigned int pc = 0; | |
objc_property_t* pv = class_copyPropertyList(cls, &pc); | |
for (unsigned int i = 0; i != pc; i++) { | |
auto p = pv[i]; | |
auto name = property_getName(p); | |
unsigned int attrc = 0; | |
objc_property_attribute_t* attrv = property_copyAttributeList(p, &attrc); | |
NSString* Type = @"?"; | |
auto Attrs = [NSMutableArray new]; | |
if (attrc != 0) { | |
Type = ObjcTypeToCode(attrv[0].value); | |
for (unsigned int ai = 1; ai < attrc; ai++) { | |
[Attrs addObject:[NSString stringWithFormat:@"%@%s", ObjcAttrToCode(attrv[ai].name, YES), attrv[ai].value]]; | |
} | |
} | |
printf("@property(%s) %s %s;\n", | |
[Attrs componentsJoinedByString:@", "].UTF8String, Type.UTF8String, name); | |
} | |
free(pv); | |
// Methods | |
unsigned int mc = 0; | |
Method* mv = class_copyMethodList(cls, &mc); | |
for (unsigned int i = 0; i != mc; i++) { | |
struct objc_method_description* descr = method_getDescription(mv[i]); | |
if (descr == NULL) { continue; } | |
const char* name = sel_getName(descr->name); | |
unsigned int argc = method_getNumberOfArguments(mv[i]); // minimum 2 (cls, self) | |
char* rtype = method_copyReturnType(mv[i]); | |
auto* Namev = [[NSString stringWithUTF8String:name] componentsSeparatedByString:@":"]; | |
auto* Codev = [NSMutableArray new]; | |
if (argc <= 2) { | |
[Codev addObject:Namev[0]]; | |
} else { | |
for (unsigned int argi = 0; argi < argc-2; argi++) { | |
char argt[10]; | |
method_getArgumentType(mv[i], argi+2, argt, sizeof(argt)); | |
NSString* Param = Namev[argi]; | |
NSString* Arg = @"_"; | |
if (argi != 0) { | |
Arg = Param; | |
} | |
[Codev addObject:[NSString stringWithFormat:@"%@:(%@)%@", Param, ObjcTypeToCode(argt), Arg]]; | |
} | |
} | |
printf("%s\n", [NSString stringWithFormat:@"-(%@)%@;", ObjcTypeToCode(rtype), [Codev componentsJoinedByString:@" "]].UTF8String); | |
free(rtype); | |
} | |
free(mv); | |
printf("@end // %s\n", clsName); | |
} | |
} | |
static void __attribute__((constructor)) __init() { | |
ghetto_class_dump([WebView class]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment