1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header$";
4 #endif
5
6 /*
7 * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved.
8 *
9 * Please see the accompanying license file, LICENSE.TXT, for information
10 * on using and copying this software.
11 */
12 /*
13 Name
14 vmimgrb.cpp - T3 Image File Re-Builder
15 Function
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.
23
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.
29 Notes
30
31 Modified
32 07/21/99 MJRoberts - Creation
33 */
34
35 #include <stdlib.h>
36 #include <memory.h>
37
38 #include "t3std.h"
39 #include "vmfile.h"
40 #include "vmimage.h"
41 #include "vmpool.h"
42 #include "vmglob.h"
43 #include "vmwrtimg.h"
44 #include "vmobj.h"
45 #include "vmtobj.h"
46 #include "vmdict.h"
47 #include "vmhash.h"
48 #include "vmlst.h"
49 #include "vmbignum.h"
50 #include "vmstr.h"
51 #include "vmgram.h"
52 #include "vmmeta.h"
53 #include "vmimgrb.h"
54 #include "vmintcls.h"
55 #include "vmiter.h"
56 #include "vmvec.h"
57 #include "vmlookup.h"
58 #include "vmstack.h"
59 #include "vmbytarr.h"
60 #include "vmcset.h"
61 #include "vmfilobj.h"
62 #include "vmpat.h"
63 #include "vmstrcmp.h"
64
65
66 /* ------------------------------------------------------------------------ */
67 /*
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.
71 */
vm_rewrite_objs_blocks(VMG_ CVmImageWriter * writer,class CVmConstMapper * mapper)72 static void vm_rewrite_objs_blocks(VMG_ CVmImageWriter *writer,
73 class CVmConstMapper *mapper)
74 {
75 size_t i;
76
77 /* rewrite the image block for each of our defined metaclasses */
78 for (i = 0 ; i < G_meta_table->get_count() ; ++i)
79 {
80 /* write this metaclass's objects */
81 G_obj_table->rebuild_image(vmg_ i, writer, mapper);
82 }
83 }
84
85 /* ------------------------------------------------------------------------ */
86 /*
87 * Re-write the image file
88 */
vm_rewrite_image(VMG_ CVmFile * origfp,CVmFile * newfp,ulong static_cs_ofs)89 void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp,
90 ulong static_cs_ofs)
91 {
92 char buf[4096];
93 int done;
94 CVmImageWriter *writer;
95 CVmConstMapper *const_mapper;
96 uchar xor_mask;
97 ulong code_page_size;
98
99 /* we don't know the code page size yet */
100 code_page_size = 0;
101
102 /* choose an arbitrary XOR mask for our pages */
103 xor_mask = 0xEF;
104
105 /* create an image writer to help us write the output file */
106 writer = new CVmImageWriter(newfp);
107
108 /* create our constant mapper */
109 const_mapper = new CVmConstMapper(vmg0_);
110
111 /*
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
115 */
116 G_undo->drop_undo(vmg0_);
117
118 /* discard everything on the stack */
119 G_stk->discard(G_stk->get_depth());
120
121 /*
122 * perform a full garbage collection pass, to make sure we don't
123 * include any unreachable objects in the new image file
124 */
125 G_obj_table->gc_full(vmg0_);
126
127 /* add any metaclasses that weren't in the dependency table originally */
128 G_obj_table->add_metadeps_for_instances(vmg0_);
129
130 /* convert objects to constant data to the extent possible */
131 G_obj_table->rebuild_image_convert_const_data(vmg_ const_mapper);
132
133 /*
134 * copy the header (signature, UINT2 format version number, 32
135 * reserved bytes, 24-byte compilation timestamp) to the new file
136 */
137 origfp->read_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
138 newfp->write_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
139
140 /* copy or replace the data blocks */
141 for (done = FALSE ; !done ; )
142 {
143 ulong siz;
144
145 /* read the next block header */
146 origfp->read_bytes(buf, 10);
147
148 /* get the size */
149 siz = osrp4(buf + 4);
150
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"))
155 {
156 /*
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.
160 *
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.
164 */
165 origfp->set_pos(origfp->get_pos() + (long)siz);
166 }
167 else if (CVmImageLoader::block_type_is(buf, "CPDF"))
168 {
169 char cpdf_buf[20];
170 uint pool_id;
171 ulong pgcnt;
172 ulong pgsiz;
173
174 /* read the pool definition */
175 origfp->read_bytes(cpdf_buf, 10);
176
177 /* get the ID, page count, and page size from the definition */
178 pool_id = osrp2(cpdf_buf);
179 pgcnt = osrp4(cpdf_buf + 2);
180 pgsiz = osrp4(cpdf_buf + 6);
181
182 /*
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
186 */
187 if (pool_id == 2)
188 {
189 /* add in our new count */
190 pgcnt += const_mapper->get_page_count();
191
192 /* write the new count */
193 oswp4(cpdf_buf + 2, pgcnt);
194 }
195
196 /*
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
203 * initializer)
204 */
205 if (pool_id == 1 && static_cs_ofs != 0)
206 {
207 /*
208 * calculate the new count - it's the offset to the
209 * first static initializer divided by the size of each
210 * code page
211 */
212 pgcnt = static_cs_ofs / pgsiz;
213
214 /* write the new count */
215 oswp4(cpdf_buf + 2, pgcnt);
216
217 /*
218 * remember the code page size for later, when we're
219 * scanning the code pages themselves
220 */
221 code_page_size = pgsiz;
222 }
223
224 /* update the constant block definition */
225 newfp->write_bytes(buf, 10);
226 newfp->write_bytes(cpdf_buf, 10);
227 }
228 else if (CVmImageLoader::block_type_is(buf, "CPPG"))
229 {
230 char cppg_buf[20];
231 long start_pos;
232 uint pool_id;
233 ulong page_idx;
234 int keep_block;
235
236 /* presume we're going to keep this block */
237 keep_block = TRUE;
238
239 /*
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
243 * unchanged.
244 */
245 start_pos = origfp->get_pos();
246 origfp->read_bytes(cppg_buf, 7);
247 origfp->set_pos(start_pos);
248
249 /* get the pool ID and the page's index */
250 pool_id = osrp2(cppg_buf);
251 page_idx = osrp4(cppg_buf + 2);
252
253 /* if it's pool 2 (constants), read its XOR mask byte */
254 if (pool_id == 2)
255 xor_mask = cppg_buf[6];
256
257 /*
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
263 */
264 if (pool_id == 1
265 && static_cs_ofs != 0
266 && page_idx * code_page_size >= static_cs_ofs)
267 {
268 /* this is a static initializer block - skip it */
269 keep_block = FALSE;
270 }
271
272 /* keep or skip the block, as appropriate */
273 if (keep_block)
274 {
275 /*
276 * we only wanted to get information from this block, so
277 * go copy it as-is to the output
278 */
279 goto copy_block;
280 }
281 else
282 {
283 /* skip past the block */
284 origfp->set_pos(origfp->get_pos() + (long)siz);
285 }
286 }
287 else if (CVmImageLoader::block_type_is(buf, "EOF "))
288 {
289 /* end of file - note that we're done after this block */
290 done = TRUE;
291
292 /* re-write the metaclass dependency block */
293 G_meta_table->rebuild_image(writer);
294
295 /* write our new OBJS blocks */
296 vm_rewrite_objs_blocks(vmg_ writer, const_mapper);
297
298 /* write our new constant pool pages */
299 const_mapper->write_to_image_file(writer, xor_mask);
300
301 /* copy the EOF block to the new file unchanged */
302 goto copy_block;
303 }
304 else
305 {
306 copy_block:
307 /*
308 * For anything else, we'll simply copy the original block
309 * from the original image file unchanged.
310 */
311
312 /* write the block header unchanged */
313 newfp->write_bytes(buf, 10);
314
315 /* copy the block in chunks */
316 while (siz != 0)
317 {
318 size_t cur;
319
320 /*
321 * read as much as we can, up to the amount remaining or
322 * the buffer size, whichever is smaller
323 */
324 cur = sizeof(buf);
325 if (cur > siz)
326 cur = (size_t)siz;
327
328 /* read the data and write it out */
329 origfp->read_bytes(buf, cur);
330 newfp->write_bytes(buf, cur);
331
332 /* deduct this chunk from the amount remaining */
333 siz -= cur;
334 }
335 }
336 }
337
338 /* we're done with the image writer */
339 delete writer;
340
341 /* we're done with the constant mapper */
342 delete const_mapper;
343 }
344
345 /* ------------------------------------------------------------------------ */
346 /*
347 * Object Table extension - rebuild the image file representations for
348 * all objects of a particular metaclass.
349 */
rebuild_image(VMG_ int meta_dep_idx,CVmImageWriter * writer,class CVmConstMapper * mapper)350 void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
351 class CVmConstMapper *mapper)
352 {
353 int pass;
354
355 /* write persistent and transient objects separately */
356 for (pass = 1 ; pass <= 2 ; ++pass)
357 {
358 int trans;
359
360 /* write persistent on pass 1, transient on pass 2 */
361 trans = (pass == 2);
362
363 /* write the objects for this pass */
364 rebuild_image(vmg_ meta_dep_idx, writer, mapper, trans);
365 }
366 }
367
368 /*
369 * Write all of the transient or persistent objects of a given metaclass.
370 */
rebuild_image(VMG_ int meta_dep_idx,CVmImageWriter * writer,class CVmConstMapper * mapper,int trans)371 void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
372 class CVmConstMapper *mapper, int trans)
373 {
374 CVmObjPageEntry **pg;
375 size_t i;
376 vm_obj_id_t id;
377 char *buf;
378 ulong bufsiz;
379 int block_cnt;
380 ulong block_size;
381 int large_objects;
382
383 /* we haven't written anything to the file yet */
384 block_cnt = 0;
385 block_size = 0;
386
387 /* presume we'll use small objects */
388 large_objects = FALSE;
389
390 /*
391 * allocate an initial object buffer - we'll expand this as needed
392 * if we find an object that doesn't fit
393 */
394 bufsiz = 4096;
395 buf = (char *)t3malloc((size_t)bufsiz);
396 if (buf == 0)
397 err_throw(VMERR_OUT_OF_MEMORY);
398
399 /* go through each page in the object table */
400 for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
401 {
402 size_t j;
403 CVmObjPageEntry *entry;
404
405 /* start at the start of the page */
406 j = VM_OBJ_PAGE_CNT;
407 entry = *pg;
408
409 /* go through each entry on this page */
410 for ( ; j > 0 ; --j, ++entry, ++id)
411 {
412 /*
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
416 */
417 if (!entry->free_
418 && (entry->transient_ != 0) == (trans != 0)
419 && (G_meta_table->get_dependency_index(
420 entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx())
421 == meta_dep_idx))
422 {
423 ulong objsiz;
424
425 /*
426 * if this object has been mapped to a constant value,
427 * there's no need to store it, since it is no longer
428 * reachable
429 */
430 if (mapper->get_pool_addr(id) != 0)
431 continue;
432
433 /* try building the object */
434 objsiz = entry->get_vm_obj()->rebuild_image(vmg_ buf, bufsiz);
435
436 /* if they need more space, reallocate the buffer */
437 if (objsiz > bufsiz)
438 {
439 /* if the object is too large, throw an error */
440 if (objsiz > OSMALMAX)
441 err_throw(VMERR_OBJ_SIZE_OVERFLOW);
442
443 /* reallocate to next 4k increment */
444 bufsiz = (objsiz + 4095) & ~4095;
445 buf = (char *)t3realloc(buf, (size_t)bufsiz);
446 if (buf == 0)
447 err_throw(VMERR_OUT_OF_MEMORY);
448
449 /* try it again */
450 objsiz = entry->get_vm_obj()->
451 rebuild_image(vmg_ buf, bufsiz);
452 }
453
454 /* if the object is not empty, write it out */
455 if (objsiz != 0)
456 {
457 char prefix[20];
458
459 /*
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
463 */
464 if (objsiz > 65535 && !large_objects)
465 {
466 /* if we have a block open, end it */
467 if (block_cnt != 0)
468 writer->end_objs_block(block_cnt);
469
470 /* reset the count and size for the new block */
471 block_cnt = 0;
472 block_size = 0;
473
474 /* make the next block a large block */
475 large_objects = TRUE;
476 }
477
478 /*
479 * if this object plus its prefix would push this
480 * OBJS block over 64k, close it off and start a new
481 * block
482 */
483 if (block_size + objsiz + 6 > 64000L && block_cnt != 0)
484 {
485 /* close this block */
486 writer->end_objs_block(block_cnt);
487
488 /* reset the count and size for the new block */
489 block_cnt = 0;
490 block_size = 0;
491
492 /*
493 * use small size fields if this block isn't
494 * itself large
495 */
496 if (objsiz <= 65535)
497 large_objects = FALSE;
498 }
499
500 /* if this is the first object, write the header */
501 if (block_cnt == 0)
502 writer->begin_objs_block(meta_dep_idx, large_objects,
503 trans);
504
505 /* write the prefix information */
506 oswp4(prefix, id);
507 if (large_objects)
508 {
509 /* write the 32-bit object size */
510 oswp4(prefix + 4, objsiz);
511
512 /* write the header */
513 writer->write_objs_bytes(prefix, 8);
514 }
515 else
516 {
517 /* write the 16-bit object size */
518 oswp2(prefix + 4, objsiz);
519
520 /* write the header */
521 writer->write_objs_bytes(prefix, 6);
522 }
523
524 /* write the object data */
525 writer->write_objs_bytes(buf, objsiz);
526
527 /* count the object */
528 ++block_cnt;
529
530 /* count the size (including the prefix) */
531 block_size += objsiz + 6;
532 }
533 }
534 }
535 }
536
537 /* if we wrote any objects, end the OBJS block */
538 if (block_cnt != 0)
539 writer->end_objs_block(block_cnt);
540
541 /* delete our buffer */
542 t3free(buf);
543 }
544
545 /*
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.
550 */
rebuild_image_convert_const_data(VMG_ CVmConstMapper * const_mapper)551 void CVmObjTable::rebuild_image_convert_const_data
552 (VMG_ CVmConstMapper *const_mapper)
553 {
554 CVmObjPageEntry **pg;
555 size_t i;
556 vm_obj_id_t id;
557
558 /*
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.
562 */
563 for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
564 {
565 size_t j;
566 CVmObjPageEntry *entry;
567
568 /* start at the start of the page, but skip object ID = 0 */
569 j = VM_OBJ_PAGE_CNT;
570 entry = *pg;
571
572 /* go through each entry on this page */
573 for ( ; j > 0 ; --j, ++entry, ++id)
574 {
575 /*
576 * if this entry is in use, tell it to reserve space for
577 * conversion to constant data
578 */
579 if (!entry->free_)
580 entry->get_vm_obj()
581 ->reserve_const_data(vmg_ const_mapper, id);
582 }
583 }
584
585 /* prepare the constant mapper to begin storing */
586 const_mapper->prepare_to_store_data();
587
588 /*
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.
594 */
595 for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
596 {
597 size_t j;
598 CVmObjPageEntry *entry;
599
600 /* start at the start of the page, but skip object ID = 0 */
601 j = VM_OBJ_PAGE_CNT;
602 entry = *pg;
603
604 /* go through each entry on this page */
605 for ( ; j > 0 ; --j, ++entry, ++id)
606 {
607 /* if this entry is in use, convert it if possible */
608 if (!entry->free_)
609 entry->get_vm_obj()
610 ->convert_to_const_data(vmg_ const_mapper, id);
611 }
612 }
613 }
614
615 /* ------------------------------------------------------------------------ */
616 /*
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.
620 */
convert_dh_to_const_data(VMG_ CVmConstMapper * mapper,char * p)621 static void convert_dh_to_const_data(VMG_ CVmConstMapper *mapper, char *p)
622 {
623 /* if the value is an object, check for conversion */
624 if (vmb_get_dh_type(p) == VM_OBJ)
625 {
626 vm_obj_id_t obj;
627 ulong addr;
628
629 /* get the object value */
630 obj = vmb_get_dh_obj(p);
631
632 /* look up the object's converted constant pool address */
633 if ((addr = mapper->get_pool_addr(obj)) != 0)
634 {
635 vm_val_t val;
636
637 /* this value has been converted - set the new value */
638 val.typ = vm_objp(vmg_ obj)->get_convert_to_const_data_type();
639 val.val.ofs = addr;
640 vmb_put_dh(p, &val);
641 }
642 }
643 }
644
645 /*
646 * Convert a vm_val_t value to constant data if appropriate.
647 */
convert_val_to_const_data(VMG_ CVmConstMapper * mapper,vm_val_t * val)648 static void convert_val_to_const_data(VMG_ CVmConstMapper *mapper,
649 vm_val_t *val)
650 {
651 /* if it's an object, check for conversion */
652 if (val->typ == VM_OBJ)
653 {
654 ulong addr;
655
656 /* look up the object's converted constant pool address */
657 if ((addr = mapper->get_pool_addr(val->val.obj)) != 0)
658 {
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();
662 val->val.ofs = addr;
663 }
664 }
665 }
666
667 /* ------------------------------------------------------------------------ */
668 /*
669 * List Metaclass implementation - image rebuilding
670 */
671
672 /*
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
677 * object data.
678 */
rebuild_image(VMG_ char * buf,ulong buflen)679 ulong CVmObjList::rebuild_image(VMG_ char *buf, ulong buflen)
680 {
681 size_t copy_size;
682
683 /* calculate how much space we need to store the data */
684 copy_size = calc_alloc(vmb_get_len(ext_));
685
686 /* make sure we have room for our data */
687 if (copy_size > buflen)
688 return copy_size;
689
690 /* copy the data */
691 memcpy(buf, ext_, copy_size);
692
693 /* return the size */
694 return copy_size;
695 }
696
697 /*
698 * Reserve space in a rebuilt constant pool for converting our data to a
699 * constant value.
700 */
reserve_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)701 void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *mapper,
702 vm_obj_id_t self)
703 {
704 /* reserve the space for our list data */
705 mapper->alloc_pool_space(self, calc_alloc(vmb_get_len(ext_)));
706 }
707
708
709 /*
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.
714 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)715 void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *mapper,
716 vm_obj_id_t self)
717 {
718 size_t cnt;
719 char *p;
720
721 /* get my element count */
722 cnt = vmb_get_len(ext_);
723
724 /* mark as referenced each object in our list */
725 for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
726 {
727 /* convert the value to constant data if possible */
728 convert_dh_to_const_data(vmg_ mapper, p);
729 }
730
731 /*
732 * if we managed to convert our object to constant data, store our
733 * value
734 */
735 if (mapper->get_pool_addr(self))
736 mapper->store_data(self, ext_, calc_alloc(vmb_get_len(ext_)));
737 }
738
739
740 /* ------------------------------------------------------------------------ */
741 /*
742 * String Metaclass implementation - image rebuilding
743 */
744
745 /*
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.
751 */
rebuild_image(VMG_ char * buf,ulong buflen)752 ulong CVmObjString::rebuild_image(VMG_ char *buf, ulong buflen)
753 {
754 size_t copy_size;
755
756 /* calculate how much space we need to store the data */
757 copy_size = vmb_get_len(ext_) + VMB_LEN;
758
759 /* make sure we have room for our data */
760 if (copy_size > buflen)
761 return copy_size;
762
763 /* copy the data */
764 memcpy(buf, ext_, copy_size);
765
766 /* return the size */
767 return copy_size;
768 }
769
770 /*
771 * Reserve space in a rebuilt constant pool for converting our data to a
772 * constant value.
773 */
reserve_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)774 void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *mapper,
775 vm_obj_id_t self)
776 {
777 /* reserve the space for our string data */
778 mapper->alloc_pool_space(self, vmb_get_len(ext_) + VMB_LEN);
779 }
780
781
782 /*
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.
786 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)787 void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *mapper,
788 vm_obj_id_t self)
789 {
790 /*
791 * if we managed to convert our object to constant data, store our
792 * value
793 */
794 if (mapper->get_pool_addr(self))
795 mapper->store_data(self, ext_, vmb_get_len(ext_) + VMB_LEN);
796 }
797
798
799 /* ------------------------------------------------------------------------ */
800 /*
801 * TADS Object implementation - image rebuilding
802 */
803
804
805 /* property comparison callback for qsort() */
prop_compare(const void * p1,const void * p2)806 static int prop_compare(const void *p1, const void *p2)
807 {
808 uint id1, id2;
809
810 /* get the ID's */
811 id1 = osrp2(p1);
812 id2 = osrp2(p2);
813
814 /* compare them and return the result */
815 return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
816 }
817
818
819 /*
820 * build an image file record for the object
821 */
rebuild_image(VMG_ char * buf,ulong buflen)822 ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen)
823 {
824 size_t max_size;
825 char *p;
826 char *props;
827 char *props_end;
828 size_t prop_cnt;
829 size_t i;
830 vm_tadsobj_prop *entry;
831
832 /*
833 * Make sure the buffer is big enough. Start out with worst-case
834 * assumption that we'll need every allocated property slot; we might
835 * actually need fewer, since some of the slots could be empty by
836 * virtue of having been undone. We need space for our own header
837 * (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a
838 * UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property.
839 */
840 max_size = (2 + 2 + 2)
841 + get_sc_count() * 4
842 + (get_hdr()->prop_entry_free * 7);
843
844 /* if it's more than we have available, ask for more space */
845 if (max_size > buflen)
846 return max_size;
847
848 /*
849 * set up our header - use a placeholder 0 for the property count
850 * for now, until we calculate the real value
851 */
852 oswp2(buf, get_sc_count());
853 oswp2(buf+2, 0);
854 oswp2(buf+4, get_li_obj_flags());
855 p = buf + 6;
856
857 /* copy the superclass list */
858 for (i = 0 ; i < get_sc_count() ; ++i, p += 4)
859 oswp4(p, get_sc(i));
860
861 /* remember where the properties start */
862 props = p;
863
864 /* copy the non-empty property slots */
865 for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr,
866 prop_cnt = 0 ;
867 i != 0 ;
868 --i, ++entry)
869 {
870 /* if this slot is non-empty, store it */
871 if (entry->val.typ != VM_EMPTY)
872 {
873 /* set up this slot */
874 oswp2(p, entry->prop);
875 vmb_put_dh(p + 2, &entry->val);
876
877 /* count the additional property */
878 ++prop_cnt;
879
880 /* move past it in the buffer */
881 p += 7;
882 }
883 }
884
885 /* remember where the properties end */
886 props_end = p;
887
888 /* fill in actual the property count now that we know it */
889 oswp2(buf+2, prop_cnt);
890
891 /* sort the new table */
892 qsort(props, prop_cnt, 7, &prop_compare);
893
894 /* return the final size */
895 return p - buf;
896 }
897
898 /*
899 * Convert to constant data. We must check each object that we
900 * reference via a modified property and convert it to a constant data
901 * item if necessary.
902 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t)903 void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper,
904 vm_obj_id_t /*self*/)
905 {
906 size_t i;
907 vm_tadsobj_prop *entry;
908
909 /*
910 * Scan the property entries. Note that we don't have to worry about
911 * the original properties, since they can only refer to data that was
912 * in the original image file, and hence never need to be converted --
913 * if it was good enough for the original image file, it's good enough
914 * for us.
915 */
916 for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ;
917 i != 0 ; --i, ++entry)
918 {
919 /* if this slot is modified, convert it */
920 if ((entry->flags & VMTO_PROP_MOD) != 0
921 && entry->val.typ != VM_EMPTY)
922 {
923 /* convert the value */
924 convert_val_to_const_data(vmg_ mapper, &entry->val);
925 }
926 }
927 }
928
929
930 /* ------------------------------------------------------------------------ */
931 /*
932 * Dictionary object implementation - image rebuilding
933 */
934
935 /*
936 * callback context for image rebuild
937 */
938 struct rebuild_ctx
939 {
940 /* space needed so far */
941 size_t space_needed;
942
943 /* number of entries */
944 size_t entry_cnt;
945
946 /* next available output pointer */
947 char *dst;
948 };
949
950 /*
951 * rebuild for image file
952 */
rebuild_image(VMG_ char * buf,ulong buflen)953 ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen)
954 {
955 rebuild_ctx ctx;
956
957 /*
958 * calculate the amount of space we need - start with the comparator
959 * object, which needs four bytes, and the entry count, which needs two
960 * bytes
961 */
962 ctx.space_needed = 6;
963
964 /* enumerate the entries to count space */
965 ctx.entry_cnt = 0;
966 get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx);
967
968 /* if we need more space than is available, ask for more */
969 if (ctx.space_needed > buflen)
970 return ctx.space_needed;
971
972 /* write the comparator object and the entry count */
973 oswp4(buf, get_ext()->comparator_);
974 oswp2(buf + 4, ctx.entry_cnt);
975
976 /* start writing after the count */
977 ctx.dst = buf + 6;
978
979 /* enumerate the entries to write to the image buffer */
980 get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx);
981
982 /* return the size */
983 return ctx.dst - buf;
984 }
985
986 /*
987 * enumeration callback - rebuild phase 1: count the space needed for
988 * this table entry
989 */
rebuild_cb_1(void * ctx0,class CVmHashEntry * entry0)990 void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0)
991 {
992 rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
993 CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
994 vm_dict_entry *cur;
995
996 /*
997 * count the space needed for this string - one byte for the length
998 * prefix, the bytes of the name string, and two bytes for the item
999 * count
1000 */
1001 ctx->space_needed += 1 + entry->getlen() + 2;
1002
1003 /* count this entry */
1004 ++ctx->entry_cnt;
1005
1006 /* count the items */
1007 for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1008 {
1009 /*
1010 * add the space for this item - four byte for the object ID,
1011 * two bytes for the property ID
1012 */
1013 ctx->space_needed += 6;
1014 }
1015 }
1016
1017 /*
1018 * enumeration callback - rebuild phase 2: write the entries to the
1019 * image buffer
1020 */
rebuild_cb_2(void * ctx0,class CVmHashEntry * entry0)1021 void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0)
1022 {
1023 rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
1024 CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1025 vm_dict_entry *cur;
1026 size_t cnt;
1027 char *p;
1028 size_t rem;
1029
1030 /* count the entries in our list */
1031 for (cnt = 0, cur = entry->get_head() ; cur != 0 ;
1032 cur = cur->nxt_, ++cnt) ;
1033
1034 /* write the length of the key string followed by the key string */
1035 *ctx->dst++ = entry->getlen();
1036 memcpy(ctx->dst, entry->getstr(), entry->getlen());
1037
1038 /* xor the key string's bytes */
1039 for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ;
1040 --rem, ++p)
1041 *p ^= 0xBD;
1042
1043 /* move past the key string */
1044 ctx->dst += entry->getlen();
1045
1046 /* write the item count */
1047 oswp2(ctx->dst, cnt);
1048 ctx->dst += 2;
1049
1050 /* write the items */
1051 for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1052 {
1053 /* write the object ID and property ID for this item */
1054 oswp4(ctx->dst, (ulong)cur->obj_);
1055 oswp2(ctx->dst + 4, (uint)cur->prop_);
1056 ctx->dst += 6;
1057 }
1058 }
1059
1060 /*
1061 * callback context for constant data conversion
1062 */
1063 struct cvt_const_ctx
1064 {
1065 /* constant mapper */
1066 CVmConstMapper *mapper;
1067 };
1068
1069 /*
1070 * convert to constant data
1071 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1072 void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1073 vm_obj_id_t self)
1074 {
1075 cvt_const_ctx ctx;
1076
1077 /* make sure the comparator object isn't mappable to a constant */
1078 if (mapper->get_pool_addr(get_ext()->comparator_) != 0)
1079 err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1080 "<comparator>", 12);
1081
1082 /*
1083 * Go through our dictionary and make sure we don't have any
1084 * references to constant data. We don't actually have to perform
1085 * any conversions, because we simply don't allow references to
1086 * anything but TADS-object objects in the dictionary.
1087 */
1088 ctx.mapper = mapper;
1089 get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx);
1090 }
1091
1092 /*
1093 * enumeration callback - convert to constant data
1094 */
cvt_const_cb(void * ctx0,class CVmHashEntry * entry0)1095 void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0)
1096 {
1097 cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0;
1098 CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1099 vm_dict_entry *cur;
1100
1101 /* inspect the items in this entry's list */
1102 for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1103 {
1104 /* if this item refers to constant data, it's an error */
1105 if (ctx->mapper->get_pool_addr(cur->obj_) != 0)
1106 err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1107 entry->getstr(), entry->getlen());
1108 }
1109 }
1110
1111 /* ------------------------------------------------------------------------ */
1112 /*
1113 * Grammar production object - image rebuilding operations
1114 */
1115
1116 /*
1117 * rebuild for image file - we can't change at run-time, so we must
1118 * simply copy our image file data back out unchanged
1119 */
rebuild_image(VMG_ char * buf,ulong buflen)1120 ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen)
1121 {
1122 /* make sure we have room */
1123 if (get_ext()->image_data_size_ > buflen)
1124 return get_ext()->image_data_size_;
1125
1126 /* copy the data */
1127 memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_);
1128
1129 /* return the size */
1130 return get_ext()->image_data_size_;
1131 }
1132
1133 /* ------------------------------------------------------------------------ */
1134 /*
1135 * BigNumber Metaclass implementation - image rebuilding
1136 */
1137
1138 /*
1139 * Build an image file record
1140 */
rebuild_image(VMG_ char * buf,ulong buflen)1141 ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen)
1142 {
1143 size_t copy_size;
1144
1145 /* calculate how much space we need to store the data */
1146 copy_size = calc_alloc(get_prec(ext_));
1147
1148 /* make sure we have room for our data */
1149 if (copy_size > buflen)
1150 return copy_size;
1151
1152 /* copy the data */
1153 memcpy(buf, ext_, copy_size);
1154
1155 /* return the size */
1156 return copy_size;
1157 }
1158
1159 /*
1160 * Reserve space in a rebuilt constant pool for converting our data to a
1161 * constant value.
1162 */
reserve_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1163 void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper,
1164 vm_obj_id_t self)
1165 {
1166 /* BigNumber values cannot be converted to constants */
1167 }
1168
1169
1170 /*
1171 * Convert to constant data. We don't reference any other objects, so
1172 * we simply need to store our data in the constant pool, if we
1173 * successfully reserved an address for it.
1174 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1175 void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1176 vm_obj_id_t self)
1177 {
1178 /*
1179 * if we managed to convert our object to constant data, store our
1180 * value
1181 */
1182 if (mapper->get_pool_addr(self))
1183 mapper->store_data(self, ext_, calc_alloc(get_prec(ext_)));
1184 }
1185
1186
1187 /* ------------------------------------------------------------------------ */
1188 /*
1189 * Object-to-Constant mapper
1190 */
1191
1192 /*
1193 * initialize
1194 */
CVmConstMapper(VMG0_)1195 CVmConstMapper::CVmConstMapper(VMG0_)
1196 {
1197 vm_obj_id_t max_id;
1198 size_t i;
1199
1200 /* get the maximum object ID we've allocated */
1201 max_id = G_obj_table->get_max_used_obj_id();
1202
1203 /*
1204 * Allocate our master translation page list so that we have room
1205 * for enough pages to store the maximum object ID. We need one
1206 * slot for each page of 1024 translation elements.
1207 */
1208 obj_addr_cnt_ = max_id / 1024;
1209 obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0]));
1210 if (obj_addr_ == 0)
1211 err_throw(VMERR_OUT_OF_MEMORY);
1212
1213 /* clear out the page slots - we haven't allocated any pages yet */
1214 for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1215 obj_addr_[i] = 0;
1216
1217 /* get the constant pool's page size */
1218 page_size_ = G_const_pool->get_page_size();
1219
1220 /*
1221 * our first page is the next page after the last page in the
1222 * current pool
1223 */
1224 first_page_idx_ = G_const_pool->get_page_count();
1225
1226 /*
1227 * get the starting address - we'll start writing our data at the
1228 * first page after all existing pages in the pool
1229 */
1230 base_addr_ = page_size_ * G_const_pool->get_page_count();
1231
1232 /* allocate from our base address */
1233 next_free_ = base_addr_;
1234
1235 /* we have the entire first page available */
1236 rem_ = page_size_;
1237
1238 /*
1239 * we haven't allocated any page data yet (we'll do this after we've
1240 * reserved all space, when the client invokes
1241 * prepare_to_store_data())
1242 */
1243 pages_ = 0;
1244 pages_cnt_ = 0;
1245 }
1246
1247 /*
1248 * delete
1249 */
~CVmConstMapper()1250 CVmConstMapper::~CVmConstMapper()
1251 {
1252 size_t i;
1253
1254 /* delete each page in our mapping table */
1255 for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1256 {
1257 /* free this page if we allocated it */
1258 if (obj_addr_[i] != 0)
1259 t3free(obj_addr_[i]);
1260 }
1261
1262 /* delete our mapping table */
1263 t3free(obj_addr_);
1264
1265 /* delete each constant pool page */
1266 for (i = 0 ; i < pages_cnt_ ; ++i)
1267 t3free(pages_[i]);
1268
1269 /* free the page list */
1270 if (pages_ != 0)
1271 t3free(pages_);
1272 }
1273
1274 /*
1275 * Get an object's pool address, if it has one
1276 */
get_pool_addr(vm_obj_id_t obj_id)1277 ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id)
1278 {
1279 /* determine if the page is mapped - if not, the object isn't mapped */
1280 if (obj_addr_[obj_id / 1024] == 0)
1281 return 0;
1282
1283 /* return the mapping entry */
1284 return obj_addr_[obj_id / 1024][obj_id % 1024];
1285 }
1286
1287
1288 /*
1289 * Allocate space in the pool for an object's data
1290 */
alloc_pool_space(vm_obj_id_t obj_id,size_t len)1291 ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len)
1292 {
1293 ulong ret;
1294
1295 /*
1296 * if the data block is too large to fit on a constant pool page, we
1297 * can't store it
1298 */
1299 if (len > page_size_)
1300 return 0;
1301
1302 /* if the translation page isn't mapped yet, map it */
1303 if (obj_addr_[obj_id / 1024] == 0)
1304 {
1305 /* allocate a new page */
1306 obj_addr_[obj_id / 1024] =
1307 (ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0]));
1308
1309 /* if that failed, throw an error */
1310 if (obj_addr_[obj_id / 1024] == 0)
1311 err_throw(VMERR_OUT_OF_MEMORY);
1312
1313 /* clear the new page */
1314 memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0]));
1315 }
1316
1317 /* if the block doesn't fit on this page, skip to the next page */
1318 if (len > rem_)
1319 {
1320 /* skip past the remainder of this page */
1321 next_free_ += rem_;
1322
1323 /* we have the whole next page available now */
1324 rem_ = page_size_;
1325 }
1326
1327 /* remember the object ID's address in the translation list */
1328 ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_;
1329
1330 /* skip past the data */
1331 next_free_ += len;
1332 rem_ -= len;
1333
1334 /* return the new address */
1335 return ret;
1336 }
1337
1338 /*
1339 * Prepare to begin storing data
1340 */
prepare_to_store_data()1341 void CVmConstMapper::prepare_to_store_data()
1342 {
1343 size_t i;
1344
1345 /* figure out how many pages we need */
1346 pages_cnt_ = calc_page_count();
1347
1348 /* allocate space for the list of pages */
1349 pages_ = (vm_const_mapper_page **)
1350 t3malloc(pages_cnt_ * sizeof(pages_[0]));
1351 if (pages_ == 0)
1352 err_throw(VMERR_OUT_OF_MEMORY);
1353
1354 /* allocate each page */
1355 for (i = 0 ; i < pages_cnt_ ; ++i)
1356 {
1357 /*
1358 * allocate this page - allocate the header structure plus space
1359 * for the page data
1360 */
1361 pages_[i] = (vm_const_mapper_page *)
1362 t3malloc(sizeof(pages_[i]) + page_size_);
1363 if (pages_[i] == 0)
1364 err_throw(VMERR_OUT_OF_MEMORY);
1365
1366 /* the page has nothing stored yet */
1367 pages_[i]->max_ofs_used = 0;
1368 }
1369 }
1370
1371 /*
1372 * Store an object's data
1373 */
store_data(vm_obj_id_t obj_id,const void * ptr,size_t len)1374 void CVmConstMapper::store_data(vm_obj_id_t obj_id,
1375 const void *ptr, size_t len)
1376 {
1377 ulong addr;
1378 size_t page_idx;
1379 size_t page_ofs;
1380
1381 /* get the pool address that was reserved for the object */
1382 addr = get_pool_addr(obj_id);
1383
1384 /* if the object had no space reserved, ignore the request */
1385 if (addr == 0)
1386 return;
1387
1388 /* figure out which page this address is in, and the offset in the page */
1389 page_idx = (size_t)((addr - base_addr_) / page_size_);
1390 page_ofs = (size_t)((addr - base_addr_) % page_size_);
1391
1392 /*
1393 * if this address takes us above the high-water mark for the page,
1394 * move the page's marker accordingly
1395 */
1396 if (page_ofs + len > pages_[page_idx]->max_ofs_used)
1397 pages_[page_idx]->max_ofs_used = page_ofs + len;
1398
1399 /* copy the data */
1400 memcpy(pages_[page_idx]->buf + page_ofs, ptr, len);
1401 }
1402
1403 /*
1404 * Write the pool pages to an image file
1405 */
write_to_image_file(CVmImageWriter * writer,uchar xor_mask)1406 void CVmConstMapper::write_to_image_file(CVmImageWriter *writer,
1407 uchar xor_mask)
1408 {
1409 size_t i;
1410
1411 /* go through each of our pages */
1412 for (i = 0 ; i < pages_cnt_ ; ++i)
1413 {
1414 /* write this page - it's in pool 2 (the constant data pool) */
1415 writer->write_pool_page(2, first_page_idx_ + i,
1416 pages_[i]->buf, pages_[i]->max_ofs_used,
1417 TRUE, xor_mask);
1418 }
1419 }
1420
1421 /* ------------------------------------------------------------------------ */
1422 /*
1423 * metaclass table - image rewriter implementation
1424 */
1425
1426 /*
1427 * write the new metaclass dependency table
1428 */
rebuild_image(CVmImageWriter * writer)1429 void CVmMetaTable::rebuild_image(CVmImageWriter *writer)
1430 {
1431 size_t i;
1432
1433 /* begin the new metaclass dependency table */
1434 writer->begin_meta_dep(get_count());
1435
1436 /* write the new metaclass dependency table items */
1437 for (i = 0 ; i < get_count() ; ++i)
1438 {
1439 vm_meta_entry_t *entry;
1440 uint j;
1441
1442 /* get this entry */
1443 entry = get_entry(i);
1444
1445 /* write this metaclass name */
1446 writer->write_meta_dep_item(entry->image_meta_name_);
1447
1448 /*
1449 * Write the property translation list. Note that xlat_func()
1450 * requires a 1-based index, so we loop from 1 to the count,
1451 * rather than following the usual C-style 0-based conventions.
1452 */
1453 for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
1454 writer->write_meta_item_prop(entry->xlat_func(j));
1455 }
1456
1457 /* end the metaclass dependency table */
1458 writer->end_meta_dep();
1459 }
1460
1461
1462 /* ------------------------------------------------------------------------ */
1463 /*
1464 * Hashtable Metaclass implementation - image rebuilding
1465 */
1466
1467 /*
1468 * Build an image file record
1469 */
rebuild_image(VMG_ char * buf,ulong buflen)1470 ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1471 {
1472 size_t copy_size;
1473 vm_lookup_ext *ext = get_ext();
1474 uint i;
1475 vm_lookup_val **bp;
1476 vm_lookup_val *val;
1477 char *dst;
1478 uint idx;
1479
1480 /*
1481 * we need space for the fixed header (6 bytes), 2 bytes per bucket,
1482 * and the entries themselves
1483 */
1484 copy_size = 6
1485 + (ext->bucket_cnt * 2)
1486 + (ext->value_cnt * VMLOOKUP_VALUE_SIZE);
1487
1488 /* make sure we have room for our data */
1489 if (copy_size > buflen)
1490 return copy_size;
1491
1492 /* write the fixed data */
1493 oswp2(buf, ext->bucket_cnt);
1494 oswp2(buf + 2, ext->value_cnt);
1495 idx = ext->val_to_img_idx(ext->first_free);
1496 oswp2(buf + 4, idx);
1497 dst = buf + 6;
1498
1499 /* write the buckets */
1500 for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp)
1501 {
1502 /* write this bucket's index */
1503 idx = ext->val_to_img_idx(*bp);
1504 oswp2(dst, idx);
1505 dst += 2;
1506 }
1507
1508 /* write the values */
1509 for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val)
1510 {
1511 /* store the key, value, and index */
1512 vmb_put_dh(dst, &val->key);
1513 vmb_put_dh(dst + VMB_DATAHOLDER, &val->val);
1514 idx = ext->val_to_img_idx(val->nxt);
1515 oswp2(dst + VMB_DATAHOLDER*2, idx);
1516
1517 /* skip the data in the output */
1518 dst += VMLOOKUP_VALUE_SIZE;
1519 }
1520
1521 /* return the size */
1522 return copy_size;
1523 }
1524
1525 /*
1526 * Convert to constant data. We must convert each object we reference in
1527 * a key or in a value to constant data if possible.
1528 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1529 void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1530 vm_obj_id_t self)
1531 {
1532 uint i;
1533 vm_lookup_val *entry;
1534
1535 /* run through each entry */
1536 for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ;
1537 i != 0 ; --i, ++entry)
1538 {
1539 /* convert the key */
1540 convert_val_to_const_data(vmg_ mapper, &entry->key);
1541
1542 /* convert the value */
1543 convert_val_to_const_data(vmg_ mapper, &entry->val);
1544 }
1545 }
1546
1547 /* ------------------------------------------------------------------------ */
1548 /*
1549 * IntrinsicClass Metaclass implementation - image rebuilding
1550 */
1551
1552 /*
1553 * Build an image file record
1554 */
rebuild_image(VMG_ char * buf,ulong buflen)1555 ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen)
1556 {
1557 size_t copy_size;
1558
1559 /* get our size */
1560 copy_size = osrp2(ext_);
1561
1562 /* make sure we have room for our data */
1563 if (copy_size > buflen)
1564 return copy_size;
1565
1566 /* copy the data */
1567 memcpy(buf, ext_, copy_size);
1568
1569 /* return the size */
1570 return copy_size;
1571 }
1572
1573 /* ------------------------------------------------------------------------ */
1574 /*
1575 * Indexed Iterator
1576 */
1577
1578 /*
1579 * Build an image file record
1580 */
rebuild_image(VMG_ char * buf,ulong buflen)1581 ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen)
1582 {
1583 size_t copy_size;
1584
1585 /* calculate our data size - just store our entire extension */
1586 copy_size = VMOBJITERIDX_EXT_SIZE;
1587
1588 /* make sure we have room for our data */
1589 if (copy_size > buflen)
1590 return copy_size;
1591
1592 /* copy the data */
1593 memcpy(buf, ext_, copy_size);
1594
1595 /* return the size */
1596 return copy_size;
1597 }
1598
1599 /*
1600 * Convert to constant data. We must convert our collection object
1601 * reference to constant data if possible.
1602 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1603 void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1604 vm_obj_id_t self)
1605 {
1606 /* convert our collection reference to constant data if possible */
1607 convert_dh_to_const_data(vmg_ mapper, ext_);
1608 }
1609
1610
1611 /* ------------------------------------------------------------------------ */
1612 /*
1613 * Hashtable Iterator
1614 */
1615
1616 /*
1617 * Build an image file record
1618 */
rebuild_image(VMG_ char * buf,ulong buflen)1619 ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1620 {
1621 size_t copy_size;
1622
1623 /* calculate our data size - just store our entire extension */
1624 copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE;
1625
1626 /* make sure we have room for our data */
1627 if (copy_size > buflen)
1628 return copy_size;
1629
1630 /* copy the data */
1631 memcpy(buf, ext_, copy_size);
1632
1633 /* return the size */
1634 return copy_size;
1635 }
1636
1637 /*
1638 * Convert to constant data. We must convert our collection object
1639 * reference to constant data if possible.
1640 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1641 void CVmObjIterLookupTable::convert_to_const_data(
1642 VMG_ CVmConstMapper *mapper, vm_obj_id_t self)
1643 {
1644 /* convert our collection reference to constant data if possible */
1645 convert_dh_to_const_data(vmg_ mapper, ext_);
1646 }
1647
1648
1649 /* ------------------------------------------------------------------------ */
1650 /*
1651 * Vector Metaclass implementation - image rebuilding
1652 */
1653
1654 /*
1655 * Build an image file record
1656 */
rebuild_image(VMG_ char * buf,ulong buflen)1657 ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen)
1658 {
1659 size_t copy_size;
1660 size_t ele_cnt;
1661 size_t alloc_cnt;
1662
1663 /*
1664 * calculate how much space we need to store the data - store only
1665 * the data, not the undo bits
1666 */
1667 ele_cnt = get_element_count();
1668 alloc_cnt = get_allocated_count();
1669 copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt);
1670
1671 /* make sure we have room for our data */
1672 if (copy_size > buflen)
1673 return copy_size;
1674
1675 /* save the allocation count and in-use element count */
1676 vmb_put_len(buf, alloc_cnt);
1677 vmb_put_len(buf + VMB_LEN, ele_cnt);
1678
1679 /* copy the element data */
1680 memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt));
1681
1682 /* return the size */
1683 return copy_size;
1684 }
1685
1686 /*
1687 * Reserve space in a rebuilt constant pool for converting our data to a
1688 * constant value.
1689 */
reserve_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1690 void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper,
1691 vm_obj_id_t self)
1692 {
1693 /* Vector values cannot be converted to constants */
1694 }
1695
1696
1697 /*
1698 * Convert to constant data. We must convert each object we reference to
1699 * constant data if possible; we use the same algorithm as CVmObjList.
1700 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t self)1701 void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1702 vm_obj_id_t self)
1703 {
1704 size_t cnt;
1705 char *p;
1706
1707 /* get my element count */
1708 cnt = get_element_count();
1709
1710 /* mark as referenced each object in our list */
1711 for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
1712 {
1713 /* convert the value to constant data if possible */
1714 convert_dh_to_const_data(vmg_ mapper, p);
1715 }
1716 }
1717
1718 /* ------------------------------------------------------------------------ */
1719 /*
1720 * ByteArray Metaclass implementation - image rebuilding
1721 */
1722
1723 /*
1724 * Build an image file record
1725 */
rebuild_image(VMG_ char * buf,ulong buflen)1726 ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen)
1727 {
1728 ulong copy_size;
1729 ulong rem;
1730 ulong idx;
1731
1732 /* we need four bytes for our count plus space for our byte array */
1733 copy_size = 4 + get_element_count();
1734
1735 /* make sure we have room for our data */
1736 if (copy_size > buflen)
1737 return copy_size;
1738
1739 /* store our element count */
1740 oswp4(buf, get_element_count());
1741 buf += 4;
1742
1743 /* copy our data in chunks */
1744 for (idx = 1, rem = get_element_count() ; rem != 0 ; )
1745 {
1746 unsigned char *p;
1747 size_t avail;
1748 size_t chunk;
1749
1750 /* get the next chunk */
1751 p = get_ele_ptr(idx, &avail);
1752
1753 /* limit copying to the remaining size */
1754 chunk = avail;
1755 if (chunk > rem)
1756 chunk = rem;
1757
1758 /* store this chunk */
1759 memcpy(buf, p, chunk);
1760
1761 /* skip this chunk */
1762 buf += chunk;
1763 idx += chunk;
1764 rem -= chunk;
1765 }
1766
1767 /* return the size */
1768 return copy_size;
1769 }
1770
1771 /* ------------------------------------------------------------------------ */
1772 /*
1773 * CharacterSet Metaclass implementation - image rebuilding
1774 */
1775
1776 /*
1777 * Build an image file record
1778 */
rebuild_image(VMG_ char * buf,ulong buflen)1779 ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen)
1780 {
1781 ulong copy_size;
1782
1783 /* we need two bytes for the name length count plus the name's bytes */
1784 copy_size = 2 + get_ext_ptr()->charset_name_len;
1785
1786 /* make sure we have room for our data */
1787 if (copy_size > buflen)
1788 return copy_size;
1789
1790 /* store the length of the name, and the name itself */
1791 oswp2(buf, get_ext_ptr()->charset_name_len);
1792 memcpy(buf + 2, get_ext_ptr()->charset_name,
1793 get_ext_ptr()->charset_name_len);
1794
1795 /* return the size */
1796 return copy_size;
1797 }
1798
1799 /* ------------------------------------------------------------------------ */
1800 /*
1801 * File Metaclass implementation - image rebuilding
1802 */
1803
1804 /*
1805 * Build an image file record
1806 */
rebuild_image(VMG_ char * buf,ulong buflen)1807 ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen)
1808 {
1809 ulong copy_size;
1810
1811 /*
1812 * we need the character set object ID, the mode byte, the access
1813 * byte, and the uint32 flags
1814 */
1815 copy_size = VMB_OBJECT_ID + 1 + 1 + 4;
1816
1817 /* make sure we have room for our data */
1818 if (copy_size > buflen)
1819 return copy_size;
1820
1821 /* store the character set object ID */
1822 vmb_put_objid(buf, get_ext()->charset);
1823 buf += VMB_OBJECT_ID;
1824
1825 /* store the mode and access values */
1826 *buf++ = get_ext()->mode;
1827 *buf++ = get_ext()->access;
1828
1829 /* store the flags */
1830 oswp4(buf, get_ext()->flags);
1831
1832 /* return the size */
1833 return copy_size;
1834 }
1835
1836 /* ------------------------------------------------------------------------ */
1837 /*
1838 * Pattern metaclass
1839 */
1840
1841 /*
1842 * build an image file record for the object
1843 */
rebuild_image(VMG_ char * buf,ulong buflen)1844 ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen)
1845 {
1846 size_t need_size;
1847
1848 /* we need a DATAHOLDER to store the original pattern string reference */
1849 need_size = VMB_DATAHOLDER;
1850
1851 /* if we need more space, just return our size requirements */
1852 if (need_size > buflen)
1853 return need_size;
1854
1855 /* write our value */
1856 vmb_put_dh(buf, get_orig_str());
1857
1858 /* return our size */
1859 return need_size;
1860 }
1861
1862 /*
1863 * Convert to constant data.
1864 */
convert_to_const_data(VMG_ CVmConstMapper * mapper,vm_obj_id_t)1865 void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1866 vm_obj_id_t /*self*/)
1867 {
1868 /* check our original source string value */
1869 convert_val_to_const_data(vmg_ mapper, &get_ext()->str);
1870 }
1871
1872 /* ------------------------------------------------------------------------ */
1873 /*
1874 * StringComparator intrinsic class
1875 */
1876
1877 /*
1878 * build the image data
1879 */
rebuild_image(VMG_ char * buf,ulong buflen)1880 ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen)
1881 {
1882 /* set up a stream writer on the buffer, and write it out */
1883 CVmMemoryStream str(buf, buflen);
1884 return write_to_stream(vmg_ &str, &buflen);
1885 }
1886
1887