Behavior.st
author claus
Thu, 09 Mar 1995 00:40:27 +0100
changeset 302 1f76060d58a4
parent 295 14d0cf46c739
child 308 f04744ef7b5d
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1988 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Object subclass:#Behavior
       instanceVariableNames:'superclass otherSuperclasses
			      selectorArray methodArray
			      instSize flags'
       classVariableNames:''
       poolDictionaries:''
       category:'Kernel-Classes'
!

Behavior comment:'
COPYRIGHT (c) 1988 by Claus Gittinger
	      All Rights Reserved

$Header: /cvs/stx/stx/libbasic/Behavior.st,v 1.30 1995-03-06 20:58:51 claus Exp $
'!

!Behavior class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1988 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

version
"
$Header: /cvs/stx/stx/libbasic/Behavior.st,v 1.30 1995-03-06 20:58:51 claus Exp $
"
!

documentation
"
    Every class in the system inherits from Behavior (via Class, ClassDescription);
    so here is where most of the class messages end up being implemented.
    (to answer a FAQ: 'Point basicNew' will be done here :-)

    Beginners should keep in mind, that all classes are instances of subclasses
    of Behavior, therefore you will find the above mentioned 'basicNew:' method 
    under the 'instance'-methods of Behavior - NOT under the class methods 
    ('Behavior new' will create and return a new class, while sending 'new' to 
    any instance of Behavior (i.e. any class) will return an instance of that class).

    Behavior provides minimum support for all classes - additional stuff is
    found in ClassDescription and Class. Behaviors provides all mechanisms needed
    to create instances, and send messages to those. However, Behavior does not provide 
    all the (symbolic) information needed to compile methods for a class or to get
    useful information in inspectors.

    In contrast to other ST implementations, the methods have been separated
    from the selectors (there is no Dictionary, but two separate Arrays)
    - this avoids the need for knowledge about Dictionaries in the runtime library (VM)
    (lookup and search in these is seldom anyway, so the added benefit from using a 
     hashed dictionary is almost void).

    Instance variables:

	superclass        <Class>           the receivers superclass
	otherSuperclasses <Array of Class>  experimental: other superclasses
	selectorArray     <Array of Symbol> the selectors for which inst-methods are defined here
	methodArray       <Array of Method> the inst-methods corresponding to the selectors
	instSize          <SmallInteger>    the number of instance variables
	flags             <SmallInteger>    special flag bits coded in a number

    flag bits (see stc.h):

    NOTICE: layout known by compiler and runtime system; be careful when changing
"
! !

!Behavior class methodsFor:'queries'!

isBuiltInClass
    "this class is known by the run-time-system"

    ^ true
! !

!Behavior class methodsFor:'creating new classes'!

new
    "creates and return a new class"

    |newClass|

    newClass := self basicNew.
    newClass setSuperclass:Object
		 selectors:(Array new:0)
		   methods:(Array new:0)
		  instSize:0
		     flags:0.
    ^ newClass
! !

!Behavior methodsFor:'initialization'!

initialize
    "to catch initialize for classes which do not"

    ^ self
!

reinitialize
    "to catch reinitialize for classes which do not"

    ^ self
! !

!Behavior methodsFor:'copying'!

deepCopy
    "return a deep copy of the receiver
     - return the receiver here - time will show if this is ok"

    ^ self
!

deepCopyUsing:aDictionary
    "return a deep copy of the receiver
     - return the receiver here - time will show if this is ok"

    ^ self
!

simpleDeepCopy
    "return a deep copy of the receiver
     - return the receiver here - time will show if this is ok"

    ^ self
! !

!Behavior methodsFor:'creating an instance of myself'!

uninitializedNew
    "same as new - only redefined in ByteArray"

    ^ self basicNew
!

uninitializedNew:anInteger
    "same as new:anInteger - only redefined in ByteArray"

    ^ self basicNew:anInteger
!

niceBasicNew:anInteger
    "same as basicNew:anInteger, but tries to avoid long pauses
     due to garbage collection. This method checks to see if
     allocation is possible without a pause, and does a background
     incremental garbage collect first if there is not enough memory
     available at the moment for fast allocation. 
     This is useful in low-priority background processes which like to 
     avoid disturbing any higher priority foreground process while allocating
     big amounts of memory. Of course, using this method only makes
     sense for big or huge objects (say > 200k).

     EXPERIMENTAL: this is a non-standard interface and should only 
     be used for special applications. There is no guarantee, that this
     method will be available in future ST/X releases."

    |size|

    size := self sizeOfInst:anInteger.
    (ObjectMemory checkForFastNew:size) ifFalse:[
	"
	 incrementally collect garbage
	"
	ObjectMemory incrementalGC.
    ].
    ^ self basicNew:anInteger
!

new
    "return an instance of myself without indexed variables"

    ^ self basicNew
!

new:anInteger
    "return an instance of myself with anInteger indexed variables"

    ^ self basicNew:anInteger
!

basicNew
    "return an instance of myself without indexed variables.
     If the receiver-class has indexed instvars, the new object will have
     a basicSize of zero - 
     i.e. 'aClass basicNew' is equivalent to 'aClass basicNew:0'.

     ** Do not redefine this method in any class **"

