cfad47cfa3/t3compiler/tads3/vmimgrb.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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
 */
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
 */
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 = t3rp4u(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 = t3rp4u(cpdf_buf + 2);
180
            pgsiz = t3rp4u(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 = t3rp4u(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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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
 */
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() */
806
extern "C"
807
{
808
    static int prop_compare(const void *p1, const void *p2)
809
    {
810
        uint id1, id2;
811
812
        /* get the ID's */
813
        id1 = osrp2(p1);
814
        id2 = osrp2(p2);
815
816
        /* compare them and return the result */
817
        return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
818
    }
819
}
820
821
/*
822
 *   build an image file record for the object 
823
 */
824
ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen)
825
{
826
    size_t max_size;
827
    char *p;
828
    char *props;
829
    char *props_end;
830
    size_t prop_cnt;
831
    size_t i;
832
    vm_tadsobj_prop *entry;
833
    
834
    /* 
835
     *   Make sure the buffer is big enough.  Start out with worst-case
836
     *   assumption that we'll need every allocated property slot; we might
837
     *   actually need fewer, since some of the slots could be empty by
838
     *   virtue of having been undone.  We need space for our own header
839
     *   (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a
840
     *   UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property.  
841
     */
842
    max_size = (2 + 2 + 2)
843
               + get_sc_count() * 4
844
               + (get_hdr()->prop_entry_free * 7);
845
846
    /* if it's more than we have available, ask for more space */
847
    if (max_size > buflen)
848
        return max_size;
849
850
    /* 
851
     *   set up our header - use a placeholder 0 for the property count
852
     *   for now, until we calculate the real value 
853
     */
854
    oswp2(buf, get_sc_count());
855
    oswp2(buf+2, 0);
856
    oswp2(buf+4, get_li_obj_flags());
857
    p = buf + 6;
858
859
    /* copy the superclass list */
860
    for (i = 0 ; i < get_sc_count() ; ++i, p += 4)
861
        oswp4(p, get_sc(i));
862
863
    /* remember where the properties start */
864
    props = p;
865
866
    /* copy the non-empty property slots */
867
    for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr,
868
         prop_cnt = 0 ;
869
         i != 0 ;
870
         --i, ++entry)
871
    {
872
        /* if this slot is non-empty, store it */
873
        if (entry->val.typ != VM_EMPTY)
874
        {
875
            /* set up this slot */
876
            oswp2(p, entry->prop);
877
            vmb_put_dh(p + 2, &entry->val);
878
879
            /* count the additional property */
880
            ++prop_cnt;
881
882
            /* move past it in the buffer */
883
            p += 7;
884
        }
885
    }
886
887
    /* remember where the properties end */
888
    props_end = p;
889
890
    /* fill in actual the property count now that we know it */
891
    oswp2(buf+2, prop_cnt);
892
893
    /* sort the new table */
894
    qsort(props, prop_cnt, 7, &prop_compare);
895
896
    /* return the final size */
897
    return p - buf;
898
}
899
900
/*
901
 *   Convert to constant data.  We must check each object that we
902
 *   reference via a modified property and convert it to a constant data
903
 *   item if necessary.  
904
 */
905
void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper,
906
                                       vm_obj_id_t /*self*/)
907
{
908
    size_t i;
909
    vm_tadsobj_prop *entry;
910
911
    /* 
912
     *   Scan the property entries.  Note that we don't have to worry about
913
     *   the original properties, since they can only refer to data that was
914
     *   in the original image file, and hence never need to be converted --
915
     *   if it was good enough for the original image file, it's good enough
916
     *   for us.  
917
     */
918
    for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ;
919
         i != 0 ; --i, ++entry)
920
    {
921
        /* if this slot is modified, convert it */
922
        if ((entry->flags & VMTO_PROP_MOD) != 0
923
            && entry->val.typ != VM_EMPTY)
924
        {
925
            /* convert the value */
926
            convert_val_to_const_data(vmg_ mapper, &entry->val);
927
        }
928
    }
929
}
930
931
932
/* ------------------------------------------------------------------------ */
933
/*
934
 *   Dictionary object implementation - image rebuilding 
935
 */
