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