2 \ ** ficl/softwords/oo.fr
3 \ ** F I C L O - O E X T E N S I O N S
4 \ ** john sadler aug 1998
6 \ $FreeBSD: src/sys/boot/ficl/softwords/oo.fr,v 1.5 2007/03/23 22:26:01 jkim Exp $
7 \ $DragonFly: src/sys/boot/ficl/softwords/oo.fr,v 1.4 2008/03/29 23:31:07 swildner Exp $
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
36 \ A ficl class consists of a parent class pointer, a wordlist
37 \ ID for the methods of the class, and a size for the payload
38 \ of objects created by the class. A class is an object.
39 \ The NEW method creates and initializes an instance of a class.
40 \ Classes have this footprint:
41 \ cell 0: parent class address
43 \ cell 2: size of instance's payload
45 \ Methods expect an object couple ( instance class )
46 \ on the stack. This is by convention - ficl has no way to
47 \ police your code to make sure this is always done, but it
48 \ happens naturally if you use the facilities presented here.
50 \ Overridden methods must maintain the same stack signature as
51 \ their predecessors. Ficl has no way of enforcing this, either.
53 \ Revised Apr 2001 - Added Guy Carver's vtable extensions. Class now
54 \ has an extra field for the vtable method count. Hasvtable declares
55 \ refs to vtable classes
57 \ Revised Nov 2001 - metaclass debug method now finds only metaclass methods
59 \ Planned: Ficl vtable support
60 \ Each class has a vtable size parameter
61 \ END-CLASS allocates and clears the vtable - then it walks class's method
62 \ list and inserts all new methods into table. For each method, if the table
63 \ slot is already nonzero, do nothing (overridden method). Otherwise fill
64 \ vtable slot. Now do same check for parent class vtable, filling only
65 \ empty slots in the new vtable.
66 \ Methods are now structured as follows:
70 \ :noname definition for code
72 \ : is redefined to check for override, fill in vtable index, increment method
73 \ count if not an override, create header and fill in index. Allot code pointer
75 \ ; is overridden to fill in xt returned by :noname
76 \ --> compiles code to fetch vtable address, offset by index, and execute
77 \ => looks up xt in the vtable and compiles it directly
84 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
85 \ ** L A T E B I N D I N G
86 \ Compile the method name, and code to find and
87 \ execute it at run-time...
90 \ p a r s e - m e t h o d
91 \ compiles a method name so that it pushes
92 \ the string base address and count at run-time.
94 : parse-method \ name run: ( -- c-addr u )
101 : (lookup-method) { class 2:name -- class 0 | class xt 1 | class xt -1 }
102 class name class cell+ @ ( class c-addr u wid )
106 \ l o o k u p - m e t h o d
107 \ takes a counted string method name from the stack (as compiled
108 \ by parse-method) and attempts to look this method up in the method list of
109 \ the class that's on the stack. If successful, it leaves the class on the stack
110 \ and pushes the xt of the method. If not, it aborts with an error message.
112 : lookup-method { class 2:name -- class xt }
113 class name (lookup-method) ( 0 | xt 1 | xt -1 )
115 name type ." not found in "
116 class body> >name type
121 : find-method-xt \ name ( class -- class xt )
122 parse-word lookup-method
125 : catch-method ( instance class c-addr u -- <method-signature> exc-flag )
129 : exec-method ( instance class c-addr u -- <method-signature> )
130 lookup-method execute
133 \ Method lookup operator takes a class-addr and instance-addr
134 \ and executes the method from the class's wordlist if
135 \ interpreting. If compiling, bind late.
137 : --> ( instance class -- ??? )
139 find-method-xt execute
141 parse-method postpone exec-method
145 \ Method lookup with CATCH in case of exceptions
146 : c-> ( instance class -- ?? exc-flag )
150 parse-method postpone catch-method
154 \ METHOD makes global words that do method invocations by late binding
155 \ in case you prefer this style (no --> in your code)
156 \ Example: everything has next and prev for array access, so...
159 \ my-instance next ( does whatever next does to my-instance by late binding )
161 : method create does> body> >name lookup-method execute ;
164 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
165 \ ** E A R L Y B I N D I N G
166 \ Early binding operator compiles code to execute a method
167 \ given its class at compile time. Classes are immediate,
168 \ so they leave their cell-pair on the stack when compiling.
170 \ : get-wid metaclass => .wid @ ;
172 \ my-class get-wid ( -- wid-of-my-class )
174 1 ficl-named-wordlist instance-vars
175 instance-vars dup >search ficl-set-current
177 : => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
178 drop find-method-xt compile, drop
179 ; immediate compile-only
181 : my=> \ c:( -- ) run: ( -- ??? ) late bind compiled method of current-class
182 current-class @ dup postpone =>
183 ; immediate compile-only
185 \ Problem: my=[ assumes that each method except the last is am obj: member
186 \ which contains its class as the first field of its parameter area. The code
187 \ detects non-obect members and assumes the class does not change in this case.
188 \ This handles methods like index, prev, and next correctly, but does not deal
189 \ correctly with CLASS.
190 : my=[ \ same as my=> , but binds a chain of methods
193 parse-word 2dup ( class c-addr u c-addr u )
194 s" ]" compare while ( class c-addr u )
195 lookup-method ( class xt )
196 dup compile, ( class xt )
197 dup ?object if \ If object member, get new class. Otherwise assume same class
198 nip >body cell+ @ ( new-class )
203 ; immediate compile-only
206 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
207 \ ** I N S T A N C E V A R I A B L E S
208 \ Instance variables (IV) are represented by words in the class's
209 \ private wordlist. Each IV word contains the offset
210 \ of the IV it represents, and runs code to add that offset
211 \ to the base address of an instance when executed.
212 \ The metaclass SUB method, defined below, leaves the address
213 \ of the new class's offset field and its initial size on the
214 \ stack for these words to update. When a class definition is
215 \ complete, END-CLASS saves the final size in the class's size
216 \ field, and restores the search order and compile wordlist to
217 \ prior state. Note that these words are hidden in their own
218 \ wordlist to prevent accidental use outside a SUB END-CLASS pair.
221 does> ( instance class addr[offset] -- addr[field] )
225 : addr-units: ( offset size "name" -- offset' )
230 : chars: \ ( offset nCells "name" -- offset' ) Create n char member.
233 : char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
236 : cells: ( offset nCells "name" -- offset' )
237 cells >r aligned r> addr-units:
240 : cell: ( offset nCells "name" -- offset' )
243 \ Aggregate an object into the class...
244 \ Needs the class of the instance to create
245 \ Example: object obj: m_obj
249 does> ( instance class pfa -- a-instance a-class )
250 2@ ( inst class a-class a-offset )
251 2swap drop ( a-class a-offset inst )
252 + swap ( a-inst a-class )
255 : obj: { offset class meta -- offset' } \ "name"
256 create offset , class ,
257 class meta --> get-size offset +
261 \ Aggregate an array of objects into a class
263 \ 3 my-class array: my-array
264 \ Makes an instance variable array of 3 instances of my-class
267 : array: ( offset n class meta "name" -- offset' )
268 locals| meta class nobjs offset |
269 create offset , class ,
270 class meta --> get-size nobjs * offset +
274 \ Aggregate a pointer to an object: REF is a member variable
275 \ whose class is set at compile time. This is useful for wrapping
276 \ data structures in C, where there is only a pointer and the type
277 \ it refers to is known. If you want polymorphism, see c_ref
278 \ in classes.fr. REF is only useful for pre-initialized structures,
279 \ since there's no supported way to set one.
280 : ref: ( offset class meta "name" -- offset' )
281 locals| meta class offset |
282 create offset , class ,
284 does> ( inst class pfa -- ptr-inst ptr-class )
285 2@ ( inst class ptr-class ptr-offset )
289 \ #if FICL_WANT_VCALL
290 \ vcall extensions contributed by Guy Carver
291 : vcall: ( paramcnt "name" -- )
292 current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
293 create , , \ ( paramcnt index -- )
294 does> \ ( inst class pfa -- ptr-inst ptr-class )
295 nip 2@ vcall \ ( params offset inst class offset -- )
298 : vcallr: 0x80000000 or vcall: ; \ Call with return address desired.
300 \ #if FICL_WANT_FLOAT
301 : vcallf: \ ( paramcnt -<name>- f: r )
303 current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
304 create , , \ ( paramcnt index -- )
305 does> \ ( inst class pfa -- ptr-inst ptr-class )
306 nip 2@ vcall f> \ ( params offset inst class offset -- f: r )
311 \ END-CLASS terminates construction of a class by storing
312 \ the size of its instance variables in the class's size field
313 \ ( -- old-wid addr[size] 0 )
315 : end-class ( old-wid addr[size] size -- )
317 search> drop \ pop struct builder wordlist
320 \ See resume-class (a metaclass method) below for usage
321 \ This is equivalent to end-class for now, but that will change
322 \ when we support vtable bindings.
323 : suspend-class ( old-wid addr[size] size -- ) end-class ;
326 \ E N D I N S T A N C E V A R I A B L E S
329 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
330 \ D O - D O - I N S T A N C E
331 \ Makes a class method that contains the code for an
332 \ instance of the class. This word gets compiled into
333 \ the wordlist of every class by the SUB method.
334 \ PRECONDITION: current-class contains the class address
335 \ why use a state variable instead of the stack?
336 \ >> Stack state is not well-defined during compilation (there are
337 \ >> control structure match codes on the stack, of undefined size
338 \ >> easiest way around this is use of this thread-local variable
340 : do-do-instance ( -- )
341 s" : .do-instance does> [ current-class @ ] literal ;"
345 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
346 \ ** M E T A C L A S S
347 \ Every class is an instance of metaclass. This lets
348 \ classes have methods that are different from those
349 \ of their instances.
350 \ Classes are IMMEDIATE to make early binding simpler
357 0 , \ NULL parent class
359 \ #if FICL_WANT_VCALL
360 4 cells , \ instance size
362 3 cells , \ instance size
367 \ now brand OBJECT's wordlist (so that ORDER can display it by name)
368 metaclass drop cell+ @ brand-wordlist
370 metaclass drop current-class !
374 \ C L A S S M E T H O D S
376 instance-vars >search
378 create .super ( class metaclass -- parent-class )
379 0 cells , do-instance-var
381 create .wid ( class metaclass -- wid ) \ return wid of class
382 1 cells , do-instance-var
384 \ #if FICL_WANT_VCALL
385 create .vtCount \ Number of VTABLE methods, if any
386 2 cells , do-instance-var
388 create .size ( class metaclass -- size ) \ return class's payload size
389 3 cells , do-instance-var
391 create .size ( class metaclass -- size ) \ return class's payload size
392 2 cells , do-instance-var
395 : get-size metaclass => .size @ ;
396 : get-wid metaclass => .wid @ ;
397 : get-super metaclass => .super @ ;
398 \ #if FICL_WANT_VCALL
399 : get-vtCount metaclass => .vtCount @ ;
400 : get-vtAdd metaclass => .vtCount ;
403 \ create an uninitialized instance of a class, leaving
404 \ the address of the new instance and its class
406 : instance ( class metaclass "name" -- instance class )
407 locals| meta parent |
409 here parent --> .do-instance \ ( inst class )
410 parent meta metaclass => get-size
411 allot \ allocate payload space
414 \ create an uninitialized array
415 : array ( n class metaclass "name" -- n instance class )
416 locals| meta parent nobj |
418 here parent --> .do-instance \ ( nobj inst class )
419 parent meta metaclass => get-size
420 nobj * allot \ allocate payload space
423 \ create an initialized instance
425 : new \ ( class metaclass "name" -- )
426 metaclass => instance --> init
429 \ create an initialized array of instances
430 : new-array ( n class metaclass "name" -- )
435 \ Create an anonymous initialized instance from the heap
436 : alloc \ ( class metaclass -- instance class )
438 class meta metaclass => get-size allocate ( -- addr fail-flag )
439 abort" allocate failed " ( -- addr )
443 \ Create an anonymous array of initialized instances from the heap
444 : alloc-array \ ( n class metaclass -- instance class )
445 locals| meta class nobj |
446 class meta metaclass => get-size
447 nobj * allocate ( -- addr fail-flag )
448 abort" allocate failed " ( -- addr )
449 nobj over class --> array-init
453 \ Create an anonymous initialized instance from the dictionary
454 : allot { 2:this -- 2:instance }
455 here ( instance-address )
456 this my=> get-size allot
457 this drop 2dup --> init
460 \ Create an anonymous array of initialized instances from the dictionary
461 : allot-array { nobj 2:this -- 2:instance }
462 here ( instance-address )
463 this my=> get-size nobj * allot
464 this drop 2dup ( 2instance 2instance )
465 nobj -rot --> array-init
468 \ create a proxy object with initialized payload address given
469 : ref ( instance-addr class metaclass "name" -- )
474 \ suspend-class and resume-class help to build mutually referent classes.
476 \ object subclass c-akbar
477 \ suspend-class ( put akbar on hold while we define jeff )
478 \ object subclass c-jeff
479 \ c-akbar ref: .akbar
480 \ ( and whatever else comprises this class )
481 \ end-class ( done with c-jeff )
482 \ c-akbar --> resume-class
484 \ ( and whatever else goes in c-akbar )
485 \ end-class ( done with c-akbar )
487 : resume-class { 2:this -- old-wid addr[size] size }
488 this --> .wid @ ficl-set-current ( old-wid )
489 this --> .size dup @ ( old-wid addr[size] size )
490 instance-vars >search
494 \ This method leaves the stack and search order ready for instance variable
495 \ building. Pushes the instance-vars wordlist onto the search order,
496 \ and sets the compilation wordlist to be the private wordlist of the
497 \ new class. The class's wordlist is deliberately NOT in the search order -
498 \ to prevent methods from getting used with wrong data.
499 \ Postcondition: leaves the address of the new class in current-class
500 : sub ( class metaclass "name" -- old-wid addr[size] size )
502 locals| wid meta parent |
503 parent meta metaclass => get-wid
504 wid wid-set-super \ set superclass
505 create immediate \ get the subclass name
506 wid brand-wordlist \ label the subclass wordlist
507 here current-class ! \ prep for do-do-instance
508 parent , \ save parent class
510 \ #if FICL_WANT_VCALL
511 parent meta --> get-vtCount ,
513 here parent meta --> get-size dup , ( addr[size] size )
514 metaclass => .do-instance
515 wid ficl-set-current -rot
517 instance-vars >search \ push struct builder wordlist
520 \ OFFSET-OF returns the offset of an instance variable
521 \ from the instance base address. If the next token is not
522 \ the name of in instance variable method, you get garbage
523 \ results -- there is no way at present to check for this error.
524 : offset-of ( class metaclass "name" -- offset )
525 drop find-method-xt nip >body @ ;
527 \ ID returns the string name cell-pair of its class
528 : id ( class metaclass -- c-addr u )
531 \ list methods of the class
532 : methods \ ( class meta -- )
535 class body> >name type ." methods:" cr
536 class meta --> get-wid >search words cr previous
537 class meta metaclass => get-super
542 \ list class's ancestors
543 : pedigree ( class meta -- )
546 class body> >name type space
547 class meta metaclass => get-super
552 \ decompile an instance method
553 : see ( class meta -- )
554 metaclass => get-wid >search see previous ;
556 \ debug a method of metaclass
557 \ Eg: my-class --> debug my-method
558 : debug ( class meta -- )
559 find-method-xt debug-xt ;
562 \ E N D M E T A C L A S S
564 \ ** META is a nickname for the address of METACLASS...
568 \ ** SUBCLASS is a nickname for a class's SUB method...
569 \ Subclass compilation ends when you invoke end-class
570 \ This method is late bound for safety...
573 \ #if FICL_WANT_VCALL
574 \ VTABLE Support extensions (Guy Carver)
575 \ object --> sub mine hasvtable
576 : hasvtable 4 + ; immediate
580 \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
582 \ Root of all classes
586 0 , \ NULL parent class
592 \ now brand OBJECT's wordlist (so that ORDER can display it by name)
593 object drop cell+ @ brand-wordlist
595 object drop current-class !
597 instance-vars >search
599 \ O B J E C T M E T H O D S
600 \ Convert instance cell-pair to class cell-pair
601 \ Useful for binding class methods from an instance
602 : class ( instance class -- class metaclass )
605 \ default INIT method zero fills an instance
606 : init ( instance class -- )
608 metaclass => get-size ( inst size )
611 \ Apply INIT to an array of NOBJ objects...
613 : array-init ( nobj inst class -- )
614 0 dup locals| &init &next class inst |
616 \ bind methods outside the loop to save time
618 class s" init" lookup-method to &init
619 s" next" lookup-method to &next
624 &next execute drop to inst
628 \ free storage allocated to a heap instance by alloc or alloc-array
629 \ NOTE: not protected against errors like FREEing something that's
630 \ really in the dictionary.
631 : free \ ( instance class -- )
636 \ Instance aliases for common class methods
637 \ Upcast to parent class
638 : super ( instance class -- instance parent-class )
639 meta metaclass => get-super ;
641 : pedigree ( instance class -- )
643 metaclass => pedigree ;
645 : size ( instance class -- sizeof-instance )
647 metaclass => get-size ;
649 : methods ( instance class -- )
651 metaclass => methods ;
653 \ Array indexing methods...
655 \ 10 object-array --> index
658 : index ( n instance class -- instance[n] class )
662 metaclass => get-size * ( n*size )
665 : next ( instance[n] class -- instance[n+1] class )
669 metaclass => get-size
673 : prev ( instance[n] class -- instance[n-1] class )
677 metaclass => get-size
681 : debug ( 2this -- ?? )
682 find-method-xt debug-xt ;
687 \ reset to default search order
690 \ redefine oop in default search order to put OOP words in the search order and make them
691 \ the compiling wordlist...
693 : oo only also oop definitions ;