936
937
/* 
938
 *   callback context for image rebuild 
939
 */
940
struct rebuild_ctx
941
{
942
    /* space needed so far */
943
    size_t space_needed;
944
945
    /* number of entries */
946
    size_t entry_cnt;
947
948
    /* next available output pointer */
949
    char *dst;
950
};
951
952
/* 
953
 *   rebuild for image file 
954
 */
955
ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen)
956
{
957
    rebuild_ctx ctx;
958
    
959
    /* 
960
     *   calculate the amount of space we need - start with the comparator
961
     *   object, which needs four bytes, and the entry count, which needs two
962
     *   bytes 
963
     */
964
    ctx.space_needed = 6;
965
966
    /* enumerate the entries to count space */
967
    ctx.entry_cnt = 0;
968
    get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx);
969
970
    /* if we need more space than is available, ask for more */
971
    if (ctx.space_needed > buflen)
972
        return ctx.space_needed;
973
974
    /* write the comparator object and the entry count */
975
    oswp4(buf, get_ext()->comparator_);
976
    oswp2(buf + 4, ctx.entry_cnt);
977
978
    /* start writing after the count */
979
    ctx.dst = buf + 6;
980
981
    /* enumerate the entries to write to the image buffer */
982
    get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx);
983
984
    /* return the size */
985
    return ctx.dst - buf;
986
}
987
988
/*
989
 *   enumeration callback - rebuild phase 1: count the space needed for
990
 *   this table entry 
991
 */
992
void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0)
993
{
994
    rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
995
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
996
    vm_dict_entry *cur;
997
998
    /* 
999
     *   count the space needed for this string - one byte for the length
1000
     *   prefix, the bytes of the name string, and two bytes for the item
1001
     *   count 
1002
     */
1003
    ctx->space_needed += 1 + entry->getlen() + 2;
1004
1005
    /* count this entry */
1006
    ++ctx->entry_cnt;
1007
1008
    /* count the items */
1009
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1010
    {
1011
        /* 
1012
         *   add the space for this item - four byte for the object ID,
1013
         *   two bytes for the property ID 
1014
         */
1015
        ctx->space_needed += 6;
1016
    }
1017
}
1018
1019
/*
1020
 *   enumeration callback - rebuild phase 2: write the entries to the
1021
 *   image buffer 
1022
 */
1023
void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0)
1024
{
1025
    rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
1026
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1027
    vm_dict_entry *cur;
1028
    size_t cnt;
1029
    char *p;
1030
    size_t rem;
1031
1032
    /* count the entries in our list */
1033
    for (cnt = 0, cur = entry->get_head() ; cur != 0 ;
1034
         cur = cur->nxt_, ++cnt) ;
1035
1036
    /* write the length of the key string followed by the key string */
1037
    *ctx->dst++ = (char)entry->getlen();
1038
    memcpy(ctx->dst, entry->getstr(), entry->getlen());
1039
1040
    /* xor the key string's bytes */
1041
    for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ;
1042
         --rem, ++p)
1043
        *p ^= 0xBD;
1044
1045
    /* move past the key string */
1046
    ctx->dst += entry->getlen();
1047
1048
    /* write the item count */
1049
    oswp2(ctx->dst, cnt);
1050
    ctx->dst += 2;
1051
1052
    /* write the items */
1053
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1054
    {
1055
        /* write the object ID and property ID for this item */
1056
        oswp4(ctx->dst, (ulong)cur->obj_);
1057
        oswp2(ctx->dst + 4, (uint)cur->prop_);
1058
        ctx->dst += 6;
1059
    }
1060
}
1061
1062
/*
1063
 *   callback context for constant data conversion 
1064
 */
1065
struct cvt_const_ctx
1066
{
1067
    /* constant mapper */
1068
    CVmConstMapper *mapper;
1069
};
1070
1071
/* 
1072
 *   convert to constant data 
1073
 */
1074
void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1075
                                       vm_obj_id_t self)
1076
{
1077
    cvt_const_ctx ctx;
1078
    
1079
    /* make sure the comparator object isn't mappable to a constant */
1080
    if (mapper->get_pool_addr(get_ext()->comparator_) != 0)
1081
        err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1082
                    "<comparator>", 12);