%{  /* NOCONTEXT */
    OBJ new();
    REGISTER OBJ newobj;
    REGISTER char *nextPtr;
    unsigned int instsize;
    REGISTER unsigned int nInstVars;

    /*
     * the following ugly code is nothing more than a new() followed
     * by a nilling of the new instance.
     * Unrolled for a bit more speed since this is one of the central object 
     * allocation methods in the system
     */
    nInstVars = _intVal(_INST(instSize));
    instsize = OHDR_SIZE + __OBJS2BYTES__(nInstVars);

    newobj = (OBJ) newNextPtr;
    nextPtr = ((char *)newobj) + instsize;

    /*
     * dont argue about the goto and the arrangement below - it saves 
     * an extra nil-compare and branch in the common case ...
     * (i.e. if no GC is needed, we fall through without a branch)
     */
    if (nextPtr < newEndPtr) {
	_objPtr(newobj)->o_size = instsize;
	/* o_allFlags(newobj) = 0;              */
	/* _objPtr(newobj)->o_space = newSpace; */
	o_setAllFlags(newobj, newSpace);
#ifdef ALIGN4
	newNextPtr = nextPtr;
#else
	if (instsize & (ALIGN-1)) {
	    newNextPtr = (char *)newobj + (instsize & ~(ALIGN-1)) + ALIGN;
	} else {
	    newNextPtr = nextPtr;
	}
#endif

ok:
	_InstPtr(newobj)->o_class = self;

	if (nInstVars) {
#if defined(FAST_OBJECT_MEMSET4)
	    memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
#else
	    REGISTER OBJ *op;

	    op = _InstPtr(newobj)->i_instvars;

# if !defined(NEGATIVE_ADDRESSES)
	    /*
	     * knowing that nil is 0
	     */
#  if defined(FAST_OBJECT_MEMSET_DOUBLES_UNROLLED)
	    if (nInstVars > 8) {
		*op++ = nil;    /* for alignment */
		nInstVars--;
		while (nInstVars >= 8) {
		    *(double *)op = 0.0;
		    ((double *)op)[1] = 0.0;
		    ((double *)op)[2] = 0.0;
		    ((double *)op)[3] = 0.0;
		    op += 8;
		    nInstVars -= 8;
		}
	    }
	    while (nInstVars != 0) {
		*op++ = 0;
		nInstVars--;
	    }
#  else
#   if defined(FAST_OBJECT_MEMSET_LONGLONG_UNROLLED)
	    if (nInstVars > 8) {
		*op++ = nil;    /* for alignment */
		nInstVars--;
		while (nInstVars >= 8) {
		    *(long long *)op = 0;
		    ((long long *)op)[1] = 0;
		    ((long long *)op)[2] = 0;
		    ((long long *)op)[3] = 0;
		    op += 8;
		    nInstVars -= 8;
		}
	    }
	    while (nInstVars != 0) {
		*op++ = 0;
		nInstVars--;
	    }

#   else
#    if defined(FAST_OBJECT_MEMSET_WORDS_UNROLLED)
	    while (nInstVars >= 8) {
		*op = nil;
		*(op+1) = nil;
		*(op+2) = nil;
		*(op+3) = nil;
		*(op+4) = nil;
		*(op+5) = nil;
		*(op+6) = nil;
		*(op+7) = nil;
		op += 8;
		nInstVars -= 8;
	    }
	    while (nInstVars != 0) {
		*op++ = nil;
		nInstVars--;
	    }
#    else
#     if defined(FAST_MEMSET)
	    memset(_InstPtr(newobj)->i_instvars, 0, instsize-OHDR_SIZE);
#     else
	    do {
		*op++ = nil;
		nInstVars--;
	    } while (nInstVars != 0);
#     endif
#    endif
#   endif
#  endif
# else /* nil could be ~~ 0 */
	    while (nInstVars >= 8) {
		*op = nil;
		*(op+1) = nil;
		*(op+2) = nil;
		*(op+3) = nil;
		*(op+4) = nil;
		*(op+5) = nil;
		*(op+6) = nil;
		*(op+7) = nil;
		op += 8;
		nInstVars -= 8;
	    }
	    while (nInstVars != 0) {
		*op++ = nil;
		nInstVars--;
	    }
# endif
#endif
	}
	RETURN ( newobj );
    }

    /*
     * the slow case - a GC will occur
     */
    PROTECT_CONTEXT
    newobj = new(instsize, SENDER);
    UNPROTECT_CONTEXT
    if (newobj != nil) goto ok;
%}
.
    "
     memory allocation failed.
     When we arrive here, there was no memory, even after
     a garbage collect. 
     This means, that the VM wanted to get some more memory from the 
     Operatingsystem, which was not kind enough to give it.
     Bad luck - you should increase the swap space on your machine.
    "
    ^ ObjectMemory allocationFailureSignal raise.
!

basicNew:anInteger
    "return an instance of myself with anInteger indexed variables.
     If the receiver-class has no indexed instvars, this is only allowed
     if the argument, anInteger is zero.
     ** Do not redefine this method in any class **"

