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 $
6 \ $DragonFly: src/sys/boot/ficl/softwords/oo.fr,v 1.2 2003/06/17 04:28:18 dillon Exp $
9 .( loading ficl O-O extensions ) cr
14 \ 0. Traditional OOP: late binding by default for safety.
15 \ Early binding if you ask for it.
16 \ 1. Single inheritance
17 \ 2. Object aggregation (has-a relationship)
18 \ 3. Support objects in the dictionary and as proxies for
19 \ existing structures (by reference):
20 \ *** A ficl object can wrap a C struct ***
21 \ 4. Separate name-spaces for methods - methods are
22 \ only visible in the context of a class / object
23 \ 5. Methods can be overridden, and subclasses can add methods.
24 \ No limit on number of methods.
27 \ Classes are objects, too: all classes are instances of METACLASS
28 \ All classes are derived (by convention) from OBJECT. This
29 \ base class provides a default initializer and superclass
32 \ A ficl object binds instance storage (payload) to a class.
33 \ object ( -- instance class )
34 \ All objects push their payload address and class address when
35 \ executed. All objects have this footprint:
36 \ cell 0: first payload cell
38 \ A ficl class consists of a parent class pointer, a wordlist
39 \ ID for the methods of the class, and a size for the payload
40 \ of objects created by the class. A class is an object.
41 \ The NEW method creates and initializes an instance of a class.
42 \ Classes have this footprint:
43 \ cell 0: parent class address
45 \ cell 2: size of instance's payload
47 \ Methods expect an object couple ( instance class )
49 \ Overridden methods must maintain the same stack signature as
50 \ their predecessors. Ficl has no way of enforcing this, though.
55 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
56 \ ** L A T E B I N D I N G
57 \ Compile the method name, and code to find and
58 \ execute it at run-time...
59 \ parse-method compiles the method name so that it pushes
60 \ the string base address and count at run-time.
62 : parse-method \ name run: ( -- c-addr u )
67 : lookup-method ( class c-addr u -- class xt )
72 2 pick cell+ @ ( -- class c-addr u wid )
73 search-wordlist ( -- class 0 | xt 1 | xt -1 )
75 c-addr u type ." not found in "
81 : exec-method ( instance class c-addr u -- <method-signature> )
85 : find-method-xt \ name ( class -- class xt )
86 parse-word lookup-method
90 \ Method lookup operator takes a class-addr and instance-addr
91 \ and executes the method from the class's wordlist if
92 \ interpreting. If compiling, bind late.
94 : --> ( instance class -- ??? )
96 find-method-xt execute
98 parse-method postpone exec-method
103 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
104 \ ** E A R L Y B I N D I N G
105 \ Early binding operator compiles code to execute a method
106 \ given its class at compile time. Classes are immediate,
107 \ so they leave their cell-pair on the stack when compiling.
109 \ : get-wid metaclass => .wid @ ;
111 \ my-class get-wid ( -- wid-of-my-class )
113 : => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
114 drop find-method-xt compile, drop
115 ; immediate compile-only
118 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
119 \ ** I N S T A N C E V A R I A B L E S
120 \ Instance variables (IV) are represented by words in the class's
121 \ private wordlist. Each IV word contains the offset
122 \ of the IV it represents, and runs code to add that offset
123 \ to the base address of an instance when executed.
124 \ The metaclass SUB method, defined below, leaves the address
125 \ of the new class's offset field and its initial size on the
126 \ stack for these words to update. When a class definition is
127 \ complete, END-CLASS saves the final size in the class's size
128 \ field, and restores the search order and compile wordlist to
129 \ prior state. Note that these words are hidden in their own
130 \ wordlist to prevent accidental use outside a SUB END-CLASS pair.
133 dup constant instance-vars
134 dup >search ficl-set-current
136 does> ( instance class addr[offset] -- addr[field] )
140 : addr-units: ( offset size "name" -- offset' )
145 : chars: \ ( offset nCells "name" -- offset' ) Create n char member.
148 : char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
151 : cells: ( offset nCells "name" -- offset' )
152 cells >r aligned r> addr-units:
155 : cell: ( offset nCells "name" -- offset' )
158 \ Aggregate an object into the class...
159 \ Needs the class of the instance to create
160 \ Example: object obj: m_obj
163 does> ( instance class pfa -- a-instance a-class )
164 2@ ( inst class a-class a-offset )
165 2swap drop ( a-class a-offset inst )
166 + swap ( a-inst a-class )
169 : obj: ( offset class meta "name" -- offset' )
170 locals| meta class offset |
171 create offset , class ,
172 class meta --> get-size offset +
176 \ Aggregate an array of objects into a class
178 \ 3 my-class array: my-array
179 \ Makes an instance variable array of 3 instances of my-class
182 : array: ( offset n class meta "name" -- offset' )
183 locals| meta class nobjs offset |
184 create offset , class ,
185 class meta --> get-size nobjs * offset +
189 \ Aggregate a pointer to an object: REF is a member variable
190 \ whose class is set at compile time. This is useful for wrapping
191 \ data structures in C, where there is only a pointer and the type
192 \ it refers to is known. If you want polymorphism, see c_ref
193 \ in classes.fr. REF is only useful for pre-initialized structures,
194 \ since there's no supported way to set one.
195 : ref: ( offset class meta "name" -- offset' )
196 locals| meta class offset |
197 create offset , class ,
199 does> ( inst class pfa -- ptr-inst ptr-class )
200 2@ ( inst class ptr-class ptr-offset )
204 \ END-CLASS terminates construction of a class by storing
205 \ the size of its instance variables in the class's size field
206 \ ( -- old-wid addr[size] 0 )
208 : end-class ( old-wid addr[size] size -- )
210 search> drop \ pop struct builder wordlist
214 \ E N D I N S T A N C E V A R I A B L E S
217 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
218 \ D O - D O - I N S T A N C E
219 \ Makes a class method that contains the code for an
220 \ instance of the class. This word gets compiled into
221 \ the wordlist of every class by the SUB method.
222 \ PRECONDITION: current-class contains the class address
223 \ why use a state variable instead of the stack?
224 \ >> Stack state is not well-defined during compilation (there are
225 \ >> control structure match codes on the stack, of undefined size
226 \ >> easiest way around this is use of this thread-local variable
228 : do-do-instance ( -- )
229 s" : .do-instance does> [ current-class @ ] literal ;"
233 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
234 \ ** M E T A C L A S S
235 \ Every class is an instance of metaclass. This lets
236 \ classes have methods that are different from those
237 \ of their instances.
238 \ Classes are IMMEDIATE to make early binding simpler
245 0 , \ NULL parent class
247 3 cells , \ instance size
252 metaclass drop current-class !
256 \ C L A S S M E T H O D S
258 instance-vars >search
260 create .super ( class metaclass -- parent-class )
261 0 cells , do-instance-var
263 create .wid ( class metaclass -- wid ) \ return wid of class
264 1 cells , do-instance-var
266 create .size ( class metaclass -- size ) \ return class's payload size
267 2 cells , do-instance-var
271 : get-size metaclass => .size @ ;
272 : get-wid metaclass => .wid @ ;
273 : get-super metaclass => .super @ ;
275 \ create an uninitialized instance of a class, leaving
276 \ the address of the new instance and its class
278 : instance ( class metaclass "name" -- instance class )
279 locals| meta parent |
281 here parent --> .do-instance \ ( inst class )
282 parent meta metaclass => get-size
283 allot \ allocate payload space
286 \ create an uninitialized array
287 : array ( n class metaclass "name" -- n instance class )
288 locals| meta parent nobj |
290 here parent --> .do-instance \ ( nobj inst class )
291 parent meta metaclass => get-size
292 nobj * allot \ allocate payload space
295 \ create an initialized instance
297 : new \ ( class metaclass "name" -- )
298 metaclass => instance --> init
301 \ create an initialized array of instances
302 : new-array ( n class metaclass "name" -- )
307 \ Create an anonymous initialized instance from the heap
308 : alloc \ ( class metaclass -- instance class )
310 class meta metaclass => get-size allocate ( -- addr fail-flag )
311 abort" allocate failed " ( -- addr )
315 \ Create an anonymous array of initialized instances from the heap
316 : alloc-array \ ( n class metaclass -- instance class )
317 locals| meta class nobj |
318 class meta metaclass => get-size
319 nobj * allocate ( -- addr fail-flag )
320 abort" allocate failed " ( -- addr )
321 nobj over class --> array-init
325 \ create a proxy object with initialized payload address given
326 : ref ( instance-addr class metaclass "name" -- )
332 : sub ( class metaclass "name" -- old-wid addr[size] size )
334 locals| wid meta parent |
335 parent meta metaclass => get-wid
338 here current-class ! \ prep for do-do-instance
339 parent , \ save parent class
341 here parent meta --> get-size dup , ( addr[size] size )
342 metaclass => .do-instance
343 wid ficl-set-current -rot
345 instance-vars >search \ push struct builder wordlist
348 \ OFFSET-OF returns the offset of an instance variable
349 \ from the instance base address. If the next token is not
350 \ the name of in instance variable method, you get garbage
351 \ results -- there is no way at present to check for this error.
352 : offset-of ( class metaclass "name" -- offset )
353 drop find-method-xt nip >body @ ;
355 \ ID returns the string name cell-pair of its class
356 : id ( class metaclass -- c-addr u )
359 \ list methods of the class
360 : methods \ ( class meta -- )
363 class body> >name type ." methods:" cr
364 class meta --> get-wid >search words cr previous
365 class meta metaclass => get-super
370 \ list class's ancestors
371 : pedigree ( class meta -- )
374 class body> >name type space
375 class meta metaclass => get-super
381 : see ( class meta -- )
382 metaclass => get-wid >search see previous ;
385 \ E N D M E T A C L A S S
387 \ META is a nickname for the address of METACLASS...
391 \ SUBCLASS is a nickname for a class's SUB method...
392 \ Subclass compilation ends when you invoke end-class
393 \ This method is late bound for safety...
397 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
399 \ Root of all classes
403 0 , \ NULL parent class
410 object drop current-class !
413 \ O B J E C T M E T H O D S
414 \ Convert instance cell-pair to class cell-pair
415 \ Useful for binding class methods from an instance
416 : class ( instance class -- class metaclass )
419 \ default INIT method zero fills an instance
420 : init ( instance class -- )
422 metaclass => get-size ( inst size )
425 \ Apply INIT to an array of NOBJ objects...
427 : array-init ( nobj inst class -- )
428 0 dup locals| &init &next class inst |
430 \ bind methods outside the loop to save time
432 class s" init" lookup-method to &init
433 s" next" lookup-method to &next
438 &next execute drop to inst
442 \ free storage allocated to a heap instance by alloc or alloc-array
443 \ NOTE: not protected against errors like FREEing something that's
444 \ really in the dictionary.
445 : free \ ( instance class -- )
450 \ Instance aliases for common class methods
451 \ Upcast to parent class
452 : super ( instance class -- instance parent-class )
453 meta metaclass => get-super ;
455 : pedigree ( instance class -- )
457 metaclass => pedigree ;
459 : size ( instance class -- sizeof-instance )
461 metaclass => get-size ;
463 : methods ( instance class -- )
465 metaclass => methods ;
467 \ Array indexing methods...
469 \ 10 object-array --> index
472 : index ( n instance class -- instance[n] class )
476 metaclass => get-size * ( n*size )
479 : next ( instance[n] class -- instance[n+1] class )
483 metaclass => get-size
487 : prev ( instance[n] class -- instance[n-1] class )
491 metaclass => get-size