1083
1084
    /* 
1085
     *   Go through our dictionary and make sure we don't have any
1086
     *   references to constant data.  We don't actually have to perform
1087
     *   any conversions, because we simply don't allow references to
1088
     *   anything but TADS-object objects in the dictionary.  
1089
     */
1090
    ctx.mapper = mapper;
1091
    get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx);
1092
}
1093
1094
/*
1095
 *   enumeration callback - convert to constant data 
1096
 */
1097
void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0)
1098
{
1099
    cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0;
1100
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
1101
    vm_dict_entry *cur;
1102
1103
    /* inspect the items in this entry's list */
1104
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
1105
    {
1106
        /* if this item refers to constant data, it's an error */
1107
        if (ctx->mapper->get_pool_addr(cur->obj_) != 0)
1108
            err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
1109
                        entry->getstr(), entry->getlen());
1110
    }
1111
}
1112
1113
/* ------------------------------------------------------------------------ */
1114
/*
1115
 *   Grammar production object - image rebuilding operations 
1116
 */
1117
1118
/* 
1119
 *   rebuild for image file - we can't change at run-time, so we must
1120
 *   simply copy our image file data back out unchanged 
1121
 */
1122
ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen)
1123
{
1124
    /* make sure we have room */
1125
    if (get_ext()->image_data_size_ > buflen)
1126
        return get_ext()->image_data_size_;
1127
1128
    /* copy the data */
1129
    memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_);
1130
1131
    /* return the size */
1132
    return get_ext()->image_data_size_;
1133
}
1134
1135
/* ------------------------------------------------------------------------ */
1136
/*
1137
 *   BigNumber Metaclass implementation - image rebuilding 
1138
 */
1139
1140
/*
1141
 *   Build an image file record
1142
 */
1143
ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen)
1144
{
1145
    size_t copy_size;
1146
1147
    /* calculate how much space we need to store the data */
1148
    copy_size = calc_alloc(get_prec(ext_));
1149
1150
    /* make sure we have room for our data */
1151
    if (copy_size > buflen)
1152
        return copy_size;
1153
1154
    /* copy the data */
1155
    memcpy(buf, ext_, copy_size);
1156
1157
    /* return the size */
1158
    return copy_size;
1159
}
1160
1161
/*
1162
 *   Reserve space in a rebuilt constant pool for converting our data to a
1163
 *   constant value.
1164
 */
1165
void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper,
1166
                                      vm_obj_id_t self)
1167
{
1168
    /* BigNumber values cannot be converted to constants */
1169
}
1170
1171
1172
/*
1173
 *   Convert to constant data.  We don't reference any other objects, so
1174
 *   we simply need to store our data in the constant pool, if we
1175
 *   successfully reserved an address for it.  
1176
 */
1177
void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1178
                                         vm_obj_id_t self)
1179
{
1180
    /* 
1181
     *   if we managed to convert our object to constant data, store our
1182
     *   value 
1183
     */
1184
    if (mapper->get_pool_addr(self))
1185
        mapper->store_data(self, ext_, calc_alloc(get_prec(ext_)));
1186
}
1187
1188
1189
/* ------------------------------------------------------------------------ */
1190
/*
1191
 *   Object-to-Constant mapper 
1192
 */
1193
1194
/*
1195
 *   initialize 
1196
 */
1197
CVmConstMapper::CVmConstMapper(VMG0_)
1198
{
1199
    vm_obj_id_t max_id;
1200
    size_t i;
1201
    
1202
    /* get the maximum object ID we've allocated */
1203
    max_id = G_obj_table->get_max_used_obj_id();
1204
1205
    /* 
1206
     *   Allocate our master translation page list so that we have room
1207
     *   for enough pages to store the maximum object ID.  We need one
1208
     *   slot for each page of 1024 translation elements.  
1209
     */
1210
    obj_addr_cnt_ = max_id / 1024;
1211
    obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0]));
1212
    if (obj_addr_ == 0)
1213
        err_throw(VMERR_OUT_OF_MEMORY);
1214
1215
    /* clear out the page slots - we haven't allocated any pages yet */
1216
    for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1217
        obj_addr_[i] = 0;