%{  /* NOCONTEXT */

    OBJ newobj;
    unsigned INT instsize, nInstVars;
    INT nindexedinstvars;
    unsigned INT flags;
#if ! defined(FAST_ARRAY_MEMSET) || defined(NEGATIVE_ADDRESSES)
    REGISTER char *cp;
    short *sp;
    long *lp;
#endif
    REGISTER OBJ *op;
    float *fp;
    double *dp;
    extern OBJ new();

    if (__isSmallInteger(anInteger)) {
	nindexedinstvars = _intVal(anInteger);
	if (nindexedinstvars >= 0) {
	    nInstVars = _intVal(_INST(instSize));
	    flags = _intVal(_INST(flags)) & ARRAYMASK;
	    switch (flags) {
		case BYTEARRAY:
		    instsize = OHDR_SIZE + nindexedinstvars;
		    if (nInstVars == 0) {
			if (_CanDoQuickNew(instsize)) {
			    /*
			     * the most common case
			     */
			    _qCheckedNew(newobj, instsize);
			    _InstPtr(newobj)->o_class = self;
#if defined(FAST_ARRAY_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
			    memset(_InstPtr(newobj)->i_instvars, 0, nindexedinstvars);
#else
			    cp = (char *)_InstPtr(newobj)->i_instvars;
			    while (nindexedinstvars >= sizeof(long)) {
				*(long *)cp = 0;
				cp += sizeof(long);
				nindexedinstvars -= sizeof(long);
			    }
			    while (nindexedinstvars--)
				*cp++ = '\0';
#endif
			    RETURN ( newobj );
			}
		    } else {
			instsize += __OBJS2BYTES__(nInstVars);
		    }
		    PROTECT_CONTEXT
		    _qNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
#if defined(FAST_ARRAY_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
		    /*
		     * knowing that nil is 0
		     */
		    memset(_InstPtr(newobj)->i_instvars, 0, instsize-OHDR_SIZE);
#else
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
		    cp = (char *)op;
		    while (nindexedinstvars >= sizeof(long)) {
			*(long *)cp = 0;
			cp += sizeof(long);
			nindexedinstvars -= sizeof(long);
		    }
		    while (nindexedinstvars--)
			*cp++ = '\0';
#endif
		    RETURN ( newobj );
		    break;

		case WORDARRAY:
		    instsize = OHDR_SIZE + 
			       __OBJS2BYTES__(nInstVars) + 
			       nindexedinstvars * sizeof(short);
		    PROTECT_CONTEXT
		    _qNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
#if defined(FAST_ARRAY_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
		    /*
		     * knowing that nil is 0
		     */
		    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
#else
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
		    sp = (short *)op;
		    while (nindexedinstvars--)
			*sp++ = 0;
#endif
		    RETURN ( newobj );
		    break;

	       case LONGARRAY:
		    instsize = OHDR_SIZE + 
			       __OBJS2BYTES__(nInstVars) + 
			       nindexedinstvars * sizeof(long);
		    PROTECT_CONTEXT
		    _qAlignedNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
#if defined(FAST_ARRAY_MEMSET4) && ! defined(NEGATIVE_ADDRESSES)
		    /*
		     * knowing that nil is 0
		     */
		    memset4(_InstPtr(newobj)->i_instvars, 0, nInstVars + nindexedinstvars);
#else
# if defined(FAST_ARRAY_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
		    /*
		     * knowing that nil is 0
		     */
		    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
# else
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
		    lp = (long *)op;
		    while (nindexedinstvars--)
			*lp++ = 0;
# endif
#endif
		    RETURN ( newobj );
		    break;

	       case FLOATARRAY:
		    instsize = sizeof(struct floatArray) + 
			       __OBJS2BYTES__(nInstVars) + 
			       (nindexedinstvars - 1) * sizeof(float);

		    PROTECT_CONTEXT
		    _qNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
		    op = _InstPtr(newobj)->i_instvars;
# if defined(mips) /* knowin that float 0.0 is all-zeros */
		    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
# else
		    while (nInstVars--)
			*op++ = nil;
		    fp = (float *)op;
		    while (nindexedinstvars--)
			*fp++ = 0.0;
# endif
		    RETURN ( newobj );
		    break;

	       case DOUBLEARRAY:
		    instsize = sizeof(struct doubleArray) + 
			       __OBJS2BYTES__(nInstVars) + 
			       (nindexedinstvars - 1) * sizeof(double);

		    PROTECT_CONTEXT
		    _qAlignedNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
#ifdef NEED_DOUBLE_ALIGN
		    /*
		     * care for double alignment
		     */
		    if ((INT)op & (ALIGN-1)) {
			*op++ = nil;
		    }
#endif
		    dp = (double *)op;
		    while (nindexedinstvars--)
			*dp++ = 0.0;
		    RETURN ( newobj );
		    break;

		case WKPOINTERARRAY:
		case POINTERARRAY:
		    nInstVars += nindexedinstvars;
		    instsize = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
		    PROTECT_CONTEXT
		    _qAlignedNew(newobj, instsize, SENDER);
		    UNPROTECT_CONTEXT
		    if (newobj == nil) {
			break;
		    }
		    _InstPtr(newobj)->o_class = self;
#if defined(FAST_ARRAY_MEMSET4)
		    memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
#else
# if !defined(NEGATIVE_ADDRESSES)
		    /*
		     * knowing that nil is 0
		     */
#ifdef mips
# undef FAST_ARRAY_MEMSET_DOUBLES_UNROLLED
# undef FAST_ARRAY_MEMSET_LONGLONG_UNROLLED
/* seems to be slightly faster */
# define FAST_ARRAY_MEMSET
#endif
#ifdef sparc
# define FAST_ARRAY_MEMSET_DOUBLES_UNROLLED
#endif

#  if defined(FAST_ARRAY_MEMSET_DOUBLES_UNROLLED)
		    op = _InstPtr(newobj)->i_instvars;
		    if (nInstVars > 8) {
			*op++ = nil;    /* for alignment */
			nInstVars--;
			while (nInstVars >= 8) {
			    *(double *)op = 0.0;
			    ((double *)op)[1] = 0.0;
			    ((double *)op)[2] = 0.0;
			    ((double *)op)[3] = 0.0;
			    op += 8;
			    nInstVars -= 8;
			}
		    }
		    while (nInstVars) {
			*op++ = 0;
			nInstVars--;
		    }
#  else
#   if defined(FAST_ARRAY_MEMSET_LONGLONG_UNROLLED)
		    op = _InstPtr(newobj)->i_instvars;
		    if (nInstVars > 8) {
			*op++ = nil;    /* for alignment */
			nInstVars--;
			while (nInstVars >= 8) {
			    *(long long *)op = 0;
			    ((long long *)op)[1] = 0;
			    ((long long *)op)[2] = 0;
			    ((long long *)op)[3] = 0;
			    op += 8;
			    nInstVars -= 8;
			}
		    }
		    while (nInstVars) {
			*op++ = 0;
			nInstVars--;
		    }
#   else
#    if defined(FAST_ARRAY_MEMSET)
		    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
#    else
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
#    endif
#   endif
#  endif
# else
		    op = _InstPtr(newobj)->i_instvars;
		    while (nInstVars--)
			*op++ = nil;
# endif
#endif
		    RETURN ( newobj );
		    break;

		default:
		    /*
		     * new:n for non-variable classes only allowed if
		     * n == 0
		     */
		    if (nindexedinstvars == 0) {
			instsize = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
			PROTECT_CONTEXT
			_qAlignedNew(newobj, instsize, SENDER);
			UNPROTECT_CONTEXT
			if (newobj == nil) {
			    break;
			}
			_InstPtr(newobj)->o_class = self;
			if (nInstVars) {
#if defined(FAST_OBJECT_MEMSET4)
			    memset4(_InstPtr(newobj)->i_instvars, nil, nInstVars);
#else
# if defined(FAST_MEMSET) && ! defined(NEGATIVE_ADDRESSES)
			    /*
			     * knowing that nil is 0
			     */
			    memset(_InstPtr(newobj)->i_instvars, 0, instsize - OHDR_SIZE);
# else
			    op = _InstPtr(newobj)->i_instvars;
			    do {
				*op++ = nil;
			    } while (--nInstVars);
# endif
#endif
			}
			RETURN ( newobj );
		    }
		    break;
	    }
	}
    }
%}.
    "
     arrive here if something went wrong ...
     figure out what it was
    "

    (anInteger isMemberOf:SmallInteger) ifFalse:[
	"
	 the argument is either not an integer,
	 or a LargeInteger (which means that its definitely too big)
	"
	self error:'argument to new: must be Integer'.
	^ nil
    ].
    (anInteger < 0) ifTrue:[
	"
	 the argument is negative,
	"
	self error:'bad (negative) argument to new:'.
	^ nil
    ].
    self isVariable ifFalse:[
	"
	 this class does not have any indexed instance variables
	"
	self error:'class has no indexed instvars - cannot create with new:'.
	^ nil
    ].
    "
     memory allocation failed.
     When we arrive here, there was no memory, even after
     a garbage collect. 
     This means, that the VM wanted to get some more memory from the 
     Operatingsystem, which was not kind enough to give it.
     Bad luck - you should increase the swap space on your machine.
    "
    ^ ObjectMemory allocationFailureSignal raise.
!

readFrom:aStream
    "read an objects printed representation from the argument, aStream 
     and return it. 
     The read object must be a kind of myself if its not, an error is raised.
     This is the reverse operation to 'storeOn:'.

     WARNING: storeOn: does not handle circular references and multiple 
	      references to the same object.
	      Use #storeBinary:/readBinaryFrom: for this."

    ^ self readFrom:aStream onError:[self error:'expected ' , self name]

    "
     |s|
     s := WriteStream on:String new.
     #(1 2 3 4) storeOn:s.
     Object readFrom:(ReadStream on:s contents)  
    "
!

