1 \ ** ficl/softwords/oo.fr
2 \ ** F I C L O - O E X T E N S I O N S
3 \ ** john sadler aug 1998
5 \ $FreeBSD: src/sys/boot/ficl/softwords/oo.fr,v 1.1.6.1 2000/07/06 23:51:46 obrien Exp $
8 .( loading ficl O-O extensions ) cr
13 \ 0. Traditional OOP: late binding by default for safety.
14 \ Early binding if you ask for it.
15 \ 1. Single inheritance
16 \ 2. Object aggregation (has-a relationship)
17 \ 3. Support objects in the dictionary and as proxies for
18 \ existing structures (by reference):
19 \ *** A ficl object can wrap a C struct ***
20 \ 4. Separate name-spaces for methods - methods are
21 \ only visible in the context of a class / object
22 \ 5. Methods can be overridden, and subclasses can add methods.
23 \ No limit on number of methods.
26 \ Classes are objects, too: all classes are instances of METACLASS
27 \ All classes are derived (by convention) from OBJECT. This
28 \ base class provides a default initializer and superclass
31 \ A ficl object binds instance storage (payload) to a class.
32 \ object ( -- instance class )
33 \ All objects push their payload address and class address when
34 \ executed. All objects have this footprint:
35 \ cell 0: first payload cell
37 \ A ficl class consists of a parent class pointer, a wordlist
38 \ ID for the methods of the class, and a size for the payload
39 \ of objects created by the class. A class is an object.
40 \ The NEW method creates and initializes an instance of a class.
41 \ Classes have this footprint:
42 \ cell 0: parent class address
44 \ cell 2: size of instance's payload
46 \ Methods expect an object couple ( instance class )
48 \ Overridden methods must maintain the same stack signature as
49 \ their predecessors. Ficl has no way of enforcing this, though.
54 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
55 \ ** L A T E B I N D I N G
56 \ Compile the method name, and code to find and
57 \ execute it at run-time...
58 \ parse-method compiles the method name so that it pushes
59 \ the string base address and count at run-time.
61 : parse-method \ name run: ( -- c-addr u )
66 : lookup-method ( class c-addr u -- class xt )
71 2 pick cell+ @ ( -- class c-addr u wid )
72 search-wordlist ( -- class 0 | xt 1 | xt -1 )
74 c-addr u type ." not found in "
80 : exec-method ( instance class c-addr u -- <method-signature> )
84 : find-method-xt \ name ( class -- class xt )
85 parse-word lookup-method
89 \ Method lookup operator takes a class-addr and instance-addr
90 \ and executes the method from the class's wordlist if
91 \ interpreting. If compiling, bind late.
93 : --> ( instance class -- ??? )
95 find-method-xt execute
97 parse-method postpone exec-method
102 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
103 \ ** E A R L Y B I N D I N G
104 \ Early binding operator compiles code to execute a method
105 \ given its class at compile time. Classes are immediate,
106 \ so they leave their cell-pair on the stack when compiling.
108 \ : get-wid metaclass => .wid @ ;
110 \ my-class get-wid ( -- wid-of-my-class )
112 : => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
113 drop find-method-xt compile, drop
114 ; immediate compile-only
117 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
118 \ ** I N S T A N C E V A R I A B L E S
119 \ Instance variables (IV) are represented by words in the class's
120 \ private wordlist. Each IV word contains the offset
121 \ of the IV it represents, and runs code to add that offset
122 \ to the base address of an instance when executed.
123 \ The metaclass SUB method, defined below, leaves the address
124 \ of the new class's offset field and its initial size on the
125 \ stack for these words to update. When a class definition is
126 \ complete, END-CLASS saves the final size in the class's size
127 \ field, and restores the search order and compile wordlist to
128 \ prior state. Note that these words are hidden in their own
129 \ wordlist to prevent accidental use outside a SUB END-CLASS pair.
132 dup constant instance-vars
133 dup >search ficl-set-current
135 does> ( instance class addr[offset] -- addr[field] )
139 : addr-units: ( offset size "name" -- offset' )
144 : chars: \ ( offset nCells "name" -- offset' ) Create n char member.
147 : char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
150 : cells: ( offset nCells "name" -- offset' )
151 cells >r aligned r> addr-units:
154 : cell: ( offset nCells "name" -- offset' )
157 \ Aggregate an object into the class...
158 \ Needs the class of the instance to create
159 \ Example: object obj: m_obj
162 does> ( instance class pfa -- a-instance a-class )
163 2@ ( inst class a-class a-offset )
164 2swap drop ( a-class a-offset inst )
165 + swap ( a-inst a-class )
168 : obj: ( offset class meta "name" -- offset' )
169 locals| meta class offset |
170 create offset , class ,
171 class meta --> get-size offset +
175 \ Aggregate an array of objects into a class
177 \ 3 my-class array: my-array
178 \ Makes an instance variable array of 3 instances of my-class
181 : array: ( offset n class meta "name" -- offset' )
182 locals| meta class nobjs offset |
183 create offset , class ,
184 class meta --> get-size nobjs * offset +
188 \ Aggregate a pointer to an object: REF is a member variable
189 \ whose class is set at compile time. This is useful for wrapping
190 \ data structures in C, where there is only a pointer and the type
191 \ it refers to is known. If you want polymorphism, see c_ref
192 \ in classes.fr. REF is only useful for pre-initialized structures,
193 \ since there's no supported way to set one.
194 : ref: ( offset class meta "name" -- offset' )
195 locals| meta class offset |
196 create offset , class ,
198 does> ( inst class pfa -- ptr-inst ptr-class )
199 2@ ( inst class ptr-class ptr-offset )
203 \ END-CLASS terminates construction of a class by storing
204 \ the size of its instance variables in the class's size field
205 \ ( -- old-wid addr[size] 0 )
207 : end-class ( old-wid addr[size] size -- )
209 search> drop \ pop struct builder wordlist
213 \ E N D I N S T A N C E V A R I A B L E S
216 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
217 \ D O - D O - I N S T A N C E
218 \ Makes a class method that contains the code for an
219 \ instance of the class. This word gets compiled into
220 \ the wordlist of every class by the SUB method.
221 \ PRECONDITION: current-class contains the class address
222 \ why use a state variable instead of the stack?
223 \ >> Stack state is not well-defined during compilation (there are
224 \ >> control structure match codes on the stack, of undefined size
225 \ >> easiest way around this is use of this thread-local variable
227 : do-do-instance ( -- )
228 s" : .do-instance does> [ current-class @ ] literal ;"
232 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
233 \ ** M E T A C L A S S
234 \ Every class is an instance of metaclass. This lets
235 \ classes have methods that are different from those
236 \ of their instances.
237 \ Classes are IMMEDIATE to make early binding simpler
244 0 , \ NULL parent class
246 3 cells , \ instance size
251 metaclass drop current-class !
255 \ C L A S S M E T H O D S
257 instance-vars >search
259 create .super ( class metaclass -- parent-class )
260 0 cells , do-instance-var
262 create .wid ( class metaclass -- wid ) \ return wid of class
263 1 cells , do-instance-var
265 create .size ( class metaclass -- size ) \ return class's payload size
266 2 cells , do-instance-var
270 : get-size metaclass => .size @ ;
271 : get-wid metaclass => .wid @ ;
272 : get-super metaclass => .super @ ;
274 \ create an uninitialized instance of a class, leaving
275 \ the address of the new instance and its class
277 : instance ( class metaclass "name" -- instance class )
278 locals| meta parent |
280 here parent --> .do-instance \ ( inst class )
281 parent meta metaclass => get-size
282 allot \ allocate payload space
285 \ create an uninitialized array
286 : array ( n class metaclass "name" -- n instance class )
287 locals| meta parent nobj |
289 here parent --> .do-instance \ ( nobj inst class )
290 parent meta metaclass => get-size
291 nobj * allot \ allocate payload space
294 \ create an initialized instance
296 : new \ ( class metaclass "name" -- )
297 metaclass => instance --> init
300 \ create an initialized array of instances
301 : new-array ( n class metaclass "name" -- )
306 \ Create an anonymous initialized instance from the heap
307 : alloc \ ( class metaclass -- instance class )
309 class meta metaclass => get-size allocate ( -- addr fail-flag )
310 abort" allocate failed " ( -- addr )
314 \ Create an anonymous array of initialized instances from the heap
315 : alloc-array \ ( n class metaclass -- instance class )
316 locals| meta class nobj |
317 class meta metaclass => get-size
318 nobj * allocate ( -- addr fail-flag )
319 abort" allocate failed " ( -- addr )
320 nobj over class --> array-init
324 \ create a proxy object with initialized payload address given
325 : ref ( instance-addr class metaclass "name" -- )
331 : sub ( class metaclass "name" -- old-wid addr[size] size )
333 locals| wid meta parent |
334 parent meta metaclass => get-wid
337 here current-class ! \ prep for do-do-instance
338 parent , \ save parent class
340 here parent meta --> get-size dup , ( addr[size] size )
341 metaclass => .do-instance
342 wid ficl-set-current -rot
344 instance-vars >search \ push struct builder wordlist
347 \ OFFSET-OF returns the offset of an instance variable
348 \ from the instance base address. If the next token is not
349 \ the name of in instance variable method, you get garbage
350 \ results -- there is no way at present to check for this error.
351 : offset-of ( class metaclass "name" -- offset )
352 drop find-method-xt nip >body @ ;
354 \ ID returns the string name cell-pair of its class
355 : id ( class metaclass -- c-addr u )
358 \ list methods of the class
359 : methods \ ( class meta -- )
362 class body> >name type ." methods:" cr
363 class meta --> get-wid >search words cr previous
364 class meta metaclass => get-super
369 \ list class's ancestors
370 : pedigree ( class meta -- )
373 class body> >name type space
374 class meta metaclass => get-super
380 : see ( class meta -- )
381 metaclass => get-wid >search see previous ;
384 \ E N D M E T A C L A S S
386 \ META is a nickname for the address of METACLASS...
390 \ SUBCLASS is a nickname for a class's SUB method...
391 \ Subclass compilation ends when you invoke end-class
392 \ This method is late bound for safety...
396 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
398 \ Root of all classes
402 0 , \ NULL parent class
409 object drop current-class !
412 \ O B J E C T M E T H O D S
413 \ Convert instance cell-pair to class cell-pair
414 \ Useful for binding class methods from an instance
415 : class ( instance class -- class metaclass )
418 \ default INIT method zero fills an instance
419 : init ( instance class -- )
421 metaclass => get-size ( inst size )
424 \ Apply INIT to an array of NOBJ objects...
426 : array-init ( nobj inst class -- )
427 0 dup locals| &init &next class inst |
429 \ bind methods outside the loop to save time
431 class s" init" lookup-method to &init
432 s" next" lookup-method to &next
437 &next execute drop to inst
441 \ free storage allocated to a heap instance by alloc or alloc-array
442 \ NOTE: not protected against errors like FREEing something that's
443 \ really in the dictionary.
444 : free \ ( instance class -- )
449 \ Instance aliases for common class methods
450 \ Upcast to parent class
451 : super ( instance class -- instance parent-class )
452 meta metaclass => get-super ;
454 : pedigree ( instance class -- )
456 metaclass => pedigree ;
458 : size ( instance class -- sizeof-instance )
460 metaclass => get-size ;
462 : methods ( instance class -- )
464 metaclass => methods ;
466 \ Array indexing methods...
468 \ 10 object-array --> index
471 : index ( n instance class -- instance[n] class )
475 metaclass => get-size * ( n*size )
478 : next ( instance[n] class -- instance[n+1] class )
482 metaclass => get-size
486 : prev ( instance[n] class -- instance[n-1] class )
490 metaclass => get-size