1218
1219
    /* get the constant pool's page size */
1220
    page_size_ = G_const_pool->get_page_size();
1221
1222
    /* 
1223
     *   our first page is the next page after the last page in the
1224
     *   current pool 
1225
     */
1226
    first_page_idx_ = G_const_pool->get_page_count();
1227
1228
    /* 
1229
     *   get the starting address - we'll start writing our data at the
1230
     *   first page after all existing pages in the pool 
1231
     */
1232
    base_addr_ = page_size_ * G_const_pool->get_page_count();
1233
1234
    /* allocate from our base address */
1235
    next_free_ = base_addr_;
1236
1237
    /* we have the entire first page available */
1238
    rem_ = page_size_;
1239
1240
    /* 
1241
     *   we haven't allocated any page data yet (we'll do this after we've
1242
     *   reserved all space, when the client invokes
1243
     *   prepare_to_store_data()) 
1244
     */
1245
    pages_ = 0;
1246
    pages_cnt_ = 0;
1247
}
1248
1249
/*
1250
 *   delete 
1251
 */
1252
CVmConstMapper::~CVmConstMapper()
1253
{
1254
    size_t i;
1255
    
1256
    /* delete each page in our mapping table */
1257
    for (i = 0 ; i < obj_addr_cnt_ ; ++i)
1258
    {
1259
        /* free this page if we allocated it */
1260
        if (obj_addr_[i] != 0)
1261
            t3free(obj_addr_[i]);
1262
    }
1263
1264
    /* delete our mapping table */
1265
    t3free(obj_addr_);
1266
1267
    /* delete each constant pool page */
1268
    for (i = 0 ; i < pages_cnt_ ; ++i)
1269
        t3free(pages_[i]);
1270
1271
    /* free the page list */
1272
    if (pages_ != 0)
1273
        t3free(pages_);
1274
}
1275
1276
/*
1277
 *   Get an object's pool address, if it has one 
1278
 */
1279
ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id)
1280
{
1281
    /* determine if the page is mapped - if not, the object isn't mapped */
1282
    if (obj_addr_[obj_id / 1024] == 0)
1283
        return 0;
1284
1285
    /* return the mapping entry */
1286
    return obj_addr_[obj_id / 1024][obj_id % 1024];
1287
}
1288
1289
1290
/*
1291
 *   Allocate space in the pool for an object's data
1292
 */
1293
ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len)
1294
{
1295
    ulong ret;
1296
    
1297
    /* 
1298
     *   if the data block is too large to fit on a constant pool page, we
1299
     *   can't store it 
1300
     */
1301
    if (len > page_size_)
1302
        return 0;
1303
    
1304
    /* if the translation page isn't mapped yet, map it */
1305
    if (obj_addr_[obj_id / 1024] == 0)
1306
    {
1307
        /* allocate a new page */
1308
        obj_addr_[obj_id / 1024] =
1309
            (ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0]));
1310
1311
        /* if that failed, throw an error */
1312
        if (obj_addr_[obj_id / 1024] == 0)
1313
            err_throw(VMERR_OUT_OF_MEMORY);
1314
1315
        /* clear the new page */
1316
        memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0]));
1317
    }
1318
1319
    /* if the block doesn't fit on this page, skip to the next page */
1320
    if (len > rem_)
1321
    {
1322
        /* skip past the remainder of this page */
1323
        next_free_ += rem_;
1324
1325
        /* we have the whole next page available now */
1326
        rem_ = page_size_;
1327
    }
1328
1329
    /* remember the object ID's address in the translation list */
1330
    ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_;
1331
1332
    /* skip past the data */
1333
    next_free_ += len;
1334
    rem_ -= len;
1335
1336
    /* return the new address */
1337
    return ret;
1338
}
1339
1340
/*
1341
 *   Prepare to begin storing data 
1342
 */