readFrom:aStream onError:exceptionBlock
    "read an objects printed representation from the argument, aStream 
     and return it (i.e. the stream should contain some representation of
     the object which was created using #storeOn:). 
     The read object must be a kind of myself if its not, the value of
     exceptionBlock is returned.
     To get any object, use 'Object readFrom:...',
     To get any number, use 'Number readFrom:...' and so on.
     This is the reverse operation to 'storeOn:'.

     WARNING: storeOn: does not handle circular references and multiple 
	      references to the same object.
	      Use #storeBinary:/readBinaryFrom: for this."

    |newObject|

    newObject := self compilerClass evaluate:aStream.
    (newObject isKindOf:self) ifFalse:[^ exceptionBlock value].
    ^ newObject

    "
     |s|
     s := WriteStream on:String new.
     #(1 2 3 4) storeOn:s.
     Transcript showCr:(
	Array readFrom:(ReadStream on:s contents) onError:'not an Array'
     )
    "
    "
     |s|
     s := WriteStream on:String new.
     #[1 2 3 4] storeOn:s.
     Transcript showCr:(
	 Array readFrom:(ReadStream on:s contents) onError:'not an Array'
     ]
    "
!

readFromString:aString
    "create an object from its printed representation.
     For most classes, the string is expected to be in a format created by
     storeOn: or storeString; however, some (Time, Date) expect a user
     readable string here.
     See comments in Behavior>>readFromString:onError:,
     Behavior>>readFrom: and Behavior>>readFrom:onError:"

    ^ self readFromString:aString onError:[self error:'expected ' , self name]

    "
     Integer readFromString:'12345678901234567890' 
     Point readFromString:'1@2'  
    "
!

readFromString:aString onError:exceptionBlock
    "create an object from its printed representation.
     Here, the string is expected to be in a format created by
     storeOn: or storeString; however, some classes (Time, Date) may redefine
     it to expect a user readable string here.
     See comments in Behavior>>readFrom: and Behavior>>readFrom:onError:"

    ^ self readFrom:(ReadStream on:aString) onError:exceptionBlock

    "
     Integer readFromString:'12345678901234567890' 
     Integer readFromString:'abc' 
     Integer readFromString:'abc' onError:0
     Point readFromString:'1@2'  
     Point readFromString:'0'  
     Point readFromString:'0' onError:[0@0]  
    "
! !

!Behavior methodsFor:'autoload check'!

isLoaded
    "return true, if the class has been loaded; 
     redefined in Autoload; see comment there"

    ^ true
!

autoload
    "force autoloading - do nothing here; 
     redefined in Autoload; see comment there"

    ^ self
! !

!Behavior methodsFor:'snapshots'!

preSnapshot
    "sent by ObjectMemory, before a snapshot is written.
     Nothing done here."
!

postSnapshot
    "sent by ObjectMemory, after a snapshot has been written.
     Nothing done here."
! !

!Behavior class methodsFor:'flag bit constants'!

flagNotIndexed
    "return the flag code for non-indexed instances"

    ^ 0
! 

flagBytes
    "return the flag code for byte-valued indexed instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(BYTEARRAY) );
%}
    "
     Behavior flagBytes    
    "
! 

flagWords
    "return the flag code for word-valued indexed instances (i.e. 2-byte)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(WORDARRAY) );
%}
    "
     Behavior flagWords    
    "
! 

flagLongs
    "return the flag code for long-valued indexed instances (i.e. 4-byte)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(LONGARRAY) );
%}
    "
     Behavior flagLongs    
    "
! 

flagFloats
    "return the flag code for float-valued indexed instances (i.e. 4-byte reals)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(FLOATARRAY) );
%}
    "
     Behavior flagFloats    
    "
! 

flagDoubles
    "return the flag code for double-valued indexed instances (i.e. 8-byte reals)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(DOUBLEARRAY) );
%}
    "
     Behavior flagDoubles    
    "
! 

flagPointers
    "return the flag code for pointer indexed instances (i.e. Array of object)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(POINTERARRAY) );
%}
    "
     Behavior flagPointers    
    "
! 

flagWeakPointers
    "return the flag code for weak pointer indexed instances (i.e. WeakArray)"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(WKPOINTERARRAY) );
%}
! 

maskIndexType
    "return a mask to extract all index-type bits"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(ARRAYMASK) );
%}
! 

flagBlock
    "return the flag code which marks Block-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(BLOCK_INSTS) );
%}
! 

flagMethod
    "return the flag code which marks Method-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(METHOD_INSTS) );
%}
! 

flagContext
    "return the flag code which marks Context-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(CONTEXT_INSTS) );
%}
! 

flagBlockContext
    "return the flag code which marks BlockContext-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(BCONTEXT_INSTS) );
%}
! 

flagFloat
    "return the flag code which marks Float-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(FLOAT_INSTS) );
%}
! 

flagSymbol
    "return the flag code which marks Symbol-type instances"

%{  /* NOCONTEXT */
    /* this is defined as a primitive to get defines from stc.h */

    RETURN ( _MKSMALLINT(SYMBOL_INSTS) );
%}
! !

!Behavior methodsFor:'accessing'!

name
    "although behaviors have no name, we return something
     useful here - there are many places (inspectors) where
     a classes name is asked for.
     Implementing this message here allows anonymous classes
     and instances of them to be inspected."

    ^ 'someBehavior'
!

superclass
    "return the receivers superclass"

    ^ superclass
!

selectorArray 
    "return the receivers selector array.
     Notice: this is not compatible with ST-80."

    ^ selectorArray
!

methodArray
    "return the receivers method array.
     Notice: this is not compatible with ST-80."

    ^ methodArray
!

methodDictionary
    "return the receivers method dictionary. 
     Since no dictionary is actually present, create one for ST-80 compatibility."

    |dict|

    dict := IdentityDictionary new.
    1 to:selectorArray size do:[:index |
	dict at:(selectorArray at:index) put:(methodArray at:index)
    ].
    ^ dict
!

instSize
    "return the number of instance variables of the receiver.
     This includes all superclass instance variables."

    ^ instSize
!

flags
    "return the receivers flag bits"

    ^ flags
!

superclass:aClass
    "set the superclass - this actually creates a new class,
     recompiling all methods for the new one. The receiving class stays
     around anonymous to allow existing instances some life.
     This may change in the future (adjusting existing instances)"

    "must flush caches since lookup chain changes"
    ObjectMemory flushCaches.

"
    superclass := aClass
"
    "for correct recompilation, just create a new class ..."

    aClass subclass:(self name)
	   instanceVariableNames:(self instanceVariableString)
	   classVariableNames:(self classVariableString)
	   poolDictionaries:''
	   category:self category
!

addSuperclass:aClass
    "EXPERIMENTAL MI support: add aClass to the set of classes, from which instances
     inherit protocol."

    "first, check if the class is abstract - 
     allows abstract mixins are allowed in the current implementation"

    aClass instSize == 0 ifFalse:[
	self error:'only abstract mixins allowed'.
	^ self
    ].
    otherSuperclasses isNil ifTrue:[
	otherSuperclasses := Array with:aClass
    ] ifFalse:[
	otherSuperclasses := otherSuperclasses copyWith:aClass
    ].
    ObjectMemory flushCaches
!

removeSuperclass:aClass
    "EXPERIMENTAL MI support: remove aClass from the set of classes, from which instances
     inherit protocol."

    otherSuperclasses notNil ifTrue:[
	otherSuperclasses := otherSuperclasses copyWithout:aClass.
	otherSuperclasses isEmpty ifTrue:[
	    otherSuperclasses := nil
	].
	ObjectMemory flushCaches
    ].
!

selectors:newSelectors methods:newMethods
    "set both selector array and method array of the receiver,
     and flush caches"

    ObjectMemory flushCaches.
    selectorArray := newSelectors.
    methodArray := newMethods
!

addSelector:newSelector withMethod:newMethod
    "add the method given by 2nd argument under the selector given by
     1st argument to the methodDictionary. Flush all caches."

    |nargs|

    (self primAddSelector:newSelector withMethod:newMethod) ifFalse:[^ false].
    self changed:#methodDictionary with:newSelector.

    "
     if I have no subclasses, all we have to flush is cached
     data for myself ... (actually, in any case all that needs
     to be flushed is info for myself and all of my subclasses)
    "
