An object is a piece of memory, like one of the data structures
struct...end-struct. It has a field
object-map that points to the method map for the object’s
The method map34 is an array that contains the execution tokens (xts) of the methods for the object’s class. Each selector contains an offset into a method map.
selector is a defining word that uses
DOES>. The body of the
selector contains the offset; the
DOES> action for a
class selector is, basically:
( object addr ) @ over object-map @ + @ execute
object-map is the first field of the object, it
does not generate any code. As you can see, calling a selector has a
small, constant cost.
A class is basically a
struct combined with a method
map. During the class definition the alignment and size of the class
are passed on the stack, just as with
field can also be used for defining class
fields. However, passing more items on the stack would be
class builds a data structure in memory,
which is accessed through the variable
current-interface. After its definition is complete, the
class is represented on the stack by a pointer (e.g., as parameter for
a child class definition).
A new class starts off with the alignment and size of its parent,
and a copy of the parent’s method map. Defining new fields extends the
size and alignment; likewise, defining new selectors extends the
overrides just stores a new xt in the method
map at the offset given by the selector.
Class binding just gets the xt at the offset given by the selector
from the class’s method map and
compile,s (in the case of
this as a
value. At the
start of an
m:...;m method the old
stored to the return stack and restored at the end; and the object on
the TOS is stored
TO this. This technique has one
disadvantage: If the user does not leave the method via
;m, but via
this is not restored (and
crash). To deal with the
throw problem, I have redefined
catch to save and restore
this; the same
should be done with any word that can catch an exception. As for
exit, I simply forbid it (as a replacement, there is
inst-var is just the same as
@ this +
Each class also has a word list that contains the words defined with
inst-value, and its protected
words. It also has a pointer to its parent.
the word lists of the class and all its ancestors onto the search order stack,
end-class drops them.
An interface is like a class without fields, parent and protected words; i.e., it just has a method map. If a class implements an interface, its method map contains a pointer to the method map of the interface. The positive offsets in the map are reserved for class methods, therefore interface map pointers have negative offsets. Interfaces have offsets that are unique throughout the system, unlike class selectors, whose offsets are only unique for the classes where the selector is available (invokable).
This structure means that interface selectors have to perform one
indirection more than class selectors to find their method. Their body
contains the interface map pointer offset in the class method map, and
the method offset in the interface method map. The
does> action for an interface selector is, basically:
( object selector-body ) 2dup selector-interface @ ( object selector-body object interface-offset ) swap object-map @ + @ ( object selector-body map ) swap selector-offset @ + @ execute
first fields and generate no code.
As a concrete example, consider the following code:
interface selector if1sel1 selector if1sel2 end-interface if1 object class if1 implementation selector cl1sel1 cell% inst-var cl1iv1 ' m1 overrides construct ' m2 overrides if1sel1 ' m3 overrides if1sel2 ' m4 overrides cl1sel2 end-class cl1 create obj1 object dict-new drop create obj2 cl1 dict-new drop
The data structure created by this code (including the data structure
object) is shown in the
figure, assuming a cell size of 4.
This is Self terminology; in C++ terminology: virtual function table.