1343
void CVmConstMapper::prepare_to_store_data()
1344
{
1345
    size_t i;
1346
    
1347
    /* figure out how many pages we need */
1348
    pages_cnt_ = calc_page_count();
1349
1350
    /* allocate space for the list of pages */
1351
    pages_ = (vm_const_mapper_page **)
1352
             t3malloc(pages_cnt_ * sizeof(pages_[0]));
1353
    if (pages_ == 0)
1354
        err_throw(VMERR_OUT_OF_MEMORY);
1355
1356
    /* allocate each page */
1357
    for (i = 0 ; i < pages_cnt_ ; ++i)
1358
    {
1359
        /* 
1360
         *   allocate this page - allocate the header structure plus space
1361
         *   for the page data 
1362
         */
1363
        pages_[i] = (vm_const_mapper_page *)
1364
                    t3malloc(sizeof(pages_[i]) + page_size_);
1365
        if (pages_[i] == 0)
1366
            err_throw(VMERR_OUT_OF_MEMORY);
1367
1368
        /* the page has nothing stored yet */
1369
        pages_[i]->max_ofs_used = 0;
1370
    }
1371
}
1372
1373
/*
1374
 *   Store an object's data 
1375
 */
1376
void CVmConstMapper::store_data(vm_obj_id_t obj_id,
1377
                                const void *ptr, size_t len)
1378
{
1379
    ulong addr;
1380
    size_t page_idx;
1381
    size_t page_ofs;
1382
1383
    /* get the pool address that was reserved for the object */
1384
    addr = get_pool_addr(obj_id);
1385
1386
    /* if the object had no space reserved, ignore the request */
1387
    if (addr == 0)
1388
        return;
1389
1390
    /* figure out which page this address is in, and the offset in the page */
1391
    page_idx = (size_t)((addr - base_addr_) / page_size_);
1392
    page_ofs = (size_t)((addr - base_addr_) % page_size_);
1393
1394
    /* 
1395
     *   if this address takes us above the high-water mark for the page,
1396
     *   move the page's marker accordingly 
1397
     */
1398
    if (page_ofs + len > pages_[page_idx]->max_ofs_used)
1399
        pages_[page_idx]->max_ofs_used = page_ofs + len;
1400
1401
    /* copy the data */
1402
    memcpy(pages_[page_idx]->buf + page_ofs, ptr, len);
1403
}
1404
1405
/*
1406
 *   Write the pool pages to an image file 
1407
 */
1408
void CVmConstMapper::write_to_image_file(CVmImageWriter *writer,
1409
                                         uchar xor_mask)
1410
{
1411
    size_t i;
1412
    
1413
    /* go through each of our pages */
1414
    for (i = 0 ; i < pages_cnt_ ; ++i)
1415
    {
1416
        /* write this page - it's in pool 2 (the constant data pool) */
1417
        writer->write_pool_page(2, first_page_idx_ + i,
1418
                                pages_[i]->buf, pages_[i]->max_ofs_used,
1419
                                TRUE, xor_mask);
1420
    }
1421
}
1422
1423
/* ------------------------------------------------------------------------ */
1424
/*
1425
 *   metaclass table - image rewriter implementation 
1426
 */
1427
1428
/*
1429
 *   write the new metaclass dependency table 
1430
 */
1431
void CVmMetaTable::rebuild_image(CVmImageWriter *writer)
1432
{
1433
    size_t i;
1434
    
1435
    /* begin the new metaclass dependency table */
1436
    writer->begin_meta_dep(get_count());
1437
1438
    /* write the new metaclass dependency table items */
1439
    for (i = 0 ; i < get_count() ; ++i)
1440
    {
1441
        vm_meta_entry_t *entry;
1442
        ushort j;
1443
1444
        /* get this entry */
1445
        entry = get_entry(i);
1446
        
1447
        /* write this metaclass name */
1448
        writer->write_meta_dep_item(entry->image_meta_name_);
1449
1450
        /* 
1451
         *   Write the property translation list.  Note that xlat_func()
1452
         *   requires a 1-based index, so we loop from 1 to the count,
1453
         *   rather than following the usual C-style 0-based conventions.  
1454
         */
1455
        for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
1456
            writer->write_meta_item_prop(entry->xlat_func(j));
1457
    }
1458
    
1459
    /* end the metaclass dependency table */
1460
    writer->end_meta_dep();
1461
}
1462
1463
1464
/* ------------------------------------------------------------------------ */
1465
/*
1466
 *   Hashtable Metaclass implementation - image rebuilding 
1467
 */