"
    problem: this is slower; since looking for all subclasses is (currently)
	     a bit slow :-(
	     We need the hasSubclasses-info bit in Behavior; now

    self withAllSubclassesDo:[:aClass |
	ObjectMemory flushInlineCachesFor:aClass withArgs:nargs.
	ObjectMemory flushMethodCacheFor:aClass
    ].
"

    "
     actually, we would do better with less flushing ...
    "
    nargs := newSelector numArgs.

    ObjectMemory flushMethodCache.
    ObjectMemory flushInlineCachesWithArgs:nargs.

    ^ true
!

addSelector:newSelector withLazyMethod:newMethod
    "add the method given by 2nd argument under the selector given by
     1st argument to the methodDictionary. Since it does not flush
     any caches, this is only allowed for lazy methods."

    newMethod isLazyMethod ifFalse:[
	self error:'operation only allowed for lazy methods'.
	^ false
    ].
    (self primAddSelector:newSelector withMethod:newMethod) ifTrue:[
	self changed:#methodDictionary with:newSelector.
	^ true
    ].
    ^ false
!

removeSelector:aSelector
    "remove the selector, aSelector and its associated method 
     from the methodDictionary"

    |index oldSelectorArray oldMethodArray newSelectorArray newMethodArray|

    index := selectorArray identityIndexOf:aSelector startingAt:1.
    (index == 0) ifTrue:[^ false].

    newSelectorArray := selectorArray copyWithoutIndex:index.
    newMethodArray := methodArray copyWithoutIndex:index.
    oldSelectorArray := selectorArray.
    oldMethodArray := methodArray.
    selectorArray := newSelectorArray.
    methodArray := newMethodArray.
"
    [
	|nargs|
	nargs := aSelector numArgs.
	ObjectMemory flushMethodCache.
	ObjectMemory flushInlineCachesWithArgs:nargs.
    ] value
"
    "
     actually, we would do better with less flushing ...
    "
    ObjectMemory flushCaches.
    ^ true
! !

!Behavior methodsFor:'queries'!

sizeOfInst:n
    "return the number of bytes required for an instance of
     myself with n indexed instance variables. The argument n 
     should be zero for classes without indexed instance variables.
     See Behavior>>niceNew: for an application of this."

    |nInstvars|

    nInstvars := self instSize.
%{
    int nBytes;

    nBytes = _intVal(nInstvars) * sizeof(OBJ) + OHDR_SIZE; 
    if (__isSmallInteger(n)) {
	int nIndex;

	nIndex = _intVal(n);
	switch (_intVal(_INST(flags)) & ARRAYMASK) {
	    case BYTEARRAY:
		nBytes += nIndex;
		if (nBytes & (ALIGN - 1)) {
		    nBytes = (nBytes & ~(ALIGN - 1)) + ALIGN;
		}
		break;

	    case WORDARRAY:
		nBytes += nIndex * sizeof(short);
		if (nBytes & (ALIGN - 1)) {
		    nBytes = (nBytes & ~(ALIGN - 1)) + ALIGN;
		}
		break;

	    case LONGARRAY:
		nBytes += nIndex * sizeof(long);
		break;

	    case FLOATARRAY:
		nBytes += nIndex * sizeof(float);
		break;

	    case DOUBLEARRAY:
		nBytes += nIndex * sizeof(double);
		break;

	    default:
		nBytes += nIndex * sizeof(OBJ);
		break;
	}
    }
    RETURN (_MKSMALLINT(nBytes));
%}
!

isVariable
    "return true, if instances have indexed instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) ~~ 0
     "

%{  /* NOCONTEXT */

    RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? true : false ); 
%}
!

isFixed
    "return true, if instances do not have indexed instance variables"

    "this could be defined as:
	^ self isVariable not
    "

%{  /* NOCONTEXT */

    RETURN ( (_intVal(_INST(flags)) & ARRAYMASK) ? false : true ); 
%}
!

isBits
    "return true, if instances have indexed byte or short instance variables.
     Ignore long, float and double arrays, since ST-80 code using isBits are probably
     not prepared to handle them correctly."

%{  /* NOCONTEXT */

    REGISTER int flags;

    RETURN ( (((flags = (_intVal(_INST(flags)) & ARRAYMASK)) == BYTEARRAY)
	     || (flags == WORDARRAY)) ? true : false ); 
%}
!

isBytes
    "return true, if instances have indexed byte instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) == Behavior flagBytes
    "
%{  /* NOCONTEXT */

    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == BYTEARRAY) ? true : false ); 
%}
!

isWords
    "return true, if instances have indexed short instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) == Behavior flagWords
    "
%{  /* NOCONTEXT */

    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == WORDARRAY) ? true : false ); 
%}
!

isLongs
    "return true, if instances have indexed long instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) == Behavior flagLongs
    "
%{  /* NOCONTEXT */

    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == LONGARRAY) ? true : false ); 
%}
!

isFloats
    "return true, if instances have indexed float instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) == Behavior flagFloats
    "
%{  /* NOCONTEXT */

    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == FLOATARRAY) ? true : false ); 
%}
!

isDoubles
    "return true, if instances have indexed double instance variables"

    "this could be defined as:
	^ (flags bitAnd:(Behavior maskIndexType)) == Behavior flagDoubles
    "
%{  /* NOCONTEXT */

    RETURN ( ((_intVal(_INST(flags)) & ARRAYMASK) == DOUBLEARRAY) ? true : false ); 
%}
!

isPointers
    "return true, if instances have pointer instance variables 
     i.e. are either non-indexed or have indexed pointer variables"

    "QUESTION: should we ignore WeakPointers ?"

%{  /* NOCONTEXT */

    REGISTER int flags;

    flags = _intVal(_INST(flags)) & ARRAYMASK;
    switch (flags) {
	default:
	    /* normal objects */
	    RETURN ( true );

	case BYTEARRAY:
	case WORDARRAY:
	case LONGARRAY:
	case FLOATARRAY:
	case DOUBLEARRAY:
	    RETURN (false );

	case WKPOINTERARRAY:
	    /* what about those ? */
	    RETURN (true );
    }
%}
!

isBehavior
    "return true, if the receiver is describing another objects behavior,
     i.e. is a class. Defined to avoid the need to use isKindOf:"

    ^ true

    "
     True isBehavior   
     true isBehavior
    "
!

canBeSubclassed
    "return true, if its allowed to create subclasses of the receiver.
     This method is redefined in SmallInteger and UndefinedObject, since
     instances are detected by their pointer-fields, i.e. they do not have
     a class entry (you dont have to understand this :-)"

    ^ true
!

hasMultipleSuperclasses
    "Return true, if this class inherits from other classes 
     (beside its primary superclass). 
     This method is a preparation for a future multiple inheritance extension 
     - currently it is not supported by the VM"

    ^ otherSuperclasses notNil
!

superclasses
    "return a collection of the receivers immediate superclasses.
     This method is a preparation for a future multiple inheritance extension 
     - currently it is not supported by the VM"

    otherSuperclasses notNil ifTrue:[
	^ (Array with:superclass) , otherSuperclasses
    ].
    ^ Array with:superclass

    "
     String superclasses  
    "
!

allSuperclasses
    "return a collection of the receivers accumulated superclasses"

    |aCollection theSuperClass|

    theSuperClass := superclass.
    theSuperClass notNil ifTrue:[
	aCollection := OrderedCollection new.
	[theSuperClass notNil] whileTrue:[
	    aCollection add:theSuperClass.
	    theSuperClass := theSuperClass superclass
	]
    ].
    ^ aCollection

    "
     String allSuperclasses 
    "
