cfad47cfa3/tads3/vmtobj.h
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 26.9 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers/* $Header: d:/cvsroot/tads/tads3/VMTOBJ.H,v 1.2 1999/05/17 02:52:29 MJRoberts Exp $ */
/*
* 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
vmtobj.h - VM TADS Object implementation
Function
Notes
This implementation assumes a non-relocating memory manager, both for
the fixed part (the CVmObject part) and the variable part (the
"extension," located in the variable-part heap) of our objects. In the
present implementation, the memory manager satisfies this requirement, and
there are no plans to change this.
The memory manager is designed *in principal* to allow for object
relocation (specifically as a means to reduce heap fragmentation), so it's
not a foregone conclusion that such a thing will never be implemented.
However, given the large memories of modern machines (especially relative
to the size of a typical tads application), and given that recent academic
research has been calling into question the conventional wisdom that heap
fragmentation is actually a problem in practice, we consider the
probability that we will want to implement a relocation memory manager
low, and thus we feel it's better to exploit the efficiencies of using and
storing direct object pointers in some places in this code.
Modified
10/30/98 MJRoberts - Creation
*/
#ifndef VMTOBJ_H
#define VMTOBJ_H
#include <stdlib.h>
#include <string.h>
#include "t3std.h"
#include "vmtype.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmundo.h"
/* forward-declare our main class */
class CVmObjTads;
/* ------------------------------------------------------------------------ */
/*
* TADS-Object image file data. The image file state is loaded into an
* image object data block, and we set up our own internal data based on
* it at load time. The image file data block is arranged as follows:
*
*. UINT2 superclass_count
*. UINT2 load_image_property_count
*. UINT2 flags
*. UINT4 superclass_1
*. ...
*. UINT4 superclass_N
*. UINT2 load_image_property_ID_1
*. DATAHOLDER load_image_property_value_1
*. ...
*. UINT2 load_image_property_ID_N
*. DATAHOLDER load_image_property_value_N
*/
/* superclass structure for object extension */
struct vm_tadsobj_sc
{
vm_obj_id_t id;
CVmObjTads *objp;
};
/*
* For our in-memory object extension, we use a structure that stores the
* object data. We store the properties in a hash table keyed on property
* ID.
*/
struct vm_tadsobj_hdr
{
/* allocate */
static vm_tadsobj_hdr *alloc(VMG_ class CVmObjTads *self,
unsigned short sc_cnt,
unsigned short prop_cnt);
/* delete */
void free_mem();
/* reallocate an existing object to expand its property table */
static vm_tadsobj_hdr *expand(VMG_ class CVmObjTads *self,
vm_tadsobj_hdr *obj);
/*
* reallocate an existing object to expand its property table to the
* given minimum number of property entries
*/
static vm_tadsobj_hdr *expand_to(VMG_ class CVmObjTads *self,
vm_tadsobj_hdr *obj,
size_t new_sc_cnt, size_t min_prop_cnt);
/* invalidate the cached inheritance path, if any */
void inval_inh_path()
{
/* if we have an inheritance path cached, forget it */
if (inh_path != 0)
{
/* forget the path, so that we recalculate it on demand */
t3free(inh_path);
inh_path = 0;
}
}
/* find a property entry */
inline struct vm_tadsobj_prop *find_prop_entry(uint prop);
/* allocate a new hash entry */
vm_tadsobj_prop *alloc_prop_entry(vm_prop_id_t prop,
const vm_val_t *val,
unsigned int flags);
/* calculate the hash code for a property */
unsigned int calc_hash(uint prop) const
{
/*
* Simply take the property ID modulo the table size. We always
* use a power of 2 as the hash table size, so the remainder is
* easy to calculate using a bit mask rather than a more expensive
* integer division.
*/
return (unsigned int)(prop & (hash_siz - 1));
}
/* check to see if we have the required number of free entries */
int has_free_entries(size_t cnt) const
{ return cnt <= (size_t)(prop_entry_free - prop_entry_cnt); }
/* load image object flags (a combination of VMTOBJ_OBJF_xxx values) */
unsigned short li_obj_flags;
/* internal object flags (a combination of VMTO_OBJ_xxx values) */
unsigned short intern_obj_flags;
/*
* Inheritance search table. We build and save the search path for
* any class with multiple superclasses, because the inheritance path
* for a class with multiple base classes can be somewhat
* time-consuming to determine. For objects with only one base class,
* we don't bother caching a path, since the path is trivial to
* calculate in these cases.
*/
struct tadsobj_inh_path *inh_path;
/*
* Number of hash buckets, and a pointer to the bucket array. (The
* hash bucket array is allocated as part of the same memory block as
* this structure - we suballocate it from the memory block when
* allocating the structure.) 'hash_arr[hash]' points to the head of
* a list of property entries with the given hash value.
*/
unsigned short hash_siz;
struct vm_tadsobj_prop **hash_arr;
/*
* Pointer to our allocation array of hash buckets. We suballocate
* this out of our allocation block. (Note that this isn't the hash
* table; this is the pool of elements out of which hash table entries
* - not buckets, but the entries in the lists pointed to by the
* buckets - are allocated.)
*/
struct vm_tadsobj_prop *prop_entry_arr;
/* total number of hash entries allocated */
unsigned short prop_entry_cnt;
/*
* Index of next available hash entry. Hash entries are never
* deleted, so we don't have to worry about returning entries to the
* free pool. So, the free pool simply consists of entries from this
* index to the maximum index (prop_entry_cnt - 1).
*
* When we run out of entries, we must reallocate this entire
* structure to make room for more. This means that reallocation is
* fairly expensive, but this is acceptable because we will always
* want to resize the hash table at the same time anyway. We always
* resize the hash table on exhausting our current allocation size
* because we pick the hash table size based on the expected maximum
* number of entries; once we exceed that maximum, we must reconsider
* the hash table size.
*/
unsigned short prop_entry_free;
/*
* Number of superclasses, and the array of superclasses. We
* overallocate the structure to make room for enough superclasses.
* (Note that this means the 'sc' field must be the last thing in the
* structure.)
*/
unsigned short sc_cnt;
vm_tadsobj_sc sc[1];
};
/*
* Tads-object property entry. Each hash table entry points to a linked
* list of these entries.
*/
struct vm_tadsobj_prop
{
/* my property ID */
vm_prop_id_t prop;
/* pointer to the next entry at the same hash value */
vm_tadsobj_prop *nxt;
/* flags */
unsigned char flags;
/* my value */
vm_val_t val;
};
/*
* Internal object flags
*/
/* from load image - object originally came from image file */
#define VMTO_OBJ_IMAGE 0x0001
/* modified - object has been modified since being loaded from image */
#define VMTO_OBJ_MOD 0x0002
/*
* Property entry flags
*/
/* modified - this property is not from the load image file */
#define VMTO_PROP_MOD 0x01
/* we've stored undo for this property since the last savepoint */
#define VMTO_PROP_UNDO 0x02
/* ------------------------------------------------------------------------ */
/*
* Load Image Object Flag Values - these values are stored in the image
* file object header.
*/
/* class - the object represents a class, not an instance */
#define VMTOBJ_OBJF_CLASS 0x0001
/* ------------------------------------------------------------------------ */
/*
* Initial empty property table size. When we initially load an object,
* we'll allocate this many empty slots for modifiable properties.
*/
const ushort VMTOBJ_PROP_INIT = 16;
/* ------------------------------------------------------------------------ */
/*
* TADS object interface.
*/
class CVmObjTads: public CVmObject
{
friend class CVmMetaclassTads;
friend struct tadsobj_sc_search_ctx;
public:
/* metaclass registration object */
static class CVmMetaclass *metaclass_reg_;
class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
/* am I of the given metaclass? */
virtual int is_of_metaclass(class CVmMetaclass *meta) const
{
/* try my own metaclass and my base class */
return (meta == metaclass_reg_
|| CVmObject::is_of_metaclass(meta));
}
/* is the given object a TadsObject object? */
static int is_tadsobj_obj(VMG_ vm_obj_id_t obj)
{ return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }
/* create dynamically using stack arguments */
static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
uint argc)
{ return create_from_stack_intern(vmg_ pc_ptr, argc, FALSE); }
/*
* call a static property - we don't have any of our own, so simply
* "inherit" the base class handling
*/
static int call_stat_prop(VMG_ vm_val_t *result,
const uchar **pc_ptr, uint *argc,
vm_prop_id_t prop);
/* create an object with no initial extension */
static vm_obj_id_t create(VMG_ int in_root_set);
/*
* Create an object with a given number of superclasses, and a given
* number of property slots. The property slots are all initially
* allocated to modified properties.
*/
static vm_obj_id_t create(VMG_ int in_root_set,
ushort superclass_count, ushort prop_slots);
/* notify of deletion */
void notify_delete(VMG_ int in_root_set);
/* create an instance of this object */
void create_instance(VMG_ vm_obj_id_t self,
const uchar **pc_ptr, uint argc);
/* determine if the object has a finalizer method */
virtual int has_finalizer(VMG_ vm_obj_id_t /*self*/);
/* invoke the object's finalizer */
virtual void invoke_finalizer(VMG_ vm_obj_id_t self);
/* get the number of superclasses of this object */
virtual int get_superclass_count(VMG_ vm_obj_id_t self) const
{
/*
* if we have no superclass, inherit the default, since we
* inherit from the system TadsObject class; if we have our own
* superclasses, return them
*/
if (get_sc_count() == 0)
return CVmObject::get_superclass_count(vmg_ self);
else
return get_sc_count();
}
/* get the nth superclass of this object */
virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t self, int idx) const
{
/*
* if we have no superclass, inherit the default, since we
* inherit from the system TadsObject class; if we have our own
* superclasses, return them
*/
if (get_sc_count() == 0)
return CVmObject::get_superclass(vmg_ self, idx);
else if (idx >= get_sc_count())
return VM_INVALID_OBJ;
else
return get_sc(idx);
}
/* determine if I'm a class object */
virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const
{ return (get_li_obj_flags() & VMTOBJ_OBJF_CLASS) != 0; }
/* determine if I'm an instance of the given object */
int is_instance_of(VMG_ vm_obj_id_t obj);
/* this object type provides properties */
int provides_props(VMG0_) const { return TRUE; }
/* enumerate properties */
void 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);
/* set a property */
void set_prop(VMG_ class CVmUndo *undo,
vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val);
/* get a property */
int 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);
/* inherit a property */
int 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);
/* build my property list */
void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval);
/*
* Receive notification of a new savepoint. We keep track of
* whether or not we've saved undo information for each modifiable
* property in the current savepoint, so that we can avoid saving
* redundant undo information when repeatedly changing a property
* value (since only the first change in a given savepoint needs to
* be recorded). When we start a new savepoint, we obviously
* haven't yet stored any undo information for the new savepoint, so
* we can simply clear all of the undo records.
*/
void notify_new_savept()
{ clear_undo_flags(); }
/* apply undo */
void apply_undo(VMG_ struct CVmUndoRecord *rec);
/* mark a reference in an undo record */
void mark_undo_ref(VMG_ struct CVmUndoRecord *undo);
/*
* remove stale weak references from an undo record -- we keep only
* normal strong references, so we don't need to do anything here
*/
void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }
/* mark references */
void mark_refs(VMG_ uint state);
/*
* remove weak references - we keep only normal (strong) references,
* so this routine doesn't need to do anything
*/
void remove_stale_weak_refs(VMG0_) { }
/* load from an image file */
void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);
/* restore to image file state */
void reload_from_image(VMG_ vm_obj_id_t self,
const char *ptr, size_t siz);
/* determine if the object has been changed since it was loaded */
int is_changed_since_load() const;
/* save to a file */
void save_to_file(VMG_ class CVmFile *fp);
/* restore from a file */
void restore_from_file(VMG_ vm_obj_id_t self,
class CVmFile *fp, class CVmObjFixup *fixups);
/* rebuild for image file */
virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);
/* convert to constant data */
virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper,
vm_obj_id_t self);
/* get the nth superclass */
vm_obj_id_t get_sc(uint n) const
{ return get_hdr()->sc[n].id; }
/* get a pointer to the object for the nth superclass */
CVmObjTads *get_sc_objp(VMG_ ushort n) const
{
CVmObjTads **objpp;
/* if we haven't stored the superclass object pointer yet, do so */
objpp = &get_hdr()->sc[n].objp;
if (*objpp == 0)
*objpp = (CVmObjTads *)vm_objp(vmg_ get_hdr()->sc[n].id);
/* return the object pointer */
return *objpp;
}
/* set the nth superclass to the given object */
void set_sc(VMG_ ushort n, vm_obj_id_t obj)
{
get_hdr()->sc[n].id = obj;
get_hdr()->sc[n].objp = (CVmObjTads *)vm_objp(vmg_ obj);
}
/* static class initialization/termination */
static void class_init(VMG0_);
static void class_term(VMG0_);
protected:
/* create an object with no initial extension */
CVmObjTads() { ext_ = 0; }
/*
* Create an object with a given number of superclasses, and a given
* number of property slots. All property slots are initially
* allocated to the modifiable property list.
*/
CVmObjTads(VMG_ ushort superclass_count, ushort prop_count);
/* internal handler to create from stack arguments */
static vm_obj_id_t create_from_stack_intern(VMG_ const uchar **pc_ptr,
uint argc, int is_transient);
/*
* internal handler to create with multiple inheritance from arguments
* passed on the stack
*/
static vm_obj_id_t create_from_stack_multi(VMG_ uint argc,
int is_transient);
/* get the load image object flags */
uint get_li_obj_flags() const
{ return get_hdr()->li_obj_flags; }
/* set the object flags */
void set_li_obj_flags(ushort flags)
{ get_hdr()->li_obj_flags = flags; }
/*
* Allocate memory - this replaces any existing extension, so the
* caller must take care to free the extension (if one has already
* been allocated) before calling this routine.
*
* If 'from_image' is true, we're allocating memory for use with an
* object loaded from an image file, so we'll ignore the superclass
* count and leave the image_data pointer in the header unchanged.
* If 'from_image' is false, we're allocating memory for a dynamic
* object that does not have a presence in the image file, so we'll
* allocate space for the superclass list as part of the extension
* and set the image_data pointer in the header to refer the extra
* space after the modifiable property array and undo bit array.
*/
void alloc_mem(VMG_ ushort sc_count, ushort mod_prop_count,
int from_image);
/* get a property from the intrinsic class */
int 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);
/*
* Search for a property, continuing a previous search from the given
* point. defining_obj is the starting point for the search: we start
* searching in the target object's inheritance tree after
* defining_obj. This is used to continue an inheritance search from
* a given point, as needed for the 'inherited' operator, for example.
*/
static int 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);
/* cache and return the inheritance search path for this object */
tadsobj_inh_path *get_inh_search_path(VMG0_);
/* load the image file properties and superclasses */
void load_image_props_and_scs(VMG_ const char *ptr, size_t siz);
/* get/set the superclass count */
ushort get_sc_count() const
{ return get_hdr()->sc_cnt; }
void set_sc_count(ushort cnt)
{ get_hdr()->sc_cnt = cnt; }
/* change the superclass list */
void change_superclass_list(VMG_ const char *lstp, ushort cnt);
/* clear all undo flags */
void clear_undo_flags();
/* -------------------------------------------------------------------- */
/*
* Low-level format management - these routines encapsulate the byte
* layout of the object in memory. This is a bit nasty because we
* keep the object's contents in the portable image format.
*/
/* get my header */
inline struct vm_tadsobj_hdr *get_hdr() const
{ return (vm_tadsobj_hdr *)ext_; }
/* property evaluator - undefined property */
int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }
/* property evaluator - createInstance */
int getp_create_instance(VMG_ vm_obj_id_t, vm_val_t *, uint *);
/* property evaluator - createClone */
int getp_create_clone(VMG_ vm_obj_id_t, vm_val_t *, uint *);
/* property evaluator - createTransientInstance */
int getp_create_trans_instance(VMG_ vm_obj_id_t, vm_val_t *retval,
uint *argc);
/* property evaluator - createInstanceOf */
int getp_create_instance_of(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *in_argc);
/* property evaluator - createTransientInstanceOf */
int getp_create_trans_instance_of(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *in_argc);
/* common handler for createInstance and createTransientInstance */
int getp_create_common(VMG_ vm_obj_id_t, vm_val_t *retval, uint *argc,
int is_transient);
/* common handler for createInstanceOf and createTransientInstanceOf */
int getp_create_multi_common(VMG_ vm_obj_id_t, vm_val_t *retval,
uint *argc, int is_transient);
/* property evaluator - setSuperclassList */
int getp_set_sc_list(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *in_argc);
/* property evaluation function table */
static int (CVmObjTads::*func_table_[])(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc);
};
/* ------------------------------------------------------------------------ */
/*
* Registration table object
*/
class CVmMetaclassTads: public CVmMetaclass
{
public:
/* get the global name */
const char *get_meta_name() const { return "tads-object/030004"; }
/* create from image file */
void create_for_image_load(VMG_ vm_obj_id_t id)
{
new (vmg_ id) CVmObjTads();
G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
}
/* create from restoring from saved state */
void create_for_restore(VMG_ vm_obj_id_t id)
{
new (vmg_ id) CVmObjTads();
G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
}
/* create dynamically using stack arguments */
vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
{ return CVmObjTads::create_from_stack(vmg_ pc_ptr, argc); }
/* call a static property */
int call_stat_prop(VMG_ vm_val_t *result,
const uchar **pc_ptr, uint *argc,
vm_prop_id_t prop)
{
return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
}
};
/* ------------------------------------------------------------------------ */
/*
* Intrinsic class modifier object. This object is for use as a modifier
* object for an intrinsic class.
*
* This is a simple subclass of the regular TADS-Object class. The only
* difference is that we resolve properties a little differently: unlike
* regular TADS Objects, this class is essentially a mix-in, and has no
* intrinsic superclass at all. This means that the only place we look
* for a property in get_prop is in our property list; we specifically do
* not look for an intrinsic property, nor do we look for a superclass
* that provides an intrinsic property.
*/
class CVmObjIntClsMod: public CVmObjTads
{
friend class CVmMetaclassIntClsMod;
public:
static class CVmMetaclass *metaclass_reg_;
class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
/* am I of the given metaclass? */
virtual int is_of_metaclass(class CVmMetaclass *meta) const
{
/* try my own metaclass and my base class */
return (meta == metaclass_reg_
|| CVmObjTads::is_of_metaclass(meta));
}
/* is the given object an intrinsic class modifier object? */
static int is_intcls_mod_obj(VMG_ vm_obj_id_t obj)
{ return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }
/* create dynamically using stack arguments */
static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
uint argc)
{
/* can't create instances of intrinsic class modifiers */
err_throw(VMERR_ILLEGAL_NEW);
AFTER_ERR_THROW(return VM_INVALID_OBJ;)
}
/*
* call a static property - we don't have any of our own, so simply
* "inherit" the base class handling
*/
static int call_stat_prop(VMG_ vm_val_t *result,
const uchar **pc_ptr, uint *argc,
vm_prop_id_t prop)
{
return CVmObjTads::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
}
/* get a property */
int 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);
/* inherit a property */
int 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);
/* build my property list */
void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval);
/* create an object with no initial extension */
CVmObjIntClsMod() { ext_ = 0; }
/*
* Create an object with a given number of superclasses, and a given
* number of property slots. All property slots are initially
* allocated to the modifiable property list.
*/
CVmObjIntClsMod(VMG_ ushort superclass_count, ushort prop_count)
: CVmObjTads(vmg_ superclass_count, prop_count) { }
};
/*
* Registration table object
*/
class CVmMetaclassIntClsMod: public CVmMetaclass
{
public:
/* get the global name */
const char *get_meta_name() const { return "int-class-mod/030000"; }
/* create from image file */
void create_for_image_load(VMG_ vm_obj_id_t id)
{
new (vmg_ id) CVmObjIntClsMod();
G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
}
/* create from restoring from saved state */
void create_for_restore(VMG_ vm_obj_id_t id)
{
new (vmg_ id) CVmObjIntClsMod();
G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
}
/* create dynamically using stack arguments */
vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
{ return CVmObjIntClsMod::create_from_stack(vmg_ pc_ptr, argc); }
/* call a static property */
int call_stat_prop(VMG_ vm_val_t *result,
const uchar **pc_ptr, uint *argc,
vm_prop_id_t prop)
{
return CVmObjIntClsMod::
call_stat_prop(vmg_ result, pc_ptr, argc, prop);
}
};
#endif /* VMTOBJ_H */
/*
* Register the classes
*/
VM_REGISTER_METACLASS(CVmObjTads)
VM_REGISTER_METACLASS(CVmObjIntClsMod) |