cfad47cfa3/t3compiler/tads3/vmimgrb.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 53.8 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#ifdef RCSID
static char RCSid[] =
"$Header$";
#endif
/*
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
vmimgrb.cpp - T3 Image File Re-Builder
Function
This module re-builds an image file from the contents of memory after
the program has been "pre-initialized." This allows a program to
run through its static state initialization during the compilation
process, then store the result as a new image file with pre-initialized
state. Any initialization that must always happen for every run of
the program can be performed during this pre-initialization pass,
saving the time of doing the work each time the program is run.
Mostly, we just copy the old image file to the new image file; most
parts of the image file are copied without changes. We update the
object stream, replacing the original objects with the objects in
their pre-initialized state, and we add any new strings dynamically
created during pre-initialization to the constant pool.
Notes
Modified
07/21/99 MJRoberts - Creation
*/
#include <stdlib.h>
#include <memory.h>
#include "t3std.h"
#include "vmfile.h"
#include "vmimage.h"
#include "vmpool.h"
#include "vmglob.h"
#include "vmwrtimg.h"
#include "vmobj.h"
#include "vmtobj.h"
#include "vmdict.h"
#include "vmhash.h"
#include "vmlst.h"
#include "vmbignum.h"
#include "vmstr.h"
#include "vmgram.h"
#include "vmmeta.h"
#include "vmimgrb.h"
#include "vmintcls.h"
#include "vmiter.h"
#include "vmvec.h"
#include "vmlookup.h"
#include "vmstack.h"
#include "vmbytarr.h"
#include "vmcset.h"
#include "vmfilobj.h"
#include "vmpat.h"
#include "vmstrcmp.h"
/* ------------------------------------------------------------------------ */
/*
* Rebuild the OBJS blocks and write the data to the file. This goes
* through the objects in memory and constructs fixed image-file
* versions of the objects, then writes them to OBJS blocks.
*/
static void vm_rewrite_objs_blocks(VMG_ CVmImageWriter *writer,
class CVmConstMapper *mapper)
{
size_t i;
/* rewrite the image block for each of our defined metaclasses */
for (i = 0 ; i < G_meta_table->get_count() ; ++i)
{
/* write this metaclass's objects */
G_obj_table->rebuild_image(vmg_ i, writer, mapper);
}
}
/* ------------------------------------------------------------------------ */
/*
* Re-write the image file
*/
void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp,
ulong static_cs_ofs)
{
char buf[4096];
int done;
CVmImageWriter *writer;
CVmConstMapper *const_mapper;
uchar xor_mask;
ulong code_page_size;
/* we don't know the code page size yet */
code_page_size = 0;
/* choose an arbitrary XOR mask for our pages */
xor_mask = 0xEF;
/* create an image writer to help us write the output file */
writer = new CVmImageWriter(newfp);
/* create our constant mapper */
const_mapper = new CVmConstMapper(vmg0_);
/*
* clear all undo information - we don't save undo with the rebuilt
* file, so there's no reason to keep any of the objects that are
* referenced only in the undo records
*/
G_undo->drop_undo(vmg0_);
/* discard everything on the stack */
G_stk->discard(G_stk->get_depth());
/*
* perform a full garbage collection pass, to make sure we don't
* include any unreachable objects in the new image file
*/
G_obj_table->gc_full(vmg0_);
/* add any metaclasses that weren't in the dependency table originally */
G_obj_table->add_metadeps_for_instances(vmg0_);
/* convert objects to constant data to the extent possible */
G_obj_table->rebuild_image_convert_const_data(vmg_ const_mapper);
/*
* copy the header (signature, UINT2 format version number, 32
* reserved bytes, 24-byte compilation timestamp) to the new file
*/
origfp->read_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
newfp->write_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
/* copy or replace the data blocks */
for (done = FALSE ; !done ; )
{
ulong siz;
/* read the next block header */
origfp->read_bytes(buf, 10);
/* get the size */
siz = t3rp4u(buf + 4);
/* check the block type */
if (CVmImageLoader::block_type_is(buf, "OBJS")
|| CVmImageLoader::block_type_is(buf, "MCLD")
|| CVmImageLoader::block_type_is(buf, "SINI"))
{
/*
* Simply skip all of the original OBJS (object data) or
* MCLD (metaclass dependency table) blocks -- we'll replace
* them with our re-built blocks.
*
* Also skip SINI (static initializer) blocks, since these
* are only needed for pre-initialization and can be
* discarded from the final image file.
*/
origfp->set_pos(origfp->get_pos() + (long)siz);
}
else if (CVmImageLoader::block_type_is(buf, "CPDF"))
{
char cpdf_buf[20];
uint pool_id;
ulong pgcnt;
ulong pgsiz;
/* read the pool definition */
origfp->read_bytes(cpdf_buf, 10);
/* get the ID, page count, and page size from the definition */
pool_id = osrp2(cpdf_buf);
pgcnt = t3rp4u(cpdf_buf + 2);
pgsiz = t3rp4u(cpdf_buf + 6);
/*
* if this is the constant pool (pool ID = 2), increase the
* page count by the extra constant pool pages we need for
* converting new object data to constants
*/
if (pool_id == 2)
{
/* add in our new count */
pgcnt += const_mapper->get_page_count();
/* write the new count */
oswp4(cpdf_buf + 2, pgcnt);
}
/*
* if this is the code pool (pool ID = 1), and we have
* static initializers, decrease the page count to exclude
* the static initializer pages (all of the static
* initializers are grouped at the high end of the code
* pool, so we can discard them and only them by truncating
* the code pool before the page containing the first static
* initializer)
*/
if (pool_id == 1 && static_cs_ofs != 0)
{
/*
* calculate the new count - it's the offset to the
* first static initializer divided by the size of each
* code page
*/
pgcnt = static_cs_ofs / pgsiz;
/* write the new count */
oswp4(cpdf_buf + 2, pgcnt);
/*
* remember the code page size for later, when we're
* scanning the code pages themselves
*/
code_page_size = pgsiz;
}
/* update the constant block definition */
newfp->write_bytes(buf, 10);
newfp->write_bytes(cpdf_buf, 10);
}
else if (CVmImageLoader::block_type_is(buf, "CPPG"))
{
char cppg_buf[20];
long start_pos;
uint pool_id;
ulong page_idx;
int keep_block;
/* presume we're going to keep this block */
keep_block = TRUE;
/*
* This is a constant page - if it's in pool 2 (constants),
* use its XOR mask for any new pages we write. First, read
* the pool header, then seek back so we can copy the block
* unchanged.
*/
start_pos = origfp->get_pos();
origfp->read_bytes(cppg_buf, 7);
origfp->set_pos(start_pos);
/* get the pool ID and the page's index */
pool_id = osrp2(cppg_buf);
page_idx = t3rp4u(cppg_buf + 2);
/* if it's pool 2 (constants), read its XOR mask byte */
if (pool_id == 2)
xor_mask = cppg_buf[6];
/*
* if it's pool 1 (code), and it's above the start of the
* static initializers, skip it - we don't want to copy
* static initializer code to the final image file, since
* they're only needed for static initialization, which we
* necessarily have finished by the time we reach this point
*/
if (pool_id == 1
&& static_cs_ofs != 0
&& page_idx * code_page_size >= static_cs_ofs)
{
/* this is a static initializer block - skip it */
keep_block = FALSE;
}
/* keep or skip the block, as appropriate */
if (keep_block)
{
/*
* we only wanted to get information from this block, so
* go copy it as-is to the output
*/
goto copy_block;
}
else
{
/* skip past the block */
origfp->set_pos(origfp->get_pos() + (long)siz);
}
}
else if (CVmImageLoader::block_type_is(buf, "EOF "))
{
/* end of file - note that we're done after this block */
done = TRUE;
/* re-write the metaclass dependency block */
G_meta_table->rebuild_image(writer);
/* write our new OBJS blocks */
vm_rewrite_objs_blocks(vmg_ writer, const_mapper);
/* write our new constant pool pages */
const_mapper->write_to_image_file(writer, xor_mask);
/* copy the EOF block to the new file unchanged */
goto copy_block;
}
else
{
copy_block:
/*
* For anything else, we'll simply copy the original block
* from the original image file unchanged.
*/
/* write the block header unchanged */
newfp->write_bytes(buf, 10);
/* copy the block in chunks */
while (siz != 0)
{
size_t cur;
/*
* read as much as we can, up to the amount remaining or
* the buffer size, whichever is smaller
*/
cur = sizeof(buf);
if (cur > siz)
cur = (size_t)siz;
/* read the data and write it out */
origfp->read_bytes(buf, cur);
newfp->write_bytes(buf, cur);
/* deduct this chunk from the amount remaining */
siz -= cur;
}
}
}
/* we're done with the image writer */
delete writer;
/* we're done with the constant mapper */
delete const_mapper;
}
/* ------------------------------------------------------------------------ */
/*
* Object Table extension - rebuild the image file representations for
* all objects of a particular metaclass.
*/
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
class CVmConstMapper *mapper)
{
int pass;
/* write persistent and transient objects separately */
for (pass = 1 ; pass <= 2 ; ++pass)
{
int trans;
/* write persistent on pass 1, transient on pass 2 */
trans = (pass == 2);
/* write the objects for this pass */
rebuild_image(vmg_ meta_dep_idx, writer, mapper, trans);
}
}
/*
* Write all of the transient or persistent objects of a given metaclass.
*/
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
class CVmConstMapper *mapper, int trans)
{
CVmObjPageEntry **pg;
size_t i;
vm_obj_id_t id;
char *buf;
ulong bufsiz;
int block_cnt;
ulong block_size;
int large_objects;
/* we haven't written anything to the file yet */
block_cnt = 0;
block_size = 0;
/* presume we'll use small objects */
large_objects = FALSE;
/*
* allocate an initial object buffer - we'll expand this as needed
* if we find an object that doesn't fit
*/
bufsiz = 4096;
buf = (char *)t3malloc((size_t)bufsiz);
if (buf == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* go through each page in the object table */
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
{
size_t j;
CVmObjPageEntry *entry;
/* start at the start of the page */
j = VM_OBJ_PAGE_CNT;
entry = *pg;
/* go through each entry on this page */
for ( ; j > 0 ; --j, ++entry, ++id)
{
/*
* if this entry is in use, and its transient/persistent type
* matches the type we're writing, and its metaclass matches
* the one we're writing, write it out
*/
if (!entry->free_
&& (entry->transient_ != 0) == (trans != 0)
&& (G_meta_table->get_dependency_index(
entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx())
== meta_dep_idx))
{
ulong objsiz;
/*
* if this object has been mapped to a constant value,
* there's no need to store it, since it is no longer
* reachable
*/
if (mapper->get_pool_addr(id) != 0)
continue;
/* try building the object */
objsiz = entry->get_vm_obj()->rebuild_image(vmg_ buf, bufsiz);
/* if they need more space, reallocate the buffer */
if (objsiz > bufsiz)
{
/* if the object is too large, throw an error */
if (objsiz > OSMALMAX)
err_throw(VMERR_OBJ_SIZE_OVERFLOW);
/* reallocate to next 4k increment */
bufsiz = (objsiz + 4095) & ~4095;
buf = (char *)t3realloc(buf, (size_t)bufsiz);
if (buf == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* try it again */
objsiz = entry->get_vm_obj()->
rebuild_image(vmg_ buf, bufsiz);
}
/* if the object is not empty, write it out */
if (objsiz != 0)
{
char prefix[20];
/*
* if this object's size exceeds 64k, and the
* current OBJS block is a small block, end this
* OBJS block and begin a large OBJS block
*/
if (objsiz > 65535 && !large_objects)
{
/* if we have a block open, end it */
if (block_cnt != 0)
writer->end_objs_block(block_cnt);
/* reset the count and size for the new block */
block_cnt = 0;
block_size = 0;
/* make the next block a large block */
large_objects = TRUE;
}
/*
* if this object plus its prefix would push this
* OBJS block over 64k, close it off and start a new
* block
*/
if (block_size + objsiz + 6 > 64000L && block_cnt != 0)
{
/* close this block */
writer->end_objs_block(block_cnt);
/* reset the count and size for the new block */
block_cnt = 0;
block_size = 0;
/*
* use small size fields if this block isn't
* itself large
*/
if (objsiz <= 65535)
large_objects = FALSE;
}
/* if this is the first object, write the header */
if (block_cnt == 0)
writer->begin_objs_block(meta_dep_idx, large_objects,
trans);
/* write the prefix information */
oswp4(prefix, id);
if (large_objects)
{
/* write the 32-bit object size */
oswp4(prefix + 4, objsiz);
/* write the header */
writer->write_objs_bytes(prefix, 8);
}
else
{
/* write the 16-bit object size */
oswp2(prefix + 4, objsiz);
/* write the header */
writer->write_objs_bytes(prefix, 6);
}
/* write the object data */
writer->write_objs_bytes(buf, objsiz);
/* count the object */
++block_cnt;
/* count the size (including the prefix) */
block_size += objsiz + 6;
}
}
}
}
/* if we wrote any objects, end the OBJS block */
if (block_cnt != 0)
writer->end_objs_block(block_cnt);
/* delete our buffer */
t3free(buf);
}
/*
* Scan all active objects and convert objects to constant data where
* possible. Certain object metaclasses, such as strings and lists, can
* be represented in a rebuilt image file as constant data; this routine
* makes all of these conversions.
*/
void CVmObjTable::rebuild_image_convert_const_data
(VMG_ CVmConstMapper *const_mapper)
{
CVmObjPageEntry **pg;
size_t i;
vm_obj_id_t id;
/*
* First pass: go through each page in the object table, and reserve
* space for the constant conversion. This assigns each object that
* can be converted its address in the new constant pool pages.
*/
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
{
size_t j;
CVmObjPageEntry *entry;
/* start at the start of the page, but skip object ID = 0 */
j = VM_OBJ_PAGE_CNT;
entry = *pg;
/* go through each entry on this page */
for ( ; j > 0 ; --j, ++entry, ++id)
{
/*
* if this entry is in use, tell it to reserve space for
* conversion to constant data
*/
if (!entry->free_)
entry->get_vm_obj()
->reserve_const_data(vmg_ const_mapper, id);
}
}
/* prepare the constant mapper to begin storing */
const_mapper->prepare_to_store_data();
/*
* Second pass: go through the objects once again and make the
* conversions. We must do this on a separate second pass because
* we must fix up all references to objects being converted, hence
* we must know the conversion status of every object to be
* converted before we can fix up anything.
*/
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
{
size_t j;
CVmObjPageEntry *entry;
/* start at the start of the page, but skip object ID = 0 */
j = VM_OBJ_PAGE_CNT;
entry = *pg;
/* go through each entry on this page */
for ( ; j > 0 ; --j, ++entry, ++id)
{
/* if this entry is in use, convert it if possible */
if (!entry->free_)
entry->get_vm_obj()
->convert_to_const_data(vmg_ const_mapper, id);
}
}
}
/* ------------------------------------------------------------------------ */
/*
* Convert a value to constant data. If the value is an object that has
* reserved constant data space for itself, we'll update the value to
* reflect the constant data conversion for the value.
*/
static void convert_dh_to_const_data(VMG_ CVmConstMapper *mapper, char *p)
{
/* if the value is an object, check for conversion */
if (vmb_get_dh_type(p) == VM_OBJ)
{
vm_obj_id_t obj;
ulong addr;
/* get the object value */
obj = vmb_get_dh_obj(p);
/* look up the object's converted constant pool address */
if ((addr = mapper->get_pool_addr(obj)) != 0)
{
vm_val_t val;
/* this value has been converted - set the new value */
val.typ = vm_objp(vmg_ obj)->get_convert_to_const_data_type();
val.val.ofs = addr;
vmb_put_dh(p, &val);
}
}
}
/*
* Convert a vm_val_t value to constant data if appropriate.
*/
static void convert_val_to_const_data(VMG_ CVmConstMapper *mapper,
vm_val_t *val)
{
/* if it's an object, check for conversion */
if (val->typ == VM_OBJ)
{
ulong addr;
/* look up the object's converted constant pool address */
if ((addr = mapper->get_pool_addr(val->val.obj)) != 0)
{
/* this value has been converted - set the new value */
val->typ = vm_objp(vmg_ val->val.obj)
->get_convert_to_const_data_type();
val->val.ofs = addr;
}
}
}
/* ------------------------------------------------------------------------ */
/*
* List Metaclass implementation - image rebuilding
*/
/*
* Build an image file record for the list. We need this because we
* can't always convert a list to a constant value when rebuilding an
* image file; in particular, if the list exceeds the size of a constant
* pool page (unlikely but possible), we will have to store the list as
* object data.
*/
ulong CVmObjList::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* calculate how much space we need to store the data */
copy_size = calc_alloc(vmb_get_len(ext_));
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/*
* Reserve space in a rebuilt constant pool for converting our data to a
* constant value.
*/
void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/* reserve the space for our list data */
mapper->alloc_pool_space(self, calc_alloc(vmb_get_len(ext_)));
}
/*
* Convert to constant data. We must check each object that we
* reference via a modified property and convert it to a constant data
* item if necessary. Then, we must store our data in the constant
* pool, if we successfully reserved an address for it.
*/
void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
size_t cnt;
char *p;
/* get my element count */
cnt = vmb_get_len(ext_);
/* mark as referenced each object in our list */
for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
{
/* convert the value to constant data if possible */
convert_dh_to_const_data(vmg_ mapper, p);
}
/*
* if we managed to convert our object to constant data, store our
* value
*/
if (mapper->get_pool_addr(self))
mapper->store_data(self, ext_, calc_alloc(vmb_get_len(ext_)));
}
/* ------------------------------------------------------------------------ */
/*
* String Metaclass implementation - image rebuilding
*/
/*
* Build an image file record for the string. We need this because we
* can't always convert a string to a constant value when rebuilding an
* image file; in particular, if the string exceeds the size of a
* constant pool page (unlikely but possible), we will have to store the
* string as object data.
*/
ulong CVmObjString::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* calculate how much space we need to store the data */
copy_size = vmb_get_len(ext_) + VMB_LEN;
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/*
* Reserve space in a rebuilt constant pool for converting our data to a
* constant value.
*/
void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/* reserve the space for our string data */
mapper->alloc_pool_space(self, vmb_get_len(ext_) + VMB_LEN);
}
/*
* Convert to constant data. We don't reference any objects, so we need
* simply store our data in the constant pool, if we successfully
* reserved an address for it.
*/
void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/*
* if we managed to convert our object to constant data, store our
* value
*/
if (mapper->get_pool_addr(self))
mapper->store_data(self, ext_, vmb_get_len(ext_) + VMB_LEN);
}
/* ------------------------------------------------------------------------ */
/*
* TADS Object implementation - image rebuilding
*/
/* property comparison callback for qsort() */
extern "C"
{
static int prop_compare(const void *p1, const void *p2)
{
uint id1, id2;
/* get the ID's */
id1 = osrp2(p1);
id2 = osrp2(p2);
/* compare them and return the result */
return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
}
}
/*
* build an image file record for the object
*/
ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t max_size;
char *p;
char *props;
char *props_end;
size_t prop_cnt;
size_t i;
vm_tadsobj_prop *entry;
/*
* Make sure the buffer is big enough. Start out with worst-case
* assumption that we'll need every allocated property slot; we might
* actually need fewer, since some of the slots could be empty by
* virtue of having been undone. We need space for our own header
* (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a
* UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property.
*/
max_size = (2 + 2 + 2)
+ get_sc_count() * 4
+ (get_hdr()->prop_entry_free * 7);
/* if it's more than we have available, ask for more space */
if (max_size > buflen)
return max_size;
/*
* set up our header - use a placeholder 0 for the property count
* for now, until we calculate the real value
*/
oswp2(buf, get_sc_count());
oswp2(buf+2, 0);
oswp2(buf+4, get_li_obj_flags());
p = buf + 6;
/* copy the superclass list */
for (i = 0 ; i < get_sc_count() ; ++i, p += 4)
oswp4(p, get_sc(i));
/* remember where the properties start */
props = p;
/* copy the non-empty property slots */
for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr,
prop_cnt = 0 ;
i != 0 ;
--i, ++entry)
{
/* if this slot is non-empty, store it */
if (entry->val.typ != VM_EMPTY)
{
/* set up this slot */
oswp2(p, entry->prop);
vmb_put_dh(p + 2, &entry->val);
/* count the additional property */
++prop_cnt;
/* move past it in the buffer */
p += 7;
}
}
/* remember where the properties end */
props_end = p;
/* fill in actual the property count now that we know it */
oswp2(buf+2, prop_cnt);
/* sort the new table */
qsort(props, prop_cnt, 7, &prop_compare);
/* return the final size */
return p - buf;
}
/*
* Convert to constant data. We must check each object that we
* reference via a modified property and convert it to a constant data
* item if necessary.
*/
void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t /*self*/)
{
size_t i;
vm_tadsobj_prop *entry;
/*
* Scan the property entries. Note that we don't have to worry about
* the original properties, since they can only refer to data that was
* in the original image file, and hence never need to be converted --
* if it was good enough for the original image file, it's good enough
* for us.
*/
for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ;
i != 0 ; --i, ++entry)
{
/* if this slot is modified, convert it */
if ((entry->flags & VMTO_PROP_MOD) != 0
&& entry->val.typ != VM_EMPTY)
{
/* convert the value */
convert_val_to_const_data(vmg_ mapper, &entry->val);
}
}
}
/* ------------------------------------------------------------------------ */
/*
* Dictionary object implementation - image rebuilding
*/
/*
* callback context for image rebuild
*/
struct rebuild_ctx
{
/* space needed so far */
size_t space_needed;
/* number of entries */
size_t entry_cnt;
/* next available output pointer */
char *dst;
};
/*
* rebuild for image file
*/
ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen)
{
rebuild_ctx ctx;
/*
* calculate the amount of space we need - start with the comparator
* object, which needs four bytes, and the entry count, which needs two
* bytes
*/
ctx.space_needed = 6;
/* enumerate the entries to count space */
ctx.entry_cnt = 0;
get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx);
/* if we need more space than is available, ask for more */
if (ctx.space_needed > buflen)
return ctx.space_needed;
/* write the comparator object and the entry count */
oswp4(buf, get_ext()->comparator_);
oswp2(buf + 4, ctx.entry_cnt);
/* start writing after the count */
ctx.dst = buf + 6;
/* enumerate the entries to write to the image buffer */
get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx);
/* return the size */
return ctx.dst - buf;
}
/*
* enumeration callback - rebuild phase 1: count the space needed for
* this table entry
*/
void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0)
{
rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_dict_entry *cur;
/*
* count the space needed for this string - one byte for the length
* prefix, the bytes of the name string, and two bytes for the item
* count
*/
ctx->space_needed += 1 + entry->getlen() + 2;
/* count this entry */
++ctx->entry_cnt;
/* count the items */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/*
* add the space for this item - four byte for the object ID,
* two bytes for the property ID
*/
ctx->space_needed += 6;
}
}
/*
* enumeration callback - rebuild phase 2: write the entries to the
* image buffer
*/
void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0)
{
rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_dict_entry *cur;
size_t cnt;
char *p;
size_t rem;
/* count the entries in our list */
for (cnt = 0, cur = entry->get_head() ; cur != 0 ;
cur = cur->nxt_, ++cnt) ;
/* write the length of the key string followed by the key string */
*ctx->dst++ = (char)entry->getlen();
memcpy(ctx->dst, entry->getstr(), entry->getlen());
/* xor the key string's bytes */
for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ;
--rem, ++p)
*p ^= 0xBD;
/* move past the key string */
ctx->dst += entry->getlen();
/* write the item count */
oswp2(ctx->dst, cnt);
ctx->dst += 2;
/* write the items */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/* write the object ID and property ID for this item */
oswp4(ctx->dst, (ulong)cur->obj_);
oswp2(ctx->dst + 4, (uint)cur->prop_);
ctx->dst += 6;
}
}
/*
* callback context for constant data conversion
*/
struct cvt_const_ctx
{
/* constant mapper */
CVmConstMapper *mapper;
};
/*
* convert to constant data
*/
void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
cvt_const_ctx ctx;
/* make sure the comparator object isn't mappable to a constant */
if (mapper->get_pool_addr(get_ext()->comparator_) != 0)
err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
"<comparator>", 12);
/*
* Go through our dictionary and make sure we don't have any
* references to constant data. We don't actually have to perform
* any conversions, because we simply don't allow references to
* anything but TADS-object objects in the dictionary.
*/
ctx.mapper = mapper;
get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx);
}
/*
* enumeration callback - convert to constant data
*/
void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0)
{
cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_dict_entry *cur;
/* inspect the items in this entry's list */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/* if this item refers to constant data, it's an error */
if (ctx->mapper->get_pool_addr(cur->obj_) != 0)
err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
entry->getstr(), entry->getlen());
}
}
/* ------------------------------------------------------------------------ */
/*
* Grammar production object - image rebuilding operations
*/
/*
* rebuild for image file - we can't change at run-time, so we must
* simply copy our image file data back out unchanged
*/
ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen)
{
/* make sure we have room */
if (get_ext()->image_data_size_ > buflen)
return get_ext()->image_data_size_;
/* copy the data */
memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_);
/* return the size */
return get_ext()->image_data_size_;
}
/* ------------------------------------------------------------------------ */
/*
* BigNumber Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* calculate how much space we need to store the data */
copy_size = calc_alloc(get_prec(ext_));
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/*
* Reserve space in a rebuilt constant pool for converting our data to a
* constant value.
*/
void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/* BigNumber values cannot be converted to constants */
}
/*
* Convert to constant data. We don't reference any other objects, so
* we simply need to store our data in the constant pool, if we
* successfully reserved an address for it.
*/
void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/*
* if we managed to convert our object to constant data, store our
* value
*/
if (mapper->get_pool_addr(self))
mapper->store_data(self, ext_, calc_alloc(get_prec(ext_)));
}
/* ------------------------------------------------------------------------ */
/*
* Object-to-Constant mapper
*/
/*
* initialize
*/
CVmConstMapper::CVmConstMapper(VMG0_)
{
vm_obj_id_t max_id;
size_t i;
/* get the maximum object ID we've allocated */
max_id = G_obj_table->get_max_used_obj_id();
/*
* Allocate our master translation page list so that we have room
* for enough pages to store the maximum object ID. We need one
* slot for each page of 1024 translation elements.
*/
obj_addr_cnt_ = max_id / 1024;
obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0]));
if (obj_addr_ == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* clear out the page slots - we haven't allocated any pages yet */
for (i = 0 ; i < obj_addr_cnt_ ; ++i)
obj_addr_[i] = 0;
/* get the constant pool's page size */
page_size_ = G_const_pool->get_page_size();
/*
* our first page is the next page after the last page in the
* current pool
*/
first_page_idx_ = G_const_pool->get_page_count();
/*
* get the starting address - we'll start writing our data at the
* first page after all existing pages in the pool
*/
base_addr_ = page_size_ * G_const_pool->get_page_count();
/* allocate from our base address */
next_free_ = base_addr_;
/* we have the entire first page available */
rem_ = page_size_;
/*
* we haven't allocated any page data yet (we'll do this after we've
* reserved all space, when the client invokes
* prepare_to_store_data())
*/
pages_ = 0;
pages_cnt_ = 0;
}
/*
* delete
*/
CVmConstMapper::~CVmConstMapper()
{
size_t i;
/* delete each page in our mapping table */
for (i = 0 ; i < obj_addr_cnt_ ; ++i)
{
/* free this page if we allocated it */
if (obj_addr_[i] != 0)
t3free(obj_addr_[i]);
}
/* delete our mapping table */
t3free(obj_addr_);
/* delete each constant pool page */
for (i = 0 ; i < pages_cnt_ ; ++i)
t3free(pages_[i]);
/* free the page list */
if (pages_ != 0)
t3free(pages_);
}
/*
* Get an object's pool address, if it has one
*/
ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id)
{
/* determine if the page is mapped - if not, the object isn't mapped */
if (obj_addr_[obj_id / 1024] == 0)
return 0;
/* return the mapping entry */
return obj_addr_[obj_id / 1024][obj_id % 1024];
}
/*
* Allocate space in the pool for an object's data
*/
ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len)
{
ulong ret;
/*
* if the data block is too large to fit on a constant pool page, we
* can't store it
*/
if (len > page_size_)
return 0;
/* if the translation page isn't mapped yet, map it */
if (obj_addr_[obj_id / 1024] == 0)
{
/* allocate a new page */
obj_addr_[obj_id / 1024] =
(ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0]));
/* if that failed, throw an error */
if (obj_addr_[obj_id / 1024] == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* clear the new page */
memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0]));
}
/* if the block doesn't fit on this page, skip to the next page */
if (len > rem_)
{
/* skip past the remainder of this page */
next_free_ += rem_;
/* we have the whole next page available now */
rem_ = page_size_;
}
/* remember the object ID's address in the translation list */
ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_;
/* skip past the data */
next_free_ += len;
rem_ -= len;
/* return the new address */
return ret;
}
/*
* Prepare to begin storing data
*/
void CVmConstMapper::prepare_to_store_data()
{
size_t i;
/* figure out how many pages we need */
pages_cnt_ = calc_page_count();
/* allocate space for the list of pages */
pages_ = (vm_const_mapper_page **)
t3malloc(pages_cnt_ * sizeof(pages_[0]));
if (pages_ == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* allocate each page */
for (i = 0 ; i < pages_cnt_ ; ++i)
{
/*
* allocate this page - allocate the header structure plus space
* for the page data
*/
pages_[i] = (vm_const_mapper_page *)
t3malloc(sizeof(pages_[i]) + page_size_);
if (pages_[i] == 0)
err_throw(VMERR_OUT_OF_MEMORY);
/* the page has nothing stored yet */
pages_[i]->max_ofs_used = 0;
}
}
/*
* Store an object's data
*/
void CVmConstMapper::store_data(vm_obj_id_t obj_id,
const void *ptr, size_t len)
{
ulong addr;
size_t page_idx;
size_t page_ofs;
/* get the pool address that was reserved for the object */
addr = get_pool_addr(obj_id);
/* if the object had no space reserved, ignore the request */
if (addr == 0)
return;
/* figure out which page this address is in, and the offset in the page */
page_idx = (size_t)((addr - base_addr_) / page_size_);
page_ofs = (size_t)((addr - base_addr_) % page_size_);
/*
* if this address takes us above the high-water mark for the page,
* move the page's marker accordingly
*/
if (page_ofs + len > pages_[page_idx]->max_ofs_used)
pages_[page_idx]->max_ofs_used = page_ofs + len;
/* copy the data */
memcpy(pages_[page_idx]->buf + page_ofs, ptr, len);
}
/*
* Write the pool pages to an image file
*/
void CVmConstMapper::write_to_image_file(CVmImageWriter *writer,
uchar xor_mask)
{
size_t i;
/* go through each of our pages */
for (i = 0 ; i < pages_cnt_ ; ++i)
{
/* write this page - it's in pool 2 (the constant data pool) */
writer->write_pool_page(2, first_page_idx_ + i,
pages_[i]->buf, pages_[i]->max_ofs_used,
TRUE, xor_mask);
}
}
/* ------------------------------------------------------------------------ */
/*
* metaclass table - image rewriter implementation
*/
/*
* write the new metaclass dependency table
*/
void CVmMetaTable::rebuild_image(CVmImageWriter *writer)
{
size_t i;
/* begin the new metaclass dependency table */
writer->begin_meta_dep(get_count());
/* write the new metaclass dependency table items */
for (i = 0 ; i < get_count() ; ++i)
{
vm_meta_entry_t *entry;
ushort j;
/* get this entry */
entry = get_entry(i);
/* write this metaclass name */
writer->write_meta_dep_item(entry->image_meta_name_);
/*
* Write the property translation list. Note that xlat_func()
* requires a 1-based index, so we loop from 1 to the count,
* rather than following the usual C-style 0-based conventions.
*/
for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
writer->write_meta_item_prop(entry->xlat_func(j));
}
/* end the metaclass dependency table */
writer->end_meta_dep();
}
/* ------------------------------------------------------------------------ */
/*
* Hashtable Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
vm_lookup_ext *ext = get_ext();
uint i;
vm_lookup_val **bp;
vm_lookup_val *val;
char *dst;
uint idx;
/*
* we need space for the fixed header (6 bytes), 2 bytes per bucket,
* and the entries themselves
*/
copy_size = 6
+ (ext->bucket_cnt * 2)
+ (ext->value_cnt * VMLOOKUP_VALUE_SIZE);
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* write the fixed data */
oswp2(buf, ext->bucket_cnt);
oswp2(buf + 2, ext->value_cnt);
idx = ext->val_to_img_idx(ext->first_free);
oswp2(buf + 4, idx);
dst = buf + 6;
/* write the buckets */
for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp)
{
/* write this bucket's index */
idx = ext->val_to_img_idx(*bp);
oswp2(dst, idx);
dst += 2;
}
/* write the values */
for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val)
{
/* store the key, value, and index */
vmb_put_dh(dst, &val->key);
vmb_put_dh(dst + VMB_DATAHOLDER, &val->val);
idx = ext->val_to_img_idx(val->nxt);
oswp2(dst + VMB_DATAHOLDER*2, idx);
/* skip the data in the output */
dst += VMLOOKUP_VALUE_SIZE;
}
/* return the size */
return copy_size;
}
/*
* Convert to constant data. We must convert each object we reference in
* a key or in a value to constant data if possible.
*/
void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
uint i;
vm_lookup_val *entry;
/* run through each entry */
for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ;
i != 0 ; --i, ++entry)
{
/* convert the key */
convert_val_to_const_data(vmg_ mapper, &entry->key);
/* convert the value */
convert_val_to_const_data(vmg_ mapper, &entry->val);
}
}
/* ------------------------------------------------------------------------ */
/*
* IntrinsicClass Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* get our size */
copy_size = osrp2(ext_);
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/* ------------------------------------------------------------------------ */
/*
* Indexed Iterator
*/
/*
* Build an image file record
*/
ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* calculate our data size - just store our entire extension */
copy_size = VMOBJITERIDX_EXT_SIZE;
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/*
* Convert to constant data. We must convert our collection object
* reference to constant data if possible.
*/
void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/* convert our collection reference to constant data if possible */
convert_dh_to_const_data(vmg_ mapper, ext_);
}
/* ------------------------------------------------------------------------ */
/*
* Hashtable Iterator
*/
/*
* Build an image file record
*/
ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
/* calculate our data size - just store our entire extension */
copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE;
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* copy the data */
memcpy(buf, ext_, copy_size);
/* return the size */
return copy_size;
}
/*
* Convert to constant data. We must convert our collection object
* reference to constant data if possible.
*/
void CVmObjIterLookupTable::convert_to_const_data(
VMG_ CVmConstMapper *mapper, vm_obj_id_t self)
{
/* convert our collection reference to constant data if possible */
convert_dh_to_const_data(vmg_ mapper, ext_);
}
/* ------------------------------------------------------------------------ */
/*
* Vector Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t copy_size;
size_t ele_cnt;
size_t alloc_cnt;
/*
* calculate how much space we need to store the data - store only
* the data, not the undo bits
*/
ele_cnt = get_element_count();
alloc_cnt = get_allocated_count();
copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt);
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* save the allocation count and in-use element count */
vmb_put_len(buf, alloc_cnt);
vmb_put_len(buf + VMB_LEN, ele_cnt);
/* copy the element data */
memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt));
/* return the size */
return copy_size;
}
/*
* Reserve space in a rebuilt constant pool for converting our data to a
* constant value.
*/
void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
/* Vector values cannot be converted to constants */
}
/*
* Convert to constant data. We must convert each object we reference to
* constant data if possible; we use the same algorithm as CVmObjList.
*/
void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t self)
{
size_t cnt;
char *p;
/* get my element count */
cnt = get_element_count();
/* mark as referenced each object in our list */
for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
{
/* convert the value to constant data if possible */
convert_dh_to_const_data(vmg_ mapper, p);
}
}
/* ------------------------------------------------------------------------ */
/*
* ByteArray Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen)
{
ulong copy_size;
ulong rem;
ulong idx;
/* we need four bytes for our count plus space for our byte array */
copy_size = 4 + get_element_count();
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* store our element count */
oswp4(buf, get_element_count());
buf += 4;
/* copy our data in chunks */
for (idx = 1, rem = get_element_count() ; rem != 0 ; )
{
unsigned char *p;
size_t avail;
size_t chunk;
/* get the next chunk */
p = get_ele_ptr(idx, &avail);
/* limit copying to the remaining size */
chunk = avail;
if (chunk > rem)
chunk = rem;
/* store this chunk */
memcpy(buf, p, chunk);
/* skip this chunk */
buf += chunk;
idx += chunk;
rem -= chunk;
}
/* return the size */
return copy_size;
}
/* ------------------------------------------------------------------------ */
/*
* CharacterSet Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen)
{
ulong copy_size;
/* we need two bytes for the name length count plus the name's bytes */
copy_size = 2 + get_ext_ptr()->charset_name_len;
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* store the length of the name, and the name itself */
oswp2(buf, get_ext_ptr()->charset_name_len);
memcpy(buf + 2, get_ext_ptr()->charset_name,
get_ext_ptr()->charset_name_len);
/* return the size */
return copy_size;
}
/* ------------------------------------------------------------------------ */
/*
* File Metaclass implementation - image rebuilding
*/
/*
* Build an image file record
*/
ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen)
{
ulong copy_size;
/*
* we need the character set object ID, the mode byte, the access
* byte, and the uint32 flags
*/
copy_size = VMB_OBJECT_ID + 1 + 1 + 4;
/* make sure we have room for our data */
if (copy_size > buflen)
return copy_size;
/* store the character set object ID */
vmb_put_objid(buf, get_ext()->charset);
buf += VMB_OBJECT_ID;
/* store the mode and access values */
*buf++ = get_ext()->mode;
*buf++ = get_ext()->access;
/* store the flags */
oswp4(buf, get_ext()->flags);
/* return the size */
return copy_size;
}
/* ------------------------------------------------------------------------ */
/*
* Pattern metaclass
*/
/*
* build an image file record for the object
*/
ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen)
{
size_t need_size;
/* we need a DATAHOLDER to store the original pattern string reference */
need_size = VMB_DATAHOLDER;
/* if we need more space, just return our size requirements */
if (need_size > buflen)
return need_size;
/* write our value */
vmb_put_dh(buf, get_orig_str());
/* return our size */
return need_size;
}
/*
* Convert to constant data.
*/
void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper,
vm_obj_id_t /*self*/)
{
/* check our original source string value */
convert_val_to_const_data(vmg_ mapper, &get_ext()->str);
}
/* ------------------------------------------------------------------------ */
/*
* StringComparator intrinsic class
*/
/*
* build the image data
*/
ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen)
{
/* set up a stream writer on the buffer, and write it out */
CVmMemoryStream str(buf, buflen);
return write_to_stream(vmg_ &str, &buflen);
}
|