cfad47cfa3/tads3/vmmeta.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 17.7 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/tads3/VMMETA.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $";
#endif
/*
* Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
vmmeta.cpp - metaclass dependency table
Function
Notes
Modified
12/01/98 MJRoberts - Creation
*/
#include <stdlib.h>
#include <string.h>
#include "t3std.h"
#include "vmtype.h"
#include "vmerr.h"
#include "vmerrnum.h"
#include "vmglob.h"
#include "vmmeta.h"
#include "vmobj.h"
#include "vmmcreg.h"
#include "vmfile.h"
#include "vmintcls.h"
/* ------------------------------------------------------------------------ */
/*
* Create the metaclass dependency table with a given number of initial
* entries
*/
CVmMetaTable::CVmMetaTable(size_t init_entries)
{
vm_meta_reg_t *entry;
uint cnt;
uint i;
/* allocate space for our entries */
if (init_entries != 0)
{
/* allocate the space */
table_ = (vm_meta_entry_t *)
t3malloc(init_entries * sizeof(table_[0]));
}
else
{
/* we have no entries */
table_ = 0;
}
/* no entries are defined yet */
count_ = 0;
/* remember the allocation size */
alloc_ = init_entries;
/* count the number of registered metaclasses */
for (entry = G_meta_reg_table, cnt = 0 ; entry->meta != 0 ;
++entry, ++cnt) ;
/*
* allocate the reverse dependency table -- this table lets us get
* the dependency index of a metaclass given its registration table
* index
*/
reverse_map_ = (int *)t3malloc(cnt * sizeof(reverse_map_[0]));
/*
* set each element of the table to -1, since we have no dependency
* table entries yet
*/
for (i = 0 ; i < cnt ; ++i)
reverse_map_[i] = -1;
}
/* ------------------------------------------------------------------------ */
/*
* Delete the table
*/
CVmMetaTable::~CVmMetaTable()
{
/* clear the table */
clear();
/* free the table, if we ever allocated one */
if (table_ != 0)
t3free(table_);
/* free the reverse map */
t3free(reverse_map_);
}
/* ------------------------------------------------------------------------ */
/*
* Clear all entries from the table
*/
void CVmMetaTable::clear()
{
size_t i;
/* delete all of the property tables in the entries */
for (i = 0 ; i < count_ ; ++i)
table_[i].release_mem();
/*
* Reset the entry counter. Note that this doesn't affect any
* allocation; we keep a separate count of the number of table slots
* we have allocated. Table slots don't have any additional
* associated memory, so we don't need to worry about cleaning
* anything up at this point.
*/
count_ = 0;
}
/* ------------------------------------------------------------------------ */
/*
* Ensure that we have space for a given number of entries
*/
void CVmMetaTable::ensure_space(size_t entries, size_t increment)
{
/* if we don't have enough space, allocate more */
if (entries >= alloc_)
{
size_t new_size;
/* increase the allocation size by the given increment */
alloc_ += increment;
/* if it's still too small, bump it up to the required size */
if (alloc_ < entries)
alloc_ = entries;
/* compute the new size */
new_size = alloc_ * sizeof(table_[0]);
/*
* if we have a table already, reallocate it at the larger size;
* otherwise, allocate a new table
*/
if (table_ != 0)
table_ = (vm_meta_entry_t *)t3realloc(table_, new_size);
else
table_ = (vm_meta_entry_t *)t3malloc(new_size);
}
}
/* ------------------------------------------------------------------------ */
/*
* Add an entry to the table
*/
void CVmMetaTable::add_entry(const char *metaclass_id, size_t func_cnt,
vm_prop_id_t min_prop, vm_prop_id_t max_prop)
{
vm_meta_reg_t *entry;
uint idx;
const char *vsn;
size_t name_len;
/* find the version suffix in the metaclass name, if any */
vsn = lib_find_vsn_suffix(metaclass_id, '/', "000000", &name_len);
/* ensure we have space for one more entry */
ensure_space(count_ + 1, 5);
/* look up the metaclass by name */
for (idx = 0, entry = G_meta_reg_table ; entry->meta != 0 ;
++entry, ++idx)
{
const char *entry_vsn;
size_t entry_name_len;
/* find the version number in this entry */
entry_vsn = lib_find_vsn_suffix((*entry->meta)->get_meta_name(),
'/', "000000", &entry_name_len);
/* see if this is a match */
if (name_len == entry_name_len
&& memcmp(metaclass_id, (*entry->meta)->get_meta_name(),
name_len) == 0)
{
/*
* make sure the version provided in the VM is at least as
* high as the requested version
*/
if (strcmp(vsn, entry_vsn) > 0)
err_throw_a(VMERR_METACLASS_TOO_OLD, 2,
ERR_TYPE_TEXTCHAR, metaclass_id,
ERR_TYPE_TEXTCHAR, entry_vsn);
/* add this entry */
add_entry(metaclass_id, idx, func_cnt, min_prop, max_prop);
/* we're done */
return;
}
}
/* we didn't find it - throw an error */
err_throw_a(VMERR_UNKNOWN_METACLASS, 1, ERR_TYPE_TEXTCHAR, metaclass_id);
}
/* ------------------------------------------------------------------------ */
/*
* Add an entry to the table for a given registration table index
*/
void CVmMetaTable::add_entry(const char *metaclass_id,
uint idx, size_t func_cnt,
vm_prop_id_t min_prop, vm_prop_id_t max_prop)
{
vm_meta_reg_t *entry;
size_t prop_xlat_cnt;
/* get the registration table entry from the index */
entry = &G_meta_reg_table[idx];
/* remember the defining class object */
table_[count_].meta_ = *entry->meta;
/* save the name */
table_[count_].set_meta_name(metaclass_id);
/* calculate the number of entries in the table */
if (min_prop == VM_INVALID_PROP || max_prop == VM_INVALID_PROP)
prop_xlat_cnt = 0;
else
prop_xlat_cnt = max_prop - min_prop + 1;
/* set the property count */
table_[count_].min_prop_ = min_prop;
table_[count_].prop_xlat_cnt_ = prop_xlat_cnt;
/* set the function count */
table_[count_].func_xlat_cnt_ = func_cnt;
/* we don't know the intrinsic class's representative object yet */
table_[count_].class_obj_ = VM_INVALID_OBJ;
/* allocate space for the property table, if we have any translations */
if (prop_xlat_cnt != 0)
{
size_t siz;
/* calculate the table size */
siz = prop_xlat_cnt * sizeof(table_[count_].prop_xlat_[0]);
/* allocate the table */
table_[count_].prop_xlat_ = (unsigned short *)t3malloc(siz);
/*
* initialize each entry in the table to zero - this will ensure
* that each entry that is never set to a valid function index
* will properly yield function index zero, which is defined as
* the "not found" invalid function index
*/
memset(table_[count_].prop_xlat_, 0, siz);
}
else
{
/* there are no properties to translate, so we don't need a table */
table_[count_].prop_xlat_ = 0;
}
/* allocate the function-to-property translation table */
if (func_cnt != 0)
{
size_t i;
vm_prop_id_t *prop_ptr;
/* allocate the table */
table_[count_].func_xlat_ =
(vm_prop_id_t *)t3malloc(func_cnt * sizeof(vm_prop_id_t));
/* clear the table - mark each entry as an invalid property ID */
for (i = 0, prop_ptr = table_[count_].func_xlat_ ;
i < func_cnt ; ++i, ++prop_ptr)
*prop_ptr = VM_INVALID_PROP;
}
else
{
/* no properties, so we don't need a table */
table_[count_].func_xlat_ = 0;
}
/*
* store the reverse mapping for this entry, so that we can find the
* dependency entry given the registration index
*/
reverse_map_[idx] = count_;
/* count the new entry */
++count_;
}
/* ------------------------------------------------------------------------ */
/*
* Add an entry to the dependency table only if it doesn't already
* appear in the dependency table. We add the entry based on its
* registration table index.
*/
void CVmMetaTable::add_entry_if_new(uint reg_table_idx, size_t func_cnt,
vm_prop_id_t min_prop,
vm_prop_id_t max_prop)
{
/*
* if the entry already has a valid dependency table index, as
* obtained from the reverse mapping, we need not add it again
*/
if (reverse_map_[reg_table_idx] != -1)
return;
/* add the entry */
add_entry((*G_meta_reg_table[reg_table_idx].meta)->get_meta_name(),
reg_table_idx, func_cnt, min_prop, max_prop);
}
/* ------------------------------------------------------------------------ */
/*
* Invoke the VM-stack-based constructor for the metaclass at the given
* index
*/
vm_obj_id_t CVmMetaTable::create_from_stack(VMG_ const uchar **pc_ptr,
uint idx, uint argc)
{
/* make sure the entry is defined */
if (idx >= count_)
err_throw(VMERR_BAD_METACLASS_INDEX);
/* invoke the appropriate constructor */
return table_[idx].meta_->create_from_stack(vmg_ pc_ptr, argc);
}
/* ------------------------------------------------------------------------ */
/*
* Call a static property in the metaclass at the given index
*/
int CVmMetaTable::call_static_prop(VMG_ vm_val_t *result,
const uchar **pc_ptr, uint idx,
uint *argc, vm_prop_id_t prop)
{
/* make sure the entry is defined */
if (idx >= count_)
err_throw(VMERR_BAD_METACLASS_INDEX);
/* invoke the appropriate static property evaluator */
return table_[idx].meta_->call_stat_prop(vmg_ result, pc_ptr, argc, prop);
}
/* ------------------------------------------------------------------------ */
/*
* Create an object with the given ID and load the object from the image
* file.
*/
void CVmMetaTable::create_from_image(VMG_ uint idx, vm_obj_id_t id,
const char *ptr, size_t siz)
{
/* make sure the entry is defined */
if (idx >= count_)
err_throw(VMERR_BAD_METACLASS_INDEX);
/* create the object table entry in the memory manager */
G_obj_table->alloc_obj_with_id(id, TRUE);
/* invoke the appropriate constructor */
table_[idx].meta_->create_for_image_load(vmg_ id);
/* load the object */
vm_objp(vmg_ id)->load_from_image(vmg_ id, ptr, siz);
}
/*
* Create an object of the given metaclass with the given ID, in
* preparation for restoring the object's data from saved state
* information. This doesn't fill in the object with the saved state
* data, but merely creates the object.
*
* The caller is responsible for having allocated the object ID before
* calling this function.
*/
void CVmMetaTable::create_for_restore(VMG_ uint idx, vm_obj_id_t id)
{
/* make sure the entry is defined in our table of metaclasses */
if (idx >= count_)
err_throw(VMERR_BAD_METACLASS_INDEX);
/*
* Invoke the appropriate constructor. Note that the caller must
* already have allocated the object ID, so we simply use the given ID
* without further consideration.
*/
table_[idx].meta_->create_for_restore(vmg_ id);
}
/* ------------------------------------------------------------------------ */
/*
* Write the table to a file.
*/
void CVmMetaTable::write_to_file(CVmFile *fp)
{
size_t i;
/* write the number of entries */
fp->write_int2(get_count());
/* write each entry */
for (i = 0 ; i < get_count() ; ++i)
{
const char *nm;
const vm_meta_entry_t *entry;
ushort j;
/* get the entry */
entry = get_entry(i);
/* get this entry's name */
nm = entry->image_meta_name_;
/* write the length of the name, followed by the name */
fp->write_int2(strlen(nm));
fp->write_bytes(nm, strlen(nm));
/* write our associated IntrinsicClass object's ID */
fp->write_int4(entry->class_obj_);
/*
* Write the property table information - write the number of
* function entries, and the minimum and maximum property ID's.
*/
fp->write_int2(entry->func_xlat_cnt_);
fp->write_int2(entry->min_prop_);
fp->write_int2(entry->min_prop_ + entry->prop_xlat_cnt_);
/*
* Write out the property translation table. The function
* translation table will always be smaller than (at worst, it
* will be the same size as) the property translation table;
* both tables contain the same information, so since we only
* need to write out one or the other, write out the smaller of
* the two.
*
* Note that xlat_func() requires a 1-based function index, so
* run our counter from 1 to the function table count.
*/
for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
fp->write_int2(entry->xlat_func(j));
}
}
/*
* Read the table from a file.
*/
int CVmMetaTable::read_from_file(CVmFile *fp)
{
size_t cnt;
size_t i;
/* clear the existing table */
clear();
/* read the number of entries */
cnt = fp->read_uint2();
/* read the entries */
for (i = 0 ; i < cnt ; ++i)
{
char buf[256];
size_t len;
vm_prop_id_t min_prop;
vm_prop_id_t max_prop;
ushort func_cnt;
ushort j;
vm_meta_entry_t *entry;
vm_obj_id_t class_obj;
/* read the length of this entry, and make sure it's valid */
len = fp->read_uint2();
if (len > sizeof(buf) - 1)
return VMERR_SAVED_META_TOO_LONG;
/* read the name and null-terminate it */
fp->read_bytes(buf, len);
buf[len] = '\0';
/* read the associated IntrinsicClass object */
class_obj = (vm_obj_id_t)fp->read_uint4();
/* read the property table description */
func_cnt = (ushort)fp->read_uint2();
min_prop = (vm_prop_id_t)fp->read_uint2();
max_prop = (vm_prop_id_t)fp->read_uint2();
/* add this entry */
add_entry(buf, func_cnt, min_prop, max_prop);
/* get the new entry's record */
entry = get_entry(i);
/* set the class ID */
entry->class_obj_ = class_obj;
/*
* Read the property mappings. We stored the function table
* entries, so we simply need to load those entries. Note that
* the function table has a 1-based index, so run our counter
* from 1 to the function count.
*/
for (j = 1 ; j <= func_cnt ; ++j)
entry->add_prop_xlat((short)fp->read_int2(), j);
}
/* success */
return 0;
}
#if 0 // moved inline, since it's small and used a lot
/*
* Get the function vector index for a property given the metaclass's
* registration table index.
*/
int CVmMetaTable::prop_to_vector_idx(uint reg_table_idx, vm_prop_id_t prop)
{
vm_meta_entry_t *entry;
/* get the entry for the registration table index */
entry = get_entry_from_reg(reg_table_idx);
/*
* if there's no entry for this registration index, there's
* obviously no metaclass function mapping for the property
*/
if (entry == 0)
return 0;
/* return the property translation */
return entry->xlat_prop(prop);
}
#endif
/*
* Create an IntrinsicClass object for each metaclass that doesn't
* already have one.
*/
void CVmMetaTable::create_intrinsic_class_instances(VMG0_)
{
size_t idx;
vm_meta_entry_t *entry;
/* go through our table */
for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry)
{
/* if this entry has no associated class object, create one for it */
if (entry->class_obj_ == VM_INVALID_OBJ)
{
/*
* create the class object - it will automatically register
* itself to fill in the entry
*/
CVmObjClass::create_dyn(vmg_ idx);
}
/*
* Add this object to the machine globals, if it's not a root
* object. Any dynamically-created intrinsic class instance must
* be made a machine global because it will always be reachable as
* the class object for the metaclass, but the metclass will not
* trace the object during garbage collection; the only way to
* keep these objects from being incorrectly collected is to make
* them machine globals.
*/
G_obj_table->add_to_globals(entry->class_obj_);
}
}
/*
* Forget the IntrinsicClass objects we created dynamically in
* create_intrinsic_class_instances(). This simply drops our references
* to those objects from the metaclass table.
*/
void CVmMetaTable::forget_intrinsic_class_instances(VMG0_)
{
size_t idx;
vm_meta_entry_t *entry;
/* go through our table and clear out our IntrinsicClass references */
for (idx = 0, entry = table_ ; idx < count_ ; ++idx, ++entry)
entry->class_obj_ = VM_INVALID_OBJ;
}
|