1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/tads3/VMMETA.CPP,v 1.3 1999/07/11 00:46:58 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   vmmeta.cpp - metaclass dependency table
15 Function
16 
17 Notes
18 
19 Modified
20   12/01/98 MJRoberts  - Creation
21 */
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "t3std.h"
27 #include "vmtype.h"
28 #include "vmerr.h"
29 #include "vmerrnum.h"
30 #include "vmglob.h"
31 #include "vmmeta.h"
32 #include "vmobj.h"
33 #include "vmmcreg.h"
34 #include "vmfile.h"
35 #include "vmintcls.h"
36 
37 
38 /* ------------------------------------------------------------------------ */
39 /*
40  *   Create the metaclass dependency table with a given number of initial
41  *   entries
42  */
CVmMetaTable(size_t init_entries)43 CVmMetaTable::CVmMetaTable(size_t init_entries)
44 {
45     vm_meta_reg_t *entry;
46     uint cnt;
47     uint i;
48 
49     /* allocate space for our entries */
50     if (init_entries != 0)
51     {
52         /* allocate the space */
53         table_ = (vm_meta_entry_t *)
54                  t3malloc(init_entries * sizeof(table_[0]));
55     }
56     else
57     {
58         /* we have no entries */
59         table_ = 0;
60     }
61 
62     /* no entries are defined yet */
63     count_ = 0;
64 
65     /* remember the allocation size */
66     alloc_ = init_entries;
67 
68     /* count the number of registered metaclasses */
69     for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ;
70          ++entry, ++cnt) ;
71 
72     /*
73      *   allocate the reverse dependency table -- this table lets us get
74      *   the dependency index of a metaclass given its registration table
75      *   index
76      */
77     reverse_map_ = (int *)t3malloc(cnt * sizeof(reverse_map_[0]));
78 
79     /*
80      *   set each element of the table to -1, since we have no dependency
81      *   table entries yet
82      */
83     for (i = 0 ; i < cnt ; ++i)
84         reverse_map_[i] = -1;
85 }
86 
87 /* ------------------------------------------------------------------------ */
88 /*
89  *   Delete the table
90  */
~CVmMetaTable()91 CVmMetaTable::~CVmMetaTable()
92 {
93     /* clear the table */
94     clear();
95 
96     /* free the table, if we ever allocated one */
97     if (table_ != 0)
98         t3free(table_);
99 
100     /* free the reverse map */
101     t3free(reverse_map_);
102 }
103 
104 /* ------------------------------------------------------------------------ */
105 /*
106  *   Clear all entries from the table
107  */
clear()108 void CVmMetaTable::clear()
109 {
110     size_t i;
111 
112     /* delete all of the property tables in the entries */
113     for (i = 0 ; i < count_ ; ++i)
114         table_[i].release_mem();
115 
116     /*
117      *   Reset the entry counter.  Note that this doesn't affect any
118      *   allocation; we keep a separate count of the number of table slots
119      *   we have allocated.  Table slots don't have any additional
120      *   associated memory, so we don't need to worry about cleaning
121      *   anything up at this point.
122      */
123     count_ = 0;
124 }
125 
126 /* ------------------------------------------------------------------------ */
127 /*
128  *   Ensure that we have space for a given number of entries
129  */
ensure_space(size_t entries,size_t increment)130 void CVmMetaTable::ensure_space(size_t entries, size_t increment)
131 {
132     /* if we don't have enough space, allocate more */
133     if (entries >= alloc_)
134     {
135         size_t new_size;
136 
137         /* increase the allocation size by the given increment */
138         alloc_ += increment;
139 
140         /* if it's still too small, bump it up to the required size */
141         if (alloc_ < entries)
142             alloc_ = entries;
143 
144         /* compute the new size */
145         new_size = alloc_ * sizeof(table_[0]);
146 
147         /*
148          *   if we have a table already, reallocate it at the larger size;
149          *   otherwise, allocate a new table
150          */
151         if (table_ != 0)
152             table_ = (vm_meta_entry_t *)t3realloc(table_, new_size);
153         else
154             table_ = (vm_meta_entry_t *)t3malloc(new_size);
155     }
156 }
157 
158 /* ------------------------------------------------------------------------ */
159 /*
160  *   Add an entry to the table
161  */
add_entry(const char * metaclass_id,size_t func_cnt,vm_prop_id_t min_prop,vm_prop_id_t max_prop)162 void CVmMetaTable::add_entry(const char *metaclass_id, size_t func_cnt,
163                              vm_prop_id_t min_prop, vm_prop_id_t max_prop)
164 {
165     vm_meta_reg_t *entry;
166     uint idx;
167     const char *vsn;
168     size_t name_len;
169 
170     /* find the version suffix in the metaclass name, if any */
171     vsn = lib_find_vsn_suffix(metaclass_id, '/', "000000", &name_len);
172 
173     /* ensure we have space for one more entry */
174     ensure_space(count_ + 1, 5);
175 
176     /* look up the metaclass by name */
177     for (idx = 0, entry = G_meta_reg_table ; entry->meta != 0 ;
178          ++entry, ++idx)
179     {
180         const char *entry_vsn;
181         size_t entry_name_len;
182 
183         /* find the version number in this entry */
184         entry_vsn = lib_find_vsn_suffix((*entry->meta)->get_meta_name(),
185                                         '/', "000000", &entry_name_len);
186 
187         /* see if this is a match */
188         if (name_len == entry_name_len
189             && memcmp(metaclass_id, (*entry->meta)->get_meta_name(),
190                       name_len) == 0)
191         {
192             /*
193              *   make sure the version provided in the VM is at least as
194              *   high as the requested version
195              */
196             if (strcmp(vsn, entry_vsn) > 0)
197                 err_throw_a(VMERR_METACLASS_TOO_OLD, 2,
198                             ERR_TYPE_TEXTCHAR, metaclass_id,
199                             ERR_TYPE_TEXTCHAR, entry_vsn);
200 
201             /* add this entry */
202             add_entry(metaclass_id, idx, func_cnt, min_prop, max_prop);
203 
204             /* we're done */
205             return;
206         }
207     }
208 
209     /* we didn't find it - throw an error */
210     err_throw_a(VMERR_UNKNOWN_METACLASS, 1, ERR_TYPE_TEXTCHAR, metaclass_id);
211 }
212 
213 /* ------------------------------------------------------------------------ */
214 /*
215  *   Add an entry to the table for a given registration table index
216  */
add_entry(const char * metaclass_id,uint idx,size_t func_cnt,vm_prop_id_t min_prop,vm_prop_id_t max_prop)217 void CVmMetaTable::add_entry(const char *metaclass_id,
218                              uint idx, size_t func_cnt,
219                              vm_prop_id_t min_prop, vm_prop_id_t max_prop)
220 {
221     vm_meta_reg_t *entry;
222     size_t prop_xlat_cnt;
223 
224     /* get the registration table entry from the index */
225     entry = &G_meta_reg_table[idx];
226 
227     /* remember the defining class object */
228     table_[count_].meta_ = *entry->meta;
229 
230     /* save the name */
231     table_[count_].set_meta_name(metaclass_id);
232 
233     /* calculate the number of entries in the table */
234     if (min_prop == VM_INVALID_PROP || max_prop == VM_INVALID_PROP)
235         prop_xlat_cnt = 0;
236     else
237         prop_xlat_cnt = max_prop - min_prop + 1;
238 
239     /* set the property count */
240     table_[count_].min_prop_ = min_prop;
241     table_[count_].prop_xlat_cnt_ = prop_xlat_cnt;
242 
243     /* set the function count */
244     table_[count_].func_xlat_cnt_ = func_cnt;
245 
246     /* we don't know the intrinsic class's representative object yet */
247     table_[count_].class_obj_ = VM_INVALID_OBJ;
248 
249     /* allocate space for the property table, if we have any translations */
250     if (prop_xlat_cnt != 0)
251     {
252         size_t siz;
253 
254         /* calculate the table size */
255         siz = prop_xlat_cnt * sizeof(table_[count_].prop_xlat_[0]);
256 
257         /* allocate the table */
258         table_[count_].prop_xlat_ = (unsigned short *)t3malloc(siz);
259 
260         /*
261          *   initialize each entry in the table to zero - this will ensure
262          *   that each entry that is never set to a valid function index
263          *   will properly yield function index zero, which is defined as
264          *   the "not found" invalid function index
265          */
266         memset(table_[count_].prop_xlat_, 0, siz);
267     }
268     else
269     {
270         /* there are no properties to translate, so we don't need a table */
271         table_[count_].prop_xlat_ = 0;
272     }
273 
274     /* allocate the function-to-property translation table */
275     if (func_cnt != 0)
276     {
277         size_t i;
278         vm_prop_id_t *prop_ptr;
279 
280         /* allocate the table */
281         table_[count_].func_xlat_ =
282             (vm_prop_id_t *)t3malloc(func_cnt * sizeof(vm_prop_id_t));
283 
284         /* clear the table - mark each entry as an invalid property ID */
285         for (i = 0, prop_ptr = table_[count_].func_xlat_ ;
286              i < func_cnt ; ++i, ++prop_ptr)
287             *prop_ptr = VM_INVALID_PROP;
288     }
289     else
290     {
291         /* no properties, so we don't need a table */
292         table_[count_].func_xlat_ = 0;
293     }
294 
295     /*
296      *   store the reverse mapping for this entry, so that we can find the
297      *   dependency entry given the registration index
298      */
299     reverse_map_[idx] = count_;
300 
301     /* count the new entry */
302     ++count_;
303 }
304 
305 /* ------------------------------------------------------------------------ */
306 /*
307  *   Add an entry to the dependency table only if it doesn't already
308  *   appear in the dependency table.  We add the entry based on its
309  *   registration table index.
310  */
add_entry_if_new(uint reg_table_idx,size_t func_cnt,vm_prop_id_t min_prop,vm_prop_id_t max_prop)311 void CVmMetaTable::add_entry_if_new(uint reg_table_idx, size_t func_cnt,
312                                     vm_prop_id_t min_prop,
313                                     vm_prop_id_t max_prop)
314 {
315     /*
316      *   if the entry already has a valid dependency table index, as
317      *   obtained from the reverse mapping, we need not add it again
318      */
319     if (reverse_map_[reg_table_idx] != -1)
320         return;
321 
322     /* add the entry */
323     add_entry((*G_meta_reg_table[reg_table_idx].meta)->get_meta_name(),
324               reg_table_idx, func_cnt, min_prop, max_prop);
325 }
326 
327 
328 /* ------------------------------------------------------------------------ */
329 /*
330  *   Invoke the VM-stack-based constructor for the metaclass at the given
331  *   index
332  */
create_from_stack(VMG_ const uchar ** pc_ptr,uint idx,uint argc)333 vm_obj_id_t CVmMetaTable::create_from_stack(VMG_ const uchar **pc_ptr,
334                                             uint idx, uint argc)
335 {
336     /* make sure the entry is defined */
337     if (idx >= count_)
338         err_throw(VMERR_BAD_METACLASS_INDEX);
339 
340     /* invoke the appropriate constructor */
341     return table_[idx].meta_->create_from_stack(vmg_ pc_ptr, argc);
342 }
343 
344 /* ------------------------------------------------------------------------ */
345 /*
346  *   Call a static property in the metaclass at the given index
347  */
call_static_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint idx,uint * argc,vm_prop_id_t prop)348 int CVmMetaTable::call_static_prop(VMG_ vm_val_t *result,
349                                    const uchar **pc_ptr, uint idx,
350                                    uint *argc, vm_prop_id_t prop)
351 {
352     /* make sure the entry is defined */
353     if (idx >= count_)
354         err_throw(VMERR_BAD_METACLASS_INDEX);
355 
356     /* invoke the appropriate static property evaluator */
357     return table_[idx].meta_->call_stat_prop(vmg_ result, pc_ptr, argc, prop);
358 }
359 
360 /* ------------------------------------------------------------------------ */
361 /*
362  *   Create an object with the given ID and load the object from the image
363  *   file.
364  */
create_from_image(VMG_ uint idx,vm_obj_id_t id,const char * ptr,size_t siz)365 void CVmMetaTable::create_from_image(VMG_ uint idx, vm_obj_id_t id,
366                                      const char *ptr, size_t siz)
367 {
368     /* make sure the entry is defined */
369     if (idx >= count_)
370         err_throw(VMERR_BAD_METACLASS_INDEX);
371 
372     /* create the object table entry in the memory manager */
373     G_obj_table->alloc_obj_with_id(id, TRUE);
374 
375     /* invoke the appropriate constructor */
376     table_[idx].meta_->create_for_image_load(vmg_ id);
377 
378     /* load the object */
379     vm_objp(vmg_ id)->load_from_image(vmg_ id, ptr, siz);
380 }
381 
382 /*
383  *   Create an object of the given metaclass with the given ID, in
384  *   preparation for restoring the object's data from saved state
385  *   information.  This doesn't fill in the object with the saved state
386  *   data, but merely creates the object.
387  *
388  *   The caller is responsible for having allocated the object ID before
389  *   calling this function.
390  */
create_for_restore(VMG_ uint idx,vm_obj_id_t id)391 void CVmMetaTable::create_for_restore(VMG_ uint idx, vm_obj_id_t id)
392 {
393     /* make sure the entry is defined in our table of metaclasses */
394     if (idx >= count_)
395         err_throw(VMERR_BAD_METACLASS_INDEX);
396 
397     /*
398      *   Invoke the appropriate constructor.  Note that the caller must
399      *   already have allocated the object ID, so we simply use the given ID
400      *   without further consideration.
401      */
402     table_[idx].meta_->create_for_restore(vmg_ id);
403 }
404 
405 
406 /* ------------------------------------------------------------------------ */
407 /*
408  *   Write the table to a file.
409  */
write_to_file(CVmFile * fp)410 void CVmMetaTable::write_to_file(CVmFile *fp)
411 {
412     size_t i;
413 
414     /* write the number of entries */
415     fp->write_int2(get_count());
416 
417     /* write each entry */
418     for (i = 0 ; i < get_count() ; ++i)
419     {
420         const char *nm;
421         const vm_meta_entry_t *entry;
422         size_t j;
423 
424         /* get the entry */
425         entry = get_entry(i);
426 
427         /* get this entry's name */
428         nm = entry->image_meta_name_;
429 
430         /* write the length of the name, followed by the name */
431         fp->write_int2(strlen(nm));
432         fp->write_bytes(nm, strlen(nm));
433 
434         /* write our associated IntrinsicClass object's ID */
435         fp->write_int4(entry->class_obj_);
436 
437         /*
438          *   Write the property table information - write the number of
439          *   function entries, and the minimum and maximum property ID's.
440          */
441         fp->write_int2(entry->func_xlat_cnt_);
442         fp->write_int2(entry->min_prop_);
443         fp->write_int2(entry->min_prop_ + entry->prop_xlat_cnt_);
444 
445         /*
446          *   Write out the property translation table.  The function
447          *   translation table will always be smaller than (at worst, it
448          *   will be the same size as) the property translation table;
449          *   both tables contain the same information, so since we only
450          *   need to write out one or the other, write out the smaller of
451          *   the two.
452          *
453          *   Note that xlat_func() requires a 1-based function index, so
454          *   run our counter from 1 to the function table count.
455          */
456         for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
457             fp->write_int2(entry->xlat_func(j));
458     }
459 }
460 
461 /*
462  *   Read the table from a file.
463  */
read_from_file(CVmFile * fp)464 int CVmMetaTable::read_from_file(CVmFile *fp)
465 {
466     size_t cnt;
467     size_t i;
468 
469     /* clear the existing table */
470     clear();
471 
472     /* read the number of entries */
473     cnt = fp->read_uint2();
474 
475     /* read the entries */
476     for (i = 0 ; i < cnt ; ++i)
477     {
478         char buf[256];
479         size_t len;
480         vm_prop_id_t min_prop;
481         vm_prop_id_t max_prop;
482         size_t func_cnt;
483         size_t j;
484         vm_meta_entry_t *entry;
485         vm_obj_id_t class_obj;
486 
487         /* read the length of this entry, and make sure it's valid */
488         len = fp->read_uint2();
489         if (len > sizeof(buf) - 1)
490             return VMERR_SAVED_META_TOO_LONG;
491 
492         /* read the name and null-terminate it */
493         fp->read_bytes(buf, len);
494         buf[len] = '\0';
495 
496         /* read the associated IntrinsicClass object */
497         class_obj = (vm_obj_id_t)fp->read_int4();
498 
499         /* read the property table description */
500         func_cnt = fp->read_uint2();
501         min_prop = (vm_prop_id_t)fp->read_uint2();
502         max_prop = (vm_prop_id_t)fp->read_uint2();
503 
504         /* add this entry */
505         add_entry(buf, func_cnt, min_prop, max_prop);
506 
507         /* get the new entry's record */
508         entry = get_entry(i);
509 
510         /* set the class ID */
511         entry->class_obj_ = class_obj;
512 
513         /*
514          *   Read the property mappings.  We stored the function table
515          *   entries, so we simply need to load those entries.  Note that
516          *   the function table has a 1-based index, so run our counter
517          *   from 1 to the function count.
518          */
519         for (j = 1 ; j <= func_cnt ; ++j)
520             entry->add_prop_xlat((short)fp->read_int2(), j);
521     }
522 
523     /* success */
524     return 0;
525 }
526 
527 #if 0 // moved inline, since it's small and used a lot
528 /*
529  *   Get the function vector index for a property given the metaclass's
530  *   registration table index.
531  */
532 int CVmMetaTable::prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop)
533 {
534     vm_meta_entry_t *entry;
535 
536     /* get the entry for the registration table index */
537     entry = get_entry_from_reg(reg_table_idx);
538 
539     /*
540      *   if there's no entry for this registration index, there's
541      *   obviously no metaclass function mapping for the property
542      */
543     if (entry == 0)
544         return 0;
545 
546     /* return the property translation */
547     return entry->xlat_prop(prop);
548 }
549 #endif
550 
551 /*
552  *   Create an IntrinsicClass object for each metaclass that doesn't
553  *   already have one.
554  */
create_intrinsic_class_instances(VMG0_)555 void CVmMetaTable::create_intrinsic_class_instances(VMG0_)
556 {
557     size_t idx;
558     vm_meta_entry_t *entry;
559 
560     /* go through our table */
561     for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry)
562     {
563         /* if this entry has no associated class object, create one for it */
564         if (entry->class_obj_ == VM_INVALID_OBJ)
565         {
566             /*
567              *   create the class object - it will automatically register
568              *   itself to fill in the entry
569              */
570             CVmObjClass::create_dyn(vmg_ idx);
571         }
572 
573         /*
574          *   Add this object to the machine globals, if it's not a root
575          *   object.  Any dynamically-created intrinsic class instance must
576          *   be made a machine global because it will always be reachable as
577          *   the class object for the metaclass, but the metclass will not
578          *   trace the object during garbage collection; the only way to
579          *   keep these objects from being incorrectly collected is to make
580          *   them machine globals.
581          */
582         G_obj_table->add_to_globals(entry->class_obj_);
583     }
584 }
585 
586 /*
587  *   Forget the IntrinsicClass objects we created dynamically in
588  *   create_intrinsic_class_instances().  This simply drops our references
589  *   to those objects from the metaclass table.
590  */
forget_intrinsic_class_instances(VMG0_)591 void CVmMetaTable::forget_intrinsic_class_instances(VMG0_)
592 {
593     size_t idx;
594     vm_meta_entry_t *entry;
595 
596     /* go through our table and clear out our IntrinsicClass references */
597     for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry)
598         entry->class_obj_ = VM_INVALID_OBJ;
599 }
600 
601