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