This is MiscClassDecoder.m in view mode; [Download] [Up]
// // Time-stamp: <94/12/01 20:23:30 stephan> // // MiscClassDecoder.m -- an Object subclass that is able to analyze // the definition of an Objective-C class. // // Written by Stephan Wacker <stephan@rodion.muc.de> // Copyright (c) 1994 by Stephan Wacker. // Version 1.0 All rights reserved. // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the author // and its use is governed by the MiscKit license, found in the file // "LICENSE.rtf" in the MiscKit distribution. Please refer to that file // for a list of all applicable permissions and restrictions. // #import "MiscClassDecoder.h" #import <objc/Protocol.h> #import <objc/objc-class.h> #import <objc/objc-runtime.h> #import <ctype.h> #define K_IndentInstanceVar 4 #define K_IndentStructMember 2 //====< Private methods >====// @interface MiscClassDecoder ( Private ) - (void)clearText; - (void)appendText: (NSString *) aString, ...; - (void)appendLine: (NSString *) aString, ...; - (void)finishOutput; - (const char *)showType:(const char *)aTypeString; - (void)showMethod: (Method) aMethod prefix: (NSString *) prefix; - (void)showInheritance:(Class)aClass; - (void)showProtocols:(Class)aClass; - (void)showInstanceVars:(Class)aClass; - (void)showInstanceMethods:(Class)aClass; - (void)showClassMethods:(Class)aMetaclass; + (void)initialize; - (id)initWithCoder:(NSCoder *)aDecoder; - (void)encodeWithCoder:(NSCoder *)aCoder; @end // MiscClassDecoder ( Private ) //====< Auxiliary functions >====// static NSString * type_name( char type ) // Decode single character `type'. { NSString *result = NULL; switch( type ) { case _C_ID : result = @"id"; break; case _C_CLASS : result = @"Class"; break; case _C_SEL : result = @"SEL"; break; case _C_CHR : result = @"char"; break; case _C_UCHR : result = @"unsigned char"; break; case _C_SHT : result = @"short"; break; case _C_USHT : result = @"unsigned short"; break; case _C_INT : result = @"int"; break; case _C_UINT : result = @"unsigned int"; break; case _C_LNG : result = @"long"; break; case _C_ULNG : result = @"unsigned long"; break; case _C_FLT : result = @"float"; break; case _C_DBL : result = @"double"; break; case _C_VOID : result = @"void"; break; case _C_UNDEF : result = @"<undef>"; break; case _C_CHARPTR : result = @"char *"; break; } return result; } // type_name() static NSString * struct_name( NSString *name ) // Check for well-known struct names. { static NSSet* struct_set = 0; if (struct_set == 0) { struct_set = [[NSSet alloc] initWithObjects: @"_NSFSM", @"_NSLay", @"_NSRun", @"_NSRect", @"_NSSize", @"_NSZone", @"_NSPoint", @"_NSSelPt", @"_NSLayInfo", @"_NSTabStop", @"_NSLayArray", @"_NSRunArray", @"_NSTextChunk", @"_NSCharArray", @"_NSTextBlock", @"_NSTextCache", @"_NSTextStyle", @"_NSBreakArray", @"_NSHeightInfo", @"_NSWidthArray", @"_NSHeightChange", @"_NSModalSession", 0 ]; } if ([struct_set containsObject:name]) return [name substringFromIndex:1]; else return 0; } // structName() @implementation MiscClassDecoder //====< Writing text >====// - (id)init { [super init]; classNameField = 0; text = 0; string = [[NSMutableString allocWithZone:[self zone]] init]; indent = 0; bol = YES; lineCount = 0; return self; } - (void) dealloc { [string release]; [super dealloc]; } - (void)clearText // Delete contents of `text'. { [string setString:@""]; [text setString:@""]; indent = 0, bol = YES, lineCount = 0; } // clearText - (void)appendText: (NSString *) aString, ... // Append `aString' to the contents of `text'. { va_list ap; NSString* s; if( bol ) { [string appendFormat:@"%*s", indent, ""]; bol = NO; } va_start(ap, aString); s = [[NSString alloc] initWithFormat:aString arguments:ap]; va_end(ap); [string appendString:s]; [s release]; } // appendText:... - (void)appendLine: (NSString *) aString, ... // Append `aString' to `text' and start a new line. { va_list ap; NSString* s; if( bol ) [string appendFormat:@"%*s", indent, ""]; else bol = YES; va_start(ap, aString); s = [[NSString alloc] initWithFormat:aString arguments:ap]; va_end(ap); [string appendString:s]; [s release]; [string appendString:@"\n"]; ++ lineCount; } // appendLine:... - (void)finishOutput // Final actions after all text has been output. { [text setString:string]; [classNameField selectText:self]; } // finishOutput //====< Decoding type specifications >====// - (const char *)showType:(const char *)aTypeString // Recursive interpretation of the encoded `aTypeString'. // Return position after analyzed substring. { NSString *typeName; const char *p = aTypeString; const char *pe; const char *memberName; int i; switch( *p ) { case _C_ID: if (*(p+1) == '"') { p = [self showType:p+1]; [self appendText: @" *"]; } else { p++; [self appendText: @"id"]; } break; case _C_PTR: // Pointer p = [self showType:p+1]; [self appendText: @" *"]; break; case _C_BFLD: // Bitfield for( ++p, i = 0; isdigit(*p); p++ ) { i = i * 10 + (*p - '0'); } // We don't really know the bitfield's type, // but `unsigned int' is a good guess. [self appendText: @"unsigned int : %d", i]; break; case _C_ARY_B: // Array begin for( ++p, i = 0; isdigit(*p); p++ ) { i = i * 10 + (*p - '0'); } p = [self showType:p]; [self appendText: @" [%d]", i]; // TODO: correct syntax if( *p == _C_ARY_E ) // Array end { ++p; } else { // TODO: report error // TODO: report error } break; case _C_STRUCT_B: // Struct begin for( pe = ++p; *pe && *pe != '=' && *pe != _C_STRUCT_E; pe++ ) { // keep on going // keep on going } if( (typeName = struct_name( [NSString stringWithCString:p length:pe-p] )) != NULL ) // it's a well-known struct { [self appendText: @"%s", [typeName cString]]; for( p = pe-1, i = 1; *++p && i > 0; ) { switch( *p ) { case _C_STRUCT_B: i++; break; case _C_STRUCT_E: i--; break; } } // for p } else { [self appendText: @"struct %.*s", pe-p, p]; if( *(p=pe) == '=' ) { [self appendLine: @" {"]; indent += K_IndentStructMember; for( i = 1, ++p; *p && *p != _C_STRUCT_E; i++ ) { if( *p == '"' ) { for( memberName = pe = p+1; *pe && *pe != '"'; pe++ ) { // keep on going // keep on going } p = pe+1; } else { memberName = NULL; } p = [self showType:p]; if( memberName ) { [self appendLine: @" %.*s;", pe-memberName, memberName]; } else { [self appendLine: @" member_%d;", i]; } } indent -= K_IndentStructMember; [self appendText: @"}"]; } } if( *p == _C_STRUCT_E ) // Struct end { ++p; } else { // TODO: report error // TODO: report error } break; case _C_UNION_B: // Union begin [self appendLine: @"union {"]; indent += K_IndentStructMember; for( i = 1, ++p; *p && *p != _C_UNION_E; i++ ) { p = [self showType:p]; [self appendLine: @" case_%d;", i]; } indent -= K_IndentStructMember; [self appendText: @"}"]; if( *p == _C_UNION_E ) { ++p; } else { // TODO: report error // TODO: report error } break; case '"': for( pe = ++p; *pe && *pe != '"'; pe++ ) /*empty*/ ; [self appendText: @"%.*s", pe-p, p]; p = pe + 1; break; default: if( ! (typeName = type_name( *p ) )) { [self appendText: @"<?: %c>", *p ]; } else { [self appendText: @"%s", [typeName cString]]; } ++ p; break; } // switch( first char of aTypeString ) return p; } // showType: - (void)showMethod: (Method) aMethod prefix: (NSString *) prefix // Show declaration of `aMethod'. // `prefix' is either "-" or "+". { NSString *name = NSStringFromSelector(aMethod->method_name); const char *p, *pe; int nArgs = method_getNumberOfArguments( aMethod ); int i; char *type; int offset; if( prefix ) { [self appendText: @"%s ", [prefix cString]]; } if( nArgs <= 2 ) { [self appendLine: @"%s;", [name cString]]; } else { for( i = 2, p = [name cString]; i < nArgs; i++, p = pe ) { for( pe = p; *pe && *pe++ != ':'; ) { // keep on going // keep on going } [self appendText: @"%s%.*s", (i == 2 ? "" : " "), pe-p, p]; method_getArgumentInfo( aMethod, i, &type, &offset ); [self appendText: @" ("]; [self showType:type]; [self appendText: @")"]; [self appendText: @" arg_%d", i-1]; } // for i, p [self appendLine: @";"]; } } // showMethod:prefix: //====< Showing the class definition >====// - (void)showInheritance:(Class)aClass // Show name of `aClass' and its superclass. { Class superClass = [aClass superclass]; [self appendText: @"%s", [[aClass description] cString]]; if( superClass ) { [self appendLine: @" : %s", [[superClass description] cString]]; } else { [self appendLine: @" // root class"]; } } // showInheritance: - (void)showProtocols:(Class)aClass // Show all protocols adopted by `aClass'. { if( ! aClass->protocols || aClass->protocols->count == 0 ) { // do nothing // do nothing } else { int i; Protocol **pp = aClass->protocols->list; [self appendText: @" < %s", [*pp name]]; for( i = 1, pp++; i < aClass->protocols->count; i++, pp++ ) { [self appendText: @", %s", [*pp name]]; } [self appendLine: @" >"]; } } // showProtocols: - (void)showInstanceVars:(Class)aClass // Show definition of all instance variables defined in `aClass'. { [self appendLine: @"{"]; indent += K_IndentInstanceVar; if( ! aClass->ivars ) { [self appendLine: @"// no instance variables"]; } else { int i; Ivar ivp = aClass->ivars->ivar_list; for( i = 0; i < aClass->ivars->ivar_count; i++, ivp++ ) { // [self appendLine: @"// %s", ivp->ivar_type]; [self showType:ivp->ivar_type]; [self appendLine: @" %s;", ivp->ivar_name]; } // for i } indent -= K_IndentInstanceVar; [self appendLine: @"}"]; } // showInstanceVars: - (void)showInstanceMethods:(Class)aClass // Show declaration of all instance methods defined in `aClass'. { if( ! aClass->methodLists ) { [self appendLine: @"// no instance methods"]; } else { void *iterator = 0; struct objc_method_list *mlp; int i, m; Method mp; for( m = 1; (mlp = class_nextMethodList(aClass, &iterator)) != NULL; m++ ) { [self appendLine: @""]; [self appendLine: @"// Instance method block #%d holds %d method%s", m, mlp->method_count, (mlp->method_count == 1 ? "" : "s")]; for( i = 0, mp = mlp->method_list; i < mlp->method_count; i++, mp++ ) { [self showMethod: mp prefix: @"-"]; } // for i } // for m, mlp } } // showInstanceMethods: - (void)showClassMethods:(Class)aMetaclass // Show declaration of all class methods defined in aMetaclass. { if( ! aMetaclass->methodLists ) { [self appendLine: @"// no class methods"]; } else { struct objc_method_list *mlp; void *iterator = 0; int i, m; Method mp; for( m = 1; (mlp = class_nextMethodList(aMetaclass, &iterator)) != NULL; m++) { [self appendLine: @""]; [self appendLine: @"// Class method block #%d holds %d method%s", m, mlp->method_count, (mlp->method_count == 1 ? "" : "s")]; for( i = 0, mp = mlp->method_list; i < mlp->method_count; i++, mp++ ) { [self showMethod: mp prefix: @"+"]; } // for i } // for m, mlp } } // showClassMethods: //====< Actions >====// - (void)analyze:sender // Analyze class specified in `classNameField'. { NSString *className = [classNameField stringValue]; Class class; [self clearText]; if( !(class = NSClassFromString(className)) ) { [self appendLine: @"No class named \"%s\".", [className cString]]; } else { [self appendLine: @"// Definition of class %s, Version %d", [className cString], class_getVersion(class)]; [self appendLine: @""]; [self appendText: @"@interface "]; [self showInheritance:class]; [self showProtocols:class]; [self showInstanceVars:class]; [self appendLine: @""]; [self showClassMethods:class->isa]; [self appendLine: @""]; [self showInstanceMethods:class]; [self appendLine: @""]; [self appendLine: @"@end"]; } [self finishOutput]; } // analyze: - (void)list:sender // List all known classes. { NXHashTable *classes = objc_getClasses(); NXHashState state = NXInitHashState(classes); Class aClass; [self clearText]; while( NXNextHashState(classes, &state, (void **)&aClass) ) { NS_DURING [self appendLine: @"%s", [[aClass description] cString]]; NS_HANDLER NS_ENDHANDLER } [self finishOutput]; } // list: //====< Services >====// - (void)analyzeClass:(NSString *)className // Analyze class given in `className'. { [classNameField setStringValue:className]; [self analyze:self]; } //====< Archiving >====// + (void)initialize { if( self == [MiscClassDecoder class] ) { [self setVersion: K_MiscClassDecoderVersion]; } } // +initialize - (id)initWithCoder:(NSCoder *)aDecoder { int version = [aDecoder versionForClassName:@"MiscClassDecoder"]; switch( version ) { case 0: classNameField = 0; text = 0; break; case 1: [aDecoder decodeValuesOfObjCTypes:"@@", &classNameField, &text]; break; default: classNameField = [[aDecoder decodeObject] retain]; text = [[aDecoder decodeObject] retain]; break; } // Initialize non-archived instance variables. // string = [[NSMutableString allocWithZone:[self zone]] init]; indent = 0; bol = FALSE; lineCount = 0; return self; } // initWithCoder: - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeConditionalObject:classNameField]; [aCoder encodeConditionalObject:text]; } // encodeWithCoder: @end // MiscClassDecoder
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.