!

withAllSuperclasses
    "return a collection containing the receiver and all
     of the receivers accumulated superclasses"

    |aCollection theSuperClass|

    aCollection := OrderedCollection with:self.
    theSuperClass := superclass.
    [theSuperClass notNil] whileTrue:[
	aCollection add:theSuperClass.
	theSuperClass := theSuperClass superclass
    ].
    ^ aCollection

    "
     String withAllSuperclasses 
    "
!

subclasses
    "return a collection of the direct subclasses of the receiver"

    |newColl|

    newColl := OrderedCollection new.
    self subclassesDo:[:aClass |
	newColl add:aClass
    ].
    ^ newColl

    "
     Collection subclasses
    "
!

allSubclasses
    "return a collection of all subclasses (direct AND indirect) of
     the receiver. There will be no specific order, in which entries
     are returned."

    |newColl|

    newColl := OrderedCollection new.
    self allSubclassesDo:[:aClass |
	newColl add:aClass
    ].
    ^ newColl

    "
     Collection allSubclasses
    "
!

allSubclassesInOrder
    "return a collection of all subclasses (direct AND indirect) of
     the receiver. Higher level subclasses will come before lower ones."

    |newColl|

    newColl := OrderedCollection new.
    self allSubclassesInOrderDo:[:aClass |
	newColl add:aClass
    ].
    ^ newColl

    "
     Collection allSubclassesInOrder
    "
!

withAllSubclasses
    "return a collection containing the receiver and 
     all subclasses (direct AND indirect) of the receiver"

    |newColl|

    newColl := OrderedCollection with:self.
    self allSubclassesDo:[:aClass |
	newColl add:aClass
    ].
    ^ newColl

    "
     Collection withAllSubclasses
    "
!

isSubclassOf:aClass
    "return true, if I am a subclass of the argument, aClass"

    |theClass|

    theClass := superclass.
    [theClass notNil] whileTrue:[
	(theClass == aClass) ifTrue:[^ true].
	theClass := theClass superclass
    ].
    ^ false

    "
     String isSubclassOf:Collection  
     LinkedList isSubclassOf:Array   
     1 isSubclassOf:Number              <- will fail since 1 is no class
    "     
!

allInstVarNames
    "return a collection of all the instance variable name-strings
     this includes all superclass-instance variables.
     Since Behavior has no idea of instvar-names, return an empty collection
     here. Redefined in ClassDescription."

    ^ #()
!

allClassVarNames
    "return a collection of all the class variable name-strings
     this includes all superclass-class variables.
     Since Behavior has no idea of classvar-names, return an empty collection
     here. Redefined in ClassDescription."

    ^ #()
!

allInstances
    "return a collection of all my instances"

    |coll|

    coll := OrderedCollection new:100.
    self allInstancesDo:[:anObject |
	coll add:anObject
    ].
    ^ coll 

    "
     ScrollBar allInstances
    "
!

allDerivedInstances
    "return a collection of all instances of myself and 
     instances of all subclasses of myself"

    |coll|

    coll := OrderedCollection new:100.
    self allDerivedInstancesDo:[:anObject |
	(anObject isKindOf:self) ifTrue:[
	    coll add:anObject
	]
    ].
    ^ coll 

    "
     View allDerivedInstances
    "
!

hasInstances
    "return true, if there are any instances of myself"

    ObjectMemory allObjectsDo:[:anObject |
	(anObject class == self) ifTrue:[
	    ^ true
	]
    ].
    ^ false

    "
     Object hasInstances
     SequenceableCollection hasInstances
     Float hasInstances
    "
!

instanceCount
    "return the number of instances of myself"

    |count|

    count := 0.
    ObjectMemory allObjectsDo:[:anObject |
	(anObject class == self) ifTrue:[
	    count := count + 1
	]
    ].
    ^ count

    "
     View instanceCount
     Object instanceCount
     Float instanceCount
     SequenceableCollection instanceCount
    "
!

derivedInstanceCount
    "return the number of instances of myself and of subclasses"

    |count|

    count := 0.
    ObjectMemory allObjectsDo:[:anObject |
	(anObject isKindOf:self) ifTrue:[
	    count := count + 1
	]
    ].
    ^ count

    "
     View derivedInstanceCount
     SequenceableCollection derivedInstanceCount
    "
!

selectorIndex:aSelector
    "return the index in the arrays for given selector aSelector"

    ^ selectorArray identityIndexOf:aSelector startingAt:1
!

includesSelector:aSelector
    "for ST-80 compatibility"

    ^ self implements:aSelector
!

compiledMethodAt:aSelector
    "return the method for given selector aSelector or nil.
     Only methods in the receiver - not in the superclass chain are tested."

    |index|

    index := selectorArray identityIndexOf:aSelector startingAt:1.
    (index == 0) ifTrue:[^ nil].
    ^ methodArray at:index

    "
     Object compiledMethodAt:#==
     (Object compiledMethodAt:#==) category
    "
!

sourceCodeAt:aSelector
    "return the methods source for given selector aSelector or nil.
     Only methods in the receiver - not in the superclass chain are tested."

    |method|

    method := self compiledMethodAt:aSelector.
    method isNil ifTrue:[^ nil].
    ^ method source

    "
     True sourceCodeAt:#ifTrue:
     Object sourceCodeAt:#==
     Behavior sourceCodeAt:#sourceCodeAt:
    "
!

lookupMethodFor:aSelector
    "return the method, which would be executed if aSelector was sent to
     an instance of the receiver. I.e. the selector arrays of the receiver
     and all of its superclasses are searched for aSelector.
     Return the method, or nil if instances do not understand aSelector.
     EXPERIMENTAL: take care of multiple superclasses."

    |m cls|

    cls := self.
    [cls notNil] whileTrue:[
	m := cls compiledMethodAt:aSelector.
	m notNil ifTrue:[^ m].
	cls hasMultipleSuperclasses ifTrue:[
	    cls superclasses do:[:aSuperClass |
		m := aSuperClass lookupMethodFor:aSelector.
		m notNil ifTrue:[^ m].
	    ].
	    ^ nil
	] ifFalse:[
	    cls := cls superclass
	]
    ].
    ^ nil
!

cachedLookupMethodFor:aSelector
    "return the method, which would be executed if aSelector was sent to
     an instance of the receiver. I.e. the selector arrays of the receiver
     and all of its superclasses are searched for aSelector.
     Return the method, or nil if instances do not understand aSelector.
     This interface provides exactly the same information as #lookupMethodFor:,
     but uses the lookup-cache in the VM for faster search. 
     However, keep in mind, that doing a lookup through the cache also adds new
     entries and can thus slow down the system by polluting the cache with 
     irrelevant entries. (do NOT loop over all objects calling this method).
     Does NOT (currently) handle MI"

%{  /* NOCONTEXT */
    extern OBJ lookup();

    RETURN ( lookup(self, aSelector, SENDER) );
%}

    "
     String cachedLookupMethodFor:#=
     String cachedLookupMethodFor:#asOrderedCollection
    "
!

hasMethods
    "return true, if there are any (local) methods in this class"

    ^ (methodArray size ~~ 0)

    "
     True hasMethods
     True class hasMethods
    "
!

implements:aSelector
    "return true, if the receiver implements aSelector.
     (i.e. implemented in THIS class - NOT in a superclass).
     Dont use this method to check if someone responds to a message -
     use #canUnderstand: on the class or #respondsTo: on the instance
     to do this."

    ^ (selectorArray identityIndexOf:aSelector startingAt:1) ~~ 0

    "
     True implements:#ifTrue:
     True implements:#==
    "
!

canUnderstand:aSelector
    "return true, if the receiver or one of its superclasses implements aSelector.
     (i.e. true if my instances understand aSelector)"

    ^ (self lookupMethodFor:aSelector) notNil

    "
     True canUnderstand:#ifTrue:
     True canUnderstand:#==
     True canUnderstand:#do:
    "
!

whichClassImplements:aSelector
    "return the class in the inheritance chain, which implements the method
     for aSelector; return nil if none.
     EXPERIMENTAL: handle multiple superclasses"

    |cls|

    cls := self.
    [cls notNil] whileTrue:[
	(cls implements:aSelector) ifTrue:[^ cls].
	cls hasMultipleSuperclasses ifTrue:[
	    cls superclasses do:[:aSuperClass |
		|implementingClass|

		implementingClass := aSuperClass whichClassImplements:aSelector.
		implementingClass notNil ifTrue:[^ implementingClass].
	    ].
	    ^ nil
	] ifFalse:[
	    cls := cls superclass
	]
    ].
    ^ nil

    "
     String whichClassImplements:#==
     String whichClassImplements:#collect:
    "
!

inheritsFrom:aClass
    "return true, if the receiver inherits methods from aClass"

    ^ self isSubclassOf:aClass

    "
     True inheritsFrom:Object
     LinkedList inheritsFrom:Array
    "
!

selectorAtMethod:aMethod ifAbsent:failBlock
    "return the selector for given method aMethod
     or the value of failBlock, if not found."

    |index|

    index := methodArray identityIndexOf:aMethod startingAt:1.
    (index == 0) ifTrue:[^ failBlock value].
    ^ selectorArray at:index

    "
     |m|

     m := Object compiledMethodAt:#copy.
     Object selectorAtMethod:m ifAbsent:'oops'].
    "
    "
     |m|

     m := Object compiledMethodAt:#copy.
     Fraction selectorAtMethod:m ifAbsent:'oops'].
    "
