[comp.windows.news] Print Object hierarchy for NeWS classes & a bugfix for class.ps

montnaro@sprite.steinmetz (Skip Montanaro) (09/03/87)

Here's a short PostScript program that pretty prints the object
hierarchy for Sun's PostScript implementation of Smalltalk classes and
points out a bug in class.ps. It doesn't print things graphically, but
how hard could it be to add that? :-)

Ph uses the SubClasses field in each class, which is an array of
literals of that class's subclasses. It can be used to demonstrate
that processes do not share dictionary stacks, except for systemdict.
To see this, enter NeWS, create a terminal, run psh (before doing
anything else), and then execute "/Object ph". The only subclasses of
Object defined at that point should be LiteMenu and LiteWindow. Then
execute the itemdemo from the NeWS menu. Itemdemo loads liteitem.ps,
which defines several other classes in systemdict, but itemdemo itself
defines the class SquareRadioItem, which it places in its userdict.
The SubClasses field for Item contains a "/SquareRadioItem" entry, but
no object with that name is found by the process executing ph, hence
it prints the "(Undefined)" following the class name. 

Every time you execute itemdemo, another /SquareRadioItem is appended
to CycleItem's SubClasses array. The bug is in classend, defined in
class.ps. I have enclosed a fixed (although perhaps not too efficient)
version of classend after ph.ps. 

----------ph.ps----------
% written for NeWS 1.0 - your mileage may vary

/ph { % object => - (print NeWS object hierarchy beginning at object)
    0 phhelper } def

/phhelper { % /obj nest => - (print object hierarchy beginning at obj)
    2 dict begin
        /nest exch def
	/obj exch def
	1 1 nest { (\t) print pop } for
	obj 20 string cvs print
	obj cvx
	dup where {
	    (\n) print
	    pop				% don't really want the dict entry
	    exec /SubClasses get
	    [ nest 1 add /phhelper cvx ] cvx forall
	}
	{
	    % known, but not found on this process's dictionary stack
	    ( (Undefined)\n) print
	} ifelse
    end
} def
----------classend.ps----------
systemdict begin
/classend { %  - => classname newclass
    	ObjectTemplate {def} forall   % Now initialize the class structure.
    	ClassTemplate {def} forall
    	ClassBeginArgs! {def} forall
    	currentdict /ClassBeginArgs! undef
        
	% Compile all the methods in this class:
        currentdict {
            dup xcheck {ParentDict methodcompile def} {pop pop} ifelse
        } forall

	% Initialize InstanceVarDict & ParentDictArray
	/InstanceVarDict InstanceVars def
	/ParentDictArray [] def

	% Convert InstanceVarDict to dict if was an array
        InstanceVarDict type /arraytype eq {
            /InstanceVarDict InstanceVarDict length dict dup begin
                InstanceVarDict {null def} forall
            end def
        } if
        
	% Optimizations:
	%	-Make InstanceVarDict the sum of its parents instance vars.
	%	-Create an array of all the parent dicts so that send
	%	 doesnt have to run down the ParentDict pointers.
	% Also check for redundant instance variables and add me to my
	% super class' list of subclasses.  This is to allow easy
	% cross referencing in the future.
        ParentDict null ne {
	    % only append ClassName to ParentDict's /SubClasses array if
	    % it isn't already there
	    1 dict begin
		/found false def
                ParentDict /SubClasses get
	        {
		    ClassName eq {
		        /found true def
		        exit
		    } if
	        } forall
	        found not {
	            ParentDict /SubClasses 2 copy get [ClassName] append put
	        } if
	    end
	    /InstanceVarDict
	        ParentDict /InstanceVarDict get InstanceVarDict append def
	    /ParentDictArray
	        ParentDict /ParentDictArray get [ParentDict] append def

%	    InstanceVarDict length InstanceVars length sub
%	    ParentDict /InstanceVarDict get length ne {
%	        ClassName
%	        InstanceVarDict length
%	        InstanceVars length
%	        ParentDict /InstanceVarDict get length
%	        ForceAnError!
%	    } if
	} if
	
	ClassName	% return the class name
	DictBEMulFudge /DictBEMulFudge 2 store
    dictend
    exch /DictBEMulFudge exch store
} def
end
----------end of classend.ps----------
Skip (montanaro@ge-crd.arpa or uunet!steinmetz!desdemona!montanaro)