1468
1469
/*
1470
 *   Build an image file record 
1471
 */
1472
ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1473
{
1474
    size_t copy_size;
1475
    vm_lookup_ext *ext = get_ext();
1476
    uint i;
1477
    vm_lookup_val **bp;
1478
    vm_lookup_val *val;
1479
    char *dst;
1480
    uint idx;
1481
1482
    /* 
1483
     *   we need space for the fixed header (6 bytes), 2 bytes per bucket,
1484
     *   and the entries themselves 
1485
     */
1486
    copy_size = 6
1487
                + (ext->bucket_cnt * 2)
1488
                + (ext->value_cnt * VMLOOKUP_VALUE_SIZE);
1489
1490
    /* make sure we have room for our data */
1491
    if (copy_size > buflen)
1492
        return copy_size;
1493
1494
    /* write the fixed data */
1495
    oswp2(buf, ext->bucket_cnt);
1496
    oswp2(buf + 2, ext->value_cnt);
1497
    idx = ext->val_to_img_idx(ext->first_free);
1498
    oswp2(buf + 4, idx);
1499
    dst = buf + 6;
1500
1501
    /* write the buckets */
1502
    for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp)
1503
    {
1504
        /* write this bucket's index */
1505
        idx = ext->val_to_img_idx(*bp);
1506
        oswp2(dst, idx);
1507
        dst += 2;
1508
    }
1509
1510
    /* write the values */
1511
    for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val)
1512
    {
1513
        /* store the key, value, and index */
1514
        vmb_put_dh(dst, &val->key);
1515
        vmb_put_dh(dst + VMB_DATAHOLDER, &val->val);
1516
        idx = ext->val_to_img_idx(val->nxt);
1517
        oswp2(dst + VMB_DATAHOLDER*2, idx);
1518
1519
        /* skip the data in the output */
1520
        dst += VMLOOKUP_VALUE_SIZE;
1521
    }
1522
1523
    /* return the size */
1524
    return copy_size;
1525
}
1526
1527
/*
1528
 *   Convert to constant data.  We must convert each object we reference in
1529
 *   a key or in a value to constant data if possible.
1530
 */
1531
void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1532
                                              vm_obj_id_t self)
1533
{
1534
    uint i;
1535
    vm_lookup_val *entry;
1536
1537
    /* run through each entry */
1538
    for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ;
1539
         i != 0 ; --i, ++entry)
1540
    {
1541
        /* convert the key */
1542
        convert_val_to_const_data(vmg_ mapper, &entry->key);
1543
1544
        /* convert the value */
1545
        convert_val_to_const_data(vmg_ mapper, &entry->val);
1546
    }
1547
}
1548
1549
/* ------------------------------------------------------------------------ */
1550
/*
1551
 *   IntrinsicClass Metaclass implementation - image rebuilding 
1552
 */
1553
1554
/*
1555
 *   Build an image file record 
1556
 */
1557
ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen)
1558
{
1559
    size_t copy_size;
1560
1561
    /* get our size */
1562
    copy_size = osrp2(ext_);
1563
1564
    /* make sure we have room for our data */
1565
    if (copy_size > buflen)
1566
        return copy_size;
1567
1568
    /* copy the data */
1569
    memcpy(buf, ext_, copy_size);
1570
1571
    /* return the size */
1572
    return copy_size;
1573
}
1574
1575
/* ------------------------------------------------------------------------ */
1576
/*
1577
 *   Indexed Iterator
1578
 */
1579
1580
/*
1581
 *   Build an image file record 
1582
 */
1583
ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen)
1584
{
1585
    size_t copy_size;
1586
1587
    /* calculate our data size - just store our entire extension */
1588
    copy_size = VMOBJITERIDX_EXT_SIZE;
1589
1590
    /* make sure we have room for our data */
1591
    if (copy_size > buflen)
1592
        return copy_size;
1593
1594
    /* copy the data */
1595
    memcpy(buf, ext_, copy_size);
1596
1597
    /* return the size */
1598
    return copy_size;
1599
}
1600
1601
/*
1602
 *   Convert to constant data.  We must convert our collection object
1603
 *   reference to constant data if possible.  
1604
 */
