cfad47cfa3/tads3/vmmeta.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
        ushort 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
 */
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
        ushort func_cnt;
483
        ushort 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_uint4();
498
499
        /* read the property table description */
500
        func_cnt = (ushort)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
 */
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
 */
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