home *** CD-ROM | disk | FTP | other *** search
- //
- // 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 <misckit/MiscClassDecoder.h>
-
- #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 );
- break;
- case 8:
- M_TEST_UNDERSCORE( NXChunk );
- M_TEST_UNDERSCORE( NXColor );
- M_TEST_UNDERSCORE( NXEvent );
- M_TEST_UNDERSCORE( NXPoint );
- M_TEST_UNDERSCORE( NXSelPt );
- 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 );
- 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.
- {
- Class superClass = [aClass superclass];
-
- [self appendText: "%s", [aClass name]];
- if( superClass )
- {
- [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 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
-