This is MiscClassDecoder.m in view mode; [Download] [Up]
// // Time-stamp: <96/10/22 11:42:00 don> // // 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 <misckit/MiscClassDecoder.h> #ifdef NSObject #define MISC_USE_NSOBJECTS #endif #import <objc/objc-runtime.h> #import <ctype.h> #define K_IndentInstanceVar 4 #define K_IndentStructMember 2 //====< Private methods >====// @interface MiscClassDecoder ( Private ) - clearText; - appendText: (const char *) aString, ...; - appendLine: (const char *) aString, ...; - finishOutput; - (const char *) showType: (const char *) aTypeString; - showMethod: (Method) aMethod prefix: (char *) prefix; - showInheritance: (Class) aClass; - showProtocols: (Class) aClass; - showInstanceVars: (Class) aClass; - showInstanceMethods: (Class) aClass; - showClassMethods: (Class) aMetaclass; - setText: aText; + initialize; - read: (NXTypedStream *) stream; - write: (NXTypedStream *) stream; @end // MiscClassDecoder ( Private ) //====< Auxiliary functions >====// static const char * type_name( char type ) // Decode single character `type'. { const char *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 const char * struct_name( const char *name, int len ) // Check for well-known struct names. { #define M_TEST_UNDERSCORE( type ) \ if( ! strncmp( name, "_" #type, len ) ) return (#type) switch( len ) // Add other common names to this list. { case 6: M_TEST_UNDERSCORE( NXFSM ); M_TEST_UNDERSCORE( NXLay ); M_TEST_UNDERSCORE( NXRun ); break; case 7: M_TEST_UNDERSCORE( NXRect ); M_TEST_UNDERSCORE( NXSize ); M_TEST_UNDERSCORE( NXZone ); #ifdef MISC_USE_NSOBJECTS M_TEST_UNDERSCORE( NSZone ); #endif break; case 8: M_TEST_UNDERSCORE( NXChunk ); M_TEST_UNDERSCORE( NXColor ); M_TEST_UNDERSCORE( NXEvent ); M_TEST_UNDERSCORE( NXPoint ); M_TEST_UNDERSCORE( NXSelPt ); #ifdef MISC_USE_NSOBJECTS M_TEST_UNDERSCORE( NSRange ); #endif break; case 9: M_TEST_UNDERSCORE( NXScreen ); M_TEST_UNDERSCORE( NXStream ); break; case 10: M_TEST_UNDERSCORE( NXDefault ); M_TEST_UNDERSCORE( NXHandler ); M_TEST_UNDERSCORE( NXLayInfo ); M_TEST_UNDERSCORE( NXMessage ); M_TEST_UNDERSCORE( NXTabStop ); #ifdef MISC_USE_NSOBJECTS M_TEST_UNDERSCORE( NSHandler ); #endif break; case 11: M_TEST_UNDERSCORE( NXFaceInfo ); M_TEST_UNDERSCORE( NXLayArray ); M_TEST_UNDERSCORE( NXResponse ); M_TEST_UNDERSCORE( NXRunArray ); break; case 12: M_TEST_UNDERSCORE( NXCharArray ); M_TEST_UNDERSCORE( NXTextBlock ); M_TEST_UNDERSCORE( NXTextCache ); M_TEST_UNDERSCORE( NXTextStyle ); break; case 13: M_TEST_UNDERSCORE( NXBreakArray ); M_TEST_UNDERSCORE( NXHeightInfo ); M_TEST_UNDERSCORE( NXWidthArray ); break; case 14: M_TEST_UNDERSCORE( NXAcknowledge ); M_TEST_UNDERSCORE( NXFontMetrics ); break; case 15: M_TEST_UNDERSCORE( NXHeightChange ); M_TEST_UNDERSCORE( NXModalSession ); M_TEST_UNDERSCORE( NXRemoteMethod ); break; case 16: M_TEST_UNDERSCORE( NXTrackingTimer ); break; } return NULL; #undef M_TEST_UNDERSCORE } // structName() @implementation MiscClassDecoder //====< Writing text >====// - clearText // Delete contents of `text'. { [text selectAll: self]; [text delete: self]; indent = 0, bol = YES, lineCount = 0; return self; } // clearText - appendText: (const char *) aString, ... // Append `aString' to the contents of `text'. { va_list ap; char buf[4000], *buf_p = buf; int pos; pos = [text textLength]; [text setSel: pos : pos]; if( bol ) { sprintf( buf_p, "%*s", indent, "" ); buf_p += indent; bol = NO; } va_start(ap, aString); vsprintf( buf_p, aString, ap ); [text replaceSel: buf]; return self; } // appendText:... - appendLine: (const char *) aString, ... // Append `aString' to `text' and start a new line. { va_list ap; char buf[4000], *buf_p = buf; int pos; pos = [text textLength]; [text setSel: pos : pos]; if( bol ) { sprintf( buf_p, "%*s", indent, "" ); buf_p += indent; } else { bol = YES; } va_start(ap, aString); vsprintf( buf_p, aString, ap ); strcat( buf_p, "\n" ); [text replaceSel: buf]; if( ++ lineCount == 20 ) { [[text window] disableDisplay]; [text setNeedsDisplay: YES]; } return self; } // appendLine:... - finishOutput // Final actions after all text has been output. { [classNameField selectText: self]; if( ! [[text window] isDisplayEnabled] ) { [[[text window] reenableDisplay] display]; } return self; } // finishOutput //====< Decoding type specifications >====// - (const char *) showType: (const char *) aTypeString // Recursive interpretation of the encoded `aTypeString'. // Return position after analyzed substring. { const char *typeName; const char *p = aTypeString; const char *pe; const char *memberName; int i; switch( *p ) { 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 } break; case _C_STRUCT_B: // Struct begin for( pe = ++p; *pe && *pe != '=' && *pe != _C_STRUCT_E; pe++ ) { // keep on going } if( (typeName = struct_name( p, pe-p )) != NULL ) // it's a well-known struct { [self appendText: "%s", typeName]; 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 } 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 } 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 } break; default: if( ! (typeName = type_name( *p )) ) { [self appendText: "<?: %c>", *p ]; } else { [self appendText: "%s", typeName]; } ++ p; break; } // switch( first char of aTypeString ) return p; } // showType: - showMethod: (Method) aMethod prefix: (char *) prefix // Show declaration of `aMethod'. // `prefix' is either "-" or "+". { const char *name = sel_getName( aMethod->method_name ); const char *p, *pe; int nArgs = method_getNumberOfArguments( aMethod ); int i; const char *type; int offset; if( prefix ) { [self appendText: "%s ", prefix]; } if( nArgs <= 2 ) { [self appendLine: "%s;", name]; } else { for( i = 2, p = name; i < nArgs; i++, p = pe ) { for( pe = p; *pe && *pe++ != ':'; ) { // keep on going } [self appendText: "%s%.*s", (i == 2 ? "" : " "), pe-p, p]; method_getArgumentInfo( aMethod, i, &type, &offset ); // Don't print default return type `id'. if( *type != _C_ID ) { [self appendText: " ("]; [self showType: type]; [self appendText: ")"]; } [self appendText: " arg_%d", i-1]; } // for i, p [self appendLine: ";"]; } return self; } // showMethod:prefix: //====< Showing the class definition >====// - showInheritance: (Class) aClass // Show name of `aClass' and its superclass. { // Better to access directly, since works for Object AND NSObject // Class superClass = [aClass superclass]; Class superClass = aClass->super_class; // [self appendText: "%s", [aClass name]]; [self appendText: "%s", aClass->name]; if( superClass ) { // [self appendLine: " : %s", [superClass name]]; [self appendLine: " : %s", superClass->name]; } else { [self appendLine: " // root class"]; } return self; } // showInheritance: - showProtocols: (Class) aClass // Show all protocols adopted by `aClass'. { if( ! aClass->protocols || aClass->protocols->count == 0 ) { // 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: " >"]; } return self; } // showProtocols: - 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: "}"]; return self; } // showInstanceVars: - showInstanceMethods: (Class) aClass // Show declaration of all instance methods defined in `aClass'. { if( ! aClass->methods ) { [self appendLine: "// no instance methods"]; } else { struct objc_method_list *mlp; int i, m; Method mp; for( m = 1, mlp = aClass->methods; mlp != NULL; m++, mlp = mlp->method_next ) { [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 } return self; } // showInstanceMethods: - showClassMethods: (Class) aMetaclass // Show declaration of all class methods defined in aMetaclass. { if( ! aMetaclass->methods ) { [self appendLine: "// no class methods"]; } else { struct objc_method_list *mlp; int i, m; Method mp; for( m = 1, mlp = aMetaclass->methods; mlp != NULL; m++, mlp = mlp->method_next ) { [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 } return self; } // showClassMethods: //====< Actions >====// - analyze: sender // Analyze class specified in `classNameField'. { const char *className = [classNameField stringValue]; Class class; [self clearText]; if( !(class = objc_lookUpClass( className )) ) { [self appendLine: "No class named \"%s\".", className]; } else { [self appendLine: "// Definition of class %s, Version %d", className, 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]; return self; } // analyze: - list: sender // List all known classes. { NXHashTable *classes = objc_getClasses(); NXHashState state = NXInitHashState(classes); Class aClass; [self clearText]; while( NXNextHashState(classes, &state, (void **)&aClass) ) { [self appendLine: "%s", aClass->name]; // [self appendLine: "%s", [aClass name]]; } [self finishOutput]; return self; } // list: //====< Services >====// - analyzeClass: (const char *) className // Analyze class given in `className'. { [classNameField setStringValue: className]; return [self analyze: self]; } //====< Outlet connections >====// - setText: aText // Connect to `aText' and set its attributes. { text = aText; [text setNoWrap]; [text setMonoFont: YES]; return self; } // setText: //====< Archiving >====// + initialize { if( self == [MiscClassDecoder class] ) { [self setVersion: K_MiscClassDecoderVersion]; } return self; } // +initialize - read: (NXTypedStream *) stream { int version; [super read: stream]; version = NXTypedStreamClassVersion( stream, "MiscClassDecoder" ); switch( version ) { case 1: NXReadTypes( stream, K_MiscClassDecoderRWformat_V1, K_MiscClassDecoderRWvars_V1 ); break; default: classNameField = nil; text = nil; break; } // Initialize non-archived instance variables. // indent = 0; bol = FALSE; lineCount = 0; return self; } // read: - write: (NXTypedStream *) stream { [super write: stream]; NXWriteTypes( stream, K_MiscClassDecoderRWformat_V1, K_MiscClassDecoderRWvars_V1 ); return self; } // write: @end // MiscClassDecoder
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.