7
* Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
vmimgrb.cpp - T3 Image File Re-Builder
16
This module re-builds an image file from the contents of memory after
17
the program has been "pre-initialized." This allows a program to
18
run through its static state initialization during the compilation
19
process, then store the result as a new image file with pre-initialized
20
state. Any initialization that must always happen for every run of
21
the program can be performed during this pre-initialization pass,
22
saving the time of doing the work each time the program is run.
24
Mostly, we just copy the old image file to the new image file; most
25
parts of the image file are copied without changes. We update the
26
object stream, replacing the original objects with the objects in
27
their pre-initialized state, and we add any new strings dynamically
28
created during pre-initialization to the constant pool.
32
07/21/99 MJRoberts - Creation
66
/* ------------------------------------------------------------------------ */
68
* Rebuild the OBJS blocks and write the data to the file. This goes
69
* through the objects in memory and constructs fixed image-file
70
* versions of the objects, then writes them to OBJS blocks.
72
static void vm_rewrite_objs_blocks(VMG_ CVmImageWriter *writer,
73
class CVmConstMapper *mapper)
77
/* rewrite the image block for each of our defined metaclasses */
78
for (i = 0 ; i < G_meta_table->get_count() ; ++i)
80
/* write this metaclass's objects */
81
G_obj_table->rebuild_image(vmg_ i, writer, mapper);
85
/* ------------------------------------------------------------------------ */
87
* Re-write the image file
89
void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp,
94
CVmImageWriter *writer;
95
CVmConstMapper *const_mapper;
99
/* we don't know the code page size yet */
102
/* choose an arbitrary XOR mask for our pages */
105
/* create an image writer to help us write the output file */
106
writer = new CVmImageWriter(newfp);
108
/* create our constant mapper */
109
const_mapper = new CVmConstMapper(vmg0_);
112
* clear all undo information - we don't save undo with the rebuilt
113
* file, so there's no reason to keep any of the objects that are
114
* referenced only in the undo records
116
G_undo->drop_undo(vmg0_);
118
/* discard everything on the stack */
119
G_stk->discard(G_stk->get_depth());
122
* perform a full garbage collection pass, to make sure we don't
123
* include any unreachable objects in the new image file
125
G_obj_table->gc_full(vmg0_);
127
/* add any metaclasses that weren't in the dependency table originally */
128
G_obj_table->add_metadeps_for_instances(vmg0_);
130
/* convert objects to constant data to the extent possible */
131
G_obj_table->rebuild_image_convert_const_data(vmg_ const_mapper);
134
* copy the header (signature, UINT2 format version number, 32
135
* reserved bytes, 24-byte compilation timestamp) to the new file
137
origfp->read_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
138
newfp->write_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
140
/* copy or replace the data blocks */
141
for (done = FALSE ; !done ; )
145
/* read the next block header */
146
origfp->read_bytes(buf, 10);
149
siz = t3rp4u(buf + 4);
151
/* check the block type */
152
if (CVmImageLoader::block_type_is(buf, "OBJS")
153
|| CVmImageLoader::block_type_is(buf, "MCLD")
154
|| CVmImageLoader::block_type_is(buf, "SINI"))
157
* Simply skip all of the original OBJS (object data) or
158
* MCLD (metaclass dependency table) blocks -- we'll replace
159
* them with our re-built blocks.
161
* Also skip SINI (static initializer) blocks, since these
162
* are only needed for pre-initialization and can be
163
* discarded from the final image file.
165
origfp->set_pos(origfp->get_pos() + (long)siz);
167
else if (CVmImageLoader::block_type_is(buf, "CPDF"))
174
/* read the pool definition */
175
origfp->read_bytes(cpdf_buf, 10);
177
/* get the ID, page count, and page size from the definition */
178
pool_id = osrp2(cpdf_buf);
179
pgcnt = t3rp4u(cpdf_buf + 2);
180
pgsiz = t3rp4u(cpdf_buf + 6);
183
* if this is the constant pool (pool ID = 2), increase the
184
* page count by the extra constant pool pages we need for
185
* converting new object data to constants
189
/* add in our new count */
190
pgcnt += const_mapper->get_page_count();
192
/* write the new count */
193
oswp4(cpdf_buf + 2, pgcnt);
197
* if this is the code pool (pool ID = 1), and we have
198
* static initializers, decrease the page count to exclude
199
* the static initializer pages (all of the static
200
* initializers are grouped at the high end of the code
201
* pool, so we can discard them and only them by truncating
202
* the code pool before the page containing the first static
205
if (pool_id == 1 && static_cs_ofs != 0)
208
* calculate the new count - it's the offset to the
209
* first static initializer divided by the size of each
212
pgcnt = static_cs_ofs / pgsiz;
214
/* write the new count */
215
oswp4(cpdf_buf + 2, pgcnt);
218
* remember the code page size for later, when we're
219
* scanning the code pages themselves
221
code_page_size = pgsiz;
224
/* update the constant block definition */
225
newfp->write_bytes(buf, 10);
226
newfp->write_bytes(cpdf_buf, 10);
228
else if (CVmImageLoader::block_type_is(buf, "CPPG"))
236
/* presume we're going to keep this block */
240
* This is a constant page - if it's in pool 2 (constants),
241
* use its XOR mask for any new pages we write. First, read
242
* the pool header, then seek back so we can copy the block
245
start_pos = origfp->get_pos();
246
origfp->read_bytes(cppg_buf, 7);
247
origfp->set_pos(start_pos);
249
/* get the pool ID and the page's index */
250
pool_id = osrp2(cppg_buf);
251
page_idx = t3rp4u(cppg_buf + 2);
253
/* if it's pool 2 (constants), read its XOR mask byte */
255
xor_mask = cppg_buf[6];
258
* if it's pool 1 (code), and it's above the start of the
259
* static initializers, skip it - we don't want to copy
260
* static initializer code to the final image file, since
261
* they're only needed for static initialization, which we
262
* necessarily have finished by the time we reach this point
265
&& static_cs_ofs != 0
266
&& page_idx * code_page_size >= static_cs_ofs)
268
/* this is a static initializer block - skip it */
272
/* keep or skip the block, as appropriate */
276
* we only wanted to get information from this block, so
277
* go copy it as-is to the output
283
/* skip past the block */
284
origfp->set_pos(origfp->get_pos() + (long)siz);
287
else if (CVmImageLoader::block_type_is(buf, "EOF "))
289
/* end of file - note that we're done after this block */
292
/* re-write the metaclass dependency block */
293
G_meta_table->rebuild_image(writer);
295
/* write our new OBJS blocks */
296
vm_rewrite_objs_blocks(vmg_ writer, const_mapper);
298
/* write our new constant pool pages */
299
const_mapper->write_to_image_file(writer, xor_mask);
301
/* copy the EOF block to the new file unchanged */
308
* For anything else, we'll simply copy the original block
309
* from the original image file unchanged.
312
/* write the block header unchanged */
313
newfp->write_bytes(buf, 10);
315
/* copy the block in chunks */
321
* read as much as we can, up to the amount remaining or
322
* the buffer size, whichever is smaller
328
/* read the data and write it out */
329
origfp->read_bytes(buf, cur);
330
newfp->write_bytes(buf, cur);
332
/* deduct this chunk from the amount remaining */
338
/* we're done with the image writer */
341
/* we're done with the constant mapper */
345
/* ------------------------------------------------------------------------ */
347
* Object Table extension - rebuild the image file representations for
348
* all objects of a particular metaclass.
350
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
351
class CVmConstMapper *mapper)
355
/* write persistent and transient objects separately */
356
for (pass = 1 ; pass <= 2 ; ++pass)
360
/* write persistent on pass 1, transient on pass 2 */
363
/* write the objects for this pass */
364
rebuild_image(vmg_ meta_dep_idx, writer, mapper, trans);
369
* Write all of the transient or persistent objects of a given metaclass.
371
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
372
class CVmConstMapper *mapper, int trans)
374
CVmObjPageEntry **pg;
383
/* we haven't written anything to the file yet */
387
/* presume we'll use small objects */
388
large_objects = FALSE;
391
* allocate an initial object buffer - we'll expand this as needed
392
* if we find an object that doesn't fit
395
buf = (char *)t3malloc((size_t)bufsiz);
397
err_throw(VMERR_OUT_OF_MEMORY);
399
/* go through each page in the object table */
400
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
403
CVmObjPageEntry *entry;
405
/* start at the start of the page */
409
/* go through each entry on this page */
410
for ( ; j > 0 ; --j, ++entry, ++id)
413
* if this entry is in use, and its transient/persistent type
414
* matches the type we're writing, and its metaclass matches
415
* the one we're writing, write it out
418
&& (entry->transient_ != 0) == (trans != 0)
419
&& (G_meta_table->get_dependency_index(
420
entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx())
426
* if this object has been mapped to a constant value,
427
* there's no need to store it, since it is no longer
430
if (mapper->get_pool_addr(id) != 0)
433
/* try building the object */
434
objsiz = entry->get_vm_obj()->rebuild_image(vmg_ buf, bufsiz);
436
/* if they need more space, reallocate the buffer */
439
/* if the object is too large, throw an error */
440
if (objsiz > OSMALMAX)
441
err_throw(VMERR_OBJ_SIZE_OVERFLOW);
443
/* reallocate to next 4k increment */
444
bufsiz = (objsiz + 4095) & ~4095;
445
buf = (char *)t3realloc(buf, (size_t)bufsiz);
447
err_throw(VMERR_OUT_OF_MEMORY);
450
objsiz = entry->get_vm_obj()->
451
rebuild_image(vmg_ buf, bufsiz);
454
/* if the object is not empty, write it out */
460
* if this object's size exceeds 64k, and the
461
* current OBJS block is a small block, end this
462
* OBJS block and begin a large OBJS block
464
if (objsiz > 65535 && !large_objects)
466
/* if we have a block open, end it */
468
writer->end_objs_block(block_cnt);
470
/* reset the count and size for the new block */
474
/* make the next block a large block */
475
large_objects = TRUE;
479
* if this object plus its prefix would push this
480
* OBJS block over 64k, close it off and start a new
483
if (block_size + objsiz + 6 > 64000L && block_cnt != 0)
485
/* close this block */
486
writer->end_objs_block(block_cnt);
488
/* reset the count and size for the new block */
493
* use small size fields if this block isn't
497
large_objects = FALSE;
500
/* if this is the first object, write the header */
502
writer->begin_objs_block(meta_dep_idx, large_objects,
505
/* write the prefix information */
509
/* write the 32-bit object size */
510
oswp4(prefix + 4, objsiz);
512
/* write the header */
513
writer->write_objs_bytes(prefix, 8);
517
/* write the 16-bit object size */
518
oswp2(prefix + 4, objsiz);
520
/* write the header */
521
writer->write_objs_bytes(prefix, 6);
524
/* write the object data */
525
writer->write_objs_bytes(buf, objsiz);
527
/* count the object */
530
/* count the size (including the prefix) */
531
block_size += objsiz + 6;
537
/* if we wrote any objects, end the OBJS block */
539
writer->end_objs_block(block_cnt);
541
/* delete our buffer */
546
* Scan all active objects and convert objects to constant data where
547
* possible. Certain object metaclasses, such as strings and lists, can
548
* be represented in a rebuilt image file as constant data; this routine
549
* makes all of these conversions.
551
void CVmObjTable::rebuild_image_convert_const_data
552
(VMG_ CVmConstMapper *const_mapper)
554
CVmObjPageEntry **pg;
559
* First pass: go through each page in the object table, and reserve
560
* space for the constant conversion. This assigns each object that
561
* can be converted its address in the new constant pool pages.
563
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
566
CVmObjPageEntry *entry;
568
/* start at the start of the page, but skip object ID = 0 */
572
/* go through each entry on this page */
573
for ( ; j > 0 ; --j, ++entry, ++id)
576
* if this entry is in use, tell it to reserve space for
577
* conversion to constant data
581
->reserve_const_data(vmg_ const_mapper, id);
585
/* prepare the constant mapper to begin storing */
586
const_mapper->prepare_to_store_data();
589
* Second pass: go through the objects once again and make the
590
* conversions. We must do this on a separate second pass because
591
* we must fix up all references to objects being converted, hence
592
* we must know the conversion status of every object to be
593
* converted before we can fix up anything.
595
for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
598
CVmObjPageEntry *entry;
600
/* start at the start of the page, but skip object ID = 0 */
604
/* go through each entry on this page */
605
for ( ; j > 0 ; --j, ++entry, ++id)
607
/* if this entry is in use, convert it if possible */
610
->convert_to_const_data(vmg_ const_mapper, id);
615
/* ------------------------------------------------------------------------ */
617
* Convert a value to constant data. If the value is an object that has
618
* reserved constant data space for itself, we'll update the value to
619
* reflect the constant data conversion for the value.
621
static void convert_dh_to_const_data(VMG_ CVmConstMapper *mapper, char *p)
623
/* if the value is an object, check for conversion */
624
if (vmb_get_dh_type(p) == VM_OBJ)
629
/* get the object value */
630
obj = vmb_get_dh_obj(p);
632
/* look up the object's converted constant pool address */
633
if ((addr = mapper->get_pool_addr(obj)) != 0)
637
/* this value has been converted - set the new value */
638
val.typ = vm_objp(vmg_ obj)->get_convert_to_const_data_type();
646
* Convert a vm_val_t value to constant data if appropriate.
648
static void convert_val_to_const_data(VMG_ CVmConstMapper *mapper,
651
/* if it's an object, check for conversion */
652
if (val->typ == VM_OBJ)
656
/* look up the object's converted constant pool address */
657
if ((addr = mapper->get_pool_addr(val->val.obj)) != 0)
659
/* this value has been converted - set the new value */
660
val->typ = vm_objp(vmg_ val->val.obj)
661
->get_convert_to_const_data_type();
667
/* ------------------------------------------------------------------------ */
669
* List Metaclass implementation - image rebuilding
673
* Build an image file record for the list. We need this because we
674
* can't always convert a list to a constant value when rebuilding an
675
* image file; in particular, if the list exceeds the size of a constant
676
* pool page (unlikely but possible), we will have to store the list as
679
ulong CVmObjList::rebuild_image(VMG_ char *buf, ulong buflen)
683
/* calculate how much space we need to store the data */
684
copy_size = calc_alloc(vmb_get_len(ext_));
686
/* make sure we have room for our data */
687
if (copy_size > buflen)
691
memcpy(buf, ext_, copy_size);
693
/* return the size */
698
* Reserve space in a rebuilt constant pool for converting our data to a
701
void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *mapper,
704
/* reserve the space for our list data */
705
mapper->alloc_pool_space(self, calc_alloc(vmb_get_len(ext_)));
710
* Convert to constant data. We must check each object that we
711
* reference via a modified property and convert it to a constant data
712
* item if necessary. Then, we must store our data in the constant
713
* pool, if we successfully reserved an address for it.
715
void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *mapper,
721
/* get my element count */
722
cnt = vmb_get_len(ext_);
724
/* mark as referenced each object in our list */
725
for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
727
/* convert the value to constant data if possible */
728
convert_dh_to_const_data(vmg_ mapper, p);
732
* if we managed to convert our object to constant data, store our
735
if (mapper->get_pool_addr(self))
736
mapper->store_data(self, ext_, calc_alloc(vmb_get_len(ext_)));
740
/* ------------------------------------------------------------------------ */
742
* String Metaclass implementation - image rebuilding
746
* Build an image file record for the string. We need this because we
747
* can't always convert a string to a constant value when rebuilding an
748
* image file; in particular, if the string exceeds the size of a
749
* constant pool page (unlikely but possible), we will have to store the
750
* string as object data.
752
ulong CVmObjString::rebuild_image(VMG_ char *buf, ulong buflen)
756
/* calculate how much space we need to store the data */
757
copy_size = vmb_get_len(ext_) + VMB_LEN;
759
/* make sure we have room for our data */
760
if (copy_size > buflen)
764
memcpy(buf, ext_, copy_size);
766
/* return the size */
771
* Reserve space in a rebuilt constant pool for converting our data to a
774
void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *mapper,
777
/* reserve the space for our string data */
778
mapper->alloc_pool_space(self, vmb_get_len(ext_) + VMB_LEN);
783
* Convert to constant data. We don't reference any objects, so we need
784
* simply store our data in the constant pool, if we successfully
785
* reserved an address for it.
787
void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *mapper,
791
* if we managed to convert our object to constant data, store our
794
if (mapper->get_pool_addr(self))
795
mapper->store_data(self, ext_, vmb_get_len(ext_) + VMB_LEN);
799
/* ------------------------------------------------------------------------ */
801
* TADS Object implementation - image rebuilding
805
/* property comparison callback for qsort() */
808
static int prop_compare(const void *p1, const void *p2)
816
/* compare them and return the result */
817
return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
822
* build an image file record for the object
824
ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen)
832
vm_tadsobj_prop *entry;
835
* Make sure the buffer is big enough. Start out with worst-case
836
* assumption that we'll need every allocated property slot; we might
837
* actually need fewer, since some of the slots could be empty by
838
* virtue of having been undone. We need space for our own header
839
* (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a
840
* UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property.
842
max_size = (2 + 2 + 2)
844
+ (get_hdr()->prop_entry_free * 7);
846
/* if it's more than we have available, ask for more space */
847
if (max_size > buflen)
851
* set up our header - use a placeholder 0 for the property count
852
* for now, until we calculate the real value
854
oswp2(buf, get_sc_count());
856
oswp2(buf+4, get_li_obj_flags());
859
/* copy the superclass list */
860
for (i = 0 ; i < get_sc_count() ; ++i, p += 4)
863
/* remember where the properties start */
866
/* copy the non-empty property slots */
867
for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr,
872
/* if this slot is non-empty, store it */
873
if (entry->val.typ != VM_EMPTY)
875
/* set up this slot */
876
oswp2(p, entry->prop);
877
vmb_put_dh(p + 2, &entry->val);
879
/* count the additional property */
882
/* move past it in the buffer */
887
/* remember where the properties end */
890
/* fill in actual the property count now that we know it */
891
oswp2(buf+2, prop_cnt);
893
/* sort the new table */
894
qsort(props, prop_cnt, 7, &prop_compare);
896
/* return the final size */
901
* Convert to constant data. We must check each object that we
902
* reference via a modified property and convert it to a constant data
905
void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper,
906
vm_obj_id_t /*self*/)
909
vm_tadsobj_prop *entry;
912
* Scan the property entries. Note that we don't have to worry about
913
* the original properties, since they can only refer to data that was
914
* in the original image file, and hence never need to be converted --
915
* if it was good enough for the original image file, it's good enough
918
for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ;
919
i != 0 ; --i, ++entry)
921
/* if this slot is modified, convert it */
922
if ((entry->flags & VMTO_PROP_MOD) != 0
923
&& entry->val.typ != VM_EMPTY)
925
/* convert the value */
926
convert_val_to_const_data(vmg_ mapper, &entry->val);
932
/* ------------------------------------------------------------------------ */
934
* Dictionary object implementation - image rebuilding
938
* callback context for image rebuild
942
/* space needed so far */
945
/* number of entries */
948
/* next available output pointer */
953
* rebuild for image file
955
ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen)
960
* calculate the amount of space we need - start with the comparator
961
* object, which needs four bytes, and the entry count, which needs two
964
ctx.space_needed = 6;
966
/* enumerate the entries to count space */
968
get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx);
970
/* if we need more space than is available, ask for more */
971
if (ctx.space_needed > buflen)
972
return ctx.space_needed;
974
/* write the comparator object and the entry count */
975
oswp4(buf, get_ext()->comparator_);
976
oswp2(buf + 4, ctx.entry_cnt);
978
/* start writing after the count */
981
/* enumerate the entries to write to the image buffer */
982
get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx);
984
/* return the size */
985
return ctx.dst - buf;
989
* enumeration callback - rebuild phase 1: count the space needed for
992
void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0)
994
rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
995
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
999
* count the space needed for this string - one byte for the length
1000
* prefix, the bytes of the name string, and two bytes for the item
1003
ctx->space_needed += 1 + entry->getlen() + 2;
1005
/* count this entry */
1008
/* count the items */
1009
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1012
* add the space for this item - four byte for the object ID,
1013
* two bytes for the property ID
1015
ctx->space_needed += 6;
1020
* enumeration callback - rebuild phase 2: write the entries to the
1023
void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0)
1025
rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
1026
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1032
/* count the entries in our list */
1033
for (cnt = 0, cur = entry->get_head() ; cur != 0 ;
1034
cur = cur->nxt_, ++cnt) ;
1036
/* write the length of the key string followed by the key string */
1037
*ctx->dst++ = (char)entry->getlen();
1038
memcpy(ctx->dst, entry->getstr(), entry->getlen());
1040
/* xor the key string's bytes */
1041
for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ;
1045
/* move past the key string */
1046
ctx->dst += entry->getlen();
1048
/* write the item count */
1049
oswp2(ctx->dst, cnt);
1052
/* write the items */
1053
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1055
/* write the object ID and property ID for this item */
1056
oswp4(ctx->dst, (ulong)cur->obj_);
1057
oswp2(ctx->dst + 4, (uint)cur->prop_);
1063
* callback context for constant data conversion
1065
struct cvt_const_ctx
1067
/* constant mapper */
1068
CVmConstMapper *mapper;
1072
* convert to constant data
1074
void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1079
/* make sure the comparator object isn't mappable to a constant */
1080
if (mapper->get_pool_addr(get_ext()->comparator_) != 0)
1081
err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1082
"<comparator>", 12);
1085
* Go through our dictionary and make sure we don't have any
1086
* references to constant data. We don't actually have to perform
1087
* any conversions, because we simply don't allow references to
1088
* anything but TADS-object objects in the dictionary.
1090
ctx.mapper = mapper;
1091
get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx);
1095
* enumeration callback - convert to constant data
1097
void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0)
1099
cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0;
1100
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1103
/* inspect the items in this entry's list */
1104
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1106
/* if this item refers to constant data, it's an error */
1107
if (ctx->mapper->get_pool_addr(cur->obj_) != 0)
1108
err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1109
entry->getstr(), entry->getlen());
1113
/* ------------------------------------------------------------------------ */
1115
* Grammar production object - image rebuilding operations
1119
* rebuild for image file - we can't change at run-time, so we must
1120
* simply copy our image file data back out unchanged
1122
ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen)
1124
/* make sure we have room */
1125
if (get_ext()->image_data_size_ > buflen)
1126
return get_ext()->image_data_size_;
1129
memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_);
1131
/* return the size */
1132
return get_ext()->image_data_size_;
1135
/* ------------------------------------------------------------------------ */
1137
* BigNumber Metaclass implementation - image rebuilding
1141
* Build an image file record
1143
ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen)
1147
/* calculate how much space we need to store the data */
1148
copy_size = calc_alloc(get_prec(ext_));
1150
/* make sure we have room for our data */
1151
if (copy_size > buflen)
1155
memcpy(buf, ext_, copy_size);
1157
/* return the size */
1162
* Reserve space in a rebuilt constant pool for converting our data to a
1165
void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper,
1168
/* BigNumber values cannot be converted to constants */
1173
* Convert to constant data. We don't reference any other objects, so
1174
* we simply need to store our data in the constant pool, if we
1175
* successfully reserved an address for it.
1177
void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1181
* if we managed to convert our object to constant data, store our
1184
if (mapper->get_pool_addr(self))
1185
mapper->store_data(self, ext_, calc_alloc(get_prec(ext_)));
1189
/* ------------------------------------------------------------------------ */
1191
* Object-to-Constant mapper
1197
CVmConstMapper::CVmConstMapper(VMG0_)
1202
/* get the maximum object ID we've allocated */
1203
max_id = G_obj_table->get_max_used_obj_id();
1206
* Allocate our master translation page list so that we have room
1207
* for enough pages to store the maximum object ID. We need one
1208
* slot for each page of 1024 translation elements.
1210
obj_addr_cnt_ = max_id / 1024;
1211
obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0]));
1213
err_throw(VMERR_OUT_OF_MEMORY);
1215
/* clear out the page slots - we haven't allocated any pages yet */
1216
for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1219
/* get the constant pool's page size */
1220
page_size_ = G_const_pool->get_page_size();
1223
* our first page is the next page after the last page in the
1226
first_page_idx_ = G_const_pool->get_page_count();
1229
* get the starting address - we'll start writing our data at the
1230
* first page after all existing pages in the pool
1232
base_addr_ = page_size_ * G_const_pool->get_page_count();
1234
/* allocate from our base address */
1235
next_free_ = base_addr_;
1237
/* we have the entire first page available */
1241
* we haven't allocated any page data yet (we'll do this after we've
1242
* reserved all space, when the client invokes
1243
* prepare_to_store_data())
1252
CVmConstMapper::~CVmConstMapper()
1256
/* delete each page in our mapping table */
1257
for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1259
/* free this page if we allocated it */
1260
if (obj_addr_[i] != 0)
1261
t3free(obj_addr_[i]);
1264
/* delete our mapping table */
1267
/* delete each constant pool page */
1268
for (i = 0 ; i < pages_cnt_ ; ++i)
1271
/* free the page list */
1277
* Get an object's pool address, if it has one
1279
ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id)
1281
/* determine if the page is mapped - if not, the object isn't mapped */
1282
if (obj_addr_[obj_id / 1024] == 0)
1285
/* return the mapping entry */
1286
return obj_addr_[obj_id / 1024][obj_id % 1024];
1291
* Allocate space in the pool for an object's data
1293
ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len)
1298
* if the data block is too large to fit on a constant pool page, we
1301
if (len > page_size_)
1304
/* if the translation page isn't mapped yet, map it */
1305
if (obj_addr_[obj_id / 1024] == 0)
1307
/* allocate a new page */
1308
obj_addr_[obj_id / 1024] =
1309
(ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0]));
1311
/* if that failed, throw an error */
1312
if (obj_addr_[obj_id / 1024] == 0)
1313
err_throw(VMERR_OUT_OF_MEMORY);
1315
/* clear the new page */
1316
memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0]));
1319
/* if the block doesn't fit on this page, skip to the next page */
1322
/* skip past the remainder of this page */
1325
/* we have the whole next page available now */
1329
/* remember the object ID's address in the translation list */
1330
ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_;
1332
/* skip past the data */
1336
/* return the new address */
1341
* Prepare to begin storing data
1343
void CVmConstMapper::prepare_to_store_data()
1347
/* figure out how many pages we need */
1348
pages_cnt_ = calc_page_count();
1350
/* allocate space for the list of pages */
1351
pages_ = (vm_const_mapper_page **)
1352
t3malloc(pages_cnt_ * sizeof(pages_[0]));
1354
err_throw(VMERR_OUT_OF_MEMORY);
1356
/* allocate each page */
1357
for (i = 0 ; i < pages_cnt_ ; ++i)
1360
* allocate this page - allocate the header structure plus space
1363
pages_[i] = (vm_const_mapper_page *)
1364
t3malloc(sizeof(pages_[i]) + page_size_);
1366
err_throw(VMERR_OUT_OF_MEMORY);
1368
/* the page has nothing stored yet */
1369
pages_[i]->max_ofs_used = 0;
1374
* Store an object's data
1376
void CVmConstMapper::store_data(vm_obj_id_t obj_id,
1377
const void *ptr, size_t len)
1383
/* get the pool address that was reserved for the object */
1384
addr = get_pool_addr(obj_id);
1386
/* if the object had no space reserved, ignore the request */
1390
/* figure out which page this address is in, and the offset in the page */
1391
page_idx = (size_t)((addr - base_addr_) / page_size_);
1392
page_ofs = (size_t)((addr - base_addr_) % page_size_);
1395
* if this address takes us above the high-water mark for the page,
1396
* move the page's marker accordingly
1398
if (page_ofs + len > pages_[page_idx]->max_ofs_used)
1399
pages_[page_idx]->max_ofs_used = page_ofs + len;
1402
memcpy(pages_[page_idx]->buf + page_ofs, ptr, len);
1406
* Write the pool pages to an image file
1408
void CVmConstMapper::write_to_image_file(CVmImageWriter *writer,
1413
/* go through each of our pages */
1414
for (i = 0 ; i < pages_cnt_ ; ++i)
1416
/* write this page - it's in pool 2 (the constant data pool) */
1417
writer->write_pool_page(2, first_page_idx_ + i,
1418
pages_[i]->buf, pages_[i]->max_ofs_used,
1423
/* ------------------------------------------------------------------------ */
1425
* metaclass table - image rewriter implementation
1429
* write the new metaclass dependency table
1431
void CVmMetaTable::rebuild_image(CVmImageWriter *writer)
1435
/* begin the new metaclass dependency table */
1436
writer->begin_meta_dep(get_count());
1438
/* write the new metaclass dependency table items */
1439
for (i = 0 ; i < get_count() ; ++i)
1441
vm_meta_entry_t *entry;
1444
/* get this entry */
1445
entry = get_entry(i);
1447
/* write this metaclass name */
1448
writer->write_meta_dep_item(entry->image_meta_name_);
1451
* Write the property translation list. Note that xlat_func()
1452
* requires a 1-based index, so we loop from 1 to the count,
1453
* rather than following the usual C-style 0-based conventions.
1455
for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
1456
writer->write_meta_item_prop(entry->xlat_func(j));
1459
/* end the metaclass dependency table */
1460
writer->end_meta_dep();
1464
/* ------------------------------------------------------------------------ */
1466
* Hashtable Metaclass implementation - image rebuilding
1470
* Build an image file record
1472
ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1475
vm_lookup_ext *ext = get_ext();
1483
* we need space for the fixed header (6 bytes), 2 bytes per bucket,
1484
* and the entries themselves
1487
+ (ext->bucket_cnt * 2)
1488
+ (ext->value_cnt * VMLOOKUP_VALUE_SIZE);
1490
/* make sure we have room for our data */
1491
if (copy_size > buflen)
1494
/* write the fixed data */
1495
oswp2(buf, ext->bucket_cnt);
1496
oswp2(buf + 2, ext->value_cnt);
1497
idx = ext->val_to_img_idx(ext->first_free);
1498
oswp2(buf + 4, idx);
1501
/* write the buckets */
1502
for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp)
1504
/* write this bucket's index */
1505
idx = ext->val_to_img_idx(*bp);
1510
/* write the values */
1511
for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val)
1513
/* store the key, value, and index */
1514
vmb_put_dh(dst, &val->key);
1515
vmb_put_dh(dst + VMB_DATAHOLDER, &val->val);
1516
idx = ext->val_to_img_idx(val->nxt);
1517
oswp2(dst + VMB_DATAHOLDER*2, idx);
1519
/* skip the data in the output */
1520
dst += VMLOOKUP_VALUE_SIZE;
1523
/* return the size */
1528
* Convert to constant data. We must convert each object we reference in
1529
* a key or in a value to constant data if possible.
1531
void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1535
vm_lookup_val *entry;
1537
/* run through each entry */
1538
for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ;
1539
i != 0 ; --i, ++entry)
1541
/* convert the key */
1542
convert_val_to_const_data(vmg_ mapper, &entry->key);
1544
/* convert the value */
1545
convert_val_to_const_data(vmg_ mapper, &entry->val);
1549
/* ------------------------------------------------------------------------ */
1551
* IntrinsicClass Metaclass implementation - image rebuilding
1555
* Build an image file record
1557
ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen)
1562
copy_size = osrp2(ext_);
1564
/* make sure we have room for our data */
1565
if (copy_size > buflen)
1569
memcpy(buf, ext_, copy_size);
1571
/* return the size */
1575
/* ------------------------------------------------------------------------ */
1581
* Build an image file record
1583
ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen)
1587
/* calculate our data size - just store our entire extension */
1588
copy_size = VMOBJITERIDX_EXT_SIZE;
1590
/* make sure we have room for our data */
1591
if (copy_size > buflen)
1595
memcpy(buf, ext_, copy_size);
1597
/* return the size */
1602
* Convert to constant data. We must convert our collection object
1603
* reference to constant data if possible.
1605
void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1608
/* convert our collection reference to constant data if possible */
1609
convert_dh_to_const_data(vmg_ mapper, ext_);
1613
/* ------------------------------------------------------------------------ */
1615
* Hashtable Iterator
1619
* Build an image file record
1621
ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1625
/* calculate our data size - just store our entire extension */
1626
copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE;
1628
/* make sure we have room for our data */
1629
if (copy_size > buflen)
1633
memcpy(buf, ext_, copy_size);
1635
/* return the size */
1640
* Convert to constant data. We must convert our collection object
1641
* reference to constant data if possible.
1643
void CVmObjIterLookupTable::convert_to_const_data(
1644
VMG_ CVmConstMapper *mapper, vm_obj_id_t self)
1646
/* convert our collection reference to constant data if possible */
1647
convert_dh_to_const_data(vmg_ mapper, ext_);
1651
/* ------------------------------------------------------------------------ */
1653
* Vector Metaclass implementation - image rebuilding
1657
* Build an image file record
1659
ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen)
1666
* calculate how much space we need to store the data - store only
1667
* the data, not the undo bits
1669
ele_cnt = get_element_count();
1670
alloc_cnt = get_allocated_count();
1671
copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt);
1673
/* make sure we have room for our data */
1674
if (copy_size > buflen)
1677
/* save the allocation count and in-use element count */
1678
vmb_put_len(buf, alloc_cnt);
1679
vmb_put_len(buf + VMB_LEN, ele_cnt);
1681
/* copy the element data */
1682
memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt));
1684
/* return the size */
1689
* Reserve space in a rebuilt constant pool for converting our data to a
1692
void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper,
1695
/* Vector values cannot be converted to constants */
1700
* Convert to constant data. We must convert each object we reference to
1701
* constant data if possible; we use the same algorithm as CVmObjList.
1703
void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1709
/* get my element count */
1710
cnt = get_element_count();
1712
/* mark as referenced each object in our list */
1713
for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
1715
/* convert the value to constant data if possible */
1716
convert_dh_to_const_data(vmg_ mapper, p);
1720
/* ------------------------------------------------------------------------ */
1722
* ByteArray Metaclass implementation - image rebuilding
1726
* Build an image file record
1728
ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen)
1734
/* we need four bytes for our count plus space for our byte array */
1735
copy_size = 4 + get_element_count();
1737
/* make sure we have room for our data */
1738
if (copy_size > buflen)
1741
/* store our element count */
1742
oswp4(buf, get_element_count());
1745
/* copy our data in chunks */
1746
for (idx = 1, rem = get_element_count() ; rem != 0 ; )
1752
/* get the next chunk */
1753
p = get_ele_ptr(idx, &avail);
1755
/* limit copying to the remaining size */
1760
/* store this chunk */
1761
memcpy(buf, p, chunk);
1763
/* skip this chunk */
1769
/* return the size */
1773
/* ------------------------------------------------------------------------ */
1775
* CharacterSet Metaclass implementation - image rebuilding
1779
* Build an image file record
1781
ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen)
1785
/* we need two bytes for the name length count plus the name's bytes */
1786
copy_size = 2 + get_ext_ptr()->charset_name_len;
1788
/* make sure we have room for our data */
1789
if (copy_size > buflen)
1792
/* store the length of the name, and the name itself */
1793
oswp2(buf, get_ext_ptr()->charset_name_len);
1794
memcpy(buf + 2, get_ext_ptr()->charset_name,
1795
get_ext_ptr()->charset_name_len);
1797
/* return the size */
1801
/* ------------------------------------------------------------------------ */
1803
* File Metaclass implementation - image rebuilding
1807
* Build an image file record
1809
ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen)
1814
* we need the character set object ID, the mode byte, the access
1815
* byte, and the uint32 flags
1817
copy_size = VMB_OBJECT_ID + 1 + 1 + 4;
1819
/* make sure we have room for our data */
1820
if (copy_size > buflen)
1823
/* store the character set object ID */
1824
vmb_put_objid(buf, get_ext()->charset);
1825
buf += VMB_OBJECT_ID;
1827
/* store the mode and access values */
1828
*buf++ = get_ext()->mode;
1829
*buf++ = get_ext()->access;
1831
/* store the flags */
1832
oswp4(buf, get_ext()->flags);
1834
/* return the size */
1838
/* ------------------------------------------------------------------------ */
1844
* build an image file record for the object
1846
ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen)
1850
/* we need a DATAHOLDER to store the original pattern string reference */
1851
need_size = VMB_DATAHOLDER;
1853
/* if we need more space, just return our size requirements */
1854
if (need_size > buflen)
1857
/* write our value */
1858
vmb_put_dh(buf, get_orig_str());
1860
/* return our size */
1865
* Convert to constant data.
1867
void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1868
vm_obj_id_t /*self*/)
1870
/* check our original source string value */
1871
convert_val_to_const_data(vmg_ mapper, &get_ext()->str);
1874
/* ------------------------------------------------------------------------ */
1876
* StringComparator intrinsic class
1880
* build the image data
1882
ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen)
1884
/* set up a stream writer on the buffer, and write it out */
1885
CVmMemoryStream str(buf, buflen);
1886
return write_to_stream(vmg_ &str, &buflen);