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