Classes 1
Contents
- 1 Basic Data Structure
- 1.1 About this chapter
- 1.2 Using the basic data structures
- 1.3 Using Class Object and Bytes
- 1.4 Using the scalar classes
- 1.5 Using the array classes
- 1.6 Using Collections
- 1.7 Persistent objects
- 1.8 Classes
- 1.8.1 Object
- 1.8.2 Longword
- 1.8.3 Var
- 1.8.4 Int, Uint
- 1.8.5 Byte, Ubyte
- 1.8.6 Bool
- 1.8.7 Handle
- 1.8.8 ObjHandle
- 1.8.9 Ptr
- 1.8.10 DicAddr
- 1.8.11 X-Addr
- 1.8.12 Indexed-Obj
- 1.8.13 Basic array classes - bArray, wArray, Array
- 1.8.14 X-Array
- 1.8.15 Obj_Array
- 1.8.16 Large_Obj_Array
- 1.8.17 (Col), Ordered-Col, wordCol, byteCol
- 1.8.18 X-Col
- 1.8.19 Sequence
- 1.8.20 HandleList
- 1.8.21 PtrList
- 1.8.22 Dic-Mark
- 1.8.23 Resource
Basic Data Structure
About this chapter
This chapter describes the Mops classes and words providing you with the fundamental structures that are necessary for programming in Mops. Most of these correspond to well established data structures that are available in most programming languages, but a few of them are unique to Mops.
Inside Macintosh |
Memory Manager |
Programming in Assembly Language |
Class | qpClass |
Struct | pStruct |
Using the basic data structures
This chapter will discuss the primitive classes that Mops provides as building blocks out of which you can assemble the data structures necessary to build your application. These classes are useful both as instance variables of more complex classes and as general classes from which you can derive more specialized subclasses. The classes that will be covered here include:
Object | DicAddr | Ordered-Col |
Bytes | X-Addr | WordCol |
Int | Indexed-Obj | ByteCol |
Uint | bArray | X-Col |
Longword | wArray | Sequence |
Var | Array | HandleList |
Handle | X-Array | Dic-Mark |
ObjHandle | Obj_array | Resource |
Ptr | (Col) |
Using Class Object and Bytes
The root of all classes is class Object. It has no data, but does have a set of behaviors that are generally applicable to any object, regardless of its format. A class that has no particular inheritance path should make Object its superclass, which will cause it to inherit the general properties that all objects should have. Addr: returns the base address of an object. (However you can normally just name an object without sending it an explicit message, and this has the same effect of causing its address to be pushed. The only exception is the Large_obj_array class.) Other methods in Object provide a hex dump of an object's data and access to an object's class pointer.
Bytes is not really a class, but rather is a Mops word that enables you to allocate a certain number of bytes as an instance variable within a class definition. Bytes is chiefly useful when mapping parts of Toolbox data structures that only need to be allocated but not accessed. Bytes actually creates an ivar of class Object, so you can use Object's methods, such as Addr:, on an ivar created with Bytes. As an example, class Window uses Bytes to allocate portions of the window record that Mops doesn't need direct access to. Remember, however, that Bytes is not an indexed type like barray—it's an Object. If you send it a length: message you'll always get zero, which is the length of Object—this surely won't be what you want!
Using the scalar classes
Scalar classes represent non-indexed objects which hold simple integer or pointer data. A Byte, Int or Var can hold an 8,16, or 32-bit signed integer respectively.
Using the array classes
There are three basic array classes in Mops - bArray, wArray and Array, having 1, 2 and 4-byte indexed cells. We have defined a basic set of array methods that are shared by these classes, and must be redefined if you create array classes with different indexed widths. Most array messages require that an index be on the stack that reflects which cell of the array the operation refers to (indexes begin with 0).
We have defined a generic superclass for all arrays, called Indexed-Obj. This class defines some general methods which are independent of the indexed width. These are ^Elem:, which returns the address of an indexed cell of any width, using a runtime lookup, Limit:, which tells you the maximum number of elements allocated to an object; Width:, which tells you the width of an object's indexed cells; ClearX:, which sets all of an array's cells to 0; and IXAddr:, which leaves the address of the 0th indexed cell.
There is also a group of methods that must be redefined for each array class having a different width. These include: At:, which fetches the contents of the cell at an index; To: which stores to the indexed cell at an index, +To:, which increments an indexed cell by a value; -To:, which decrements an indexed cell, and Fill:, which fills an array with a value. This group is shared by the three array classes that are predefined in Mops, and is documented later in this section. We also override ^Elem: in these three classes to give greater speed, since we know the indexed width at compile time.
Because class Array has 4-byte cells, it can be used to hold pointers to various kinds of structures in a way that the other array classes cannot.
We have defined several classes which make it easy to handle groups of objects. By multiply inheriting Obj_array with any other class, you create an array of objects of that class. You then use the select: method to make one of those objects ‘current’, and can then access the ‘current’ object exactly as if it were a normal object, i.e. not part an array at all.
Class X-Array adds to the basic Array the ability to execute one of its indexed cells, assuming that it holds the xt of a Mops word. X-Array is a very important class in Mops, because its behavior is used throughout the system itself to provide control dispatching by index, as in Menu and Event. The classinit: method in X-Array sets each indexed cell to Null so the object will behave gracefully if you fail to initialize it in your application. Use X-Array whenever you need to execute one of a group of Mops words based on a series of contiguous indices.
HandleList is an extremely useful class in Mops. It's used for sets of heap-based objects, accessed through ObjHandles. HandleList doesn't inherit from Obj_Array, but uses the same idea of a select: method to make a particular one of its ObjHandles ‘current’. Any number of objects can be in a HandleList—the only limitation is the amount of memory available.
Here's an example of how a HandleList could be used to implement a set of four windows, accessible by index:
HandleList Windows : CreateWindows 4 0 DO ['] window newObj: windows LOOP ; \ Resize window at index 2: 2 select: windows 300 100 size: [ obj: windows ]
Notice how, once we have used select: to choose which ObjHandle in the HandleList we are referring to, we can then send other methods to the HandleList exactly as if it were a single ObjHandle. We can send late-bound messages to one of the windows, as in the size: message at the bottom, by using the obj: method defined for the ObjHandle class. Actually, the objects in windows could be of any class that accepted a size: message, due to the late binding. When you are finished with Windows, you can release all its heap storage simply by sending the message
release: windows
If you look at the source for the HandleList class, you will see that release: causes each of the ObjHandles to be selected in turn, and releaseObj: sent to each one. If you now look at the source for ObjHandle, you will see that releaseObj: causes release: to be sent to the object pointed to by the handle, so that it will release any heap storage it has allocated, then finally release: super is called, which releases the heap block pointed to by the handle (that is, the object itself). Thus, by simply sending release: to a HandleList, we are releasing all the heap storage it owns. Incidentally, if you want to just release one of the handles in a HandleList, use select: followed by releaseObj:—this is the reason we have defined releaseObj: separately from release:.
Using Collections
Class Ordered-Col is another important class in Mops. It is implemented by multiply inheriting the (Col) class with one of the array classes. It adds to the array class the concept of a current length and the ability to add to and remove from the list. This list also has many of the properties of a stack, which are exploited in such classes as FileList (see Chapter III.3). When you create an Ordered-Col (O-C), you must specify, as with all indexed classes, the number of elements to allocate in the dictionary (or the heap). O-C uses this as a maximum up to which its variable-length list will grow via the Add: method. The advantage of an O-C is that you can add values to the end of the list without maintaining the index yourself, only the sequence in which to add. You might want to utilize the O-C's properties only while initializing the object, after which it is simply used as an Array. WordCol is an Ordered-Col with 16-bit cells rather than 32-bit.
Persistent objects
“Persistent Objects” are objects that can stick around after your program quits, and be accessed again the next time your program runs.
This of course means that when they're not being used, they must live in a file somewhere. In implementing persistent objects, then, we need to be able to streamline the process of writing an object out to a file and reading it back.
Now it should be clear from this, that one of the key features of persistent objects is that these objects can be serialized—that is, no matter what their structure, they can be converted to a stream of bytes suitable for writing to a file or being read back. This is why the concept of persistent objects is always linked to the problem of how to serialize an object.
Our approach to serialization is straightforward, and has two parts to it.
- We generalize the idea of a file slightly, and say that any class that supports file-style READ: and WRITE: methods, is supporting the ‘stream’ methods. This idea is really the same as an interface—we could say that READ: and WRITE: are part of the stream interface. Although Mops doesn't have a formalized interface scheme in its syntax, the idea is really the same—it's just informal in Mops.
- We assume that each object knows how to serialize itself. The serialization methods are SEND: and BRING:. These both take one parameter, the address of a stream object. SEND: is expected to send late-bound WRITE: messages to the stream, to write out the bytes of the object. The class can choose whatever format it likes, so long as the process can be reversed by the BRING: method. This method sends late-bound READ: messages to the stream, and is expected to reconstitute the object that originally received SEND:
We provide these methods in all the standard Mops classes. Class Object has the most basic implementation, and just writes and reads the local ivars and indexed area. This will work for simple objects (those that don't have other data outside these local areas).
We write the non-indexed and indexed data separately, to make these operations less sensitive to platform-related alignment questions. On the PowerPC the indexed area starts out 4-byte aligned, but only 2-byte aligned on the 68k. Of course alignment issues within the local ivars might rule out cross-platform compatibility anyway, but there will be many situations in which what we do in class Object will be quite sufficient, and in these cases no separate SEND: or BRING: will be needed.
As Handle objects have data outside the local area, class Handle needs a separate SEND: and BRING:. If you look at the source file Struct, you'll see that we transfer the length, then the bytes in the handle. There is no local ivar data except the Handle itself, which doesn't need to be saved or restored—the data stored in the Handle is what matters.
For the class ObjHandle, we could just inherit from Handle, which would mean our object gets saved as an undifferentiated string of bytes. This wouldn't be good, since we have to assume that the object knows how to send itself properly. So we send the name of the class as characters, then send SEND: to the object itself. At BRING: time, we look up the classname, create a new object, then send BRING: to it.
A nice thing about this scheme is that if the object doesn't contain any addresses, it can be reconstituted after a Mops recompilation, and often even over a platform change.
In class HandleList, we first send 2 bytes with the number of items, then SELECT: each item and send SEND: to it. We use a special 2-byte marker between each of these objects we send to the stream, so that BRING: can check that we haven't got out of sync.
These implementations of SEND: and BRING: in the basic Mops classes may well cover the majority of your needs. If your classes need something extra, you should have enough ideas there to be able to work out what to do.
There is a final step to the implementation of persistent objects. Once an object implements SEND: and BRING: correctly, we have to link it to a file. We have provided a file ‘Container’ in the “More Classes” folder which shows how this might be done. Class Container is a subclass of File, and has an INIT: method in which you pass in the address of an object. Then when you pass OPEN: and SAVE: messages to the Container, the Container sends BRING: and SEND: messages respectively, to the object.
The fact that we only link one object to the Container doesn't mean that the number of objects in the Container is in any way limited, since the object can be a HandleList which can be a collection of an arbitrary number of objects from arbitary classes. We've seen already that HandleList implements SEND: and BRING: properly (in a way that accounts for the objects in the list being of arbitrary classes).
So, after you have sent OPEN: to a Container, all the objects in the container will have been read into memory. And when you send SAVE:, they are all written out to the file. If you don't want all your objects to be in memory at the same time, you can use several containers.
This scheme is very flexible, and will probably be sufficient for most needs.
Classes
Object
Object contains behavior appropriate to all objects in the system. Every superclass chain ultimately traces back to Object.
Superclass | Meta |
---|---|
Source file | Class, qpClass |
Status | Core |
Instance variables | None |
Indexed data | None |
System objects | None |
class: | ( -- addr ) | Returns a pointer to the object's class |
---|---|---|
.id | ( -- ) | Types the object's name |
.class | ( -- ) | Types the name of the object's class |
addr: | ( -- addr ) | Returns the base address of an object's data |
length: | ( -- #bytes ) | Returns the length of the object's ivar data area |
copyto: | ( ^obj -- ) | ^obj is a pointer to another object. This method copies that object's ivar data to this object. Be careful using this method as no check is done that the objects are of the same class. However this method can be very useful in some situations |
classinit: | ( -- ) | This a very special method.
Whenever an object is created, Mops sends it a classinit: message so that it will initialize itself to reasonable values, or whatever the programmer desires all objects of that class to do when created. This method corresponds to a constructor method in C++. In class Object, it is a do-nothing method, allowing any subclass to override it as appropriate. By convention, init: is used for explicit programmatic initialization and customization thereafter,and new: is used to set up the toolbox-interface portion of toolbox objects (such as making a window known to the Macintosh window manager) |
release: | ( -- ) | This method does nothing in class Object itself. However, in general you should send release: to an object before you FORGET it or deallocate its memory. release: will cause an object to release any heap memory it has allocated and do any other cleaning up which may be necessary. This method corresponds to a destructor method in C++ |
dump: | ( -- addr ) | Dumps the dictionary entry for the object in a hex format |
print: | ( -- addr ) | Dumps the dictionary entry for the object in a hex format. This provides a default print: method for objects that don't have a more sophisticated form of displaying their data |
persistence/serialization | ||
send: | ( ^obj -- ) | Sends a write: message to the passed-in object, to write out this object's data as a stream of bytes |
bring: | ( ^obj -- ) | Sends a read: message to the passed-in object, to read in this object's data as a stream of bytes |
Error messages - None
Longword
Longword provides storage for 32-bit quantities. It is not intended to be used directly, but is a generic superclass for Var , Handle, Ptr and DicAddr.
Superclass | Object | ||||||
---|---|---|---|---|---|---|---|
Source file | Struct | ||||||
Status | Core | ||||||
Instance variables | |||||||
| |||||||
Indexed data | None | ||||||
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
get: | ( -- val ) | Returns the value in the data area as a signed number |
put: | ( val -- ) | Stores a new value in the data area |
->: | ( ^longword -- ) | Copies the passed-in Longword's data to this Longword |
clear: | ( -- ) | Stores 0 in the data area |
initialization | ||
classinit: | ( -- ) | Calls clear: |
display | ||
print: | ( -- ) | Prints the data in the current number base on the screen |
Error messages - None
Var
Var provides storage for 32-bit numeric quantities
Superclass | Longword |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None (see Longword) |
Indexed data | None |
System objects | None |
Inherits: | Longword, Object |
---|
accessing | ||
---|---|---|
+: | ( val -- ) | Adds val to the contents of the Var's data area |
-: | ( val -- ) | Subtracts val from the contents of the Var's data area |
Error messages - None
Int, Uint
Provides storage for 16-bit quantities—signed (Int) and unsigned (Uint ).
Superclass | Object | ||||||
---|---|---|---|---|---|---|---|
Source file | Struct, pStruct | ||||||
Status | Core | ||||||
Instance variables | |||||||
| |||||||
Indexed data | None | ||||||
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
get: | ( -- val ) | Returns the value in the data area. For class Int, this is a signed number—if bit 15 is on, this bit will be extended into the high-order 16 bits of the stack cell. For class Uint, the number is unsigned—the high-order 16 bits of the stack cell will be set to zero |
put: | ( val -- ) | Stores a new value in the data area |
->: | ( ^int -- ) | Copies the passed-in Int/Uint's value to this Int/Uint |
clear: | ( -- ) | Stores 0 in the data area |
int: | ( -- int ) | Returns the value in the data area as a 16-bit stack cell. This is useful for Toolbox calls that require parameters of type Int |
+: | ( val -- ) | Adds val to the contents of the Int/Uint's data area |
-: | ( val -- ) | Subtracts val from the contents of the Int/Uint's data area |
display | ||
print: | ( -- ) | Types the data in the current base on the screen |
Error messages - None
Byte, Ubyte
Provides storage for 8-bit quantities—signed (Byte) and unsigned (Ubyte).
Superclass | Object | ||||||
---|---|---|---|---|---|---|---|
Source file | Struct | ||||||
Status | Core | ||||||
Instance variables | |||||||
| |||||||
Indexed data | None | ||||||
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
get: | ( -- val ) | Returns the value in the data area. For class Byte, this is a signed number—if bit 7 is on, this bit will be extended into the high-order 24 bits of the stack cell. For class Ubyte, the number is un-signed—the high-order 24 bits of the stack cell will be set to zero |
put: | ( val -- ) | Stores a new value in the data area |
->: | ( ^int -- ) | Copies the passed-in Byte/Ubyte's value to this Byte/Ubyte |
clear: | ( -- ) | Stores 0 in the data area |
display | ||
print: | ( -- ) | Types the data in the current base on the screen |
Error messages - None
Bool
Provides storage for boolean values (true or false). These are stored in 8 bits.
Superclass | Byte |
---|---|
Source file | Struct, pStruct |
Status | Core |
Instance variables | None (see Byte) |
Indexed data | None |
System objects | None |
Inherits: | Byte, Object |
---|
accessing | ||
---|---|---|
put: | ( val -- ) | Stores a new value in the data byte. If val is nonzero, a ‘proper’ true is stored (all ones). If val is zero, false (zero) is stored. Thus val isn't just copied, but is converted to a ‘proper’ boolean value |
get: | ( -- true | -- false ) | This method is inherited from Byte, but it is worth mentioning here that since it sign-extends, and we have either all zeros or all ones in the data byte, get: returns a ‘proper’ boolean flag value on the stack (all zeros or all ones) |
set: | ( -- ) | Sets the data byte to true (all ones) |
clear: | ( -- ) | Sets the data byte to false (zero) |
display | ||
print: | ( -- ) | Types the data as ‘true’ or ‘false’ |
Error messages - None
Handle
Handle adds to Longword methods useful for manipulating relocatable blocks of heap.
Superclass | Var |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None (see Longword) |
Indexed data | None |
System objects | None |
Inherits: | Var, Longword, Object |
---|
accessing | ||
---|---|---|
ptr: | ( -- ptr ) | Returns a dereferenced pointer from the handle |
nptr: | ( -- ptr ) | Returns a dereferenced pointer from the handle, and masks out any flag bits. If your Mac is running in 24-bit mode, handles may have flag information in the high-order byte. nptr: zeros these bits, so that you may compare or do arithmetic on the resulting pointer without problems. But if you just want to access the data via the pointer, use ptr: which is slightly faster |
manipulation | ||
clear: | ( -- ) | Stores nilH in the handle |
setSize: | ( len -- ) | Sets a new size for the heap block corresponding to the handle |
size: | ( -- len ) | Returns the current size of the handle |
new: | ( #bytes -- ) | Allocates a block of relocatable heap via the Memory Manager, and stores the handle in this object's data |
release: | ( -- ) | Releases the heap block pointed to by the handle and stores nilH in the handle. Does nothing if the handle already contains nilH |
lock: | ( -- ) | Locks the block corresponding to the handle |
unlock: | ( -- ) | Unlocks the block corresponding to the handle. Does nothing if the handle contains nilH |
locked?: | ( -- b ) | Returns a boolean; true if the block is locked |
getState: | ( -- n ) | Returns the state of the handle, via the system call _HGetState. Prior to locking a handle, it is best to get the state of the handle, perform the operation that needed the handle locked, then reset the state of the handle with setState: |
setState: | ( n -- ) | Sets the state of the handle |
moveHi: | ( -- ) | Calls the system to move the heap block high in memory. It is generally a good idea to do this prior to locking the handle, to minimize heap fragmentation, unless you are going to unlock the handle again very quickly |
->: | ( ^handle -- ) | Copies the heap data pointed to by the passed-in handle to this handle's heap block, and sets the size of this handle's block appropriately. Error message if the passed-in address is not that of an object of exactly the same class as this one |
Error messages - “Set handle size failed”
Non-0 return from memory manager on a SetHSize system call, probably resulting from a setSize: or ->: call with insufficient memory available.
ObjHandle
ObjHandle adds to Handle methods for manipulating heap-based objects.
Superclass | Handle |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None |
Indexed data | None |
System objects | Many and various |
Inherits: | Handle, Var, Longword, Object |
---|
accessing | ||
---|---|---|
obj: | ( -- ^obj ) | Moves the heap block high, locks the handle and returns a pointer to the addressed object. You should normally unlock: when you're through operating on the object |
manipulation | ||
newObj: | ( [#elts] ^class -- ) | Creates a new object of the given class on the heap and sets the handle |
releaseObj: | ( -- ) | Releases the handle, first sending release: to the object it points to. Does nothing if the handle is nil |
display | ||
print: | ( -- ) | Types both the handle and the object it points to, in the latter case sending a late-bound print: to the object |
dump: | ( -- ) | Likewise dumps both the handle and the object |
persistence/serialization | ||
send: | ( ^obj -- ) | An extended version of send: in class Object. Includes sending the name of the class of the heap object, and sending send: to that object itself |
bring: | ( ^obj -- ) | An extended version of bring: in class Object. Includes recreating the heap object with its correct class, and sending bring: to that object itself |
Error messages - None
Ptr
Ptr adds to Longword methods useful for manipulating non-relocatable blocks of heap. Note: it is normally better to use Handles rather than Ptrs, to avoid the heap becoming fragmented with blocks which cannot be moved.
Superclass | Longword |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None (see Longword) |
Indexed data | None |
System objects | None |
Inherits: | Longword, Object |
---|
manipulation | ||
---|---|---|
new: | ( len -- ) | Allocates a block of non-relocatable heap (len bytes), and stores the pointer in the object's data |
release: | ( -- ) | Deallocates the memory pointed to by the Ptr, and stores nilP in the Ptr. Does nothing if the Ptr already contains nilP |
clear: | ( -- ) | Stores nilP in the Ptr |
nil?: | ( -- b ) | Returns True if the Ptr contains nilP |
Error messages - “new: on a pointer couldn't get enough heap”
DicAddr
Dicaddr is used for storing the address of a location within the dictionary. If the dictionary is saved and reloaded in a subsequent run, the address will still be valid. This is accomplished by storing the address in a relocatable format. Don't depend on details of this format, in case it changes.
Superclass | Longword |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None (see Longword) |
Indexed data | None |
System objects | None |
Inherits: | Longword, Object |
---|
accessing | ||
---|---|---|
get: | ( -- addr ) | Overrides get: in Longword. Fetches the object's data (a relocatable address), converts it to absolute and returns it |
put: | ( addr -- ) | Stores the passed-in address in the object's data, using our relocatable format |
print: | ( -- ) | Types the word name corresponding to the stored address, or (no name) if the address isn't the address of a Mops word |
Error messages - “you can't store a module address outside the module”
You attempted to put: the address of a location in a module, into a DicAddr located outside the module. This is illegal, since the module may have moved or been purged from memory when the DicAddr is next accessed.
X-Addr
An X-Addr is almost the same as a DicAddr. The only difference is that it is intended for dictionary addresses which are the execution tokens of Mops words, and so may be executed. (Note that in Mops an execution token is an address of a word, whereas in other Forth systems it may not be an actual address.) Thus we again use our relocatable format. The only difference to a DicAddr is that there is an exec: method, and no get: method.
Superclass | Longword |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None (see Longword) |
Indexed data | None |
System objects | None |
Inherits: | Longword, Object |
---|
accessing | ||
exec: | ( -- various ) | Executes the word whose xt has been stored in the X-Addr |
---|---|---|
put: | ( xt -- ) | Stores the xt in the object's data, using our relocatable format |
Error messages - “you can't store a module address outside the module”
See DicAddr.
Indexed-Obj
This class is the generic superclass for all arrays. It defines the general indexed methods, which apply regardless of indexed width.
Superclass | Object |
---|---|
Source file | Struct,Struct1 |
Status | Core |
Instance variables | None |
Indexed data | None (supplied by subclasses) |
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
^elem: | ( index -- addr ) | Returns the address for the element at index |
limit: | ( -- maxIndex+1 ) | Returns the allocated size of an indexed object. The maximum usable index for an indexed object is this value minus 1 |
width: | ( -- #bytes ) | Returns the width of each indexed element |
ixAddr: | ( -- addr ) | Returns the relative address for the 0th element |
manipulation | ||
clearX: | ( -- ) | Sets each indexed element to 0 |
Error messages - “Index or value out of range”
One of the methods taking an index found the index to be out of range for this array.
Basic array classes - bArray, wArray, Array
These basic access methods are implemented for the three array classes predefined in Mops.
Superclass | Indexed-Obj |
---|---|
Source file | Struct, Struct1 |
Status | Core |
Instance variables | None |
Indexed data | 1, 2, 4-byte cells |
System objects | None |
Inherits: | Indexed-Obj, Object |
---|
accessing | ||
---|---|---|
at: | ( index -- val ) | Returns the data at a given indexed cell |
to: | ( val index -- ) | Stores data at a given indexed cell |
+to: | ( increment index -- ) | Increments data at a given indexed cell |
-to: | ( decrement index -- ) | Decrements data at a given indexed cell |
fill: | ( val -- ) | Stores val in each cell of the array |
Error messages - “Index or value out of range”
As for Indexed-Obj.
X-Array
X-Array is an Array with the ability to execute its indexed data as xts of Mops words.
Superclass | Array |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None |
Indexed data | 4-byte cells |
System objects | ??? |
Inherits: | Array, Indexed-Obj, Object |
---|
accessing | ||
---|---|---|
exec: | ( ind -- ) | Executes the xt in the indexed cell at ind |
put: | ( xt0 ... xt(N-1) N -- ) | Stores the N xts into the indexed elements of this object. xt0 goes into element 0, xt1 into element 1, and so on |
actions: | ( xt0 ... xt(N-1) N -- ) | A synonym for put:. A more appropriate name to use in subclasses such as dialogs |
display | ||
print: | ( -- ) | Types the name of the word whose xt is in each element |
initialization | ||
classinit: | ( -- ) | Sets all indexed elements to the null xt |
Error messages - “Wrong number of xts in list”
For put:, the value N did not match the number of indexed elements for this object.
Obj_Array
This class is a generic superclass which makes it easy to generate an array of objects of a given class. Just define a new class which multiply Inherits: from the given class (or classes) and Obj_array (which must come last). This will add an indexed section to each object of the new class, with elements wide enough to contain objects of the original class(es). Then select: “switches in” the selected element to be the ‘current’ element, and all the normal methods of the class(es) can then be used. If your base class is long (longer than about 32 bytes), you'll probably get better performance by using the variant Large_Obj_Array (see below).
Superclass | Object | ||||||
---|---|---|---|---|---|---|---|
Source file | Struct | ||||||
Status | Core | ||||||
Instance variables | |||||||
| |||||||
Indexed data | Any width—the actual width is determined by the other class(es) | ||||||
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
select: | ( index -- ) | Makes the indexed element current |
current: | ( -- index ) | Returns the index of the current element |
Error messages - “Index or value out of range”
An out-of-range index value was used for select:.
Large_Obj_Array
This is a variant of Obj_array which is intended to behave identically. It's a performance optimization for Obj_arrays whose elements are large—longer than 32 bytes or so.
Sending select: to an Obj_array selects an element which will receive subsequent messages. This works by actually copying the element out of the indexed area of the object, into the non-indexed part at the front. This works well for small elements, since selection is quite quick, and sending messages to the selected element is identical in every way to sending a message to a simple object. Where performance can start to bog down, however, is if the elements are very large—in the PowerPC code generator I was using Obj_array for the sets of descriptors describing each machine register, and each descriptor is over 100 bytes long. I discovered that the code generator was spending half its time in select:
, moving all those bytes around.
So this led to Large_obj_array. This class doesn't move any of the elements in the indexed area, but keeps an offset which points to the currently selected element. Whenever you send a message to one of these objects, the offset is added. This step only takes two machine instructions, so operations on these objects are only very slightly slower. But select: is very fast.
You should be able to use Large_obj_array anywhere you're using Obj_array, without changing any other code.
Warning: There is one ‘gotcha’—if you just want the address of the currently selected element, you have to use the addr: method—you can't just use the object's name, as you normally can with objects. If you just use the name, you'll get the address of the start of the object, not the currently selected element.
(Col), Ordered-Col, wordCol, byteCol
Collections are ordered lists with a current size, that can also behave like a stack. We implement them by multiply inheriting the generic (Col) class with an array class of the appropriate width. (Col) adds the concept of a current size to the array methods.
Note: class Ordered-Col, wordCol and ByteCol are 32, 16 and 8 bit collections respectively. All methods are identical to (Col)
Superclass | Object | ||||||
---|---|---|---|---|---|---|---|
Source file | Struct | ||||||
Status | Core | ||||||
Instance variables | |||||||
| |||||||
Indexed data | None (supplied by the array class) | ||||||
System objects | None |
Inherits: | Object |
---|
accessing | ||
---|---|---|
size: | ( -- #elements ) | Returns the number of elements currently held in the list. This must always be less than or equal to limit: |
add: | ( val -- ) | Appends value in the next available cell, and increments size by 1. An error occurs if size=limit before the operation (list full) |
last: | ( -- val ) | Fetches the contents of the cell last added to the list. Error if list is empty |
remove: | ( ind -- ) | Deletes the element at ind from the list, and reduces size by 1. Error if the list is empty |
clear: | ( -- ) | Sets list to empty |
indexOf: | ( val -- ind t OR -- f ) | Searches for val within the current list, and returns the index and a True boolean if it was found, and False boolean if not found |
Error messages | |
---|---|
“My list is empty” | A remove: or last: was attempted on an empty list. |
“My list is full” | An add: was attempted with size=limit. |
X-Col
This class is a collection of execution tokens. It adds one new method, and overrides one method of X-Array.
Superclass | (Col) X-Array |
---|---|
Source file | Struct |
Status | Core |
Instance variables | None |
Indexed data | None (supplied by the X-Array) |
System objects | None |
Inherits: | (Col), X-Array, Array, Indexed-Obj, Object |
---|
removeXt: | ( xt -- ) | Removes the xt equal to the passed-in xt Does nothing if no match is found |
---|---|---|
print: | ( -- ) | As for print: in class X-Array, but only types the xt names that are actually in the collection |
Error messages - As for (Col).
Sequence
Sequence is a generic superclass for classes which have multiple items which frequently need to be looked at in sequence. At present the main function of Sequence is to implement the each: method, which makes it very simple to deal with each element. The usage is
BEGIN each: <obj> WHILE <do something to the element> REPEAT
Sequence can be multiply inherited with any class which implements the first?: and next: methods. The actual implementation details are quite irrelevant, as long as these methods are supported.
Superclass | Object | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
Source file | Struct | |||||||||
Status | Core | |||||||||
Instance variables | ||||||||||
| ||||||||||
Indexed data | None | |||||||||
System objects | None |
Inherits: | Object |
---|
each: | ( (varies) -- true OR -- false ) | Initiates processing of a sequence as in the example above |
---|---|---|
uneach: | ( -- ) | Terminates processing of a sequence before the normal end. Use prior to an EXIT out of an each: loop |
Error messages - None
HandleList
HandleList allows the implementation of a list of heap-based objects. The list can be of indefinite length. We use a heap block to store the handles to the objects contiguously, rather than have a separate block for each handle and link them together. This saves on memory overhead and reduces the number of Memory Manager calls. It also reflects the assumption that insertions and deletions into the middle of the list will be infrequent, as these could be more inefficient than with a linked scheme. We expect that elements will normally be added to the end, and probably not removed at all, or not very often.
Superclass | ObjHandle Sequence | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Source file | Struct | ||||||||||||
Status | Core | ||||||||||||
Instance variables | |||||||||||||
| |||||||||||||
Indexed data | None | ||||||||||||
System objects | None |
Inherits: | ObjHandle, Sequence, Handle, Var, Longword, Object |
---|
accessing | ||
---|---|---|
select: | ( index -- ) | Makes the indexed handle current |
selectLast: | ( -- ) | Makes the last indexed handle current |
current: | ( -- index ) | Returns the index of the current handle |
size: | ( -- size ) | Returns the number of handles in the list. (This is in fact the ivar Size divided by 4) |
setSize: | ( size -- ) | Sets the current size |
The next two methods are needed by each:, but may be called directly as well. Note that next: ASSUMES that the list is allocated in the heap and that a valid element is selected as the current element. each: ensures this, since if first?: returns false, next?: is never called. But if you call it directly, make sure this condition holds. | ||
first?: | ( -- b ) | If the list isn't empty, makes the first handle current and returns True. If the list is empty, returns False |
next?: | ( -- ) | If the current handle isn't the last one, makes the next handle current and returns True. If the current handle is the last one, returns False |
manipulation | ||
newObj: | ( ^class -- ) | Creates a new object of the passed-in class on the heap, and adds its handle to the list |
releaseObj: | ( -- ) | Releases the current handle, first sending release: to the object it points to. Does nothing if the handle is nil |
removeObj: | ( -- ) | Releases the current handle as in releaseObj:, and removes it altogether from the list |
release: | ( -- ) | Releases the whole list. Every handle is released as in releaseObj:, and the whole block containing the handles is released as well |
The next methods treat the list as a stack. This is used by fileList. In the method descriptions, we'll refer to this as the ‘stack’ (within inverted commas) to distinguish it from the data stack. | ||
top: | ( -- ) | Selects the top element of the ‘stack’ |
drop: | ( -- ) | Drops the top element of the ‘stack’, by sending removeObj: to it. Leaves the next element (which is now the top) selected |
pushNewObj: | ( ^class -- ) | As for newObj:, creates a new object of the passed-in class on the heap. Pushes its objHandle onto the ‘stack’ |
display | ||
dumpAll: | ( -- ) | Gives a dump of the whole list, including sending dump: to each of the objects |
printAll: | ( -- ) | Displays the whole list, including sending print: to each of the objects |
persistence/serialization | ||
send: | ( ^obj -- ) | Serializes the whole list, by sending send: to each object in the list |
bring: | ( ^obj -- ) | Recreates each object in the list, and sends bring: to each one, so that the entire list is reconstituted |
Error messages - None
PtrList
PtrList allows the implementation of a list of pointers which point to objects. The objects can be anywhere. Similarly to HandleList, we use a heap block to store the pointers.
Superclass | Ptr Sequence | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Source file | Struct | ||||||||||||
Status | Core | ||||||||||||
Instance variables | |||||||||||||
| |||||||||||||
Indexed data | None | ||||||||||||
System objects | None |
Inherits: | Ptr, Sequence, Longword, Object |
---|
accessing | ||
---|---|---|
select: | ( index -- ) | Makes the indexed pointer current |
selectLast: | ( -- ) | Makes the last pointer current |
current: | ( -- index ) | Returns the index of the current pointer |
size: | ( -- size ) | Returns the number of pointer in the list. (This is in fact the ivar Size divided by 4) |
The next two methods are needed by each:, but may be called directly as well. Note that next: ASSUMES that the list is allocated in the heap and that a valid element is selected as the current element. each: ensures this, since if first?: returns false, next?: is never called. But if you call it directly, make sure this condition holds | ||
first?: | ( -- b ) | If the list isn't empty, makes the first pointer current and returns True. If the list is empty, returns False |
next?: | ( -- ) | If the current pointer isn't the last one, makes the next handle current and returns True. If the current pointer is the last one, returns False |
manipulation | ||
add: | ( ptr -- ) | Adds the pointer to the end of the list |
remove: | ( -- ) | Removes the current pointer from the list |
display | ||
dumpAll: | ( -- ) | Gives a dump of the whole list, including sending dump: to the objects pointed to by all the pointers |
printAll: | ( -- ) | Displays the whole list, including sending print: to the objects pointed to by all the pointers |
Error messages - None
Dic-Mark
Dic-Mark marks a dictionary position, and includes methods for traversing the dictionary.
Superclass | Object | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
Source file | Struct | |||||||||
Status | Core | |||||||||
Instance variables | ||||||||||
| ||||||||||
Indexed data | None | |||||||||
System objects | TheMark |
Inherits: | Object |
---|
accessing | ||
---|---|---|
current: | ( -- addr ) | Returns the current position |
manipulation | ||
set: | ( addr -- ) | Sets the current position to addr (setting the array Links appropriately) |
setToTop: | ( -- ) | Sets the current position to the top of the dictionary |
next: | ( -- addr ) | Moves the current position to the preceding dictionary word, and returns the address of the link field of that word. Returns zero if we were already at the base of the dictionary |
Error messages - None
Resource
Resource implements Macintosh Resources.
Superclass | Handle | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
Source file | Struct | |||||||||
Status | Core | |||||||||
Instance variables | ||||||||||
| ||||||||||
Indexed data | None | |||||||||
System objects | Some |
Inherits: | Handle, Var, Longword, Object |
---|
accessing | ||
---|---|---|
set: | ( type ID -- ) | Stores the passed-in type and ID in this object's data |
getNew: | ( -- index ) | Accesses the resource with the current type and ID via a GetResource call, and stores the handle in this object's data |
getXstr: | ( index -- addr length ) | This resource object must have type STR# (string list). Accesses the indexed string and returns its address and length |
Error messages - “We couldn't find this resource”
A call to getnew: resulted in the Mac system not being able to locate a resource with the current type and ID. Possibly the type or ID are wrong, or the correct resource file isn't open.
Classes | Strings | |
Documentation |