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.