1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/tads3/VMTOBJ.CPP,v 1.3 1999/05/17 02:52:28 MJRoberts Exp $";
4 #endif
5 
6 /*
7  *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   vmtobj.cpp - TADS object implementation
15 Function
16 
17 Notes
18 
19 Modified
20   10/30/98 MJRoberts  - Creation
21 */
22 
23 #include <stdlib.h>
24 #include <assert.h>
25 
26 #include "t3std.h"
27 #include "vmglob.h"
28 #include "vmerr.h"
29 #include "vmerrnum.h"
30 #include "vmobj.h"
31 #include "vmtobj.h"
32 #include "vmundo.h"
33 #include "vmtype.h"
34 #include "vmfile.h"
35 #include "vmstack.h"
36 #include "vmrun.h"
37 #include "vmpredef.h"
38 #include "vmmeta.h"
39 #include "vmlst.h"
40 
41 
42 /* ------------------------------------------------------------------------ */
43 /*
44  *   object ID + pointer structure
45  */
46 struct tadsobj_objid_and_ptr
47 {
48     vm_obj_id_t id;
49     CVmObjTads *objp;
50 };
51 
52 /*
53  *   Cached superclass inheritance path.  This is a linear list, in
54  *   inheritance search order, of the superclasses of a given object.
55  */
56 struct tadsobj_inh_path
57 {
58     /* number of path elements */
59     ushort cnt;
60 
61     /* path elements (we overallocate the structure to the actual size) */
62     tadsobj_objid_and_ptr sc[1];
63 };
64 
65 
66 /* ------------------------------------------------------------------------ */
67 /*
68  *   Queue element for the inheritance path search queue
69  */
70 struct pfq_ele
71 {
72     /* object ID of this element */
73     vm_obj_id_t obj;
74 
75     /* pointer to the object */
76     CVmObjTads *objp;
77 
78     /* next queue element */
79     pfq_ele *nxt;
80 };
81 
82 /* allocation page */
83 struct pfq_page
84 {
85     /* next page in the list */
86     pfq_page *nxt;
87 
88     /* the elements for this page */
89     pfq_ele eles[50];
90 };
91 
92 /*
93  *   Queue for search_for_prop().  This implements a special-purpose work
94  *   queue that we use to keep track of the objects yet to be processed in
95  *   our depth-first search across the inheritance tree.
96  */
97 class CVmObjTadsInhQueue
98 {
99 public:
CVmObjTadsInhQueue()100     CVmObjTadsInhQueue()
101     {
102         /* there's nothing in the free list or the queue yet */
103         head_ = 0;
104         free_ = 0;
105 
106         /* we have no elements yet */
107         alloc_ = 0;
108     }
109 
~CVmObjTadsInhQueue()110     ~CVmObjTadsInhQueue()
111     {
112         pfq_page *cur;
113         pfq_page *nxt;
114 
115         /* delete all of the allocated pages */
116         for (cur = alloc_ ; cur != 0 ; cur = nxt)
117         {
118             /* remember the next page */
119             nxt = cur->nxt;
120 
121             /* free this page */
122             t3free(cur);
123         }
124     }
125 
126     /* get the head of the queue */
get_head() const127     pfq_ele *get_head() const { return head_; }
128 
129     /* remove the head of the queue and return the object ID */
remove_head()130     vm_obj_id_t remove_head()
131     {
132         /* if there's a head element, remove it */
133         if (head_ != 0)
134         {
135             pfq_ele *ele;
136 
137             /* note the element */
138             ele = head_;
139 
140             /* unlink it from the list */
141             head_ = head_->nxt;
142 
143             /* link the element into the free list */
144             ele->nxt = free_;
145             free_ = ele;
146 
147             /* return the object ID from the element we removed */
148             return ele->obj;
149         }
150         else
151         {
152             /* there's nothing in the queue */
153             return VM_INVALID_OBJ;
154         }
155     }
156 
157     /* clear the queue */
clear()158     void clear()
159     {
160         /* move everything from the queue to the free list */
161         while (head_ != 0)
162         {
163             pfq_ele *cur;
164 
165             /* unlink this element from the queue */
166             cur = head_;
167             head_ = cur->nxt;
168 
169             /* link it into the free list */
170             cur->nxt = free_;
171             free_ = cur;
172         }
173     }
174 
175     /* determine if the queue is empty */
is_empty() const176     int is_empty() const
177     {
178         /* we're empty if there's no head element in the list */
179         return (head_ == 0);
180     }
181 
182     /* allocate a path from the contents of the queue */
create_path() const183     tadsobj_inh_path *create_path() const
184     {
185         ushort cnt;
186         pfq_ele *cur;
187         tadsobj_inh_path *path;
188         tadsobj_objid_and_ptr *dst;
189 
190         /* count the elements in the queue */
191         for (cnt = 0, cur = head_ ; cur != 0 ; cur = cur->nxt)
192         {
193             /* only non-nil elements count */
194             if (cur->obj != VM_INVALID_OBJ)
195                 ++cnt;
196         }
197 
198         /* allocate the path */
199         path = (tadsobj_inh_path *)t3malloc(
200             sizeof(tadsobj_inh_path) + (cnt-1)*sizeof(path->sc[0]));
201 
202         /* initialize the path */
203         path->cnt = cnt;
204         for (dst = path->sc, cur = head_ ; cur != 0 ; cur = cur->nxt)
205         {
206             /* only store non-nil elements */
207             if (cur->obj != VM_INVALID_OBJ)
208             {
209                 dst->id = cur->obj;
210                 dst->objp = cur->objp;
211                 ++dst;
212             }
213         }
214 
215         /* return the new path */
216         return path;
217     }
218 
219     /*
220      *   Insert an object into the queue.  We'll insert after the given
221      *   element (null indicates that we insert at the head of the queue).
222      *   Returns a pointer to the newly-inserted element.
223      */
insert_obj(VMG_ vm_obj_id_t obj,CVmObjTads * objp,pfq_ele * ins_pt)224     pfq_ele *insert_obj(VMG_ vm_obj_id_t obj, CVmObjTads *objp,
225                         pfq_ele *ins_pt)
226     {
227         pfq_ele *ele;
228 
229         /*
230          *   If the exact same element is already in the queue, delete the
231          *   old copy.  This will happen in situations where we have
232          *   multiple superclasses that all inherit from a common base
233          *   class: we want the common base class to come in inheritance
234          *   order after the last superclass that inherits from the common
235          *   base.  By deleting previous queue entries that match new queue
236          *   entries, we ensure that the common class will move to follow
237          *   (in inheritance order) the last class that derives from it.
238          */
239         for (ele = head_ ; ele != 0 ; ele = ele->nxt)
240         {
241             /* if this is the same thing we're inserting, remove it */
242             if (ele->obj == obj)
243             {
244                 /*
245                  *   clear the element (don't unlink it, as this could cause
246                  *   confusion for the caller, who's tracking an insertion
247                  *   point and traversal point)
248                  */
249                 ele->obj = VM_INVALID_OBJ;
250                 ele->objp = 0;
251 
252                 /*
253                  *   no need to look any further - we know we can never have
254                  *   the same element appear twice in the queue, thanks to
255                  *   this very code
256                  */
257                 break;
258             }
259         }
260 
261         /* allocate our new element */
262         ele = alloc_ele();
263         ele->obj = obj;
264         ele->objp = objp;
265 
266         /* insert it at the insertion point */
267         if (ins_pt == 0)
268         {
269             /* insert at the head */
270             ele->nxt = head_;
271             head_ = ele;
272         }
273         else
274         {
275             /* insert after the selected item */
276             ele->nxt = ins_pt->nxt;
277             ins_pt->nxt = ele;
278         }
279 
280         /* return the new element */
281         return ele;
282     }
283 
284 protected:
285     /* allocate a new element */
alloc_ele()286     pfq_ele *alloc_ele()
287     {
288         pfq_ele *ele;
289 
290         /* if we have nothing in the free list, allocate more elements */
291         if (free_ == 0)
292         {
293             pfq_page *pg;
294             size_t i;
295 
296             /* allocate another page */
297             pg = (pfq_page *)t3malloc(sizeof(pfq_page));
298 
299             /* link it into our master page list */
300             pg->nxt = alloc_;
301             alloc_ = pg;
302 
303             /* link all of its elements into the free list */
304             for (ele = pg->eles, i = sizeof(pg->eles)/sizeof(pg->eles[0]) ;
305                  i != 0 ; --i, ++ele)
306             {
307                 /* link this one into the free list */
308                 ele->nxt = free_;
309                 free_ = ele;
310             }
311         }
312 
313         /* take the next element off the free list */
314         ele = free_;
315         free_ = free_->nxt;
316 
317         /* return the element */
318         return ele;
319     }
320 
321     /* head of the active queue */
322     pfq_ele *head_;
323 
324     /* head of the free element list */
325     pfq_ele *free_;
326 
327     /*
328      *   Linked list of element pages.  We allocate memory for elements in
329      *   blocks, to reduce allocation overhead.
330      */
331     pfq_page *alloc_;
332 };
333 
334 
335 /* ------------------------------------------------------------------------ */
336 /*
337  *   Allocate a new object header
338  */
alloc(VMG_ CVmObjTads * self,unsigned short sc_cnt,unsigned short prop_cnt)339 vm_tadsobj_hdr *vm_tadsobj_hdr::alloc(VMG_ CVmObjTads *self,
340                                       unsigned short sc_cnt,
341                                       unsigned short prop_cnt)
342 {
343     size_t hash_siz;
344     size_t siz;
345     size_t i;
346     vm_tadsobj_hdr *hdr;
347     char *mem;
348     vm_tadsobj_prop **hashp;
349 
350     /*
351      *   Figure the size of the hash table to allocate.
352      *
353      *   IMPORTANT: The hash table size is REQUIRED to be a power of 2.  We
354      *   assume this in calculating hash table indices, so if this
355      *   constraint is changed, the calc_hash() function must be changed
356      *   accordingly.
357      */
358     if (prop_cnt <= 16)
359         hash_siz = 16;
360     else if (prop_cnt <= 32)
361         hash_siz = 32;
362     else if (prop_cnt <= 64)
363         hash_siz = 64;
364     else if (prop_cnt <= 128)
365         hash_siz = 128;
366     else
367         hash_siz = 256;
368 
369     /*
370      *   increase the requested property count to the hash size at a minimum
371      *   - this will avoid the need to reallocate the object to make room
372      *   for more properties until we'd have to resize the hash table, at
373      *   which point we have to reallocate the object anyway
374      */
375     if (prop_cnt < hash_siz)
376         prop_cnt = hash_siz;
377 
378     /* figure the size of the structure we need */
379     siz = sizeof(vm_tadsobj_hdr)
380           + (sc_cnt - 1) * sizeof(hdr->sc[0])
381           + (hash_siz) * sizeof(hdr->hash_arr[0])
382           + prop_cnt * sizeof(hdr->prop_entry_arr[0]);
383 
384     /* allocate the memory */
385     hdr = (vm_tadsobj_hdr *)G_mem->get_var_heap()->alloc_mem(siz, self);
386 
387     /*
388      *   Set up to suballocate out of this block.  Free memory in the block
389      *   starts after our structure and the array of superclass entries.
390      */
391     mem = (char *)&hdr->sc[sc_cnt];
392 
393     /* clear our flags and load-image flags */
394     hdr->li_obj_flags = 0;
395     hdr->intern_obj_flags = 0;
396 
397     /* the object has no precalculated inheritance path yet */
398     hdr->inh_path = 0;
399 
400     /* suballocate the hash buckets */
401     hdr->hash_siz = hash_siz;
402     hdr->hash_arr = (vm_tadsobj_prop **)mem;
403 
404     /* clear out the hash buckets */
405     for (hashp = hdr->hash_arr, i = hash_siz ; i != 0 ; ++hashp, --i)
406         *hashp = 0;
407 
408     /* move past the memory taken by the hash buckets */
409     mem = (char *)(hdr->hash_arr + hash_siz);
410 
411     /* suballocate the array of hash entries */
412     hdr->prop_entry_cnt = prop_cnt;
413     hdr->prop_entry_arr = (vm_tadsobj_prop *)mem;
414 
415     /* all entries are currently free, so point to the first entry */
416     hdr->prop_entry_free = 0;
417 
418     /* remember the superclass count */
419     hdr->sc_cnt = sc_cnt;
420 
421     /* return the new object */
422     return hdr;
423 }
424 
425 /*
426  *   Free
427  */
free_mem()428 void vm_tadsobj_hdr::free_mem()
429 {
430     /* if I have a precalculated inheritance path, delete it */
431     if (inh_path != 0)
432         t3free(inh_path);
433 }
434 
435 /*
436  *   Expand an existing object header to make room for more properties
437  */
expand(VMG_ CVmObjTads * self,vm_tadsobj_hdr * hdr)438 vm_tadsobj_hdr *vm_tadsobj_hdr::expand(VMG_ CVmObjTads *self,
439                                        vm_tadsobj_hdr *hdr)
440 {
441     unsigned short prop_cnt;
442 
443     /*
444      *   Move up to the next property count increment.  If we're not huge,
445      *   simply double the current size.  If we're getting large, expand by
446      *   50%.
447      */
448     prop_cnt = hdr->prop_entry_cnt;
449     if (prop_cnt <= 128)
450         prop_cnt *= 2;
451     else
452         prop_cnt += prop_cnt/2;
453 
454     /* expand to the new size */
455     return expand_to(vmg_ self, hdr, hdr->sc_cnt, prop_cnt);
456 }
457 
458 /*
459  *   Expand an existing header to the given minimum property table size
460  */
expand_to(VMG_ CVmObjTads * self,vm_tadsobj_hdr * hdr,size_t new_sc_cnt,size_t new_prop_cnt)461 vm_tadsobj_hdr *vm_tadsobj_hdr::expand_to(VMG_ CVmObjTads *self,
462                                           vm_tadsobj_hdr *hdr,
463                                           size_t new_sc_cnt,
464                                           size_t new_prop_cnt)
465 {
466     vm_tadsobj_hdr *new_hdr;
467     size_t i;
468     vm_tadsobj_prop *entryp;
469 
470     /* allocate a new object at the expanded property table size */
471     new_hdr = alloc(vmg_ self, new_sc_cnt, new_prop_cnt);
472 
473     /* copy the superclasses from the original object */
474     memcpy(new_hdr->sc, hdr->sc,
475            (hdr->sc_cnt < new_sc_cnt ? hdr->sc_cnt : new_sc_cnt)
476            * sizeof(hdr->sc[0]));
477 
478     /* use the same flags from the original object */
479     new_hdr->li_obj_flags = hdr->li_obj_flags;
480     new_hdr->intern_obj_flags = hdr->intern_obj_flags;
481 
482     /*
483      *   if the superclass count is changing, we're obviously changing the
484      *   inheritance structure, in which case the old cached inheritance path
485      *   is invalid - delete it if so
486      */
487     if (new_sc_cnt != hdr->sc_cnt)
488         hdr->inval_inh_path();
489 
490     /* copy the old inheritance path (if we still have one) */
491     new_hdr->inh_path = hdr->inh_path;
492 
493     /*
494      *   Run through all of the existing properties and duplicate them in
495      *   the new object, to build the new object's hash table.  Note that
496      *   the free index is also the count of properties in use.
497      */
498     for (i = hdr->prop_entry_free, entryp = hdr->prop_entry_arr ; i != 0 ;
499          --i, ++entryp)
500     {
501         /* add this property to the new table */
502         new_hdr->alloc_prop_entry(entryp->prop, &entryp->val, entryp->flags);
503     }
504 
505     /* delete the old header */
506     G_mem->get_var_heap()->free_mem(hdr);
507 
508     /* return the new header */
509     return new_hdr;
510 }
511 
512 /*
513  *   Allocate an entry for given property from the free pool.  The caller is
514  *   responsible for checking that there's space in the free pool.  We do
515  *   not check for an existing entry with the same caller ID, so the caller
516  *   is responsible for making sure the property doesn't already exist in
517  *   our table.
518  */
alloc_prop_entry(vm_prop_id_t prop,const vm_val_t * val,unsigned int flags)519 vm_tadsobj_prop *vm_tadsobj_hdr::alloc_prop_entry(
520     vm_prop_id_t prop, const vm_val_t *val, unsigned int flags)
521 {
522     vm_tadsobj_prop *entry;
523     unsigned int hash;
524 
525     /* get the hash code for the property */
526     hash = calc_hash(prop);
527 
528     /* use the next free entry */
529     entry = &prop_entry_arr[prop_entry_free];
530 
531     /* link this entry into the list for its hash bucket */
532     entry->nxt = hash_arr[hash];
533     hash_arr[hash] = entry;
534 
535     /* count our use of the free entry */
536     ++prop_entry_free;
537 
538     /* set the new entry's property ID */
539     entry->prop = prop;
540 
541     /* set the value and flags */
542     entry->val = *val;
543     entry->flags = (unsigned char)flags;
544 
545     /* return the entry */
546     return entry;
547 }
548 
549 /*
550  *   Find an entry
551  */
find_prop_entry(uint prop)552 inline vm_tadsobj_prop *vm_tadsobj_hdr::find_prop_entry(uint prop)
553 {
554     unsigned int hash;
555     vm_tadsobj_prop *entry;
556 
557     /* get the hash code for the property */
558     hash = calc_hash(prop);
559 
560     /* scan the list of entries in this bucket */
561     for (entry = hash_arr[hash] ; entry != 0 ; entry = entry->nxt)
562     {
563         /* if this entry matches, return it */
564         if (entry->prop == prop)
565             return entry;
566     }
567 
568     /* didn't find it */
569     return 0;
570 }
571 
572 
573 /* ------------------------------------------------------------------------ */
574 /*
575  *   statics
576  */
577 
578 /* metaclass registration object */
579 static CVmMetaclassTads metaclass_reg_obj;
580 CVmMetaclass *CVmObjTads::metaclass_reg_ = &metaclass_reg_obj;
581 
582 
583 /* function table */
584 int (CVmObjTads::
585      *CVmObjTads::func_table_[])(VMG_ vm_obj_id_t self,
586                                  vm_val_t *retval, uint *argc) =
587 {
588     &CVmObjTads::getp_undef,
589     &CVmObjTads::getp_create_instance,
590     &CVmObjTads::getp_create_clone,
591     &CVmObjTads::getp_create_trans_instance,
592     &CVmObjTads::getp_create_instance_of,
593     &CVmObjTads::getp_create_trans_instance_of,
594     &CVmObjTads::getp_set_sc_list
595 };
596 
597 /*
598  *   Function table indices.  We only need constant definitions for these
599  *   for our static methods, since in other cases we translate through the
600  *   function table.
601  */
602 const int PROPIDX_CREATE_INSTANCE = 1;
603 const int PROPIDX_CREATE_CLONE = 2;
604 const int PROPIDX_CREATE_TRANS_INSTANCE = 3;
605 const int PROPIDX_CREATE_INSTANCE_OF = 4;
606 const int PROPIDX_CREATE_TRANS_INSTANCE_OF = 5;
607 
608 /* ------------------------------------------------------------------------ */
609 /*
610  *   Static class initialization
611  */
class_init(VMG0_)612 void CVmObjTads::class_init(VMG0_)
613 {
614     /* allocate the inheritance analysis object */
615     G_tadsobj_queue = new CVmObjTadsInhQueue();
616 }
617 
618 /*
619  *   Static class termination
620  */
class_term(VMG0_)621 void CVmObjTads::class_term(VMG0_)
622 {
623     /* delete the inheritance analysis object */
624     delete G_tadsobj_queue;
625     G_tadsobj_queue = 0;
626 }
627 
628 /* ------------------------------------------------------------------------ */
629 /*
630  *   Static creation methods
631  */
632 
633 /* create dynamically using stack arguments */
create_from_stack_intern(VMG_ const uchar ** pc_ptr,uint argc,int is_transient)634 vm_obj_id_t CVmObjTads::create_from_stack_intern(
635     VMG_ const uchar **pc_ptr, uint argc, int is_transient)
636 {
637     vm_obj_id_t id;
638     CVmObjTads *obj;
639     vm_val_t val;
640     vm_obj_id_t srcobj;
641 
642     /* check arguments */
643     if (argc == 0)
644     {
645         /* no superclass argument - create a base object */
646         val.set_nil();
647     }
648     else
649     {
650         /*
651          *   We have arguments.  The first is the superclass argument, which
652          *   must be an object or nil.  Retrieve it and make sure it's
653          *   valid.
654          */
655         G_stk->pop(&val);
656         if (val.typ != VM_OBJ && val.typ != VM_NIL)
657             err_throw(VMERR_OBJ_VAL_REQD_SC);
658 
659         /* if it's the invalid object, treat it as nil */
660         if (val.typ == VM_OBJ && val.val.obj == VM_INVALID_OBJ)
661             val.set_nil();
662 
663         /* we cannot create an instance of a transient object */
664         if (val.typ != VM_NIL
665             && G_obj_table->is_obj_transient(val.val.obj))
666             err_throw(VMERR_BAD_DYNAMIC_NEW);
667 
668         /* count the removal of the first argument */
669         --argc;
670     }
671 
672     /*
673      *   create the object - this type of construction is never used for
674      *   root set objects
675      */
676     id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
677 
678     /* make the object transient if desired */
679     if (is_transient)
680         G_obj_table->set_obj_transient(id);
681 
682     /*
683      *   create a TADS object with the appropriate number of superclasses
684      *   (0 if no superclass was specified, 1 if one was), and the default
685      *   number of initial mutable properties
686      */
687     obj = new (vmg_ id) CVmObjTads(vmg_ (val.typ == VM_NIL ? 0 : 1),
688                                    VMTOBJ_PROP_INIT);
689 
690     /* set the object's superclass */
691     if (val.typ != VM_NIL)
692         obj->set_sc(vmg_ 0, val.val.obj);
693 
694     /*
695      *   Invoke the object's "construct" method, passing it the arguments
696      *   that are still on the stack.  If the new object doesn't define or
697      *   inherit the "construct" method, simply push the new object
698      *   reference onto the stack directly.
699      */
700     if (obj->get_prop(vmg_ G_predef->obj_construct, &val, id, &srcobj, 0))
701     {
702         vm_val_t srcobj_val;
703         vm_val_t id_val;
704         const uchar *dummy_pc_ptr;
705         uint caller_ofs;
706 
707         /* use the null PC pointer if the caller didn't supply one */
708         if (pc_ptr == 0)
709         {
710             /* there's no caller PC pointer - use a dummy value */
711             pc_ptr = &dummy_pc_ptr;
712             caller_ofs = 0;
713         }
714         else
715         {
716             /* get the caller's offset */
717             caller_ofs = G_interpreter->pc_to_method_ofs(*pc_ptr);
718         }
719 
720         /*
721          *   A "construct" method is defined - have the interpreter invoke
722          *   it, which will set up the interpreter to start executing its
723          *   byte-code.  This is all we need to do, since we assume and
724          *   require that the constructor will return the new object as
725          *   its return value when it's done.
726          */
727         srcobj_val.set_obj(srcobj);
728         id_val.set_obj(id);
729         *pc_ptr = G_interpreter->get_prop(vmg_ caller_ofs, &srcobj_val,
730                                           G_predef->obj_construct,
731                                           &id_val, argc);
732     }
733     else
734     {
735         /*
736          *   there's no "construct" method defined - if we have any
737          *   arguments, its an error
738          */
739         if (argc != 0)
740             err_throw(VMERR_WRONG_NUM_OF_ARGS);
741 
742         /* leave the new object value in R0 */
743         G_interpreter->get_r0()->set_obj(id);
744     }
745 
746     /* return the new object */
747     return id;
748 }
749 
750 /* create an object with no initial extension */
create(VMG_ int in_root_set)751 vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set)
752 {
753     vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
754     new (vmg_ id) CVmObjTads();
755     return id;
756 }
757 
758 /*
759  *   Create an object with a given number of superclasses, and a given
760  *   property table size.  Each superclass must be set before the object
761  *   can be used, and the property table is initially empty.
762  *
763  *   This form is used to create objects dynamically; this call is never
764  *   used to load an object from an image file.
765  */
create(VMG_ int in_root_set,ushort superclass_count,ushort prop_count)766 vm_obj_id_t CVmObjTads::create(VMG_ int in_root_set,
767                                ushort superclass_count, ushort prop_count)
768 {
769     vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
770     new (vmg_ id) CVmObjTads(vmg_ superclass_count, prop_count);
771     return id;
772 }
773 
774 /*
775  *   Create an instance based on multiple superclasses, using the
776  *   createInstanceOf() interface.  Arguments are passed on the stack.  Each
777  *   argument gives a superclass, and optionally the arguments for its
778  *   inherited constructor.  If an argument is a simple object/class, then we
779  *   won't inherit that object's constructor at all.  If an argument is a
780  *   list, then the first element of the list gives the class, and the
781  *   remaining elements of the list give the arguments to pass to that
782  *   class's inherited constructor.
783  */
create_from_stack_multi(VMG_ uint argc,int is_transient)784 vm_obj_id_t CVmObjTads::create_from_stack_multi(
785     VMG_ uint argc, int is_transient)
786 {
787     vm_obj_id_t id;
788     CVmObjTads *obj;
789     uint i;
790 
791     /* allocate an object ID */
792     id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
793     if (is_transient)
794         G_obj_table->set_obj_transient(id);
795 
796     /* create the new object */
797     obj = new (vmg_ id) CVmObjTads(vmg_ argc, VMTOBJ_PROP_INIT);
798 
799     /* push the new object, for garbage collector protection */
800     G_interpreter->push_obj(vmg_ id);
801 
802     /* set the superclasses */
803     for (i = 0 ; i < argc ; ++i)
804     {
805         vm_val_t *arg;
806         vm_val_t sc;
807         const char *lstp;
808 
809         /*
810          *   get this argument (it's at i+1 because of the extra item we
811          *   pushed for gc protection)
812          */
813         arg = G_stk->get(i + 1);
814 
815         /*
816          *   if it's a list, the superclass is the first element; otherwise,
817          *   the argument is the superclass
818          */
819         if ((lstp = arg->get_as_list(vmg0_)) != 0)
820         {
821             /* it's a list - the first element is the superclass */
822             CVmObjList::index_list(vmg_ &sc, lstp, 1);
823         }
824         else
825         {
826             /* not a list - the argument is the superclass */
827             sc = *arg;
828         }
829 
830         /* make sure it's a TadsObject */
831         if (sc.typ != VM_OBJ || !is_tadsobj_obj(vmg_ sc.val.obj))
832             err_throw(VMERR_BAD_TYPE_BIF);
833 
834         /* can't create an instance of a transient object */
835         if (G_obj_table->is_obj_transient(sc.val.obj))
836             err_throw(VMERR_BAD_DYNAMIC_NEW);
837 
838         /* set this superclass */
839         obj->set_sc(vmg_ i, sc.val.obj);
840     }
841 
842     /*
843      *   The new object is ready to go.  All that remains is invoking any
844      *   inherited construtors that the caller wants us to invoked.
845      *   Constructor invocation is indicated by passing a list argument for
846      *   the corresponding superclass, so run through the arguments and
847      *   invoke each indicated constructor.
848      */
849     for (i = 0 ; i < argc ; ++i)
850     {
851         vm_val_t *arg;
852         vm_val_t sc;
853         const char *lstp;
854         uint lst_cnt;
855         uint j;
856         vm_val_t new_obj_val;
857 
858         /* get the next argument */
859         arg = G_stk->get(i + 1);
860 
861         /* if it's not a list, we don't want to invoke this constructor */
862         if ((lstp = arg->get_as_list(vmg0_)) == 0)
863         {
864             /* no constructor call is wanted - just keep going */
865             continue;
866         }
867 
868         /* get the superclass from the list */
869         CVmObjList::index_list(vmg_ &sc, lstp, 1);
870 
871         /* get the number of list elements */
872         lst_cnt = vmb_get_len(lstp);
873 
874         /* make sure we have room to push the arguments */
875         if (!G_stk->check_space(lst_cnt - 1))
876             err_throw(VMERR_STACK_OVERFLOW);
877 
878         /*
879          *   push the list elements in reverse order; don't push the first
880          *   element, since it's the superclass itself rather than an
881          *   argument to the constructor
882          */
883         for (j = lst_cnt ; j > 1 ; --j)
884             CVmObjList::index_and_push(vmg_ lstp, j);
885 
886         /*
887          *   Invoke the constructor via a recursive call into the VM.  Note
888          *   that we're inheriting the property, so 'self' is the new object,
889          *   but the 'target' object is the superclass whose constructor
890          *   we're invoking.
891          */
892         new_obj_val.set_obj(id);
893         G_interpreter->get_prop(vmg_ 0, &sc, G_predef->obj_construct,
894                                 &new_obj_val, lst_cnt - 1);
895     }
896 
897     /* discard the arguments plus our own gc protection */
898     G_stk->discard(argc + 1);
899 
900     /* return the new object */
901     return id;
902 }
903 
904 /* ------------------------------------------------------------------------ */
905 /*
906  *   Constructors
907  */
908 
909 /*
910  *   Create an object with a given number of superclasses, and a given
911  *   property table size.  The superclasses must be individually set
912  *   before the object can be used, and the property table is initially
913  *   empty.
914  *
915  *   This constructor is used only when creating a new object dynamically,
916  *   and is never used to load an object from an image file.
917  */
CVmObjTads(VMG_ ushort superclass_count,ushort prop_count)918 CVmObjTads::CVmObjTads(VMG_ ushort superclass_count, ushort prop_count)
919 {
920     /* allocate our header */
921     ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, superclass_count,
922                                          prop_count);
923 }
924 
925 
926 /* ------------------------------------------------------------------------ */
927 /*
928  *   receive notification of deletion
929  */
notify_delete(VMG_ int in_root_set)930 void CVmObjTads::notify_delete(VMG_ int in_root_set)
931 {
932     /* free our extension */
933     if (ext_ != 0)
934     {
935         /* tell the header to delete its memory */
936         get_hdr()->free_mem();
937 
938         /* delete the extension */
939         G_mem->get_var_heap()->free_mem(ext_);
940     }
941 }
942 
943 /* ------------------------------------------------------------------------ */
944 /*
945  *   Create an instance of this class
946  */
create_instance(VMG_ vm_obj_id_t self,const uchar ** pc_ptr,uint argc)947 void CVmObjTads::create_instance(VMG_ vm_obj_id_t self,
948                                  const uchar **pc_ptr, uint argc)
949 {
950     /* push myself as the superclass */
951     G_stk->push()->set_obj(self);
952 
953     /* use the normal stack creation routine */
954     create_from_stack(vmg_ pc_ptr, argc+1);
955 }
956 
957 /* ------------------------------------------------------------------------ */
958 /*
959  *   Determine if the object has a finalizer method
960  */
has_finalizer(VMG_ vm_obj_id_t self)961 int CVmObjTads::has_finalizer(VMG_ vm_obj_id_t self)
962 {
963     vm_val_t val;
964     vm_obj_id_t srcobj;
965 
966     /*
967      *   look up the finalization method - if it's defined, and it's a
968      *   method, invoke it; otherwise do nothing
969      */
970     return (G_predef->obj_destruct != VM_INVALID_PROP
971             && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0)
972             && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE));
973 }
974 
975 /* ------------------------------------------------------------------------ */
976 /*
977  *   Invoke the object's finalizer
978  */
invoke_finalizer(VMG_ vm_obj_id_t self)979 void CVmObjTads::invoke_finalizer(VMG_ vm_obj_id_t self)
980 {
981     vm_val_t val;
982     vm_obj_id_t srcobj;
983 
984     /*
985      *   look up the finalization method - if it's defined, and it's a
986      *   method, invoke it; otherwise do nothing
987      */
988     if (G_predef->obj_destruct != VM_INVALID_PROP
989         && get_prop(vmg_ G_predef->obj_destruct, &val, self, &srcobj, 0)
990         && (val.typ == VM_CODEOFS || val.typ == VM_NATIVE_CODE))
991     {
992         /*
993          *   invoke the finalizer in a protected frame, to ensure that we
994          *   catch any exceptions that are thrown out of the finalizer
995          */
996         err_try
997         {
998             vm_val_t srcobj_val;
999             vm_val_t self_val;
1000 
1001             /*
1002              *   Invoke the finalizer.  Use a recursive VM invocation,
1003              *   since the VM must return to the garbage collector, not to
1004              *   what it was doing in the enclosing stack frame.
1005              */
1006             srcobj_val.set_obj(srcobj);
1007             self_val.set_obj(self);
1008             G_interpreter->get_prop(vmg_ 0, &srcobj_val,
1009                                     G_predef->obj_destruct, &self_val, 0);
1010         }
1011         err_catch(exc)
1012         {
1013             /* silently ignore the error */
1014         }
1015         err_end;
1016     }
1017 }
1018 
1019 
1020 /* ------------------------------------------------------------------------ */
1021 /*
1022  *   Clear the undo flags for all properties
1023  */
clear_undo_flags()1024 void CVmObjTads::clear_undo_flags()
1025 {
1026     vm_tadsobj_prop *entry;
1027     uint i;
1028     vm_tadsobj_hdr *hdr = get_hdr();
1029 
1030     /* scan all property entries and clear their undo flags */
1031     for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
1032          i != 0 ; --i, ++entry)
1033     {
1034         /* clear this entry's undo flag */
1035         entry->flags &= ~VMTO_PROP_UNDO;
1036     }
1037 }
1038 
1039 /* ------------------------------------------------------------------------ */
1040 /*
1041  *   Set a property
1042  */
set_prop(VMG_ CVmUndo * undo,vm_obj_id_t self,vm_prop_id_t prop,const vm_val_t * val)1043 void CVmObjTads::set_prop(VMG_ CVmUndo *undo, vm_obj_id_t self,
1044                           vm_prop_id_t prop, const vm_val_t *val)
1045 {
1046     vm_tadsobj_prop *entry;
1047     vm_val_t oldval;
1048     vm_tadsobj_hdr *hdr = get_hdr();
1049 
1050     /* look for an existing property entry */
1051     entry = hdr->find_prop_entry(prop);
1052 
1053     /* if we didn't find an existing entry, allocate a new one */
1054     if (entry != 0)
1055     {
1056         /* note the old value */
1057         oldval = entry->val;
1058 
1059         /* store the new value */
1060         entry->val = *val;
1061     }
1062     else
1063     {
1064         /* if we don't have any free properties left, reallocate */
1065         if (!hdr->has_free_entries(1))
1066         {
1067             /* expand the extension to make room for more properties */
1068             ext_ = (char *)vm_tadsobj_hdr::expand(vmg_ this, hdr);
1069 
1070             /* get the reallocated header */
1071             hdr = get_hdr();
1072         }
1073 
1074         /* allocate a new entry */
1075         entry = hdr->alloc_prop_entry(prop, val, 0);
1076 
1077         /* the old value didn't exist, so mark it emtpy */
1078         oldval.set_empty();
1079     }
1080 
1081     /*
1082      *   If we already have undo for this property for the current
1083      *   savepoint, as indicated by the undo flag for the property, we don't
1084      *   need to save undo for this change, since we already have an undo
1085      *   record in the current savepoint.  Otherwise, we need to add an undo
1086      *   record for this savepoint.
1087      */
1088     if (undo != 0 && (entry->flags & VMTO_PROP_UNDO) == 0)
1089     {
1090         /* save the undo record */
1091         undo->add_new_record_prop_key(vmg_ self, prop, &oldval);
1092 
1093         /* mark the property as now having undo in this savepoint */
1094         entry->flags |= VMTO_PROP_UNDO;
1095 
1096         /*
1097          *   If the entry wasn't previously marked as modified, remember this
1098          *   by storing an extra 'empty' undo record after the record we just
1099          *   saved.  We undo in reverse order, so the extra empty record
1100          *   won't actually have any effect on the property value - we'll
1101          *   immediately overwrite it with the actual value we just stored
1102          *   above.  However, whenever we see an empty record, we remove the
1103          *   'modified' flag from the property, so this will have the effect
1104          *   of undoing the modified flag.  Note that we don't need to bother
1105          *   if the record we just stored was itself empty.
1106          */
1107         if ((entry->flags & VMTO_PROP_MOD) == 0 && oldval.typ != VM_EMPTY)
1108         {
1109             /* store an empty record to undo the 'modify' flag */
1110             oldval.set_empty();
1111             undo->add_new_record_prop_key(vmg_ self, prop, &oldval);
1112         }
1113     }
1114 
1115     /* mark the property entry as modified */
1116     entry->flags |= VMTO_PROP_MOD;
1117 
1118     /* mark the entire object as modified */
1119     hdr->intern_obj_flags |= VMTO_OBJ_MOD;
1120 }
1121 
1122 /* ------------------------------------------------------------------------ */
1123 /*
1124  *   Build a list of my properties
1125  */
build_prop_list(VMG_ vm_obj_id_t self,vm_val_t * retval)1126 void CVmObjTads::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval)
1127 {
1128     size_t cnt;
1129     size_t idx;
1130     CVmObjList *lst;
1131     vm_tadsobj_prop *entry;
1132     vm_tadsobj_hdr *hdr = get_hdr();
1133 
1134     /* the next free index is also the number of properties we have */
1135     cnt = hdr->prop_entry_free;
1136 
1137     /* allocate a list big enough for all of our properties */
1138     retval->set_obj(CVmObjList::create(vmg_ FALSE, cnt));
1139 
1140     /* get the list object, property cast */
1141     lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);
1142 
1143     /* add our image file properties to the list */
1144     for (idx = 0, entry = hdr->prop_entry_arr ; cnt != 0 ;
1145          --cnt, ++entry)
1146     {
1147         /* if this entry isn't empty, store it */
1148         if (entry->val.typ != VM_EMPTY)
1149         {
1150             vm_val_t val;
1151 
1152             /* make a value for this property ID */
1153             val.set_propid(entry->prop);
1154 
1155             /* add it to the list */
1156             lst->cons_set_element(idx++, &val);
1157         }
1158     }
1159 
1160     /*
1161      *   set the final length, which might differ from the allocated length:
1162      *   we might have had some slots that were empty and thus didn't
1163      *   contribute to the list
1164      */
1165     lst->cons_set_len(idx);
1166 }
1167 
1168 
1169 /* ------------------------------------------------------------------------ */
1170 /*
1171  *   Call a static method.
1172  */
call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)1173 int CVmObjTads::call_stat_prop(VMG_ vm_val_t *result,
1174                                const uchar **pc_ptr, uint *argc,
1175                                vm_prop_id_t prop)
1176 {
1177     int idx;
1178 
1179     /* convert the property to an index in our method vector */
1180     idx = G_meta_table
1181           ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
1182 
1183     /* check what property they're evaluating */
1184     switch(idx)
1185     {
1186     case PROPIDX_CREATE_INSTANCE:
1187     case PROPIDX_CREATE_TRANS_INSTANCE:
1188         {
1189             static CVmNativeCodeDesc desc(0);
1190 
1191             /* check arguments */
1192             if (get_prop_check_argc(result, argc, &desc))
1193                 return TRUE;
1194 
1195             /*
1196              *   They want to create an instance of TadsObject, which is
1197              *   just a plain base object with no superclass.  Push null as
1198              *   the base class and call our from-stack constructor.
1199              */
1200             result->set_obj(create_from_stack_intern(
1201                 vmg_ pc_ptr, 0, idx == PROPIDX_CREATE_TRANS_INSTANCE));
1202         }
1203 
1204         /* handled */
1205         return TRUE;
1206 
1207     case PROPIDX_CREATE_INSTANCE_OF:
1208     case PROPIDX_CREATE_TRANS_INSTANCE_OF:
1209         {
1210             static CVmNativeCodeDesc desc(0, 0, TRUE);
1211             uint in_argc = (argc == 0 ? 0 : *argc);
1212 
1213             /* check arguments */
1214             if (get_prop_check_argc(result, argc, &desc))
1215                 return TRUE;
1216 
1217             /*
1218              *   They want to create an instance of TadsObject, which is just
1219              *   a plain base object with no superclass.  Push null as the
1220              *   base class and call our from-stack constructor.
1221              */
1222             result->set_obj(create_from_stack_multi(
1223                 vmg_ in_argc, idx == PROPIDX_CREATE_TRANS_INSTANCE_OF));
1224         }
1225 
1226         /* handled */
1227         return TRUE;
1228 
1229     default:
1230         /* it's not one of ours; inherit the base class statics */
1231         return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
1232     }
1233 }
1234 
1235 /* ------------------------------------------------------------------------ */
1236 /*
1237  *   Superclass inheritance search context.  This keeps track of our position
1238  *   in searching the inheritance tree of a given class.
1239  */
1240 struct tadsobj_sc_search_ctx
1241 {
1242     /* initialize at a given object */
tadsobj_sc_search_ctxtadsobj_sc_search_ctx1243     tadsobj_sc_search_ctx(VMG_ vm_obj_id_t obj, CVmObjTads *objp)
1244     {
1245         /* start at the given object */
1246         cur = obj;
1247         curp = objp;
1248 
1249         /* we have no path yet */
1250         path_rem = -1;
1251     }
1252 
1253     /* current object ID and pointer */
1254     vm_obj_id_t cur;
1255     CVmObjTads *curp;
1256 
1257     /*
1258      *   If we have a search path, the position in the path and the number of
1259      *   elements remaining.  We use the special remaining path length of -1
1260      *   to indicate that we're not looking at a path at all; this is useful
1261      *   because it allows us to perform a single test to determine if we're
1262      *   operating on a path with elements remaining, operating on an empty
1263      *   path, or working without a path at all.  (This code gets hit *a
1264      *   lot*, so we want it as fast as possible.)
1265      */
1266     tadsobj_objid_and_ptr *path_sc;
1267     int path_rem;
1268 
1269     /*
1270      *   Find the given property, searching our superclass list until we find
1271      *   an object providing the property.  Returns true if found, and fills
1272      *   in *val and *source.  Returns false if not found.
1273      */
find_proptadsobj_sc_search_ctx1274     int find_prop(VMG_ uint prop, vm_val_t *val, vm_obj_id_t *source)
1275     {
1276         /* keep going until we find the property */
1277         for (;;)
1278         {
1279             vm_tadsobj_prop *entry;
1280 
1281             /* look for this property in the current object */
1282             entry = curp->get_hdr()->find_prop_entry(prop);
1283 
1284             /* if we found a non-empty entry, return the value */
1285             if (entry != 0 && entry->val.typ != VM_EMPTY)
1286             {
1287                 /* we found the property - return it */
1288                 *val = entry->val;
1289                 *source = cur;
1290                 return TRUE;
1291             }
1292 
1293             /* didn't find it - move to the next search position */
1294             if (!to_next(vmg0_))
1295             {
1296                 /* there's nowhere else to search - we've failed to find it */
1297                 return FALSE;
1298             }
1299         }
1300     }
1301 
1302     /*
1303      *   Skip to the given object.  If we find the object in the path, we'll
1304      *   leave the current position set to the given object and return true;
1305      *   if we fail to find the object, we'll return false.
1306      */
skip_totadsobj_sc_search_ctx1307     int skip_to(VMG_ vm_obj_id_t target)
1308     {
1309         /* keep going until the current object matches the target */
1310         while (cur != target)
1311         {
1312             /* move to the next element */
1313             if (!to_next(vmg0_))
1314             {
1315                 /* there's nothing left - return failure */
1316                 return FALSE;
1317             }
1318         }
1319 
1320         /* found it */
1321         return TRUE;
1322     }
1323 
1324     /*
1325      *   Move to the next superclass.  This updates 'cur' to refer to the
1326      *   next object in inheritance order.  Returns true if there is a next
1327      *   element, false if not.
1328      *
1329      *   It is legal to call this with 'cur' set to an arbitrary object, as
1330      *   we do not need the old value of 'cur' to do our work.  (This is
1331      *   important because it allows a search position to be initialized
1332      *   knowing only an object's 'this' pointer, not its object ID.)
1333      */
to_nexttadsobj_sc_search_ctx1334     int to_next(VMG0_)
1335     {
1336         tadsobj_inh_path *path;
1337         vm_tadsobj_hdr *hdr;
1338 
1339         /*
1340          *   If we have a path, continue with it.  Note that the special
1341          *   value -1 for the remaining length indicates that we're not
1342          *   working on a path at all.
1343          */
1344         switch(path_rem)
1345         {
1346         case 0:
1347             /*
1348              *   we're working on a path, and we're out of elements - we have
1349              *   nowhere else to go
1350              */
1351             return FALSE;
1352 
1353         default:
1354             /*
1355              *   we're working on a path, and we have elements remaining -
1356              *   move on to the next element
1357              */
1358             cur = path_sc->id;
1359             curp = path_sc->objp;
1360             ++path_sc;
1361             --path_rem;
1362 
1363             /* got it */
1364             return TRUE;
1365 
1366         case -1:
1367             /*
1368              *   we're not working on a path at all - this means we're
1369              *   working directly on a (so far) single-inheritance superclass
1370              *   chain, so simply follow the chain up to the next superclass
1371              */
1372 
1373             /* get this object's header */
1374             hdr = curp->get_hdr();
1375 
1376             /* we have no path, so look at our object's superclasses */
1377             switch(hdr->sc_cnt)
1378             {
1379             case 1:
1380                 /* we have exactly one superclass, so traverse to it */
1381                 cur = hdr->sc[0].id;
1382                 if ((curp = hdr->sc[0].objp) == 0)
1383                     curp = hdr->sc[0].objp = (CVmObjTads *)vm_objp(vmg_ cur);
1384                 return TRUE;
1385 
1386             case 0:
1387                 /* we have no superclasses, so there's nowhere to go */
1388                 return FALSE;
1389 
1390             default:
1391                 /* we have multiple superclasses, so set up the search path */
1392                 if ((path = hdr->inh_path) == 0
1393                     && (path = curp->get_inh_search_path(vmg0_)) == 0)
1394                 {
1395                     /* there's no path, so there's nowhere to go */
1396                     return FALSE;
1397                 }
1398 
1399                 /* move to the first element of the path */
1400                 path_rem = path->cnt - 1;
1401                 path_sc = path->sc;
1402                 cur = path_sc->id;
1403                 curp = path_sc->objp;
1404                 ++path_sc;
1405                 return TRUE;
1406             }
1407         }
1408     }
1409 };
1410 
1411 /*
1412  *   Search for a property via inheritance, starting after the given defining
1413  *   object.
1414  */
search_for_prop_from(VMG_ uint prop,vm_val_t * val,vm_obj_id_t orig_target_obj,vm_obj_id_t * source_obj,vm_obj_id_t defining_obj)1415 int CVmObjTads::search_for_prop_from(VMG_ uint prop,
1416                                      vm_val_t *val,
1417                                      vm_obj_id_t orig_target_obj,
1418                                      vm_obj_id_t *source_obj,
1419                                      vm_obj_id_t defining_obj)
1420 {
1421     /* set up a search position */
1422     tadsobj_sc_search_ctx curpos(vmg_ orig_target_obj,
1423                                  (CVmObjTads *)vm_objp(vmg_ orig_target_obj));
1424 
1425     /* if we have a starting point, skip past it */
1426     if (defining_obj != VM_INVALID_OBJ)
1427     {
1428         /* skip until we're at defining_obj */
1429         if (!curpos.skip_to(vmg_ defining_obj))
1430             return FALSE;
1431 
1432         /* skip defining_obj itself */
1433         if (!curpos.to_next(vmg0_))
1434             return FALSE;
1435     }
1436 
1437     /* find the property */
1438     return curpos.find_prop(vmg_ prop, val, source_obj);
1439 }
1440 
1441 /* ------------------------------------------------------------------------ */
1442 /*
1443  *   Get a property.  We first look in this object; if we can't find the
1444  *   property here, we look for it in one of our superclasses.
1445  */
get_prop(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t * source_obj,uint * argc)1446 int CVmObjTads::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
1447                          vm_obj_id_t self, vm_obj_id_t *source_obj,
1448                          uint *argc)
1449 {
1450     /*
1451      *   Try finding the property in our property list or a superclass
1452      *   property list.  Since we're starting a new search, 'self' is the
1453      *   original target object, and we do not have a previous defining
1454      *   object.
1455      */
1456     tadsobj_sc_search_ctx curpos(vmg_ self, this);
1457     if (curpos.find_prop(vmg_ prop, val, source_obj))
1458         return TRUE;
1459 
1460     /*
1461      *   we didn't find the property in a property list, so try the
1462      *   intrinsic class methods
1463      */
1464     if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc))
1465         return TRUE;
1466 
1467     /*
1468      *   we didn't find the property among our methods, so try inheriting it
1469      *   from the base metaclass
1470      */
1471     return CVmObject::get_prop(vmg_ prop, val, self, source_obj, argc);
1472 }
1473 
1474 /*
1475  *   Inherit a property.
1476  */
inh_prop(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t orig_target_obj,vm_obj_id_t defining_obj,vm_obj_id_t * source_obj,uint * argc)1477 int CVmObjTads::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
1478                          vm_obj_id_t self,
1479                          vm_obj_id_t orig_target_obj,
1480                          vm_obj_id_t defining_obj,
1481                          vm_obj_id_t *source_obj, uint *argc)
1482 {
1483     /*
1484      *   check to see if we're already inheriting from intrinsic class
1485      *   modifiers
1486      */
1487     if (defining_obj == VM_INVALID_OBJ
1488         || !CVmObjIntClsMod::is_intcls_mod_obj(vmg_ defining_obj))
1489     {
1490         /*
1491          *   The previous defining object wasn't itself a modifier object, so
1492          *   continue searching for TadsObject superclasses.
1493          */
1494         if (search_for_prop_from(vmg_ prop, val,
1495                                  orig_target_obj, source_obj, defining_obj))
1496             return TRUE;
1497 
1498         /*
1499          *   We didn't find the property in a property list.  Since we were
1500          *   inheriting, we must have originally found it in a property list,
1501          *   but we've found no more inherited properties.  Next, check the
1502          *   intrinsic methods of the intrinsic class.
1503          */
1504         if (get_prop_intrinsic(vmg_ prop, val, self, source_obj, argc))
1505             return TRUE;
1506 
1507         /*
1508          *   We didn't find it among our TadsObject superclasses or as an
1509          *   intrinsic method.  There's still one possibility: it could be
1510          *   defined in an intrinsic class modifier for TadsObject or one of
1511          *   its intrinsic superclasses (aka supermetaclasses).
1512          *
1513          *   This represents a new starting point in the search.  No longer
1514          *   are we looking for TadsObject overrides; we're now looking for
1515          *   modifier objects.  The modifier objects effectively form a
1516          *   separate class hierarchy alongside the intrinsic class hierarchy
1517          *   they modify.  Since we're starting a new search in this new
1518          *   context, forget the previous defining object - it has a
1519          *   different meaning in the new context, and we want to start the
1520          *   new search from the beginning.
1521          *
1522          *   Note that if this search does turn up a modifier object, and
1523          *   that modifier object further inherits, we'll come back through
1524          *   this method again to find the base class method.  At that point,
1525          *   however we'll notice that the previous defining object was a
1526          *   modifier, so we will not go through this branch again - we'll go
1527          *   directly to the base metaclass and continue the inheritance
1528          *   search there.
1529          */
1530         defining_obj = VM_INVALID_OBJ;
1531     }
1532 
1533     /* continue searching via our base metaclass */
1534     return CVmObject::inh_prop(vmg_ prop, val, self, orig_target_obj,
1535                                defining_obj, source_obj, argc);
1536 }
1537 
1538 /* ------------------------------------------------------------------------ */
1539 /*
1540  *   Get a property from the intrinsic class.
1541  */
get_prop_intrinsic(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t * source_obj,uint * argc)1542 int CVmObjTads::get_prop_intrinsic(VMG_ vm_prop_id_t prop, vm_val_t *val,
1543                                    vm_obj_id_t self, vm_obj_id_t *source_obj,
1544                                    uint *argc)
1545 {
1546     unsigned short func_idx;
1547 
1548     /* translate the property into a function vector index */
1549     func_idx = G_meta_table
1550                ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
1551 
1552     /* call the appropriate function in our function vector */
1553     if ((this->*func_table_[func_idx])(vmg_ self, val, argc))
1554     {
1555         *source_obj = metaclass_reg_->get_class_obj(vmg0_);
1556         return TRUE;
1557     }
1558 
1559     /* didn't find it */
1560     return FALSE;
1561 }
1562 
1563 /* ------------------------------------------------------------------------ */
1564 /*
1565  *   Get the inheritance search path for this object
1566  */
get_inh_search_path(VMG0_)1567 tadsobj_inh_path *CVmObjTads::get_inh_search_path(VMG0_)
1568 {
1569     CVmObjTads *curp;
1570     CVmObjTadsInhQueue *q = G_tadsobj_queue;
1571     pfq_ele *q_ele;
1572     vm_tadsobj_hdr *hdr = get_hdr();
1573     tadsobj_inh_path *path;
1574 
1575     /*
1576      *   There are multiple superclasses.  If we've already calculated a
1577      *   path for this object, simply use the pre-calculated path: the
1578      *   superclass relationships among objects never change, so the path is
1579      *   good forever.
1580      */
1581     if (hdr->inh_path != 0)
1582         return hdr->inh_path;
1583 
1584     /*
1585      *   We haven't already cached a search path for this object, so build
1586      *   the search path now and save it for future searches.  Start by
1587      *   clearing the work queue.
1588      */
1589     q->clear();
1590 
1591     /* we're not yet processing the first element */
1592     q_ele = 0;
1593 
1594     /* start with self */
1595     curp = this;
1596 
1597     /* keep going until we run out of queue elements */
1598     for (;;)
1599     {
1600         ushort i;
1601         ushort cnt;
1602         pfq_ele *q_ins;
1603         vm_tadsobj_sc *scp;
1604         vm_tadsobj_hdr *curhdr;
1605 
1606         /* get the superclass count for this object */
1607         curhdr = curp->get_hdr();
1608         cnt = curhdr->sc_cnt;
1609 
1610         /* insert my superclasses right after me */
1611         q_ins = q_ele;
1612 
1613         /* enqueue the current object's superclasses */
1614         for (i = 0, scp = curhdr->sc ; i < cnt ; ++i, ++scp)
1615         {
1616             vm_obj_id_t sc;
1617             CVmObjTads *scobj;
1618 
1619             /* get the current superclass */
1620             sc = scp->id;
1621             if ((scobj = scp->objp) == 0)
1622                 scobj = scp->objp = (CVmObjTads *)vm_objp(vmg_ sc);
1623 
1624             /* if it's not a TadsObject, skip it */
1625             if (scobj->get_metaclass_reg() != curp->get_metaclass_reg())
1626                 continue;
1627 
1628             /* enqueue this superclass */
1629             q_ins = q->insert_obj(vmg_ sc, scobj, q_ins);
1630         }
1631 
1632         /* move to the next valid element */
1633         for (;;)
1634         {
1635             /* get the next queue element */
1636             q_ele = (q_ele == 0 ? q->get_head() : q_ele->nxt);
1637 
1638             /*
1639              *   if it's valid, or we're out of elements, stop searching for
1640              *   it
1641              */
1642             if (q_ele == 0 || q_ele->obj != VM_INVALID_OBJ)
1643                 break;
1644         }
1645 
1646         /* if we ran out of elements, we're done */
1647         if (q_ele == 0)
1648             break;
1649 
1650         /* get this item */
1651         curp = q_ele->objp;
1652     }
1653 
1654     /*
1655      *   if the linearized path is empty, there's nowhere to go from here,
1656      *   so we've failed to find the property
1657      */
1658     if (q->is_empty())
1659         return 0;
1660 
1661     /* create and cache a linearized path for the queue, and return it */
1662     path = hdr->inh_path = q->create_path();
1663     return path;
1664 }
1665 
1666 /* ------------------------------------------------------------------------ */
1667 /*
1668  *   Enumerate properties
1669  */
enum_props(VMG_ vm_obj_id_t self,void (* cb)(VMG_ void * ctx,vm_obj_id_t self,vm_prop_id_t prop,const vm_val_t * val),void * cbctx)1670 void CVmObjTads::enum_props(VMG_ vm_obj_id_t self,
1671                             void (*cb)(VMG_ void *ctx, vm_obj_id_t self,
1672                                        vm_prop_id_t prop,
1673                                        const vm_val_t *val),
1674                             void *cbctx)
1675 {
1676     size_t i;
1677     size_t sc_cnt;
1678     vm_tadsobj_prop *entry;
1679     vm_tadsobj_hdr *hdr = get_hdr();
1680 
1681     /* run through our non-empty properties */
1682     for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
1683          i != 0 ; --i, ++entry)
1684     {
1685         /* if this one is non-empty, invoke the callback */
1686         if (entry->val.typ != VM_EMPTY)
1687             (*cb)(vmg_ cbctx, self, entry->prop, &entry->val);
1688     }
1689 
1690     /* enumerate properties in each superclass */
1691     sc_cnt = get_sc_count();
1692     for (i = 0 ; i < sc_cnt ; ++i)
1693     {
1694         vm_obj_id_t sc;
1695 
1696         /* get this superclass */
1697         sc = get_sc(i);
1698 
1699         /* enumerate its properties */
1700         vm_objp(vmg_ sc)->enum_props(vmg_ sc, cb, cbctx);
1701     }
1702 }
1703 
1704 
1705 /* ------------------------------------------------------------------------ */
1706 /*
1707  *   Determine if I'm an instance of the given object
1708  */
is_instance_of(VMG_ vm_obj_id_t obj)1709 int CVmObjTads::is_instance_of(VMG_ vm_obj_id_t obj)
1710 {
1711     /*
1712      *   Set up a superclass search position.  Since the first thing we'll
1713      *   do is call 'to_next', and since 'to_next' doesn't require a valid
1714      *   current object ID (only a valid 'this' pointer), we don't need to
1715      *   know our own object ID - simply set the initial object ID to the
1716      *   invalid ID.
1717      */
1718     tadsobj_sc_search_ctx curpos(vmg_ VM_INVALID_OBJ, this);
1719 
1720     /*
1721      *   scan through the search list, comparing each superclass to the
1722      *   object of interest; if we find it among our superclasses, we're an
1723      *   instance of the given object
1724      */
1725     for (;;)
1726     {
1727         /* skip to the next object */
1728         if (!curpos.to_next(vmg0_))
1729         {
1730             /* we've run out of superclasses without finding it */
1731             break;
1732         }
1733 
1734         /*
1735          *   if the current superclass is the object we're looking for, then
1736          *   we're an instance of that object
1737          */
1738         if (curpos.cur == obj)
1739             return TRUE;
1740     }
1741 
1742     /*
1743      *   None of our superclasses match the given object, and none of the
1744      *   superclasses derive from the given object, so we must not derive
1745      *   from the given object.  Our last recourse is to determine if the
1746      *   object represents our metaclass; inherit the default handling to
1747      *   make this check.
1748      */
1749     return CVmObject::is_instance_of(vmg_ obj);
1750 }
1751 
1752 /* ------------------------------------------------------------------------ */
1753 /*
1754  *   Apply undo
1755  */
apply_undo(VMG_ CVmUndoRecord * rec)1756 void CVmObjTads::apply_undo(VMG_ CVmUndoRecord *rec)
1757 {
1758     vm_tadsobj_prop *entry;
1759     vm_tadsobj_hdr *hdr = get_hdr();
1760 
1761     /* find the property entry for the property being undone */
1762     entry = hdr->find_prop_entry(rec->id.prop);
1763     if (entry == 0)
1764     {
1765         /* can't find the property - something is out of whack */
1766         assert(FALSE);
1767         return;
1768     }
1769 
1770     /*
1771      *   Restore the value from the record.  Note that if the property
1772      *   didn't previously exist, this will store 'empty' in the slot; we
1773      *   don't actually delete the slot, but the 'empty' marker is
1774      *   equivalent, in that we treat it as a property we don't define.
1775      */
1776     entry->val = rec->oldval;
1777 
1778     /*
1779      *   If the old value was 'empty', mark the slot as unmodified.  Since
1780      *   the property didn't exist previously, it can't have been modified
1781      *   previously.  Note that we add an artifical extra 'empty' record the
1782      *   first time an existing load image property is modified, so that this
1783      *   un-setting of the 'modified' flag will happen even for properties
1784      *   that existed before the first modification.
1785      */
1786     if (rec->oldval.typ == VM_EMPTY)
1787     {
1788         size_t i;
1789         int found_mod;
1790 
1791         /* clear the 'modified' flag on the property */
1792         entry->flags &= ~VMTO_PROP_MOD;
1793 
1794         /*
1795          *   scan the properties to see if we still need the 'modified' flag
1796          *   on the object itself - this might have been the only remaining
1797          *   modified property, in which case we no longer have any modified
1798          *   properties and thus no longer have a modified object
1799          */
1800         for (found_mod = FALSE, i = hdr->prop_entry_free,
1801              entry = hdr->prop_entry_arr ; i != 0 ; --i, ++entry)
1802         {
1803             /*
1804              *   if this is property is marked as modified, we still have a
1805              *   modified object
1806              */
1807             if ((entry->flags & VMTO_PROP_MOD) != 0)
1808             {
1809                 /* note that we found a modified property */
1810                 found_mod = TRUE;
1811 
1812                 /* no need to look any further */
1813                 break;
1814             }
1815         }
1816 
1817         /*
1818          *   if we found no modified properties, the object is no longer
1819          *   modified, so clear its 'modified' flag
1820          */
1821         if (!found_mod)
1822             hdr->intern_obj_flags &= ~VMTO_OBJ_MOD;
1823     }
1824 }
1825 
1826 
1827 /* ------------------------------------------------------------------------ */
1828 /*
1829  *   Mark as referenced all of the objects to which we refer
1830  */
mark_refs(VMG_ uint state)1831 void CVmObjTads::mark_refs(VMG_ uint state)
1832 {
1833     size_t i;
1834     vm_tadsobj_hdr *hdr = get_hdr();
1835     vm_tadsobj_prop *entry;
1836     vm_tadsobj_sc *scp;
1837 
1838     /*
1839      *   Go through all of our property slots and mark each object value.
1840      *   Note that we only need to worry about the modified properties;
1841      *   everything referenced in the load image list is necessarily part of
1842      *   the root set, or it couldn't have been in the load image, so we
1843      *   don't need to bother marking any of those objects, since they can
1844      *   never be deleted by virtue of being in the root set.
1845      */
1846     for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
1847          i != 0 ; --i, ++entry)
1848     {
1849         /*
1850          *   if the slot is marked as modified and contains an object
1851          *   reference, mark the reference
1852          */
1853         if ((entry->flags & VMTO_PROP_MOD) != 0
1854             && entry->val.typ == VM_OBJ
1855             && entry->val.val.obj != VM_INVALID_OBJ)
1856         {
1857             /* mark the reference */
1858             G_obj_table->mark_all_refs(entry->val.val.obj, state);
1859         }
1860     }
1861 
1862     /* mark our superclasses as referenced */
1863     for (i = hdr->sc_cnt, scp = hdr->sc ; i != 0 ; --i, ++scp)
1864         G_obj_table->mark_all_refs(scp->id, state);
1865 }
1866 
1867 
1868 /* ------------------------------------------------------------------------ */
1869 /*
1870  *   Mark a reference in an undo record
1871  */
mark_undo_ref(VMG_ CVmUndoRecord * undo)1872 void CVmObjTads::mark_undo_ref(VMG_ CVmUndoRecord *undo)
1873 {
1874     /* if the undo record refers to an object, mark the object */
1875     if (undo->oldval.typ == VM_OBJ)
1876         G_obj_table->mark_all_refs(undo->oldval.val.obj, VMOBJ_REACHABLE);
1877 }
1878 
1879 /* ------------------------------------------------------------------------ */
1880 /*
1881  *   Determine if the object has been changed since it was loaded from the
1882  *   image file.  If the object has no properties stored in the modified
1883  *   properties table, it is in exactly the same state as is stored in the
1884  *   image file.
1885  */
is_changed_since_load() const1886 int CVmObjTads::is_changed_since_load() const
1887 {
1888     /* return our 'modified' flag */
1889     return ((get_hdr()->intern_obj_flags & VMTO_OBJ_MOD) != 0);
1890 }
1891 
1892 /* ------------------------------------------------------------------------ */
1893 /*
1894  *   Save the object's state to a file.  We only need to save the modified
1895  *   property list, because the load image list never changes.
1896  */
save_to_file(VMG_ CVmFile * fp)1897 void CVmObjTads::save_to_file(VMG_ CVmFile *fp)
1898 {
1899     size_t i;
1900     vm_tadsobj_prop *entry;
1901     uint cnt;
1902     vm_tadsobj_hdr *hdr = get_hdr();
1903 
1904     /* count the number of properties that have actually been modified */
1905     for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
1906          i != 0 ; --i, ++entry)
1907     {
1908         /* if the slot is non-empty and modified, count it */
1909         if ((entry->flags & VMTO_PROP_MOD) != 0
1910             && entry->val.typ != VM_EMPTY)
1911             ++cnt;
1912     }
1913 
1914     /* write the number of modified properties */
1915     fp->write_int2(cnt);
1916 
1917     /* write the number of superclasses */
1918     fp->write_int2(get_sc_count());
1919 
1920     /* write the superclasses */
1921     for (i = 0 ; i < get_sc_count() ; ++i)
1922         fp->write_int4(get_sc(i));
1923 
1924     /* write each modified property */
1925     for (cnt = 0, i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
1926          i != 0 ; --i, ++entry)
1927     {
1928         /* if the slot is non-empty and modified, write it out */
1929         if ((entry->flags & VMTO_PROP_MOD) != 0
1930             && entry->val.typ != VM_EMPTY)
1931         {
1932             char slot[16];
1933 
1934             /* prepare the slot data */
1935             oswp2(slot, entry->prop);
1936             vmb_put_dh(slot + 2, &entry->val);
1937 
1938             /* write the slot */
1939             fp->write_bytes(slot, 2 + VMB_DATAHOLDER);
1940         }
1941     }
1942 }
1943 
1944 /* ------------------------------------------------------------------------ */
1945 /*
1946  *   Restore the object from a file
1947  */
restore_from_file(VMG_ vm_obj_id_t self,CVmFile * fp,CVmObjFixup * fixups)1948 void CVmObjTads::restore_from_file(VMG_ vm_obj_id_t self,
1949                                    CVmFile *fp, CVmObjFixup *fixups)
1950 {
1951     ushort mod_count;
1952     ushort i;
1953     ushort sc_cnt;
1954     vm_tadsobj_hdr *hdr;
1955 
1956     /* read number of modified properties */
1957     mod_count = fp->read_uint2();
1958 
1959     /* read the number of superclasses */
1960     sc_cnt = fp->read_uint2();
1961 
1962     /*
1963      *   If we don't have an extension yet, allocate one.  The only way we
1964      *   won't have an extension is if we weren't loaded from the image
1965      *   file, since we always create the extension upon construction when
1966      *   loading from an image file.
1967      */
1968     if (ext_ == 0)
1969     {
1970         /* allocate our extension */
1971         ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, mod_count);
1972     }
1973     else
1974     {
1975         /*
1976          *   We already have an extension, so we must have come from the
1977          *   image file.  Make sure we have enough memory to hold this many
1978          *   properties, and make sure we have space for the superclasses.
1979          */
1980         hdr = get_hdr();
1981         if (!hdr->has_free_entries(mod_count) || sc_cnt > hdr->sc_cnt)
1982         {
1983             /*
1984              *   we need to expand the header to accomodate the modified
1985              *   properties and/or the modified superclass list
1986              */
1987             ext_ = (char *)vm_tadsobj_hdr::expand_to(
1988                 vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt + mod_count);
1989         }
1990     }
1991 
1992     /* get the extension header */
1993     hdr = get_hdr();
1994 
1995     /* read the superclass list */
1996     hdr->sc_cnt = sc_cnt;
1997     for (i = 0 ; i < sc_cnt ; ++i)
1998     {
1999         vm_obj_id_t sc;
2000 
2001         /* read the next superclass */
2002         sc = (vm_obj_id_t)fp->read_int4();
2003 
2004         /* fix it up to the memory numbering system */
2005         sc = fixups->get_new_id(vmg_ sc);
2006 
2007         /*
2008          *   store it - as when loading from the image file, we can't count
2009          *   on the superclass having been loaded yet, so we can only store
2010          *   the superclass's ID, not its actual object pointer
2011          */
2012         hdr->sc[i].id = sc;
2013         hdr->sc[i].objp = 0;
2014     }
2015 
2016     /*
2017      *   invalidate any existing inheritance path, in case the superclass
2018      *   list changed
2019      */
2020     hdr->inval_inh_path();
2021 
2022     /* read the modified properties */
2023     for (i = 0 ; i < mod_count ; ++i)
2024     {
2025         char buf[32];
2026         vm_prop_id_t prop;
2027         vm_val_t val;
2028 
2029         /* read the next slot */
2030         fp->read_bytes(buf, 2 + VMB_DATAHOLDER);
2031 
2032         /* fix up this entry */
2033         fixups->fix_dh(vmg_ buf + 2);
2034 
2035         /* decode the entry */
2036         prop = (vm_prop_id_t)osrp2(buf);
2037         vmb_get_dh(buf + 2, &val);
2038 
2039         /*
2040          *   store the entry (don't save any undo for the operation, as we
2041          *   can't undo a load)
2042          */
2043         set_prop(vmg_ 0, self, prop, &val);
2044     }
2045 
2046     /* clear all undo information */
2047     clear_undo_flags();
2048 }
2049 
2050 /* ------------------------------------------------------------------------ */
2051 /*
2052  *   Load the object from an image file
2053  */
load_from_image(VMG_ vm_obj_id_t self,const char * ptr,size_t siz)2054 void CVmObjTads::load_from_image(VMG_ vm_obj_id_t self,
2055                                  const char *ptr, size_t siz)
2056 {
2057     ushort sc_cnt;
2058     ushort li_cnt;
2059     vm_tadsobj_hdr *hdr;
2060 
2061     /* save our image data pointer for reloading */
2062     G_obj_table->save_image_pointer(self, ptr, siz);
2063 
2064     /* if we already have memory allocated, free it */
2065     if (ext_ != 0)
2066     {
2067         G_mem->get_var_heap()->free_mem(ext_);
2068         ext_ = 0;
2069     }
2070 
2071     /* get the number of superclasses */
2072     sc_cnt = osrp2(ptr);
2073 
2074     /* get the number of load image properties */
2075     li_cnt = osrp2(ptr + 2);
2076 
2077     /* allocate our header */
2078     ext_ = (char *)vm_tadsobj_hdr::alloc(vmg_ this, sc_cnt, li_cnt);
2079     hdr = get_hdr();
2080 
2081     /* read the object flags from the image file and store them */
2082     hdr->li_obj_flags = osrp2(ptr + 4);
2083 
2084     /* set our internal flags - we come from the load image file */
2085     hdr->intern_obj_flags |= VMTO_OBJ_IMAGE;
2086 
2087     /* load the image file properties */
2088     load_image_props_and_scs(vmg_ ptr, siz);
2089 }
2090 
2091 /*
2092  *   Reset to image file state.  Discards all modified properties, so that
2093  *   we have only the image file properties.
2094  */
reload_from_image(VMG_ vm_obj_id_t,const char * ptr,size_t siz)2095 void CVmObjTads::reload_from_image(VMG_ vm_obj_id_t /*self*/,
2096                                    const char *ptr, size_t siz)
2097 {
2098     vm_tadsobj_hdr *hdr = get_hdr();
2099     ushort sc_cnt;
2100 
2101     /* get the number of superclasses */
2102     sc_cnt = osrp2(ptr);
2103 
2104     /*
2105      *   Clear the property table.  We don't have to worry about the new
2106      *   property table being larger than the existing property table,
2107      *   because we can't have shrunk since we were originally loaded.  So,
2108      *   all we need to do is mark all property entries as free and clear
2109      *   out the hash table.
2110      */
2111     hdr->prop_entry_free = 0;
2112     memset(hdr->hash_arr, 0, hdr->hash_siz * sizeof(hdr->hash_arr[0]));
2113 
2114     /* if we need space for more superclasses, reallocate the header */
2115     if (sc_cnt > hdr->sc_cnt)
2116     {
2117         /* allocate the new header */
2118         ext_ = (char *)vm_tadsobj_hdr::expand_to(
2119             vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt);
2120     }
2121 
2122     /* reload the image properties */
2123     load_image_props_and_scs(vmg_ ptr, siz);
2124 }
2125 
2126 /*
2127  *   Load the property list from the image data
2128  */
load_image_props_and_scs(VMG_ const char * ptr,size_t siz)2129 void CVmObjTads::load_image_props_and_scs(VMG_ const char *ptr, size_t siz)
2130 {
2131     vm_tadsobj_hdr *hdr = get_hdr();
2132     ushort i;
2133     ushort sc_cnt;
2134     ushort li_cnt;
2135     const char *p;
2136 
2137     /* get the number of superclasses */
2138     sc_cnt = osrp2(ptr);
2139 
2140     /* get the number of load image properties */
2141     li_cnt = osrp2(ptr + 2);
2142 
2143     /* read the superclasses from the load image and store them */
2144     for (i = 0, p = ptr + 6 ; i < sc_cnt ; ++i, p += 4)
2145     {
2146         /* store the object ID */
2147         hdr->sc[i].id = (vm_obj_id_t)osrp4(p);
2148 
2149         /*
2150          *   we can't store the superclass pointer yet, as the superclass
2151          *   object might not be loaded yet
2152          */
2153         hdr->sc[i].objp = 0;
2154     }
2155 
2156     /* read the properties from the load image and store them */
2157     for (i = 0 ; i < li_cnt ; ++i, p += 2 + VMB_DATAHOLDER)
2158     {
2159         vm_prop_id_t prop;
2160         vm_val_t val;
2161 
2162         /* decode the property data */
2163         prop = (vm_prop_id_t)osrp2(p);
2164         vmb_get_dh(p + 2, &val);
2165 
2166         /* store the property */
2167         hdr->alloc_prop_entry(prop, &val, 0);
2168     }
2169 }
2170 
2171 /* ------------------------------------------------------------------------ */
2172 /*
2173  *   Property evaluator - createInstance
2174  */
getp_create_instance(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2175 int CVmObjTads::getp_create_instance(VMG_ vm_obj_id_t self,
2176                                      vm_val_t *retval, uint *in_argc)
2177 {
2178     /* create a persistent instance */
2179     return getp_create_common(vmg_ self, retval, in_argc, FALSE);
2180 }
2181 
2182 /*
2183  *   Property evaluator - createTransientInstance
2184  */
getp_create_trans_instance(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2185 int CVmObjTads::getp_create_trans_instance(VMG_ vm_obj_id_t self,
2186                                            vm_val_t *retval, uint *in_argc)
2187 {
2188     /* create a transient instance */
2189     return getp_create_common(vmg_ self, retval, in_argc, TRUE);
2190 }
2191 
2192 /*
2193  *   Common handler for createInstance() and createTransientInstance()
2194  */
getp_create_common(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc,int is_transient)2195 int CVmObjTads::getp_create_common(VMG_ vm_obj_id_t self,
2196                                    vm_val_t *retval, uint *in_argc,
2197                                    int is_transient)
2198 {
2199     uint argc = (in_argc != 0 ? *in_argc : 0);
2200     static CVmNativeCodeDesc desc(0, 0, TRUE);
2201 
2202     /* check arguments - any number are allowed */
2203     if (get_prop_check_argc(retval, in_argc, &desc))
2204         return TRUE;
2205 
2206     /*
2207      *   push myself as the first argument - 'self' is the superclass of the
2208      *   object to be created
2209      */
2210     G_interpreter->push_obj(vmg_ self);
2211 
2212     /*
2213      *   Create an instance - this will recursively execute the new object's
2214      *   constructor, if it has one.  Note that we have one more argument
2215      *   than provided by the caller, because we've pushed the implicit
2216      *   argument ('self') that create_from_stack uses to identify the
2217      *   superclass.
2218      */
2219     retval->set_obj(create_from_stack_intern(vmg_ 0, argc + 1,
2220                                              is_transient));
2221 
2222     /* handled */
2223     return TRUE;
2224 }
2225 
2226 /* ------------------------------------------------------------------------ */
2227 /*
2228  *   Property evaluator - createClone
2229  */
getp_create_clone(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2230 int CVmObjTads::getp_create_clone(VMG_ vm_obj_id_t self,
2231                                   vm_val_t *retval, uint *argc)
2232 {
2233     static CVmNativeCodeDesc desc(0);
2234     vm_obj_id_t new_obj;
2235     CVmObjTads *tobj;
2236     vm_tadsobj_prop *entry;
2237     size_t i;
2238     vm_tadsobj_hdr *hdr = get_hdr();
2239 
2240     /* check arguments */
2241     if (get_prop_check_argc(retval, argc, &desc))
2242         return TRUE;
2243 
2244     /*
2245      *   create a new object with the same number of superclasses as I have,
2246      *   and with space for all of my properties
2247      */
2248     new_obj = create(vmg_ FALSE, get_sc_count(), hdr->prop_entry_free);
2249     tobj = (CVmObjTads *)vm_objp(vmg_ new_obj);
2250 
2251     /* copy my superclass list to the new object */
2252     for (i = 0 ; i < get_sc_count() ; ++i)
2253         tobj->set_sc(vmg_ i, get_sc(i));
2254 
2255     /* copy my properties to the new object */
2256     for (i = hdr->prop_entry_free, entry = hdr->prop_entry_arr ;
2257          i != 0 ; --i, ++entry)
2258     {
2259         /*
2260          *   If this entry is non-empty, store the property in the new
2261          *   object.  We don't need to store undo for the property, as the
2262          *   object is entirely new since the last savepoint (as there can't
2263          *   have been a savepoint while we've been working, obviously)
2264          */
2265         if (entry->val.typ != VM_EMPTY)
2266             tobj->set_prop(vmg_ 0, self, entry->prop, &entry->val);
2267     }
2268 
2269     /* the return value is the new object ID */
2270     retval->set_obj(new_obj);
2271 
2272     /* handled */
2273     return TRUE;
2274 }
2275 
2276 /* ------------------------------------------------------------------------ */
2277 /*
2278  *   Property evaluator - createInstanceOf
2279  */
getp_create_instance_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2280 int CVmObjTads::getp_create_instance_of(VMG_ vm_obj_id_t self,
2281                                         vm_val_t *retval, uint *in_argc)
2282 {
2283     /* create a persistent instance */
2284     return getp_create_multi_common(vmg_ self, retval, in_argc, FALSE);
2285 }
2286 
2287 /*
2288  *   Property evaluator - createTransientInstanceOf
2289  */
getp_create_trans_instance_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2290 int CVmObjTads::getp_create_trans_instance_of(
2291     VMG_ vm_obj_id_t self, vm_val_t *retval, uint *in_argc)
2292 {
2293     /* create a persistent instance */
2294     return getp_create_multi_common(vmg_ self, retval, in_argc, TRUE);
2295 }
2296 
2297 /*
2298  *   Common handler for createInstanceOf() and createTransientInstanceOf()
2299  */
getp_create_multi_common(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc,int is_transient)2300 int CVmObjTads::getp_create_multi_common(VMG_ vm_obj_id_t self,
2301                                          vm_val_t *retval, uint *in_argc,
2302                                          int is_transient)
2303 {
2304     uint argc = (in_argc != 0 ? *in_argc : 0);
2305     static CVmNativeCodeDesc desc(0, 0, TRUE);
2306 
2307     /* check arguments - any number are allowed */
2308     if (get_prop_check_argc(retval, in_argc, &desc))
2309         return TRUE;
2310 
2311     /* create the new instance */
2312     retval->set_obj(create_from_stack_multi(vmg_ argc, is_transient));
2313 
2314     /* handled */
2315     return TRUE;
2316 }
2317 
2318 /* ------------------------------------------------------------------------ */
2319 /*
2320  *   Property evaluator - setSuperclassList
2321  */
getp_set_sc_list(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2322 int CVmObjTads::getp_set_sc_list(VMG_ vm_obj_id_t self,
2323                                  vm_val_t *retval, uint *in_argc)
2324 {
2325     static CVmNativeCodeDesc desc(1);
2326     const char *lstp;
2327     size_t cnt;
2328     size_t i;
2329     vm_val_t ele;
2330     size_t sc_cnt;
2331     vm_tadsobj_hdr *hdr = get_hdr();
2332 
2333     /* check arguments */
2334     if (get_prop_check_argc(retval, in_argc, &desc))
2335         return TRUE;
2336 
2337     /* get the list argument (but leave it on the stack for now) */
2338     lstp = G_stk->get(0)->get_as_list(vmg0_);
2339     if (lstp == 0)
2340         err_throw(VMERR_BAD_TYPE_BIF);
2341 
2342     /* get the number of superclasses for the new object */
2343     cnt = vmb_get_len(lstp);
2344 
2345     /* we need at least one argument - the minimal root is TadsObject */
2346     if (cnt < 1)
2347         err_throw(VMERR_BAD_VAL_BIF);
2348 
2349     /*
2350      *   Check for a special case: our entire superclass list consists of
2351      *   [TadsObject].  In this case, we have nothing in our internal
2352      *   superclass list, since our only superclass is our metaclass.
2353      */
2354     CVmObjList::index_list(vmg_ &ele, lstp, 1);
2355     if (cnt == 1
2356         && ele.typ == VM_OBJ
2357         && ele.val.obj == metaclass_reg_->get_class_obj(vmg0_))
2358     {
2359         /* use an empty internal superclass list */
2360         sc_cnt = 0;
2361     }
2362     else
2363     {
2364         /*
2365          *   Scan the superclasses.  Each superclass must be a TadsObject,
2366          *   with the one exception that if we have only one superclass, it
2367          *   can be the TadsObject intrinsic class itself, signifying that we
2368          *   have no superclasses.
2369          */
2370         for (i = 1 ; i <= cnt ; ++i)
2371         {
2372             /* get this element from the list */
2373             CVmObjList::index_list(vmg_ &ele, lstp, i);
2374 
2375             /* it has to be an object of type TadsObject */
2376             if (ele.typ != VM_OBJ || !is_tadsobj_obj(vmg_ ele.val.obj))
2377                 err_throw(VMERR_BAD_VAL_BIF);
2378 
2379             /*
2380              *   make sure that this superclass doesn't inherit from 'self' -
2381              *   if it does, that would create a circular inheritance
2382              *   hierarchy, which is illegal
2383              */
2384             if (vm_objp(vmg_ ele.val.obj)->is_instance_of(vmg_ self))
2385                 err_throw(VMERR_BAD_VAL_BIF);
2386         }
2387 
2388         /* the list is valid - we need one superclass per list element */
2389         sc_cnt = cnt;
2390     }
2391 
2392     /*
2393      *   if we're increasing the number of superclasses, expand our object
2394      *   header to make room
2395      */
2396     if (sc_cnt > hdr->sc_cnt)
2397     {
2398         /* expand the header to accomodate the new superclass list */
2399         ext_ = (char *)vm_tadsobj_hdr::expand_to(
2400             vmg_ this, hdr, sc_cnt, hdr->prop_entry_cnt);
2401 
2402         /* get the new header */
2403         hdr = get_hdr();
2404     }
2405 
2406     /* set the new superclass count */
2407     hdr->sc_cnt = sc_cnt;
2408 
2409     /* set the new superclasses */
2410     for (i = 0 ; i < sc_cnt ; ++i)
2411     {
2412         /* get this element from the list */
2413         CVmObjList::index_list(vmg_ &ele, lstp, i + 1);
2414 
2415         /* set this superclass in the header */
2416         hdr->sc[i].id = ele.val.obj;
2417         hdr->sc[i].objp = (CVmObjTads *)vm_objp(vmg_ ele.val.obj);
2418     }
2419 
2420     /* invalidate the cached inheritance path */
2421     hdr->inval_inh_path();
2422 
2423     /* discard arguments */
2424     G_stk->discard();
2425 
2426     /* handled */
2427     return TRUE;
2428 }
2429 
2430 /* ------------------------------------------------------------------------ */
2431 /*
2432  *   Intrinsic Class Modifier object implementation
2433  */
2434 
2435 /* metaclass registration object */
2436 static CVmMetaclassIntClsMod metaclass_reg_obj_icm;
2437 CVmMetaclass *CVmObjIntClsMod::metaclass_reg_ = &metaclass_reg_obj_icm;
2438 
2439 /*
2440  *   Get a property.  Intrinsic class modifiers do not have intrinsic
2441  *   superclasses, because they're effectively mix-in classes.  Therefore,
2442  *   do not look for intrinsic properties or intrinsic superclass properties
2443  *   to resolve the property lookup.
2444  */
get_prop(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t * source_obj,uint * argc)2445 int CVmObjIntClsMod::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
2446                               vm_obj_id_t self, vm_obj_id_t *source_obj,
2447                               uint *argc)
2448 {
2449     /*
2450      *   try finding the property in our property list or a superclass
2451      *   property list
2452      */
2453     tadsobj_sc_search_ctx curpos(vmg_ self, this);
2454     if (curpos.find_prop(vmg_ prop, val, source_obj))
2455         return TRUE;
2456 
2457     /*
2458      *   We didn't find it in our list, so we don't have the property.
2459      *   Because we're an intrinsic mix-in, we don't look for an intrinsic
2460      *   implementation or an intrinsic superclass implementation.
2461      */
2462     return FALSE;
2463 }
2464 
2465 /*
2466  *   Inherit a property.  As with get_prop(), we don't want to inherit from
2467  *   any intrinsic superclass if we don't find the property in our property
2468  *   list or an inherited property list.
2469  */
inh_prop(VMG_ vm_prop_id_t prop,vm_val_t * val,vm_obj_id_t self,vm_obj_id_t orig_target_obj,vm_obj_id_t defining_obj,vm_obj_id_t * source_obj,uint * argc)2470 int CVmObjIntClsMod::inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
2471                               vm_obj_id_t self,
2472                               vm_obj_id_t orig_target_obj,
2473                               vm_obj_id_t defining_obj,
2474                               vm_obj_id_t *source_obj, uint *argc)
2475 {
2476     /*
2477      *   try finding the property in our property list or a superclass
2478      *   property list
2479      */
2480     if (search_for_prop_from(vmg_ prop, val, orig_target_obj,
2481                              source_obj, defining_obj))
2482         return TRUE;
2483 
2484     /*
2485      *   we didn't find it in our list, and we don't want to inherit from any
2486      *   intrinsic superclass, so we don't have the property
2487      */
2488     return FALSE;
2489 }
2490 
2491 /*
2492  *   Build my property list.  We build the complete list of methods defined
2493  *   in the intrinsic class modifier for all classes, including any modify
2494  *   base classes that we further modify.
2495  */
build_prop_list(VMG_ vm_obj_id_t self,vm_val_t * retval)2496 void CVmObjIntClsMod::build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval)
2497 {
2498     /* push a self-reference for gc protection */
2499     G_stk->push()->set_obj(self);
2500 
2501     /* build our own list */
2502     CVmObjTads::build_prop_list(vmg_ self, retval);
2503 
2504     /* if we have a base class that we further modify, add its list */
2505     if (get_sc_count() != 0)
2506     {
2507         vm_obj_id_t base_id;
2508         CVmObject *base_obj;
2509 
2510         /* get the base class */
2511         base_id = get_sc(0);
2512         base_obj = vm_objp(vmg_ base_id);
2513 
2514         /* get its list only if it's of our same metaclass */
2515         if (base_obj->get_metaclass_reg() == get_metaclass_reg())
2516         {
2517             vm_val_t base_val;
2518 
2519             /* save our list for gc protection */
2520             G_stk->push(retval);
2521 
2522             /* get our base class's list */
2523             base_obj->build_prop_list(vmg_ base_id, &base_val);
2524 
2525             /* add this list to our result list */
2526             vm_objp(vmg_ retval->val.obj)->
2527                 add_val(vmg_ retval, retval->val.obj, &base_val);
2528 
2529             /* discard our gc protection */
2530             G_stk->discard();
2531         }
2532     }
2533 
2534     /* discard gc protection */
2535     G_stk->discard();
2536 }
2537