!

selectorForMethod:aMethod
    "Return the selector for given method aMethod."

    ^ self selectorAtMethod:aMethod ifAbsent:[nil]
!

containsMethod:aMethod
    "Return true, if the argument, aMethod is a method of myself"

    methodArray isNil ifTrue:[^ false].  "degenerated class"
    ^ (methodArray identityIndexOf:aMethod startingAt:1) ~~ 0
! !

!Behavior methodsFor:'private accessing'!

setSuperclass:sup selectors:sels methods:m instSize:i flags:f
    "set some inst vars. 
     this method is for special uses only - there will be no recompilation
     and no change record is written here. Also, if the receiver class has 
     already been in use, future operation of the system is not guaranteed to
     be correct, since no caches are flushed.
     Therefore: do NOT use it; use Behavior>>superclass: (or flush the caches, at least)"

    superclass := sup.
    selectorArray := sels.
    methodArray := m.
    instSize := i.
    flags := f
!

setSuperclass:aClass
    "set the superclass of the receiver.
     this method is for special uses only - there will be no recompilation
     and no change record written here. Also, if the receiver class has
     already been in use, future operation of the system is not guaranteed to
     be correct, since no caches are flushed.
     Therefore: do NOT use it; use Behavior>>superclass: (or flush the caches, at least)"

    superclass := aClass
!

setOtherSuperclasses:anArrayOfClasses
    "EXPERIMENTAL: set the other superclasses of the receiver.
     this method is for special uses only - there will be no recompilation
     and no change record written here; 
     Do NOT use it."

    otherSuperclasses := anArrayOfClasses
!

instSize:aNumber
    "set the instance size.
     this method is for special uses only - there will be no recompilation
     and no change record written here; 
     Do NOT use it."

    instSize := aNumber
!

flags:aNumber
    "set the flags.
     this method is for special uses only - there will be no recompilation
     and no change record written here; 
     Do NOT use it."

    flags := aNumber
!

setSelectors:sels methods:m
    "set some inst vars. 
     this method is for special uses only - there will be no recompilation
     and no change record written here; 
     Do NOT use it."

    selectorArray := sels.
    methodArray := m.
!

setSelectorArray:anArray
    "set the selector array of the receiver.
     this method is for special uses only - there will be no recompilation
     and no change record written here.
     NOT for general use."

    selectorArray := anArray
!

setMethodArray:anArray
    "set the method array of the receiver.
     this method is for special uses only - there will be no recompilation
     and no change record written here.
     NOT for general use."

    methodArray := anArray
!

setMethodDictionary:aDictionary
    "set the receivers method dictionary. 
     Since no dictionary is actually used, decompose into selector- and
     method arrays and set those. For ST-80 compatibility.
     NOT for general use."

    |n newSelectorArray newMethodArray idx|

    n := aDictionary size.
    newSelectorArray := Array new:n.
    newMethodArray := Array new:n.
    idx := 1.
    aDictionary keysAndValuesDo:[:sel :method |
	newSelectorArray at:idx put:sel.
	newMethodArray at:idx put:method.
	idx := idx + 1
    ].
    selectorArray := newSelectorArray.
    methodArray := newMethodArray
!

primAddSelector:newSelector withMethod:newMethod
    "add the method given by 2nd argument under the selector given by
     the 1st argument to the methodDictionary. 
     Does NOT flush any caches, does NOT write a change record.

     Do not use this in normal situations, strange behavior will be
     the consequence.
     I.e. executing obsolete methods, since the old method will still 
     be executed out of the caches."

    |index oldSelectorArray oldMethodArray newSelectorArray newMethodArray|

    (newSelector isMemberOf:Symbol) ifFalse:[
	self error:'invalid selector'. 
	^ false
    ].
    newMethod isNil ifTrue:[
	self error:'invalid method'. 
	^ false
    ].

    index := selectorArray identityIndexOf:newSelector startingAt:1.
    (index == 0) ifTrue:[
	"
	 a new selector
	"
	newSelectorArray := selectorArray copyWith:newSelector.
	newMethodArray := methodArray copyWith:newMethod.
	"
	 keep a reference so they wont go away ...
	 mhmh: this is no longer needed - try without
	"
	oldSelectorArray := selectorArray.
	oldMethodArray := methodArray.
	selectorArray := newSelectorArray.
	methodArray := newMethodArray
    ] ifFalse:[
	methodArray at:index put:newMethod
    ].
    ^ true
! !

!Behavior methodsFor:'compiler interface'!

compiler
    "return the compiler to use for this class.
     This is the old interface, kept for migration. Dont use it."

    ^ self CompilerClass
!

compilerClass
    "return the compiler to use for this class - 
     this can be redefined in special classes, to get classes with
     Lisp, Prolog, ASN1, Basic :-) or whatever syntax."

    ^ Compiler
