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.