1605
void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1606
                                          vm_obj_id_t self)
1607
{
1608
    /* convert our collection reference to constant data if possible */
1609
    convert_dh_to_const_data(vmg_ mapper, ext_);
1610
}
1611
1612
1613
/* ------------------------------------------------------------------------ */
1614
/*
1615
 *   Hashtable Iterator 
1616
 */
1617
1618
/*
1619
 *   Build an image file record 
1620
 */
1621
ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
1622
{
1623
    size_t copy_size;
1624
1625
    /* calculate our data size - just store our entire extension */
1626
    copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE;
1627
1628
    /* make sure we have room for our data */
1629
    if (copy_size > buflen)
1630
        return copy_size;
1631
1632
    /* copy the data */
1633
    memcpy(buf, ext_, copy_size);
1634
1635
    /* return the size */
1636
    return copy_size;
1637
}
1638
1639
/*
1640
 *   Convert to constant data.  We must convert our collection object
1641
 *   reference to constant data if possible.  
1642
 */
1643
void CVmObjIterLookupTable::convert_to_const_data(
1644
    VMG_ CVmConstMapper *mapper, vm_obj_id_t self)
1645
{
1646
    /* convert our collection reference to constant data if possible */
1647
    convert_dh_to_const_data(vmg_ mapper, ext_);
1648
}
1649
1650
1651
/* ------------------------------------------------------------------------ */
1652
/*
1653
 *   Vector Metaclass implementation - image rebuilding 
1654
 */
1655
1656
/*
1657
 *   Build an image file record 
1658
 */
1659
ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen)
1660
{
1661
    size_t copy_size;
1662
    size_t ele_cnt;
1663
    size_t alloc_cnt;
1664
1665
    /* 
1666
     *   calculate how much space we need to store the data - store only
1667
     *   the data, not the undo bits 
1668
     */
1669
    ele_cnt = get_element_count();
1670
    alloc_cnt = get_allocated_count();
1671
    copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt);
1672
1673
    /* make sure we have room for our data */
1674
    if (copy_size > buflen)
1675
        return copy_size;
1676
1677
    /* save the allocation count and in-use element count */
1678
    vmb_put_len(buf, alloc_cnt);
1679
    vmb_put_len(buf + VMB_LEN, ele_cnt);
1680
1681
    /* copy the element data */
1682
    memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt));
1683
1684
    /* return the size */
1685
    return copy_size;
1686
}
1687
1688
/*
1689
 *   Reserve space in a rebuilt constant pool for converting our data to a
1690
 *   constant value.
1691
 */
1692
void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper,
1693
                                      vm_obj_id_t self)
1694
{
1695
    /* Vector values cannot be converted to constants */
1696
}
1697
1698
1699
/*
1700
 *   Convert to constant data.  We must convert each object we reference to
1701
 *   constant data if possible; we use the same algorithm as CVmObjList.  
1702
 */
1703
void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1704
                                        vm_obj_id_t self)
1705
{
1706
    size_t cnt;
1707
    char *p;
1708
1709
    /* get my element count */
1710
    cnt = get_element_count();
1711
1712
    /* mark as referenced each object in our list */
1713
    for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
1714
    {
1715
        /* convert the value to constant data if possible */
1716
        convert_dh_to_const_data(vmg_ mapper, p);
1717
    }
1718
}
1719
1720
/* ------------------------------------------------------------------------ */
1721
/*
1722
 *   ByteArray Metaclass implementation - image rebuilding 
1723
 */
1724
1725
/*
1726
 *   Build an image file record 
1727
 */