! !

!Behavior methodsFor:'enumerating'!

allInstancesDo:aBlock
    "evaluate aBlock for all of my instances"

    ObjectMemory allObjectsDo:[:anObject |
	(anObject class == self) ifTrue:[
	    aBlock value:anObject
	]
    ]

    "
     StandardSystemView allInstancesDo:[:v | Transcript showCr:(v name)]
    "
!

allDerivedInstancesDo:aBlock
    "evaluate aBlock for all of my instances and all instances of subclasses"

    ObjectMemory allObjectsDo:[:anObject |
	(anObject isKindOf:self) ifTrue:[
	    aBlock value:anObject
	]
    ]

    "
     StandardSystemView allDerivedInstancesDo:[:v | Transcript showCr:(v name)]
    "
!

subclassesDo:aBlock
    "evaluate the argument, aBlock for all immediate subclasses.
     This will only enumerate globally known classes - for anonymous
     behaviors, you have to walk over all instances of Behavior."

    Smalltalk allBehaviorsDo:[:aClass |
	(aClass superclass == self) ifTrue:[
	    aBlock value:aClass
	]
    ]

    "
     Collection subclassesDo:[:c | Transcript showCr:(c name)]
    "
!

allSubclassesDo:aBlock
    "evaluate aBlock for all of my subclasses.
     There is no specific order, in which the entries are enumerated.
     This will only enumerate globally known classes - for anonymous
     behaviors, you have to walk over all instances of Behavior."

    Smalltalk allBehaviorsDo:[:aClass |
	(aClass isSubclassOf:self) ifTrue:[
	    aBlock value:aClass
	]
    ]

    "
     Collection allSubclassesDo:[:c | Transcript showCr:(c name)]
    "
!

allSubclassesInOrderDo:aBlock
    "evaluate aBlock for all of my subclasses.
     Higher level subclasses will be enumerated before the deeper ones,
     so the order in which aBlock gets called is ok to fileOut classes in
     correct order for later fileIn.
     This will only enumerate globally known classes - for anonymous
     behaviors, you have to walk over all instances of Behavior"

    self subclassesDo:[:aClass |
	aBlock value:aClass.
	aClass allSubclassesInOrderDo:aBlock
    ]

    "
     Collection allSubclassesInOrderDo:[:c | Transcript showCr:(c name)]
    "
!

allSuperclassesDo:aBlock
    "evaluate aBlock for all of my superclasses"

    |theClass|

    theClass := superclass.
    [theClass notNil] whileTrue:[
	aBlock value:theClass.
	theClass := theClass superclass
    ]

    "
     String allSuperclassesDo:[:c | Transcript showCr:(c name)]
    "
! !

!Behavior methodsFor:'binary storage'!

readBinaryFrom:aStream
    "read an objects binary representation from the argument,
     aStream and return it. 
     The read object must be a kind of myself, otherwise an error is raised. 
     To get any object, use 'Object readBinaryFrom:...',
     To get any number, use 'Number readBinaryFrom:...' and so on.
     This is the reverse operation to 'storeBinaryOn:'. "

    ^ self readBinaryFrom:aStream onError:[self error:('expected ' , self name)]

    "
     |s|
     s := WriteStream on:(ByteArray new).
     #(1 2 3 4) storeBinaryOn:s.
     Object readBinaryFrom:(ReadStream on:s contents)  
    "
    "
     |s|
     s := 'testFile' asFilename writeStream binary.
     #(1 2 3 4) storeBinaryOn:s.
     'hello world' storeBinaryOn:s.
     s close.

     s := 'testFile' asFilename readStream binary.
     Transcript showCr:(Object readBinaryFrom:s).
     Transcript showCr:(Object readBinaryFrom:s).
     s close.
    "
!

readBinaryFrom:aStream onError:exceptionBlock
    "read an objects binary representation from the argument,
     aStream and return it. 
     The read object must be a kind of myself, otherwise the value of
     the exceptionBlock is returned.
     To get any object, use 'Object readBinaryFrom:...',
     To get any number, use 'Number readBinaryFrom:...' and so on.
     This is the reverse operation to 'storeBinaryOn:'. "

    |newObject|

    newObject := (BinaryInputManager new:1024) readFrom:aStream.
    (newObject isKindOf:self) ifFalse:[^ exceptionBlock value].
    ^ newObject

    "
     |s|
     s := WriteStream on:(ByteArray new).
     #(1 2 3 4) storeBinaryOn:s.
     Object readBinaryFrom:(ReadStream on:s contents) onError:['oops'] 
    "
    "
     |s|
     s := WriteStream on:(ByteArray new).
     #[1 2 3 4] storeBinaryOn:s.
     Array readBinaryFrom:(ReadStream on:s contents)  onError:['oops']  
    "
!

storeBinaryDefinitionOn: stream manager: manager
    "classes will store the name only and restore by looking for
     that name in the Smalltalk dictionary."

    | myName |

    myName := self name.
    stream nextNumber:4 put:self signature.
    stream nextNumber:2 put:0.
    stream nextNumber:2 put:myName size.
    myName do:[:c| 
	stream nextPut:c asciiValue
    ]

    "
     |s|
     s := WriteStream on:ByteArray new.
     #(1 2 3 4) storeBinaryOn:s.
     Object readBinaryFrom:(ReadStream on:s contents)  

     |s|
     s := WriteStream on:ByteArray new.
     Rectangle storeBinaryOn:s.
     Object readBinaryFrom:(ReadStream on:s contents)  
    "
!

binaryDefinitionFrom:stream manager:manager
    "read the definition on an empty instance (of my class) from stream.
     All pointer instances are left nil, while all bits are read in here.
     return the new object."

    |obj t
     basicSize "{ Class: SmallInteger }" |

    self isPointers ifTrue: [
	stream next. "skip instSize"
	self isVariable ifTrue: [
	    ^ self basicNew:(stream nextNumber:3)
	].
	^ self basicNew
    ].

    basicSize := stream nextNumber:4.
    obj := self basicNew:basicSize.

    self isBytes ifTrue: [
	stream nextBytes:basicSize into:obj
"
	1 to:basicSize do:[:i |
	    obj basicAt:i put:stream next
	]
"
    ] ifFalse: [
	self isWords ifTrue: [
	    1 to:basicSize do:[:i |
		obj basicAt:i put:(stream nextNumber:2)
	    ]
	] ifFalse:[
	    self isLongs ifTrue: [
		1 to:basicSize do:[:i |
		    obj basicAt:i put:(stream nextNumber:4)
		]
	    ] ifFalse:[
		self isFloats ifTrue: [
		    "could do it in one big read on machines which use IEEE floats ..."
		    1 to:basicSize do:[:i |
			t := Float basicNew.
			Float readBinaryIEEESingleFrom:stream into:t.
			obj basicAt:i put: t
		    ]
		] ifFalse:[
		    self isDoubles ifTrue: [
			"could do it in one big read on machines which use IEEE doubles ..."
			1 to:basicSize do:[:i |
			    t := Float basicNew.
			    Float readBinaryIEEEDoubleFrom:stream into:t.
			    obj basicAt:i put: t
			]
		    ]
		]
	    ]
	]
    ].
    ^obj
! !