1728
ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen)
1729
{
1730
    ulong copy_size;
1731
    ulong rem;
1732
    ulong idx;
1733
1734
    /* we need four bytes for our count plus space for our byte array */
1735
    copy_size = 4 + get_element_count();
1736
1737
    /* make sure we have room for our data */
1738
    if (copy_size > buflen)
1739
        return copy_size;
1740
1741
    /* store our element count */
1742
    oswp4(buf, get_element_count());
1743
    buf += 4;
1744
1745
    /* copy our data in chunks */
1746
    for (idx = 1, rem = get_element_count() ; rem != 0 ; )
1747
    {
1748
        unsigned char *p;
1749
        size_t avail;
1750
        size_t chunk;
1751
1752
        /* get the next chunk */
1753
        p = get_ele_ptr(idx, &avail);
1754
1755
        /* limit copying to the remaining size */
1756
        chunk = avail;
1757
        if (chunk > rem)
1758
            chunk = rem;
1759
1760
        /* store this chunk */
1761
        memcpy(buf, p, chunk);
1762
1763
        /* skip this chunk */
1764
        buf += chunk;
1765
        idx += chunk;
1766
        rem -= chunk;
1767
    }
1768
1769
    /* return the size */
1770
    return copy_size;
1771
}
1772
1773
/* ------------------------------------------------------------------------ */
1774
/*
1775
 *   CharacterSet Metaclass implementation - image rebuilding 
1776
 */
1777
1778
/*
1779
 *   Build an image file record 
1780
 */
1781
ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen)
1782
{
1783
    ulong copy_size;
1784
1785
    /* we need two bytes for the name length count plus the name's bytes */
1786
    copy_size = 2 + get_ext_ptr()->charset_name_len;
1787
1788
    /* make sure we have room for our data */
1789
    if (copy_size > buflen)
1790
        return copy_size;
1791
1792
    /* store the length of the name, and the name itself */
1793
    oswp2(buf, get_ext_ptr()->charset_name_len);
1794
    memcpy(buf + 2, get_ext_ptr()->charset_name,
1795
           get_ext_ptr()->charset_name_len);
1796
1797
    /* return the size */
1798
    return copy_size;
1799
}
1800
1801
/* ------------------------------------------------------------------------ */
1802
/*
1803
 *   File Metaclass implementation - image rebuilding 
1804
 */
1805
1806
/*
1807
 *   Build an image file record 
1808
 */
1809
ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen)
1810
{
1811
    ulong copy_size;
1812
1813
    /* 
1814
     *   we need the character set object ID, the mode byte, the access
1815
     *   byte, and the uint32 flags 
1816
     */
1817
    copy_size = VMB_OBJECT_ID + 1 + 1 + 4;
1818
1819
    /* make sure we have room for our data */
1820
    if (copy_size > buflen)
1821
        return copy_size;
1822
1823
    /* store the character set object ID */
1824
    vmb_put_objid(buf, get_ext()->charset);
1825
    buf += VMB_OBJECT_ID;
1826
1827
    /* store the mode and access values */
1828
    *buf++ = get_ext()->mode;
1829
    *buf++ = get_ext()->access;
1830
1831
    /* store the flags */
1832
    oswp4(buf, get_ext()->flags);
1833
1834
    /* return the size */
1835
    return copy_size;
1836
}
1837
1838
/* ------------------------------------------------------------------------ */
1839
/*
1840
 *   Pattern metaclass 
1841
 */
1842
1843
/*
1844
 *   build an image file record for the object 
1845
 */
1846
ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen)
1847
{
1848
    size_t need_size;
1849
1850
    /* we need a DATAHOLDER to store the original pattern string reference */
1851
    need_size = VMB_DATAHOLDER;
1852
1853
    /* if we need more space, just return our size requirements */
1854
    if (need_size > buflen)
1855
        return need_size;
1856
1857
    /* write our value */
1858
    vmb_put_dh(buf, get_orig_str());
1859
1860
    /* return our size */
1861
    return need_size;
1862
}
1863
1864
/*
1865
 *   Convert to constant data.
1866
 */
1867
void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper,
1868
                                          vm_obj_id_t /*self*/)
1869
{
1870
    /* check our original source string value */
1871
    convert_val_to_const_data(vmg_ mapper, &get_ext()->str);
1872
}
1873
1874
/* ------------------------------------------------------------------------ */
1875
/*
1876
 *   StringComparator intrinsic class 
1877
 */
1878
1879
/*
1880
 *   build the image data 
1881
 */
1882
ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen)
1883
{
1884
    /* set up a stream writer on the buffer, and write it out */
1885
    CVmMemoryStream str(buf, buflen);
1886
    return write_to_stream(vmg_ &str, &buflen);
1887
}
1888