cfad47cfa3/tads3/vmobj.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/VMOBJ.H,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $ */
2
3
/* 
4
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   Please see the accompanying license file, LICENSE.TXT, for information
7
 *   on using and copying this software.  
8
 */
9
/*
10
Name
11
  vmobj.h - VM object memory manager
12
Function
13
  
14
Notes
15
  
16
Modified
17
  10/20/98 MJRoberts  - Creation
18
*/
19
20
#ifndef VMOBJ_H
21
#define VMOBJ_H
22
23
#include <stdlib.h>
24
#include <memory.h>
25
26
#include "vmglob.h"
27
#include "vmtype.h"
28
#include "vmerr.h"
29
#include "vmerrnum.h"
30
31
32
/* ------------------------------------------------------------------------ */
33
/*
34
 *   If the VM_REGISTER_METACLASS macro hasn't been defined, define it
35
 *   now.  This macro is defined different ways on different inclusions,
36
 *   but when it's not defined, it should always be defined to do nothing.
37
 */
38
#ifndef VM_REGISTER_METACLASS
39
# define VM_REGISTER_METACLASS(metaclass)
40
#endif
41
42
/* ------------------------------------------------------------------------ */
43
/*
44
 *   NOTE TO METACLASS IMPLEMENTORS - each final concrete class derived
45
 *   from CVmObject that is to be available to VM programs for static
46
 *   image file loading and/or dynamic creation (via the "NEW"
47
 *   instructions) must specify a VM_REGISTER_METACLASS declaration to
48
 *   register the metaclass.  This declaration must occur in the
49
 *   metaclass's header file, and must be OUTSIDE of the region protected
50
 *   against multiple inclusion.  The definition should look like this:
51
 *   
52
 *   VM_REGISTER_METACLASS("metaclass-string-name", CVmClassName)
53
 *   
54
 *   The string name must be the universally unique metaclass identifier
55
 *   string registered with the T3 VM Specification Maintainer.  The class
56
 *   name is the C++ class (derived from CVmObject).
57
 *   
58
 *   Each CVmObject subclass must define certain static construction
59
 *   methods in addition to the virtual methods defined as abstract in
60
 *   CVmObject.  See the comments on CVmObject for details.
61
 */
62
63
64
/* ------------------------------------------------------------------------ */
65
/*
66
 *   To get a pointer to an object (CVmObject *) given an object ID
67
 *   (vm_obj_id_t), use this:
68
 *   
69
 *   CVmObject *obj = vm_objp(vmg_ id);
70
 *   
71
 *   To allocate a new object:
72
 *   
73
 *   vm_obj_id_t id = vm_newid(vmg_ in_root_set);
74
 *.  CVmObject *obj = new (vmg_ id) CVmObjXxx(constructor params);
75
 *   
76
 *   The functions vm_objp() and vm_newid() are defined later in this
77
 *   file.  
78
 */
79
80
81
/* ------------------------------------------------------------------------ */
82
/*
83
 *   Garbage collector work increment.  This is the number of objects that
84
 *   the GC will process on each call to gc_pass_queue().
85
 *   
86
 *   The point of running the GC incrementally is to allow GC work to be
87
 *   interleaved with long-running user I/O operations (such as reading a
88
 *   line of text from the keyboard) in the foreground thread, so the work
89
 *   increment should be chosen so that each call to this routine
90
 *   completes quickly enough that the user will perceive no delay.
91
 *   However, making this number too small will introduce additional
92
 *   overhead by making an excessive number of function calls.  
93
 */
94
const int VM_GC_WORK_INCREMENT = 500;
95
96
97
98
/* ------------------------------------------------------------------------ */
99
/* 
100
 *   flag values for propDefined 
101
 */
102
#define VMOBJ_PROPDEF_ANY           1
103
#define VMOBJ_PROPDEF_DIRECTLY      2
104
#define VMOBJ_PROPDEF_INHERITS      3
105
#define VMOBJ_PROPDEF_GET_CLASS     4
106
107
108
/* ------------------------------------------------------------------------ */
109
/*
110
 *   Objects are composed of two parts.  The first is a fixed-size
111
 *   portion, or header, which consists of an abstract interface (in terms
112
 *   of stored data, this is simply a vtable pointer) and an extension
113
 *   pointer.  The extension pointer points to the variable-size second
114
 *   part of the object, which contains all of the additional instance
115
 *   data for the object.  
116
 */
117
118
/* ------------------------------------------------------------------------ */
119
/*
120
 *   
121
 *   The fixed-size parts, or object headers, are allocated from a set of
122
 *   arrays.  A master array has a pointer to the sub-arrays, and each
123
 *   sub-array has a fixed number of slots for the fixed-size parts.
124
 *   Thus, it's very fast to find an object header given an object ID.
125
 *   
126
 *   Because each fixed-size part has an abstract interface, we can have
127
 *   multiple implementations for the objects.  This would allow us, for
128
 *   example, to include native objects (with an appropriate interface) as
129
 *   though they were normal VM objects.  It also allows us to have
130
 *   different types of implementations for VM objects.
131
 *   
132
 *   The fixed-size object parts never move in memory, so we can refer to
133
 *   them with pointers.  We can efficiently re-use space as object
134
 *   headers are allocated and deleted, because a new object will always
135
 *   take up exactly the same size as a previous object whose space was
136
 *   freed.
137
 *   
138
 *   The fixed-size objects are always allocated within the page table,
139
 *   thus operator new is overridden to allocate memory within the page
140
 *   table.  
141
 */
142
/*
143
 *   In addition to the virtual methods listed, every object must define a
144
 *   data member as follows:
145
 *   
146
 *   public: static class CVmMetaclass *metaclass_reg_;
147
 *   
148
 *   This must be a static singleton instance of the CVmMetaclass subclass
149
 *   (see below) for the CVmObject subclass.  Each CVmObject subclass must
150
 *   have a corresponding CVmMetaclass subclass; the singleton member
151
 *   variable metaclass_reg_ provides the registration table entry that
152
 *   allows instances of this object to be dynamically linked from the
153
 *   image file.  
154
 */
155
class CVmObject
156
{
157
    friend class CVmVarHeap;
158
    
159
public:
160
    /* metaclass registration object (for the root object implementation) */
161
    static class CVmMetaclass *metaclass_reg_;
162
163
    /*
164
     *   Default implementation for calling a static property.  We don't
165
     *   have any static properties at this level, so we'll simply return
166
     *   false to indicate that the property wasn't evaluated.  
167
     */
168
    static int call_stat_prop(VMG_ vm_val_t *retval, const uchar **pc_ptr,
169
                              uint *argc, vm_prop_id_t);
170
171
    /* get the registration object for this metaclass */
172
    virtual class CVmMetaclass *get_metaclass_reg() const
173
        { return metaclass_reg_; }
174
175
    /* 
176
     *   Is this object of the given metaclass?  Returns true if the
177
     *   object is an instance of 'meta' or inherits from the metaclass.
178
     *   Each object class must override this.  
179
     */
180
    virtual int is_of_metaclass(class CVmMetaclass *meta) const
181
        { return (meta == metaclass_reg_); }
182
183
    /* 
184
     *   Receive notification that this object is being deleted - the
185
     *   garbage collector calls this function when the object is
186
     *   unreachable.
187
     *   
188
     *   Note that we don't use the real destructor, since we use our own
189
     *   memory management; instead, we have this virtual finalizer that
190
     *   we explicitly call when it's time to delete the object.  (This
191
     *   isn't entirely symmetrical with the overridden operator new, but
192
     *   the GC is the only code that can delete objects, and this saves
193
     *   us the trouble of overriding operator delete for the object.)  
194
     */
195
    virtual void notify_delete(VMG_ int in_root_set) = 0;
196
197
    /*
198
     *   Create an instance of this class.  If this object does not
199
     *   represent a class, or cannot be instanced, throw an error.  By
200
     *   default, objects cannot be instanced, so we'll simply throw an
201
     *   error.  If successful, leaves the new object in register R0.
202
     *   
203
     *   Parameters to the constructor are passed on the VM stack; 'argc'
204
     *   gives the number of arguments on the stack.  This routine will
205
     *   remove the arguments from the stack before returning.  
206
     */
207
    virtual void create_instance(VMG_ vm_obj_id_t self,
208
                                 const uchar **pc_ptr, uint argc)
209
    {
210
        /* throw the error */
211
        err_throw(VMERR_CANNOT_CREATE_INST);
212
    }
213
214
    /*
215
     *   Determine if the object has a non-trivial finalizer.  Returns
216
     *   true if the object has a non-trivial finalizer, false if it has
217
     *   no finalizer or the finalizer is trivial and hence can be
218
     *   ignored.  We'll return false by default.  
219
     */
220
    virtual int has_finalizer(VMG_ vm_obj_id_t /*self*/)
221
        { return FALSE; }
222
223
    /*
224
     *   Invoke the object's finalizer.  This need do nothing if the
225
     *   object does not define or inherit a finalizer method.  Any
226
     *   exceptions thrown in the course of executing the finalizer should
227
     *   be caught and discarded.  By default, we'll do nothing at all.
228
     */
229
    virtual void invoke_finalizer(VMG_ vm_obj_id_t /*self*/) { }
230
231
    /*
232
     *   Determine if this is a class object.  This returns true if this
233
     *   object is a class, false if it's an instance.  
234
     */
235
    virtual int is_class_object(VMG_ vm_obj_id_t /*self*/) const
236
        { return FALSE; }
237
238
    /*
239
     *   Determine if this object is an instance of another object.
240
     *   Returns true if this object derives from the other object,
241
     *   directly or indirectly.  If this object derives from an object
242
     *   which in turn derives from the given object, then this object
243
     *   derives (indirectly) from the given object.  
244
     */
245
    virtual int is_instance_of(VMG_ vm_obj_id_t obj);
246
247
    /* 
248
     *   Get the number of superclasses of the object, and get the nth
249
     *   superclass.  By default, we have one superclass, which is the
250
     *   IntrinsicClass object that represents this metaclass.  
251
     */
252
    virtual int get_superclass_count(VMG_ vm_obj_id_t /*self*/) const
253
        { return 1; }
254
    virtual vm_obj_id_t get_superclass(VMG_ vm_obj_id_t /*self*/,
255
                                       int /*superclass_index*/) const;
256
257
    /*
258
     *   Determine if the object has properties that can be enumerated.
259
     *   Returns true if so, false if not.  Note that this should return
260
     *   true for an object of a type that provides properties, even if
261
     *   the instance happens to have zero properties.  
262
     */
263
    virtual int provides_props(VMG0_) const { return FALSE; }
264
265
    /*
266
     *   Enumerate properties of the object.  Invoke the callback for each
267
     *   property.  The callback is not permitted to make any changes to
268
     *   the object or its properties and should not invoke garbage
269
     *   collection.  
270
     */
271
    virtual void enum_props(VMG_ vm_obj_id_t self,
272
                            void (*cb)(VMG_ void *ctx,
273
                                       vm_obj_id_t self, vm_prop_id_t prop,
274
                                       const vm_val_t *val),
275
                            void *cbctx)
276
    {
277
        /* by default, assume we have nothing to enumerate */
278
    }
279
280
    /* set a property value */
281
    virtual void set_prop(VMG_ class CVmUndo *undo,
282
                          vm_obj_id_t self, vm_prop_id_t prop,
283
                          const vm_val_t *val) = 0;
284
285
    /* 
286
     *   Get a property value.  We do not evaluate the property, but
287
     *   merely get the raw value; thus, if the property value is of type
288
     *   code, we simply retrieve the code offset pointer.  Returns true
289
     *   if the property was found, false if not.
290
     *   
291
     *   If we find the object, we'll set *source_obj to the ID of the
292
     *   object in which we found the object.  If the property was
293
     *   supplied by this object directly, we'll simply set source_id to
294
     *   self; if we inherited the property from a superclass, we'll set
295
     *   *source_id to the ID of the superclass object that actually
296
     *   supplied the value.
297
     *   
298
     *   If 'argc' is not null, this function can consume arguments from
299
     *   the run-time stack, and set *argc to zero to indicate that the
300
     *   arguments have been consumed.
301
     *   
302
     *   If 'argc' is null, and evaluating the property would involve
303
     *   running system code (with or without consuming arguments), this
304
     *   function should return TRUE, but should NOT run the system code -
305
     *   instead, set val->typ to VM_NATIVE_CODE to indicate that the
306
     *   value is a "native code" value (i.e., evaluating it requires
307
     *   executing system code).  
308
     */
309
    virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
310
                         vm_obj_id_t self, vm_obj_id_t *source_obj,
311
                         uint *argc);
312
313
    /*
314
     *   Inherit a property value.  This works similarly to get_prop, but
315
     *   finds an inherited definition of the property, as though
316
     *   orig_target_obj.prop (and anything overriding orig_target_obj.prop)
317
     *   were undefined.
318
     *   
319
     *   In broad terms, the algorithm for this method is to do the same
320
     *   thing as get_prop(), but to ignore every definition of the property
321
     *   found in the class tree until after reaching and skipping
322
     *   orig_target_obj.prop.  Once orig_target_obj.prop is found, this
323
     *   method simply continues searching in the same manner as get_prop()
324
     *   and returns the next definition it finds.
325
     *   
326
     *   'defining_obj' is the object containing the method currently
327
     *   running.  This is not necessarily 'self', because the method
328
     *   currently running might already have been inherited from a
329
     *   superclass of 'self'.
330
     *   
331
     *   'orig_target_obj' is the object that was originally targeted for the
332
     *   get_prop() operation that invoked the calling method (or invoked the
333
     *   method that inherited the calling method, or so on).  This gives us
334
     *   the starting point in the search, so that we can continue the
335
     *   original inheritance tree search that started with get_prop().
336
     *   
337
     *   'argc' has the same meaning as for get_prop().  
338
     *   
339
     *   Objects that cannot be subclassed via byte-code can ignore this
340
     *   method.  The base class implementation follows the inheritance chain
341
     *   of "modifier" objects, which allow byte-code methods to be plugged
342
     *   in under the native code class tree.  
343
     */
344
    virtual int inh_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
345
                         vm_obj_id_t self, vm_obj_id_t orig_target_obj,
346
                         vm_obj_id_t defining_obj, vm_obj_id_t *source_obj,
347
                         uint *argc);
348
349
    /*
350
     *   Build a list of the properties directly defined on this object
351
     *   instance.  On return, retval must be filled in with the new list
352
     *   object.
353
     *   
354
     *   Note that a self-reference must be pushed by the caller to protect
355
     *   against garbage collection of self while this routine is running.
356
     *   
357
     *   Most object types do not define any properties in the instance, so
358
     *   this default implementation will simply return an empty list.
359
     *   Classes that can define properties in instances (such as
360
     *   TadsObjects), and the "intrinsic class" class that represents
361
     *   classes, must override this to build their lists.  
362
     */
363
    virtual void build_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval);
364
365
    /* 
366
     *   Mark all strongly-referenced objects.  Calls
367
     *   obj_table->mark_refs() for each referenced object.  
368
     */
369
    virtual void mark_refs(VMG_ uint state) = 0;
370
371
    /* 
372
     *   Remove stale weak references.  For each weakly-referenced object,
373
     *   check to see if the object is marked as reachable; if not, it's
374
     *   about to be deleted, so forget the weak reference. 
375
     */
376
    virtual void remove_stale_weak_refs(VMG0_) = 0;
377
378
    /*
379
     *   Receive notification that the undo manager is creating a new
380
     *   savepoint.  
381
     */
382
    virtual void notify_new_savept() = 0;
383
384
    /* 
385
     *   apply an undo record created by this object; if the record has
386
     *   any additional data associated with it (allocated by the object
387
     *   when the undo record was created), this should also discard the
388
     *   additional data 
389
     */
390
    virtual void apply_undo(VMG_ struct CVmUndoRecord *rec) = 0;
391
392
    /* 
393
     *   Discard any extra information associated with this undo record.
394
     *   Note that this will not be called if apply_undo() is called,
395
     *   since apply_undo() is expected to discard any extra information
396
     *   itself after applying the record.  
397
     */
398
    virtual void discard_undo(VMG_ struct CVmUndoRecord *) { }
399
400
    /*
401
     *   Mark an object object reference.  If this object keeps strong
402
     *   references, this should mark any object contained in the undo
403
     *   record's saved value as referenced; if this object keeps only
404
     *   weak references, this doesn't need to do anything. 
405
     */
406
    virtual void mark_undo_ref(VMG_ struct CVmUndoRecord *rec) = 0;
407
408
    /*
409
     *   Remove any stale weak reference contained in an undo record.  For
410
     *   objects with ordinary strong references, this doesn't need to do
411
     *   anything.  For objects that keep weak references to other
412
     *   objects, this should check the object referenced in the undo
413
     *   record, if any, to determine if the object is about to be
414
     *   deleted, and if so clear the undo record (by setting the object
415
     *   in the old value to 'invalid'). 
416
     */
417
    virtual void remove_stale_undo_weak_ref(VMG_
418
                                            struct CVmUndoRecord *rec) = 0;
419
420
    /*
421
     *   Post-load initialization.  This routine is called only if the object
422
     *   specifically requests this by calling request_post_load_init().
423
     *   This routine is called exactly once for each initial program load,
424
     *   restart, or restore, and is called after ALL objects are loaded.
425
     *   
426
     *   The purpose of this routine is to allow an object to perform
427
     *   initializations that depend upon other objects.  During the normal
428
     *   load/restore/reset methods, an object cannot assume that any other
429
     *   objects are already loaded, because the order of loading is
430
     *   arbitrary.  When this routine is called, it is guaranteed that all
431
     *   objects are already loaded.  
432
     */
433
    virtual void post_load_init(VMG_ vm_obj_id_t /*self*/)
434
    {
435
        /* we do nothing by default */
436
    }
437
438
    /*
439
     *   Load the object from an image file.  The object's data block is
440
     *   at the given address and has the given size.
441
     *   
442
     *   The underlying memory is owned by the image file loader.  The
443
     *   object must not attempt to deallocate this memory.  
444
     */
445
    virtual void load_from_image(VMG_ vm_obj_id_t self,
446
                                 const char *ptr, size_t siz) = 0;
447
448
    /*
449
     *   Reload the object from an image file.  The object's data block is
450
     *   at the given address and has the given size.  Discards any changes
451
     *   made since the object was loaded and restores its state as it was
452
     *   immediately after it was loaded from the image file.  By default,
453
     *   we do nothing.
454
     *   
455
     *   NOTE 1: this routine can be implemented instead of
456
     *   reset_to_image().  If an object doesn't have any other need to
457
     *   store a pointer to its image file data in its own extension, but
458
     *   the image file data is necessary to effect a reset, then this
459
     *   routine should be used, so as to avoid having to enlarge the
460
     *   object's extension for non-image instances.
461
     *   
462
     *   NOTE 2: in order to use this routine, the object MUST call the
463
     *   object table's save_image_pointer() routine during the initial
464
     *   loading (i.e., in the object's load_from_image()).  During a reset,
465
     *   the object table will only call this routine on objects whose image
466
     *   pointers were previously saved with save_image_pointer().  
467
     */
468
    virtual void reload_from_image(VMG_ vm_obj_id_t self,
469
                                   const char *ptr, size_t siz) { }
470
471
    /*
472
     *   Reset to the image file state.  Discards any changes made since the
473
     *   object was loaded and restores its state as it was immediately
474
     *   after it was loaded from the image file.  By default, we do nothing.
475
     *   
476
     *   NOTE: this routine doesn't have to do anything if
477
     *   reload_from_image() is implemented for the object.  
478
     */
479
    virtual void reset_to_image(VMG_ vm_obj_id_t /*self*/) { }
480
481
    /*
482
     *   Determine if the object has been changed since it was loaded from
483
     *   the image file.  This can only be called for objects that were
484
     *   originally loaded from the image file.  Returns true if the
485
     *   object's internal state has been changed since loading, false if
486
     *   the object is in exactly the same state that's stored in the
487
     *   image file.
488
     *   
489
     *   If this returns false, then it is not necessary to save the
490
     *   object to a saved state file, because the object's state can be
491
     *   restored simply by resetting to the image file state.  
492
     */
493
    virtual int is_changed_since_load() const { return FALSE; }
494
495
    /* 
496
     *   save this object to a file, so that it can be restored to its
497
     *   current state via restore_from_file 
498
     */
499
    virtual void save_to_file(VMG_ class CVmFile *fp) = 0;
500
501
    /* 
502
     *   Restore the state of the object from a file into which state was
503
     *   previously stored via save_to_file.  This routine may need
504
     *   access to the memory manager because it may need to allocate
505
     *   memory to make room for the variable data.  
506
     */
507
    virtual void restore_from_file(VMG_ vm_obj_id_t self,
508
                                   class CVmFile *fp,
509
                                   class CVmObjFixup *fixups) = 0;
510
511
    /*
512
     *   Compare to another value for equality.  Returns true if the value is
513
     *   equal, false if not.  By default, we return true only if the other
514
     *   value is an object with the same ID (i.e., a reference to exactly
515
     *   the same instance).  However, subclasses can override this to
516
     *   provide different behavior; the string class, for example, may
517
     *   override this so that it compares equal to any string object or
518
     *   constant containing the same string text.
519
     *   
520
     *   'depth' has the same meaning as in calc_hash().  
521
     */
522
    virtual int equals(VMG_ vm_obj_id_t self, const vm_val_t *val,
523
                       int /*depth*/) const
524
    {
525
        /* return true if the other value is a reference to this object */
526
        return (val->typ == VM_OBJ && val->val.obj == self);
527
    }
528
529
    /*
530
     *   Compare magnitude of this object and another object.  Returns a
531
     *   positive value if this object is greater than the other object, a
532
     *   negative value if this object is less than the other object, or
533
     *   zero if this object equals the other object.  Throws an error if
534
     *   a magnitude comparison is not meaningful between the two objects.
535
     *   
536
     *   By default, magnitude comparisons between objects are not
537
     *   meaningful, so we throw an error. 
538
     */
539
    virtual int compare_to(VMG_ vm_obj_id_t /*self*/, const vm_val_t *) const
540
    {
541
        /* by default, magnitude comparisons between objects are illegal */
542
        err_throw(VMERR_INVALID_COMPARISON);
543
544
        /* the compiler doesn't know that we'll never get here */
545
        AFTER_ERR_THROW(return 0;)
546
    }
547
548
    /*
549
     *   Calculate a hash value for the object.
550
     *   
551
     *   'depth' is the recursion depth of the hash calculation.  Objects
552
     *   that can potentially contain cyclical references back to themselves,
553
     *   and which follow those references to calculate the hash value
554
     *   recursively, must check the depth counter to see if it exceeds
555
     *   VM_MAX_TREE_DEPTH_EQ, throwing VMERR_TREE_TOO_DEEP_EQ if so; and
556
     *   they must increment the depth counter in the recursive traversals.
557
     *   Objects that don't traverse into their contents can ignore the depth
558
     *   counter.  
559
     */
560
    virtual uint calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const
561
    {
562
        /* 
563
         *   by default, use a 16-bit hash of our object ID as the hash
564
         *   value 
565
         */
566
        return (uint)(((ulong)self & 0xffff)
567
                      ^ (((ulong)self & 0xffff0000) >> 16));
568
    }
569
                   
570
571
    /* 
572
     *   Add a value to this object, returning the result in *result.
573
     *   This may create a new object to hold the result, or may modify
574
     *   the existing object in place, depending on the subclass
575
     *   implementation.  'self' is the object ID of this object.
576
     *   
577
     *   By default, we'll throw an error indicating that the value cannot
578
     *   be added.  
579
     */
580
    virtual void add_val(VMG_ vm_val_t * /*result*/,
581
                         vm_obj_id_t /*self*/, const vm_val_t * /*val*/)
582
    {
583
        /* throw an error */
584
        err_throw(VMERR_BAD_TYPE_ADD);
585
    }
586
587
    /*
588
     *   Subtract a value from this object, returning the result in
589
     *   *result.  This may create a new object to hold the result, or may
590
     *   modify the existing object in place, depending upon the subclass
591
     *   implementation.  'self' is the object ID of this object.  
592
     */
593
    virtual void sub_val(VMG_ vm_val_t * /*result*/,
594
                         vm_obj_id_t /*self*/, const vm_val_t * /*val*/)
595
    {
596
        /* throw an error */
597
        err_throw(VMERR_BAD_TYPE_SUB);
598
    }
599
600
    /* multiply this object by a value, returning the result in *result */
601
    virtual void mul_val(VMG_ vm_val_t * /* result*/,
602
                         vm_obj_id_t /*self*/, const vm_val_t * /*val*/)
603
    {
604
        /* throw an error */
605
        err_throw(VMERR_BAD_TYPE_MUL);
606
    }
607
608
    /* divide a value into this object, returning the result in *result */
609
    virtual void div_val(VMG_ vm_val_t * /* result*/,
610
                         vm_obj_id_t /*self*/, const vm_val_t * /*val*/)
611
    {
612
        /* throw an error */
613
        err_throw(VMERR_BAD_TYPE_DIV);
614
    }
615
616
    /* negate the value, returning the result in *result */
617
    virtual void neg_val(VMG_ vm_val_t * /* result */, vm_obj_id_t self)
618
    {
619
        /* throw an error */
620
        err_throw(VMERR_BAD_TYPE_NEG);
621
    }
622
623
    /*
624
     *   Get the effective number of values this value contributes when
625
     *   used as the right-hand side of a '+' or '-' operator with a
626
     *   left-hand type that treats these operators as concatenation/set
627
     *   subtraction operators.  By default, a value adds/subtracts only
628
     *   itself, but certain collection types (List, Vector, Array)
629
     *   add/subtract their elements individually. 
630
     */
631
    virtual size_t get_coll_addsub_rhs_ele_cnt(VMG0_) const
632
    {
633
        /* by default, we contribute only 'self', thus only one element */
634
        return 1;
635
    }
636
637
    /* get the nth element as the rhs of a collection add/subtract */
638
    virtual void get_coll_addsub_rhs_ele(VMG_ vm_val_t *retval,
639
                                         vm_obj_id_t self, size_t /*idx*/) 
640
    {
641
        /* by default, we contribute only 'self' */
642
        retval->set_obj(self);
643
    }
644
645
    /*
646
     *   Index the object.  Most object types cannot be indexed, so by
647
     *   default we'll throw an error.  Subclasses that can be indexed
648
     *   (such as lists) should look up the element given by the index
649
     *   value, and store the value at that index in *result.  
650
     */
651
    virtual void index_val(VMG_ vm_val_t * /*result*/,
652
                           vm_obj_id_t /*self*/,
653
                           const vm_val_t * /*index_val*/)
654
    {
655
        /* by default, throw an error */
656
        err_throw(VMERR_CANNOT_INDEX_TYPE);
657
    }
658
659
    /*
660
     *   Set an indexed element of the object, and fill in *new_container
661
     *   with the new object that results if an entire new object is
662
     *   created to hold the modified contents, or with the original
663
     *   object if the contents of the original object are actually
664
     *   modified by this operation.  Lists, for example, cannot be
665
     *   modified, so always create a new list object when an element is
666
     *   set; arrays, in contrast, can be directly modified, so will
667
     *   simply return 'self' in *new_container.
668
     *   
669
     *   By default, we'll throw an error, since default objects cannot be
670
     *   indexed.  
671
     */
672
    virtual void set_index_val(VMG_ vm_val_t * /*new_container*/,
673
                               vm_obj_id_t /*self*/,
674
                               const vm_val_t * /*index_val*/,
675
                               const vm_val_t * /*new_val*/)
676
    {
677
        /* by default, throw an error */
678
        err_throw(VMERR_CANNOT_INDEX_TYPE);
679
    }
680
681
    /*
682
     *   Get a string representation of the object.  If necessary, this
683
     *   can create a new string object to represent the result.
684
     *   
685
     *   Returns the string representation in portable string format: the
686
     *   first two bytes give the byte length of the rest of the string in
687
     *   portable UINT2 format, and immediately following the length
688
     *   prefix are the bytes of the string's contents.  The length prefix
689
     *   does not count the length prefix itself in the length of the
690
     *   string.
691
     *   
692
     *   If a new string object is created, new_str must be set to a
693
     *   reference to the new string object.  If a pointer into our data
694
     *   is returned, we must set new_str to self.  If no object data are
695
     *   involved, new_str can be set to nil.
696
     *   
697
     *   If it is not possible to create a string representation of the
698
     *   object, throw an error (VMERR_NO_STR_CONV).
699
     *   
700
     *   By default, we'll throw an error indicating that the object
701
     *   cannot be converted to a string.  
702
     */
703
    virtual const char *cast_to_string(VMG_ vm_obj_id_t /*self*/,
704
                                       vm_val_t * /*new_str*/) const
705
    {
706
        /* throw an error */
707
        err_throw(VMERR_NO_STR_CONV);
708
709
        /* we can't get here, but the compiler doesn't know that */
710
        AFTER_ERR_THROW(return 0;)
711
    }
712
713
    /*
714
     *   Get the list contained in the object, if possible, or null if
715
     *   not.  If the value contained in the object is a list, this
716
     *   returns a pointer to the list value in portable format (the first
717
     *   two bytes are the length prefix as a portable UINT2, and the
718
     *   subsequent bytes form a packed array of data holders in portable
719
     *   format).
720
     *   
721
     *   Most object classes will return null for this, since they do not
722
     *   store lists.  By default, we return null.
723
     */
724
    virtual const char *get_as_list() const { return 0; }
725
726
    /*
727
     *   Get the string contained in the object, if possible, or null if
728
     *   not.  If the value contained in the object is a string, this
729
     *   returns a pointer to the string value in portable format (the
730
     *   first two bytes are the string prefix as a portable UINT2, and
731
     *   the bytes of the string's text immediately follow in UTF8 format).
732
     *   
733
     *   Most object classes will return null for this, since they do not
734
     *   store strings.  By default, we return null.  
735
     */
736
    virtual const char *get_as_string(VMG0_) const { return 0; }
737
738
    /*
739
     *   Rebuild the image data for the object.  This is used to write the
740
     *   program state to a new image file after executing the program for
741
     *   a while, such as after a 'preinit' step after compilation.
742
     *   
743
     *   This should write the complete metaclass-specific record needed
744
     *   for an OBJS data block entry, not including the generic header.
745
     *   
746
     *   On success, return the size in bytes of the data stored to the
747
     *   buffer; these bytes must be copied directly to the new image
748
     *   file.  If the given buffer isn't big enough, this should return
749
     *   the size needed and otherwise leave the buffer empty.  If the
750
     *   object doesn't need to be written at all, this should return
751
     *   zero.  
752
     */
753
    virtual ulong rebuild_image(VMG_ char *, ulong)
754
        { return 0; }
755
756
    /*
757
     *   Allocate space for conversion to constant data for storage in a
758
     *   rebuilt image file.  If this object can be stored as constant
759
     *   data in a rebuilt image file, this should reserve space in the
760
     *   provided constant mapper object for the object's data in
761
     *   preparation for conversion.
762
     *   
763
     *   Most objects cannot be converted to constant data, so the default
764
     *   here does nothing.  Those that can, such as strings and lists,
765
     *   should override this.  
766
     */
767
    virtual void reserve_const_data(VMG_ class CVmConstMapper *,
768
                                    vm_obj_id_t /*self*/)
769
        { /* default does nothing */ }
770
771
    /*
772
     *   Convert to constant data.  If this object can be stored as
773
     *   constant data in a rebuilt image file, it should override this
774
     *   routine to store its data in the provided constant mapper object.
775
     *   If this object makes reference to other objects, it must check
776
     *   each object that it references to determine if the object has a
777
     *   non-zero address in the mapper, and if so must convert its
778
     *   reference to the object to a constant data item at the given
779
     *   address.  There is no default implementation of this routine,
780
     *   because it must be overridden by essentially all objects (at
781
     *   least, it must be overridden by objects that can be converted or
782
     *   that can refer to objects that can be converted).  
783
     */
784
    virtual void convert_to_const_data(VMG_ class CVmConstMapper *,
785
                                       vm_obj_id_t /*self*/)
786
        { /* default does nothing */ }
787
788
    /*
789
     *   Get the type that this object uses when converted to constant
790
     *   data.  Objects that can be converted to constant data via
791
     *   convert_to_const_data() should return the appropriate type
792
     *   (VM_SSTRING, VM_LIST, etc); others can return VM_NIL to indicate
793
     *   that they have no conversion.  This will be called only when an
794
     *   object actually has been converted as indicated in a mapper
795
     *   (CVmConstMapper) object.  We'll return NIL by default.  
796
     */
797
    virtual vm_datatype_t get_convert_to_const_data_type() const
798
        { return VM_NIL; }
799
    
800
801
    /*
802
     *   Allocate memory for the fixed part from a memory manager.  We
803
     *   override operator new so that we can provide custom memory
804
     *   management for object headers.
805
     *   
806
     *   To create an object, use a sequence like this:
807
     *   
808
     *   obj_id = mem_mgr->get_obj_table()->alloc_obj(vmg_ in_root_set);
809
     *.  new (vmg_ obj_id) CVmObjString(subclass constructor params)
810
     *   
811
     *   The caller need not store the result of 'new'; the caller
812
     *   identifies the object by the obj_id value.
813
     *   
814
     *   Each CVmObject subclass should provide static methods that cover
815
     *   the above sequence, since it's a bit verbose to sprinkle
816
     *   throughout client code.  
817
     */
818
    void *operator new(size_t siz, VMG_ vm_obj_id_t obj_id);
819
820
protected:
821
    /*
822
     *   Find a modifier property.  This is an internal service routine that
823
     *   we use to traverse the hierarchy of modifier objects associated with
824
     *   our intrinsic class hierarchy.  
825
     */
826
    int find_modifier_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
827
                           vm_obj_id_t self, vm_obj_id_t orig_target_obj,
828
                           vm_obj_id_t defining_obj, vm_obj_id_t *source_obj,
829
                           uint *argc);
830
831
    /*
832
     *   Service routine - check a get_prop() argument count for executing
833
     *   a native code method implementation.  If 'argc' is null, we'll
834
     *   set the value to a VM_NATIVE_CODE indication and return true,
835
     *   which the caller should do from get_prop() as well.  If 'argc' is
836
     *   non-null, we'll check it against the given required argument
837
     *   count, and throw an error if it doesn't match.  If 'argc' is
838
     *   non-null and it matches the given argument count, we'll set *argc
839
     *   to zero to indicate that we've consumed the arguments, and return
840
     *   false - get_prop() should in this case execute the native code
841
     *   and return the appropriate result value.  
842
     */
843
    static int get_prop_check_argc(vm_val_t *val, uint *argc,
844
                                   const CVmNativeCodeDesc *desc)
845
    {
846
        /* 
847
         *   if there's no 'argc', we can't execute the native code - we're
848
         *   simply being asked for a description of the method, not to
849
         *   evaluate its result 
850
         */
851
        if (argc == 0)
852
        {
853
            /* indicate a native code evaluation is required */
854
            val->set_native(desc);
855
856
            /* tell get_prop() to return without further work */
857
            return TRUE;
858
        }
859
860
        /* check the argument count - throw an error if out of range */
861
        if (!desc->args_ok(*argc))
862
            err_throw(VMERR_WRONG_NUM_OF_ARGS);
863
864
        /* everything's fine - consume the arguments */
865
        *argc = 0;
866
867
        /* tell get_prop() to proceed with the native evaluation */
868
        return FALSE;
869
    }
870
    
871
#if 0
872
    /*
873
     *   Important note:
874
     *   
875
     *   This function has been removed because we no longer consider it
876
     *   likely that we will ever wish to implement a relocating heap
877
     *   manager.  Given the large memory sizes of modern computers, and
878
     *   recent academic research calling into question the conventional
879
     *   wisdom that heap fragmentation is actually a problem in practical
880
     *   applications, we no longer feel that maintaining the possibility of
881
     *   a relocating heap manager is justified.
882
     *   
883
     *   Note that some code in the present implementation assumes that the
884
     *   heap is non-relocating, so if a relocating heap manager is ever
885
     *   implemented, those assumptions will have to be addressed.  For
886
     *   example, the CVmObjTads code stores direct object pointers inside
887
     *   its objects, which is only possible with a non-relocating heap.  
888
     */
889
890
    /* 
891
     *   Adjust the extension pointer for a change - this must be called by
892
     *   the variable heap page when compacting the heap or when the object
893
     *   must be reallocated.
894
     *   
895
     *   This routine can be called ONLY during garbage collection.  The
896
     *   heap manager is not allowed to move variable-size blocks at any
897
     *   other time.
898
     *   
899
     *   This method is virtual because a given object could have more than
900
     *   one block allocated in the variable heap.  By default, we'll fix up
901
     *   the address of our extension if the block being moved (as
902
     *   identified by its old address) matches our existing extension
903
     *   pointer.  
904
     */
905
    virtual void move_var_part(void *old_pos, void *new_pos)
906
    {
907
        /* if the block being moved is our extension, note the new location */
908
        if (ext_ == (char *)old_pos)
909
            ext_ = (char *)new_pos;
910
    }
911
#endif
912
913
    /* 
914
     *   Pointer to extension.  This pointer may be used in any way
915
     *   desired by the subclassed implementation of the CVmObject
916
     *   interface; normally, this pointer will contain the address of the
917
     *   data associated with the object.  
918
     */
919
    char *ext_;
920
921
    /* property evaluator - undefined function */
922
    int getp_undef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc,
923
                   vm_prop_id_t prop, vm_obj_id_t *source_obj);
924
925
    /* property evaluator - ofKind */
926
    int getp_of_kind(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc,
927
                     vm_prop_id_t prop, vm_obj_id_t *source_obj);
928
929
    /* property evaluator - getSuperclassList */
930
    int getp_sclist(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc,
931
                    vm_prop_id_t prop, vm_obj_id_t *source_obj);
932
933
    /* property evaluator - propDefined */
934
    int getp_propdef(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc,
935
                     vm_prop_id_t prop, vm_obj_id_t *source_obj);
936
937
    /* property evaluator - propType */
938
    int getp_proptype(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc,
939
                      vm_prop_id_t prop, vm_obj_id_t *source_obj);
940
941
    /* property evaluator - getPropList */
942
    int getp_get_prop_list(VMG_ vm_obj_id_t self, vm_val_t *retval,
943
                           uint *argc, vm_prop_id_t prop,
944
                           vm_obj_id_t *source_obj);
945
946
    /* property evaluator - getPropParams */
947
    int getp_get_prop_params(VMG_ vm_obj_id_t self, vm_val_t *retval,
948
                             uint *argc, vm_prop_id_t prop,
949
                             vm_obj_id_t *source_obj);
950
951
    /* property evaluator - isClass */
952
    int getp_is_class(VMG_ vm_obj_id_t self, vm_val_t *retval,
953
                      uint *argc, vm_prop_id_t prop,
954
                      vm_obj_id_t *source_obj);
955
956
    /* property evaluator - propInherited */
957
    int getp_propinh(VMG_ vm_obj_id_t self, vm_val_t *retval,
958
                     uint *argc, vm_prop_id_t prop,
959
                     vm_obj_id_t *source_obj);
960
961
    /* property evaluator - isTransient */
962
    int getp_is_transient(VMG_ vm_obj_id_t self, vm_val_t *retval,
963
                          uint *argc, vm_prop_id_t prop,
964
                          vm_obj_id_t *source_obj);
965
    
966
    /* find the intrinsic class for the given modifier object */
967
    virtual vm_obj_id_t find_intcls_for_mod(VMG_ vm_obj_id_t self,
968
                                            vm_obj_id_t mod_obj);
969
970
    /* property evaluation function table */
971
    static int (CVmObject::*func_table_[])(VMG_ vm_obj_id_t self,
972
                                           vm_val_t *retval, uint *argc,
973
                                           vm_prop_id_t prop,
974
                                           vm_obj_id_t *source_obj);
975
};
976
977
/*
978
 *   Function table indices for 'Object' intrinsic class methods.  Each of
979
 *   these gives the index in our function table for the property ID (as
980
 *   defined in the image file) corresponding to that method.  
981
 */
982
const int VMOBJ_IDX_OF_KIND = 1;
983
const int VMOBJ_IDX_SCLIST = 2;
984
const int VMOBJ_IDX_PROPDEF = 3;
985
const int VMOBJ_IDX_PROPTYPE = 4;
986
const int VMOBJ_IDX_GET_PROP_LIST = 5;
987
const int VMOBJ_IDX_GET_PROP_PARAMS = 6;
988
const int VMOBJ_IDX_IS_CLASS = 7;
989
const int VMOBJ_IDX_PROPINH = 8;
990
const int VMOBJ_IDX_IS_TRANSIENT = 9;
991
992
/* ------------------------------------------------------------------------ */
993
/*
994
 *   Each CVmObject subclass must define a singleton instance of
995
 *   CVmMetaclass that describes the class and instantiates objects of the
996
 *   class.  
997
 */
998
999
/*
1000
 *   The "registration index" is set at startup, when we register the
1001
 *   metaclasses.  This is static to the class - it isn't a per-instance
1002
 *   value.  The purpose of setting this value is to allow
1003
 *   get_registration_index() in the instance to get the class value.
1004
 *   This information is used when saving our state to save information on
1005
 *   an instance's metaclass, so that the instance can be re-created when
1006
 *   the saved state is restored.
1007
 *   
1008
 *   The registration index is NOT persistent data.  The registration
1009
 *   index of a particular metaclass can vary from execution to execution
1010
 *   (although it will remain fixed throughout the lifetime of one set of
1011
 *   VM globals, hence throughout an image file's execution).  The
1012
 *   registration index allows us to find the registration table entry,
1013
 *   which in turn will give us the metaclass global ID, which is suitable
1014
 *   for persistent storage.  
1015
 */
1016
1017
class CVmMetaclass
1018
{
1019
public:
1020
    /*
1021
     *   Get the metaclass registration table index.  This gives the index
1022
     *   of the metaclass in the system registration table.  This value is
1023
     *   fixed during execution; it is set during startup, when the
1024
     *   registration table is being built, and never changes after that. 
1025
     */
1026
    uint get_reg_idx() const { return meta_reg_idx_; }
1027
    
1028
    /* 
1029
     *   Get the metaclass name.  This is the universally unique
1030
     *   identifier assigned to the metaclass, and is the name used to
1031
     *   dynamically link the image file to the metaclass.  
1032
     */
1033
    virtual const char *get_meta_name() const = 0;
1034
1035
    /* 
1036
     *   Create an instance of the metaclass using arguments from the VM
1037
     *   stack.  Returns the ID of the newly-created object.  If the class
1038
     *   has a byte-code constructor, invoke the constructor using the
1039
     *   normal call-procedure protocol.  Leave the result in register R0. 
1040
     */
1041
    virtual vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
1042
                                          uint argc) = 0;
1043
1044
    /*
1045
     *   Create an instance of the metaclass with the given ID.  The
1046
     *   object table entry will already be allocated by the caller; this
1047
     *   ro utine only needs to invoke the metaclass-specific constructor
1048
     *   to initialize the object.  The object must be initialized in such
1049
     *   a way that it can subsequently be loaded from the image file with
1050
     *   load_from_iamge().  In general, this routine only needs to do
1051
     *   something like this:
1052
     *   
1053
     *   new (vmg_ id) CVmObjXxx(); 
1054
     */
1055
    virtual void create_for_image_load(VMG_ vm_obj_id_t id) = 0;
1056
1057
    /*
1058
     *   Create an instance of the metaclass with the given ID in
1059
     *   preparation for restoring the object from a saved state file. 
1060
     */
1061
    virtual void create_for_restore(VMG_ vm_obj_id_t id) = 0;
1062
1063
    /* 
1064
     *   Call a static property of the metaclass 
1065
     */
1066
    virtual int call_stat_prop(VMG_ vm_val_t *result,
1067
                               const uchar **pc_ptr, uint *argc,
1068
                               vm_prop_id_t prop) = 0;
1069
1070
    /*
1071
     *   Get the number of superclasses of the metaclass, and get the
1072
     *   super-metaclass at the given index.  All metaclasses have exactly
1073
     *   one super-metaclass, except for the root object metaclass, which
1074
     *   has no super-metaclass.  
1075
     */
1076
    virtual int get_supermeta_count(VMG0_) const { return 1; }
1077
    virtual vm_obj_id_t get_supermeta(VMG_ int idx) const;
1078
1079
    /* determine if I'm an instance of the given object */
1080
    virtual int is_meta_instance_of(VMG_ vm_obj_id_t obj) const;
1081
1082
    /* 
1083
     *   set the metaclass registration table index - this can only be
1084
     *   done by vm_register_metaclass() during initialization 
1085
     */
1086
    void set_metaclass_reg_index(uint idx) { meta_reg_idx_ = idx; }
1087
1088
    /* most metaclasses are simply derived from Object */
1089
    virtual CVmMetaclass *get_supermeta_reg() const
1090
        { return CVmObject::metaclass_reg_; }
1091
1092
    /* 
1093
     *   get my class object - we'll look up our class object in the
1094
     *   metaclass registration table 
1095
     */
1096
    vm_obj_id_t get_class_obj(VMG0_) const;
1097
1098
private:
1099
    /* system metaclass registration table index */
1100
    uint meta_reg_idx_;
1101
};
1102
     
1103
1104
/* ------------------------------------------------------------------------ */
1105
/*
1106
 *   Root Object definition.  The root object can never be instantiated;
1107
 *   it is defined purely to provide an object to represent as the root in
1108
 *   the type system.  
1109
 */
1110
class CVmMetaclassRoot: public CVmMetaclass
1111
{
1112
public:
1113
    /* get the metaclass name */
1114
    const char *get_meta_name() const { return "root-object/030004"; }
1115
1116
    /* create an instance - this class cannot be instantiated */
1117
    vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
1118
    {
1119
        err_throw(VMERR_BAD_DYNAMIC_NEW);
1120
        AFTER_ERR_THROW(return VM_INVALID_OBJ;)
1121
    }
1122
1123
    /* create an object - this class cannot be instantiated */
1124
    void create_for_image_load(VMG_ vm_obj_id_t id)
1125
        { err_throw(VMERR_BAD_STATIC_NEW); }
1126
1127
    /* create an instance for loading from a saved state */
1128
    void create_for_restore(VMG_ vm_obj_id_t id)
1129
        { err_throw(VMERR_BAD_STATIC_NEW); }
1130
1131
    /* call a static property */
1132
    int call_stat_prop(VMG_ vm_val_t *result,
1133
                       const uchar **pc_ptr, uint *argc,
1134
                       vm_prop_id_t prop)
1135
    {
1136
        /* call the base object implementation */
1137
        return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
1138
    }
1139
1140
    /* the root object has no supermetaclasses */
1141
    int get_supermeta_count(VMG0_) const { return 0; }
1142
    vm_obj_id_t get_supermeta(VMG_ int) const { return VM_INVALID_OBJ; }
1143
1144
    /* 
1145
     *   determine if I'm an instance of the given object - the root
1146
     *   object is not a subclass of anything 
1147
     */
1148
    virtual int is_meta_instance_of(VMG_ vm_obj_id_t obj) const
1149
        { return FALSE; }
1150
1151
    /* the base Object metaclass has no supermetaclass */
1152
    virtual CVmMetaclass *get_supermeta_reg() const { return 0; }
1153
};
1154
1155
/* ------------------------------------------------------------------------ */
1156
/*
1157
 *   Object fixup table entry 
1158
 */
1159
struct obj_fixup_entry
1160
{
1161
    /* old ID */
1162
    vm_obj_id_t old_id;
1163
1164
    /* new ID */
1165
    vm_obj_id_t new_id;
1166
};
1167
1168
/*
1169
 *   Object ID Fixup Table.  This is used during state restoration to map
1170
 *   from the saved state file's object numbering system to the new
1171
 *   in-memory numbering system.
1172
 *   
1173
 *   The objects must be added to the table IN ASCENDING ORDER OF OLD ID.
1174
 *   We assume this sorting order to perform a binary lookup when asked to
1175
 *   map an ID.  
1176
 */
1177
1178
/* fixup table subarray size */
1179
#define VMOBJFIXUP_SUB_SIZE 2048
1180
1181
class CVmObjFixup
1182
{
1183
public:
1184
    CVmObjFixup(ulong entry_cnt);
1185
    ~CVmObjFixup();
1186
1187
    /* add a fixup to the table */
1188
    void add_fixup(vm_obj_id_t old_id, vm_obj_id_t new_id);
1189
1190
    /* 
1191
     *   Translate from the file numbering system to the new numbering
1192
     *   system.  If the object isn't found, it must be a static object and
1193
     *   hence doesn't require translation, so we'll return the original ID
1194
     *   unchanged.  
1195
     */
1196
    vm_obj_id_t get_new_id(VMG_ vm_obj_id_t old_id);
1197
1198
    /* fix up a DATAHOLDER value */
1199
    void fix_dh(VMG_ char *dh);
1200
1201
    /* fix up an array of DATAHOLDER values */
1202
    void fix_dh_array(VMG_ char *arr, size_t cnt);
1203
1204
    /* fix a portable VMB_OBJECT_ID field */
1205
    void fix_vmb_obj(VMG_ char *p);
1206
1207
    /* fix an array of portable VMB_OBJECT_ID fields */
1208
    void fix_vmb_obj_array(VMG_ char *p, size_t cnt);
1209
1210
private:
1211
    /* find an entry given the old object ID */
1212
    struct obj_fixup_entry *find_entry(vm_obj_id_t old_entry);
1213
1214
    /* get an entry at the given array index */
1215
    struct obj_fixup_entry *get_entry(ulong idx) const
1216
    {
1217
        return &arr_[idx / VMOBJFIXUP_SUB_SIZE][idx % VMOBJFIXUP_SUB_SIZE];
1218
    }
1219
1220
    /* array of subarrays */
1221
    struct obj_fixup_entry **arr_;
1222
1223
    /* number of subarray pages */
1224
    ulong pages_;
1225
1226
    /* number of entries in the array */
1227
    ulong cnt_;
1228
1229
    /* number of entries used so far */
1230
    ulong used_;
1231
};
1232
1233
1234
/* ------------------------------------------------------------------------ */
1235
/*
1236
 *   Global variable structure.  We maintain a linked list of these
1237
 *   structures for miscellaneous global variables required by other
1238
 *   subsystems.  
1239
 */
1240
struct vm_globalvar_t
1241
{
1242
    /* the variable's value */
1243
    vm_val_t val;
1244
1245
    /* next and previous pointer in linked list of global variables */
1246
    vm_globalvar_t *nxt;
1247
    vm_globalvar_t *prv;
1248
};
1249
1250
1251
/* ------------------------------------------------------------------------ */
1252
/*
1253
 *   Global object page.  We keep a linked list of pages of globals that
1254
 *   refer to objects that are always reachable but were not loaded from the
1255
 *   image file.  
1256
 */
1257
class CVmObjGlobPage
1258
{
1259
public:
1260
    CVmObjGlobPage()
1261
    {
1262
        /* we're not in a list yet */
1263
        nxt_ = 0;
1264
1265
        /* we have no allocated entries yet */
1266
        used_ = 0;
1267
    }
1268
1269
    ~CVmObjGlobPage()
1270
    {
1271
        /* delete the next page */
1272
        delete nxt_;
1273
    }
1274
1275
    /* 
1276
     *   add an entry to this page; returns true on success, false if we're
1277
     *   too full to add another entry 
1278
     */
1279
    int add_entry(vm_obj_id_t obj)
1280
    {
1281
        /* if we're full, indicate failure */
1282
        if (used_ == sizeof(objs_)/sizeof(objs_[0]))
1283
            return FALSE;
1284
1285
        /* store the entry and count it */
1286
        objs_[used_] = obj;
1287
        ++used_;
1288
1289
        /* indicate success */
1290
        return TRUE;
1291
    }
1292
1293
    /* next page in list */
1294
    CVmObjGlobPage *nxt_;
1295
1296
    /* number of entries on this page that are in use */
1297
    size_t used_;
1298
1299
    /* array of entries on this page */
1300
    vm_obj_id_t objs_[30];
1301
};
1302
1303
/* ------------------------------------------------------------------------ */
1304
/*
1305
 *   Object Header Manager 
1306
 */
1307
1308
/* 
1309
 *   Number of objects in a page.  We constrain this to be a power of two
1310
 *   to make certain calculations fast (in particular, so that division by
1311
 *   and modulo the page count can be done as bit shifts and masks).  
1312
 */
1313
const unsigned int VM_OBJ_PAGE_CNT_LOG2 = 12;
1314
const unsigned int VM_OBJ_PAGE_CNT = (1 << VM_OBJ_PAGE_CNT_LOG2);
1315
1316
/*
1317
 *   Reachability states.  A Reachable object is in the root set, or can
1318
 *   be reached directly or indirectly from the root set.  A
1319
 *   Finalizer-Reachable object can be reached directly or indirectly from
1320
 *   a finalizable object that is Finalizer-Reachable or Unreachable, but
1321
 *   not from any reachable object.  An Unreachable object cannot be
1322
 *   reached from any Reachable or Finalizable object.
1323
 *   
1324
 *   We deliberately arrange the objects in a hierarchical order:
1325
 *   Finalizer-Reachable is "more reachable" than Unreachable, and
1326
 *   Reachable is more reachable than Finalizer-Reachable.  The numeric
1327
 *   values of these states are arranged so that a higher number indicates
1328
 *   stronger reachability.  
1329
 */
1330
#define VMOBJ_UNREACHABLE   0x00
1331
#define VMOBJ_F_REACHABLE   0x01
1332
#define VMOBJ_REACHABLE     0x02
1333
1334
/*
1335
 *   Finalization states.  An Unfinalizable object is one which has not
1336
 *   ever been detected by the garbage collector to be less than fully
1337
 *   Reachable.  A Finalizable object is one which has been found during a
1338
 *   garbage collection pass to be either F-Reachable or Unreachable, but
1339
 *   which has not yet been finalized.  A Finalized object is one which
1340
 *   has had its finalizer method invoked.  
1341
 */
1342
#define VMOBJ_UNFINALIZABLE 0x00
1343
#define VMOBJ_FINALIZABLE   0x01
1344
#define VMOBJ_FINALIZED     0x02
1345
1346
1347
/*
1348
 *   Object table page entry.
1349
 */
1350
struct CVmObjPageEntry
1351
{
1352
    /*
1353
     *   An entry is either a member of the free list, or it's a valid
1354
     *   object.
1355
     */
1356
    union
1357
    {
1358
        /* 
1359
         *   If it's not in the free list, then it's a VM object.  We
1360
         *   can't actually embed a true CVmObject here - that's an
1361
         *   abstract type and we therefore can't directly instantiate it
1362
         *   through embedding.  So, just allocate enough space for the
1363
         *   object; the memory manager will claim this space (via
1364
         *   operator new) and store the actual CVmObject here when the
1365
         *   slot is allocated to an object.  
1366
         */
1367
        char obj_[sizeof(CVmObject)];
1368
        
1369
        /* 
1370
         *   if it's in the free list, we just have a pointer to the
1371
         *   previous element of the free list 
1372
         */
1373
        vm_obj_id_t prev_free_;
1374
    } ptr_;
1375
1376
    /* next object in list (either the GC work queue or the free list) */
1377
    vm_obj_id_t next_obj_;
1378
1379
    /* get my VM object pointer */
1380
    CVmObject *get_vm_obj() const { return (CVmObject *)ptr_.obj_; }
1381
1382
    /* flag: the object is in the free list */
1383
    int free_ : 1;
1384
1385
    /* 
1386
     *   flag: the object is part of the root set (that is, there's a
1387
     *   reference to this object from some static location outside of the
1388
     *   root set, such as in p-code or in a constant list) 
1389
     */
1390
    int in_root_set_ : 1;
1391
1392
    /*
1393
     *   Reachability state.  This indicates whether the object is
1394
     *   reachable, reachable from finalizable objects only, or
1395
     *   unreachable.  This is set during garbage collection. 
1396
     */
1397
    uint reachable_ : 2;
1398
1399
    /*
1400
     *   Finalization state.  This indicates whether an object is
1401
     *   unfinalizable, finalizable, or finalized. 
1402
     */
1403
    uint finalize_state_ : 2;
1404
1405
    /*
1406
     *   Flag: the object is part of an undo savepoint.  This is cleared
1407
     *   when an object is initially created, and set for all existing
1408
     *   objects when an undo savepoint is created - this means that this
1409
     *   will be set for an object only if an undo savepoint has been
1410
     *   created since the object was created.
1411
     *   
1412
     *   When the undo mechanism is asked to create an undo record
1413
     *   associated with an object, it will do nothing if the object is not
1414
     *   part of the undo savepoint.  This means that we won't save undo
1415
     *   records for objects created since the start of the most recent
1416
     *   savepoint - keeping undo for such objects is unnecessary, since if
1417
     *   we roll back to the savepoint, the object won't even be in
1418
     *   existence any more and hence has no need to restore any of its
1419
     *   state.  
1420
     */
1421
    uint in_undo_ : 1;
1422
1423
    /*
1424
     *   Flag: the object is "transient."  A transient object does not
1425
     *   participate in undo, is not saved or restored to a saved state, and
1426
     *   is not affected by restarting.  
1427
     */
1428
    uint transient_ : 1;
1429
1430
    /* flag: the object has requested post-load initialization */
1431
    uint requested_post_load_init_ : 1;
1432
1433
    /*
1434
     *   Garbage collection hint flags.  These flags provide hints on how
1435
     *   the object's metaclass interacts with the garbage collector.  These
1436
     *   do NOT indicate the object's current status, but rather indicate
1437
     *   the metaclass's capabilities - so 'can_have_refs_' does not
1438
     *   indicate that the object current has or doesn't have any references
1439
     *   to other objects, but rather indicates if it's POSSIBLE for the
1440
     *   object EVER to have references to other objects.  For example, all
1441
     *   Strings would set 'can_have_refs_' to false, and all TadsObjects
1442
     *   would set it to true.
1443
     *   
1444
     *   We set these flags to true by default, for the maximally
1445
     *   conservative settings.  A metaclass can simply ignore these
1446
     *   settings and be assured of correct GC behavior.  However, if a
1447
     *   metaclass knows that it can correctly set one of these flags to
1448
     *   false, it should do so after instances are created, because doing
1449
     *   so allows the garbage collector to reduce the amount of work it
1450
     *   must do for the object.
1451
     *   
1452
     *   'can_have_refs_' indicates if the object can ever contain
1453
     *   references to other objects.  By default, this is always set to
1454
     *   true, but a metaclass that is not capable of storing references to
1455
     *   other objects should set this to false.  When this is set to false,
1456
     *   the garbage collector will avoid tracing into this object when
1457
     *   tracing references, because it will know in advance that tracing
1458
     *   into the object will have no effect.
1459
     *   
1460
     *   'can_have_weak_refs_' indicates if the object can ever contain weak
1461
     *   references to other objects.  By default, this is set to true, but
1462
     *   a metaclass that never uses weak references can set it to false.
1463
     *   When this is set to false, the garbage collector can avoid
1464
     *   notifying this object of the need to remove stale weak references.
1465
     *   
1466
     *   IMPORTANT: We assume that a metaclass that cannot have
1467
     *   references/weak references must ALSO never have references/weak
1468
     *   references) in its undo information.  It's hard to imagine a case
1469
     *   where we'd have no possibility of a kind of references in an object
1470
     *   but still have the possibility of the same kind of references in
1471
     *   the object's undo records; but should such a case arise, the
1472
     *   metaclass must indicate that it does have the possibility of that
1473
     *   kind of references.  
1474
     */
1475
    uint can_have_refs_ : 1;
1476
    uint can_have_weak_refs_ : 1;
1477
1478
    /* 
1479
     *   An entry is deletable if it's unreachable and has been finalized.
1480
     *   If the entry is marked as free, it's already been deleted, hence
1481
     *   is certainly deletable. 
1482
     */
1483
    int is_deletable() const
1484
    {
1485
        return (free_
1486
                || (reachable_ == VMOBJ_UNREACHABLE
1487
                    && finalize_state_ == VMOBJ_FINALIZED));
1488
    }
1489
1490
    /* 
1491
     *   Determine if the object should participate in undo.  An object
1492
     *   participates in undo if it existed as of the most recent savepoint,
1493
     *   and the object is not transient. 
1494
     */
1495
    int is_in_undo() const { return in_undo_ && !transient_; }
1496
1497
    /*
1498
     *   A "saveable" object is one which must be written to a saved state
1499
     *   file.
1500
     *   
1501
     *   To be saveable, an object must not be free, must not be transient,
1502
     *   must be fully reachable, and must have been modified since loading
1503
     *   (or simply have been created dynamically, since all an object that
1504
     *   was created dynamically has inherently been modified since the
1505
     *   program was loaded, as it didn't even exist when the program was
1506
     *   loaded).
1507
     *   
1508
     *   Do not save objects that are only reachable through a finalizer;
1509
     *   assume that these objects do not figure into the persistent VM
1510
     *   state, but are still around merely because they have some external
1511
     *   resource deallocation to which they must yet tend.
1512
     *   
1513
     *   Do not save objects that are in the root set and which haven't been
1514
     *   modified since loading.  We always reset before restoring to the
1515
     *   initial image file state, so there's no need to save data for any
1516
     *   object that's simply in its initial image file state.  
1517
     */
1518
    int is_saveable() const
1519
    {
1520
        return (!free_
1521
                && !transient_
1522
                && reachable_ == VMOBJ_REACHABLE
1523
                && (!in_root_set_
1524
                    || get_vm_obj()->is_changed_since_load()));
1525
    }
1526
1527
    /*
1528
     *   Determine if the object is "persistent."  A persistent object is
1529
     *   one which will survive saving and restoring machine state.  An
1530
     *   object is persistent if it is not transient, and either it is
1531
     *   saveable (in which case it will be explicitly saved and restored),
1532
     *   or it is merely in the root set (in which case it is always
1533
     *   present).  
1534
     */
1535
    int is_persistent() const
1536
    {
1537
        return !transient_ && (in_root_set_ || is_saveable());
1538
    }
1539
};
1540
1541
/* ------------------------------------------------------------------------ */
1542
/*
1543
 *   Object table.
1544
 */
1545
class CVmObjTable
1546
{
1547
public:
1548
    /* create the table */
1549
    CVmObjTable() { init(); }
1550
1551
    /* initialize */
1552
    void init();
1553
1554
    /* 
1555
     *   Destroy the table - call this rather BEFORE using operator delete
1556
     *   directly.  After this routine is called, the object table can be
1557
     *   deleted.  
1558
     */
1559
    void delete_obj_table(VMG0_);
1560
1561
    /* clients must call delete_obj_table() before deleting the object */
1562
    ~CVmObjTable();
1563
1564
    /* get an object given an object ID */
1565
    inline CVmObject *get_obj(vm_obj_id_t id) const
1566
    {
1567
        /* get the page entry, and get the object from the entry */
1568
        return (CVmObject *)&get_entry(id)->ptr_.obj_;
1569
    }
1570
1571
    /*
1572
     *   Turn garbage collection on or off.  When performing a series of
1573
     *   allocations of values that won't be stored on the stack, this can
1574
     *   be used to ensure that the intermediate allocations aren't
1575
     *   collected as unreferenced before the group of operations is
1576
     *   completed.  Returns previous status for later restoration.  
1577
     */
1578
    int enable_gc(VMG_ int enable);
1579
1580
    /* allocate a new object ID */
1581
    vm_obj_id_t alloc_obj(VMG_ int in_root_set)
1582
    {
1583
        /* allocate, using maximally conservative GC characteristics */
1584
        return alloc_obj(vmg_ in_root_set, TRUE, TRUE);
1585
    }
1586
1587
    /* allocate a new object ID */
1588
    vm_obj_id_t alloc_obj(VMG_ int in_root_set, int can_have_refs,
1589
                          int can_have_weak_refs);
1590
1591
    /*
1592
     *   Allocate an object at a particular object ID.  This is used when
1593
     *   loading objects from an image file or restoring objects from a
1594
     *   saved state file, since objects must be loaded or restored with
1595
     *   the same object number which they were originally assigned.  This
1596
     *   routine throws an error if the object is already allocated.
1597
     */
1598
    void alloc_obj_with_id(vm_obj_id_t id, int in_root_set)
1599
    {
1600
        /* allocate with maximally conservative GC characteristics */
1601
        alloc_obj_with_id(id, in_root_set, TRUE, TRUE);
1602
    }
1603
1604
    /* allocate an object with a given ID */
1605
    void alloc_obj_with_id(vm_obj_id_t id, int in_root_set,
1606
                           int can_have_refs, int can_have_weak_refs);
1607
1608
    /* 
1609
     *   Collect all garbage.  This runs an entire garbage collection pass
1610
     *   to completion with a single call.  This can be used for
1611
     *   simplicity when the caller does not require incremental operation
1612
     *   of the garbage collector.
1613
     *   
1614
     *   This function actually performs two garbage collection passes to
1615
     *   ensure that all collectible objects are collected.  We perform
1616
     *   one pass to detect finalizable objects, then finalize all objects
1617
     *   that we can, then make one more pass to sweep up all of the
1618
     *   finalized objects that can be deleted.  
1619
     */
1620
    void gc_full(VMG0_);
1621
1622
    /* 
1623
     *   Incremental garbage collection.  Call gc_pass_init() to
1624
     *   initialize the pass.  Call gc_pass_continue() repeatedly to
1625
     *   perform incremental collection; this routine runs for a short
1626
     *   time and then returns.  gc_pass_continue() returns true if
1627
     *   there's more work to do, false if not, so the caller can stop
1628
     *   invoking it as soon as it returns false.  Call gc_pass_finish()
1629
     *   to complete the garbage collection.  gc_pass_finish() will call
1630
     *   gc_pass_continue() if necessary to finish its work, so the caller
1631
     *   need not keep invoking gc_pass_continue() if it runs out of work
1632
     *   to interleave with the garbage collector.
1633
     *   
1634
     *   Once garbage collection is started, it must be finished before
1635
     *   any other VM activity occurs.  So, after a call to
1636
     *   gc_pass_init(), the caller is not allowed to perform any other VM
1637
     *   operations until gc_pass_finish() returns (in particular, no new
1638
     *   objects may be created, and no references to existing objects may
1639
     *   be created or changed).
1640
     */
1641
    void gc_pass_init(VMG0_);
1642
    int  gc_pass_continue(VMG0_) { return gc_pass_continue(vmg_ TRUE); }
1643
    void gc_pass_finish(VMG0_);
1644
1645
    /*
1646
     *   Run pending finalizers.  This can be run at any time other than
1647
     *   during garbage collection (i.e., between gc_pass_init() and
1648
     *   gc_pass_finish()).
1649
     */
1650
    void run_finalizers(VMG0_);
1651
1652
    /*
1653
     *   Determine if a given object is subject to deletion.  This only
1654
     *   gives meaningful results during the final garbage collector pass.
1655
     *   Returns true if the object is ready for deletion, which can only
1656
     *   happen when the object is both Unreachable and Finalized, or
1657
     *   false if it not.  A true return means that the object can be
1658
     *   deleted at any time.  This can be used by weak referencers to
1659
     *   determine if objects they are referencing are about to be
1660
     *   deleted, and thus that the weak reference must be forgotten.  
1661
     */
1662
    int is_obj_deletable(vm_obj_id_t obj) const
1663
    {
1664
        /* 
1665
         *   If it's not a valid object, consider it deletable, since it
1666
         *   has indeed already been deleted; otherwise, it's deletable if
1667
         *   its object table entry is deletable.  
1668
         */
1669
        return (obj == VM_INVALID_OBJ
1670
                || get_entry(obj)->is_deletable());
1671
    }
1672
1673
    /*
1674
     *   Mark object references (for GC tracing) made in an object's undo
1675
     *   record.  If the object is marked as having no possibility of
1676
     *   containing references to other objects, we won't bother invoking
1677
     *   the object's tracing method, as we can be assured that the undo
1678
     *   records won't contain any references either.  
1679
     */
1680
    void mark_obj_undo_rec(VMG_ vm_obj_id_t obj,
1681
                           struct CVmUndoRecord *undo_rec)
1682
    {
1683
        CVmObjPageEntry *entry;
1684
1685
        /* get the object entry */
1686
        entry = get_entry(obj);
1687
1688
        /* 
1689
         *   if the object can have any references, mark any references the
1690
         *   object makes from the undo record; if the object can't have any
1691
         *   references, assume its undo records cannot either, in which
1692
         *   case there should be nothing to mark 
1693
         */
1694
        if (entry->can_have_refs_)
1695
            entry->get_vm_obj()->mark_undo_ref(vmg_ undo_rec);
1696
    }
1697
1698
    /* 
1699
     *   Remove stale weak undo references for an object.  If the object is
1700
     *   marked as having no possibility of weak references, we won't bother
1701
     *   invoking the object's weak undo reference remover method, since we
1702
     *   know it won't do anything.  
1703
     */
1704
    void remove_obj_stale_undo_weak_ref(VMG_ vm_obj_id_t obj,
1705
                                        struct CVmUndoRecord *undo_rec)
1706
    {
1707
        CVmObjPageEntry *entry;
1708
        
1709
        /* get the object entry */
1710
        entry = get_entry(obj);
1711
1712
        /* 
1713
         *   if the object can have weak references, notify it; if not,
1714
         *   there's no need to do anything 
1715
         */
1716
        if (entry->can_have_weak_refs_)
1717
            entry->get_vm_obj()->remove_stale_undo_weak_ref(vmg_ undo_rec);
1718
    }
1719
1720
    /*
1721
     *   Determine if the given object is part of the latest undo savepoint.
1722
     *   Returns true if an undo savepoint has been created since the object
1723
     *   was created, false if not.  
1724
     */
1725
    int is_obj_in_undo(vm_obj_id_t obj) const
1726
    {
1727
        return (obj != VM_INVALID_OBJ
1728
                && get_entry(obj)->is_in_undo());
1729
    }
1730
1731
    /*
1732
     *   Determine if a vm_val_t contains a reference to a deletable object.
1733
     *   This is a simple convenience routine.  This returns true only if
1734
     *   the value contains a valid object reference, and the object is
1735
     *   currently deletable.  
1736
     */
1737
    int is_obj_deletable(const vm_val_t *val) const
1738
    {
1739
        return (val->typ == VM_OBJ
1740
                && val->val.obj != VM_INVALID_OBJ
1741
                && is_obj_deletable(val->val.obj));
1742
    }
1743
1744
    /*
1745
     *   Determine if the object is saveable.  If this returns true, the
1746
     *   object should be saved to a saved state file.  If not, the object
1747
     *   should not be included in the saved state file.  Note that a
1748
     *   non-saveable object might still be a persistent object - if the
1749
     *   object is a root set object and hasn't been modified since
1750
     *   loading, it's still peristent even though it doesn't get written
1751
     *   to the saved state file.  
1752
     */
1753
    int is_obj_saveable(vm_obj_id_t obj) const
1754
    {
1755
        return (obj != VM_INVALID_OBJ
1756
                && get_entry(obj)->is_saveable());
1757
    }
1758
1759
    /*
1760
     *   Determine if the object is persistent in a saved state.  If this
1761
     *   returns true, the object will survive saving and restoring the
1762
     *   machine state; if not, the object will not be present after the
1763
     *   machine state is restored.  This can be used to test if a weak
1764
     *   reference should be included in a saved state file.  
1765
     */
1766
    int is_obj_persistent(vm_obj_id_t obj) const
1767
    {
1768
        return (obj != VM_INVALID_OBJ
1769
                && get_entry(obj)->is_persistent());
1770
    }
1771
1772
    /* determine if the given object is transient */
1773
    int is_obj_transient(vm_obj_id_t obj) const
1774
    {
1775
        return (obj != VM_INVALID_OBJ
1776
                && get_entry(obj)->transient_);
1777
    }
1778
1779
    /* mark an object as transient */
1780
    void set_obj_transient(vm_obj_id_t obj) const
1781
    {
1782
        /* set the 'transient' flag in the object */
1783
        get_entry(obj)->transient_ = TRUE;
1784
    }
1785
1786
    /* set an object's garbage collection characteristics */
1787
    void set_obj_gc_characteristics(
1788
        vm_obj_id_t obj, int can_have_refs, int can_have_weak_refs) const
1789
    {
1790
        CVmObjPageEntry *entry;
1791
1792
        /* get the object's entry */
1793
        entry = get_entry(obj);
1794
1795
        /* set the entry's GC flags as specified */
1796
        entry->can_have_refs_ = can_have_refs;
1797
        entry->can_have_weak_refs_ = can_have_weak_refs;
1798
    }
1799
1800
    /* determine if the given object is in the root set */
1801
    int is_obj_in_root_set(vm_obj_id_t obj) const
1802
    {
1803
        return (obj != VM_INVALID_OBJ
1804
                && get_entry(obj)->in_root_set_);
1805
    }
1806
1807
    /*
1808
     *   Mark the given object as referenced, and recursively mark all of
1809
     *   the objects to which it refers as referenced.  
1810
     */
1811
    void mark_all_refs(vm_obj_id_t obj, uint state)
1812
        { add_to_gc_queue(obj, state); }
1813
1814
    /*
1815
     *   Receive notification from the undo manager that we're starting a
1816
     *   new savepoint.  We'll simply notify all of the objects of this. 
1817
     */
1818
    void notify_new_savept();
1819
1820
    /*
1821
     *   Apply an undo record 
1822
     */
1823
    void apply_undo(VMG_ struct CVmUndoRecord *rec);
1824
1825
    /*
1826
     *   Rebuild the image file's OBJS blocks for a particular metaclass.
1827
     *   We'll write all of the objects of the given metaclass to one or
1828
     *   more OBJS blocks in the given output file.  This can be used to
1829
     *   dump the program state to a new image file after running
1830
     *   'preinit' or a similar compile-time pre-initialization procedure.
1831
     *   
1832
     *   'meta_dep_idx' is the index in the metaclass dependency table of
1833
     *   the metaclass to be written.  
1834
     */
1835
    void rebuild_image(VMG_ int meta_dep_idx, class CVmImageWriter *writer,
1836
                       class CVmConstMapper *mapper);
1837
1838
    /*
1839
     *   Scan all objects and add metaclass entries to the metaclass
1840
     *   dependency table for any metaclasses of which there are existing
1841
     *   instances. 
1842
     */
1843
    void add_metadeps_for_instances(VMG0_);
1844
1845
    /*
1846
     *   Scan all active objects and convert objects to constant data
1847
     *   where possible.  Certain object metaclasses, such as strings and
1848
     *   lists, can be represented in a rebuilt image file as constant
1849
     *   data; this routine makes all of these conversions. 
1850
     */
1851
    void rebuild_image_convert_const_data(VMG_
1852
                                          class CVmConstMapper *const_mapper);
1853
1854
    /*
1855
     *   Get the maximum object ID that has ever been allocated.  This
1856
     *   establishes an upper bound on the object ID's that can be found
1857
     *   among the active objects.  
1858
     */
1859
    vm_obj_id_t get_max_used_obj_id() const
1860
        { return pages_used_ * VM_OBJ_PAGE_CNT; }
1861
1862
    /* determine if an object ID refers to a valid object */
1863
    int is_obj_id_valid(vm_obj_id_t obj) const
1864
    {
1865
        /* 
1866
         *   the object is valid as long as it's not free, and the ID is
1867
         *   within the valid range 
1868
         */
1869
        return (obj != VM_INVALID_OBJ
1870
                && obj < get_max_used_obj_id()
1871
                && !get_entry(obj)->free_);
1872
    }
1873
1874
    /*
1875
     *   Get the object state.  This is intended primarily as a debugging
1876
     *   and testing aid for the VM itself; this value should be of no
1877
     *   interest to normal programs.  Returns a value suitable for use
1878
     *   with CVmBifT3Test::get_obj_gc_state().  
1879
     */
1880
    ulong get_obj_internal_state(vm_obj_id_t id) const
1881
    {
1882
        /* if the object ID is invalid, return 0xF000 to so indicate */
1883
        if (id >= get_max_used_obj_id())
1884
            return 0xF000;
1885
1886
        /* 
1887
         *   return the state as a combination of these bits:
1888
         *   
1889
         *   free ? 0 : 1
1890
         *.  Unreachable=0x00, F-Reachable=0x10, Reachable=0x20
1891
         *.  Unfinalizable=0x000, Finalizable=0x100, Finalized=0x200 
1892
         */
1893
        return ((get_entry(id)->free_ ? 0 : 1)
1894
                + (((ulong)get_entry(id)->reachable_) << 4)
1895
                + (((ulong)get_entry(id)->finalize_state_) << 8));
1896
    }
1897
1898
    /*
1899
     *   Reset to the initial image file state.  Discards all objects not
1900
     *   in the root set, skipping finalizers, and resets all objects to
1901
     *   their initial image file state.  
1902
     */
1903
    void reset_to_image(VMG0_);
1904
1905
    /* 
1906
     *   Save state to a file.  We write out each object's state to the
1907
     *   file so that the state can be restored later. 
1908
     */
1909
    void save(VMG_ class CVmFile *fp);
1910
1911
    /* 
1912
     *   Restore state from a previously saved file.  Returns zero on
1913
     *   success, or a VMERR_xxx code on failure.
1914
     *   
1915
     *   This routine creates an object fixup table, and returns it in
1916
     *   *fixups.  The caller is responsible for deleting this object if a
1917
     *   non-null pointer is returned in *fixups.  
1918
     */
1919
    int restore(VMG_ class CVmFile *fp, class CVmObjFixup **fixups);
1920
1921
    /*
1922
     *   Save an object's image data pointer.  An object's load_from_image()
1923
     *   routine may call this routine (it cannot be called from anywhere
1924
     *   else) to save the loaded image location for the object.  For each
1925
     *   object that calls this routine, we will call the object's
1926
     *   reload_from_image() method during a reset to initial image file
1927
     *   state.  
1928
     */
1929
    void save_image_pointer(vm_obj_id_t obj, const char *ptr, size_t siz);
1930
1931
    /*
1932
     *   Request post-load initialization.  An object can call this to
1933
     *   request that its post_load_init() method be called after an initial
1934
     *   program load, restart, or restore operation.  The post_load_init()
1935
     *   method will not be called until the load/restart/restore operation
1936
     *   has loaded every object, so the method can be used to perform any
1937
     *   initialization that depends upon other objects being loaded.  
1938
     */
1939
    void request_post_load_init(vm_obj_id_t obj);
1940
1941
    /* remove a post-load initialization request */
1942
    void remove_post_load_init(vm_obj_id_t obj);
1943
1944
    /* invoke all registered post-load initializations */
1945
    void do_all_post_load_init(VMG0_);
1946
1947
    /*
1948
     *   Ensure that the given object has had its post-load initialization
1949
     *   performed during the current load/restart/restore.  If an object's
1950
     *   post-load initialization depends upon another object having already
1951
     *   been initialized, the first object should call this to ensure that
1952
     *   the other object it depends upon has been initialized.  This allows
1953
     *   objects to ensure that initialization dependencies are handled in
1954
     *   the correct order, regardless of the order in which the objects were
1955
     *   loaded.
1956
     *   
1957
     *   Post-load initialization is guaranteed to be executed exactly once
1958
     *   per load/restart/restore cycle.  When this routine is called, we
1959
     *   check to see if the target object has already been initialized
1960
     *   during this operation, and we do nothing if so.  If we do invoke the
1961
     *   target object's post_load_init(), then this will be the only
1962
     *   invocation for this operation.
1963
     *   
1964
     *   Circular dependencies are prohibited.  If object A's
1965
     *   post_load_init() method calls this to initialize object B, we will
1966
     *   invoke object B's post_load_init().  If object B in turn calls this
1967
     *   routine to initialize object A, we will observe that object A is
1968
     *   already in the process of being initialized and throw an error.  
1969
     */
1970
    void ensure_post_load_init(VMG_ vm_obj_id_t obj);
1971
1972
    /*
1973
     *   Add an object to the list of machine globals.  An object added to
1974
     *   this list will never be deleted.  If the object is in the root set
1975
     *   (which means it was loaded from the image file), we will ignore
1976
     *   this request, since a root object is inherently global.  
1977
     */
1978
    void add_to_globals(vm_obj_id_t obj);
1979
1980
    /*
1981
     *   Create a "global variable."  A global variable is part of the root
1982
     *   set: any value in a variable allocated here will be traced during
1983
     *   garbage collection.  Global variables are meant for use by other
1984
     *   subsystems, so that other subsystems can include their own static
1985
     *   and global variables in the root set.
1986
     *   
1987
     *   Global variables are not affected by RESTORE, RESTART, or UNDO (like
1988
     *   the stack, global variables are transient).
1989
     *   
1990
     *   The caller can use delete_global_var() to delete the variable when
1991
     *   done with it.  Any global variable that is never explicitly deleted
1992
     *   will be automatically deleted when the object table itself is
1993
     *   destroyed.  
1994
     */
1995
    vm_globalvar_t *create_global_var();
1996
    void delete_global_var(vm_globalvar_t *var);
1997
1998
private:
1999
    /* rebuild the image, writing only transient or only persistent objects */
2000
    void rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
2001
                       class CVmConstMapper *mapper, int trans);
2002
2003
    /* invoke a post-load initialization method */
2004
    static void call_post_load_init(VMG_ class CVmHashEntryPLI *entry);
2005
2006
    /* enumeration callbacks for post-load initialization */
2007
    static void pli_status_cb(void *ctx, class CVmHashEntry *entry);
2008
    static void pli_invoke_cb(void *ctx, class CVmHashEntry *entry);
2009
2010
    /* get the page entry for a given ID */
2011
    inline CVmObjPageEntry *get_entry(vm_obj_id_t id) const
2012
    {
2013
        return &pages_[id >> VM_OBJ_PAGE_CNT_LOG2][id & (VM_OBJ_PAGE_CNT - 1)];
2014
    }
2015
    
2016
    /* delete an entry */
2017
    void delete_entry(VMG_ vm_obj_id_t id, CVmObjPageEntry *entry);
2018
2019
    /* allocate a new page of objects */
2020
    void alloc_new_page();
2021
2022
    /* 
2023
     *   initialize a newly-allocated object table entry -- removes the
2024
     *   entry from the free list, marks the entry as allocated, marks it
2025
     *   in or out of the root set as appropriate, and initializes its GC
2026
     *   status as appropriate 
2027
     */
2028
    void init_entry_for_alloc(vm_obj_id_t id, CVmObjPageEntry *entry,
2029
                              int in_root_set, int can_have_refs,
2030
                              int can_have_weak_refs);
2031
2032
    /*
2033
     *   Mark an object as referenced for the garbage collector and add it
2034
     *   to the garbage collector's work queue.  If the object is already
2035
     *   marked as referenced, this does nothing. 
2036
     */
2037
    void add_to_gc_queue(vm_obj_id_t id, uint state)
2038
    {
2039
        /* get the object header and add it to the work queue */
2040
        add_to_gc_queue(id, get_entry(id), state);
2041
    }
2042
2043
    /* add an object to the gc work queue given the object header entry */
2044
    void add_to_gc_queue(vm_obj_id_t id, CVmObjPageEntry *entry, uint state)
2045
    {
2046
        /* 
2047
         *   If it's not already referenced somehow, add it to the queue.
2048
         *   If it's marked as referenced, it's already in the queue (or
2049
         *   it's already been in the queue and it's been processed).
2050
         *   
2051
         *   If the object cannot have references to other objects, don't
2052
         *   add it to the queue - simply elevate its reachability state.
2053
         *   We put objects in the queue in order to trace into the objects
2054
         *   they reference, so an object that can't reference any other
2055
         *   objects doesn't need to be put in the queue.  
2056
         */
2057
        if (entry->can_have_refs_ && entry->reachable_ == VMOBJ_UNREACHABLE)
2058
        {
2059
            /* add it to the work queue */
2060
            entry->next_obj_ = gc_queue_head_;
2061
            gc_queue_head_ = id;
2062
2063
            /* 
2064
             *   Since the entry is unreachable, and unreachable is the
2065
             *   lowest reachability state, we know that 'state' is at least
2066
             *   as reachable, so we can without further condidtions elevate
2067
             *   the reachability state.  (We can thus avoid the extra
2068
             *   comparison we have to do below for other current states.) 
2069
             */
2070
            entry->reachable_ = state;
2071
        }
2072
        else
2073
        {
2074
            /* 
2075
             *   Elevate the reachability state.  Never reduce an object's
2076
             *   reachability state: Finalizer-Reachable is higher than
2077
             *   Unreachable, and Reachable is higher than
2078
             *   Finalizer-Reachable.
2079
             *   
2080
             *   In other words, if an object is already marked Reachable,
2081
             *   never reduce its state to Finalizer-Reachable just because
2082
             *   we find that it can also be reached from a
2083
             *   Finalizer-Reachable object, when we already know that it
2084
             *   can be reached from a root-set object.  
2085
             */
2086
            if (state > entry->reachable_)
2087
                entry->reachable_ = state;
2088
        }
2089
    }
2090
2091
    /* 
2092
     *   Add an object to the finalizer work queue.  An object can only be
2093
     *   in one queue - it can't be in both the finalizer queue and the
2094
     *   main gc work queue.  
2095
     */
2096
    void add_to_finalize_queue(vm_obj_id_t id, CVmObjPageEntry *entry)
2097
    {
2098
        /* mark the object as finalizer-reachable */
2099
        entry->reachable_ = VMOBJ_F_REACHABLE;
2100
        
2101
        /* link it into the finalize list */
2102
        entry->next_obj_ = finalize_queue_head_;
2103
        finalize_queue_head_ = id;
2104
    }
2105
2106
    /*
2107
     *   Count an allocation and check to see if we should run garbage
2108
     *   collection.  We run gc after a certain number of consecutive
2109
     *   allocations to ensure that allocation-intensive operations don't
2110
     *   fill up memory if we can avoid it by removing unreferenced
2111
     *   objects.  
2112
     */
2113
    void alloc_check_gc(VMG_ int do_count)
2114
    {
2115
        /* count the allocation if desired */
2116
        if (do_count)
2117
            ++allocs_since_gc_;
2118
2119
        /* if we've passed our threshhold for collecting garbage, do so now */
2120
        if (gc_enabled_ && allocs_since_gc_ > max_allocs_between_gc_)
2121
            gc_before_alloc(vmg0_);
2122
    }
2123
2124
    /*
2125
     *   Run a garbage collection in preparation to allocate memory.  Runs
2126
     *   one garbage collection pass then one finalizer pass.  
2127
     */
2128
    void gc_before_alloc(VMG0_);
2129
2130
    /* garbage collection: trace objects reachable from the stack */
2131
    void gc_trace_stack(VMG0_);
2132
2133
    /* garbage collection: trace objects reachable from the imports */
2134
    void gc_trace_imports(VMG0_);
2135
2136
    /* garbage collection: trace objects reachable from machine globals */
2137
    void gc_trace_globals(VMG0_);
2138
2139
    /* garbage collection: trace all objects reachable from the work queue */
2140
    void gc_trace_work_queue(VMG_ int trace_transient);
2141
2142
    /* continue a GC pass */
2143
    int gc_pass_continue(VMG_ int trace_transient);
2144
2145
    /* 
2146
     *   set the initial GC conditions for an object -- this puts the
2147
     *   object into the appropriate queue and sets the appropriate
2148
     *   reachability state in preparation for the start of the next GC
2149
     *   pass 
2150
     */
2151
    void gc_set_init_conditions(vm_obj_id_t id,
2152
                                struct CVmObjPageEntry *entry)
2153
    {
2154
        /* 
2155
         *   Mark the object as unreachable -- at the start of each GC pass,
2156
         *   all non-root-set objects must be marked unreachable.  (It
2157
         *   doesn't matter how we mark root set objects, so we simply mark
2158
         *   everything as reachable to avoid an unnecessary test.)  
2159
         */
2160
        entry->reachable_ = VMOBJ_UNREACHABLE;
2161
2162
        /* 
2163
         *   If it's in the root set, add it to the GC work queue -- all
2164
         *   root-set objects must be in the work queue and marked as
2165
         *   reachable at the start of each GC pass.
2166
         */
2167
        if (entry->in_root_set_)
2168
            add_to_gc_queue(id, entry, VMOBJ_REACHABLE);
2169
    }
2170
2171
    /* hash table of objects requested post_load_init() service */
2172
    class CVmHashTable *post_load_init_table_;
2173
2174
    /* 
2175
     *   Head of global object page list.  The global objects are objects
2176
     *   that are always reachable but which weren't necessarily loaded from
2177
     *   the image file; these objects must be treated as dynamically
2178
     *   created for purposes such as saving, but must never be deleted as
2179
     *   long as they are globally reachable.  
2180
     */
2181
    CVmObjGlobPage *globals_;
2182
2183
    /* head of global variable list */
2184
    struct vm_globalvar_t *global_var_head_;
2185
2186
    /*
2187
     *   Master page table.  This is an array of pointers to pages.  Each
2188
     *   page contains a fixed number of slots for fixed-size parts.  
2189
     */
2190
    CVmObjPageEntry **pages_;
2191
2192
    /* number of page slots allocated, and the number actually used */
2193
    size_t page_slots_;
2194
    size_t pages_used_;
2195
2196
    /* first free object */
2197
    vm_obj_id_t first_free_;
2198
2199
    /* first page of saved image data pointers */
2200
    struct vm_image_ptr_page *image_ptr_head_;
2201
2202
    /* last page of saved image data pointers */
2203
    struct vm_image_ptr_page *image_ptr_tail_;
2204
2205
    /* number of image data pointers stored on last image pointer page */
2206
    size_t image_ptr_last_cnt_;
2207
2208
    /* head of garbage collection work queue */
2209
    vm_obj_id_t gc_queue_head_;
2210
2211
    /* head of finalizer queue */
2212
    vm_obj_id_t finalize_queue_head_;
2213
2214
    /* 
2215
     *   Allocations since last garbage collection.  We increment this on
2216
     *   each allocation, and reset it to zero each time we collect
2217
     *   garbage.  When we've performed too many allocations since the
2218
     *   last garbage collection, we force a gc pass. 
2219
     */
2220
    uint allocs_since_gc_;
2221
2222
    /*
2223
     *   Maximum number of allocations before we run the garbage
2224
     *   collector. 
2225
     */
2226
    uint max_allocs_between_gc_;
2227
2228
    /* garbage collection enabled */
2229
    uint gc_enabled_ : 1;
2230
};
2231
2232
/* ------------------------------------------------------------------------ */
2233
/*
2234
 *   An image data pointer.  The object table uses this structure to save
2235
 *   the image data location for a given object when the object requests
2236
 *   that this information be saved.  
2237
 */
2238
struct vm_image_ptr
2239
{
2240
    /* object ID */
2241
    vm_obj_id_t obj_id_;
2242
2243
    /* pointer to image data and length of the data */
2244
    const char *image_data_ptr_;
2245
    size_t image_data_len_;
2246
};
2247
2248
/*
2249
 *   Maximum number of image pointers stored per image pointer page 
2250
 */
2251
const size_t VM_IMAGE_PTRS_PER_PAGE = 400;
2252
2253
/*
2254
 *   A page of image pointers.  
2255
 */
2256
struct vm_image_ptr_page
2257
{
2258
    /* next page in the list of pages */
2259
    vm_image_ptr_page *next_;
2260
2261
    /* array of image pointers */
2262
    vm_image_ptr ptrs_[VM_IMAGE_PTRS_PER_PAGE];
2263
};
2264
2265
2266
/* ------------------------------------------------------------------------ */
2267
/*
2268
 *   The variable-size parts are stored in a heap separately from the object
2269
 *   headers.  Because the variable-size objects can expand or contract
2270
 *   dynamically, objects in the heap can move to new addresses.
2271
 *   
2272
 *   A particular block of memory in the heap is referenced by zero or one
2273
 *   object at any given time; a heap block is never shared among multiple
2274
 *   objects.  Furthermore, the only thing that can directly reference a
2275
 *   heap block is an object's fixed portion (through its extension
2276
 *   pointer).  Hence, we manage the variable heap directly through the
2277
 *   object headers: when an object becomes unreferenced, we explicitly free
2278
 *   the associated variable part.
2279
 *   
2280
 *   It is possible for a single object to allocate more than one heap
2281
 *   block.  In practice, most objects will allocate only one heap block (if
2282
 *   they allocate a heap block at all), but there is no reason an object
2283
 *   can't allocate more than one block.
2284
 *   
2285
 *   Note that polymorphism in the variable parts is handled via the fixed
2286
 *   part.  Because the fixed part is responsible for interactions with the
2287
 *   variable part, each fixed part implementation will be mated to a
2288
 *   particular variable part implementation.  These implementations may
2289
 *   themselves be polymorphic, of course, but this isn't directly necessary
2290
 *   in the base class.  
2291
 */
2292
2293
/*
2294
 *   Variable-size object heap interface.
2295
 */
2296
class CVmVarHeap
2297
{
2298
public:
2299
    virtual ~CVmVarHeap() { }
2300
    
2301
    /*
2302
     *   Initialize.  The global object table is valid at this point, and
2303
     *   will remain valid until after terminate() is called.  
2304
     */
2305
    virtual void init(VMG0_) = 0;
2306
2307
    /*
2308
     *   Terminate.  The global object table will remain valid until after
2309
     *   this function returns.
2310
     *   
2311
     *   The object table is expected to free each object's variable part
2312
     *   explicitly, so this function need not deallocate the memory used
2313
     *   by variable parts.  
2314
     */
2315
    virtual void terminate() = 0;
2316
    
2317
    /* 
2318
     *   Allocate a variable-size part.  'siz' is the size requested in
2319
     *   bytes, and 'obj' is a pointer to the object header.  The object
2320
     *   header will never move in memory, so this pointer is valid for as
2321
     *   long as the object remains allocated, hence the heap manager can
2322
     *   store the header pointer with the memory block if desired.  
2323
     */
2324
    virtual void *alloc_mem(size_t siz, CVmObject *obj) = 0;
2325
2326
    /*
2327
     *   Resize a variable-size part.  The 'siz' is the new size requested
2328
     *   in bytes, and 'varpart' is the old variable-size memory block.
2329
     *   This should return a new variable-size memory block containing a
2330
     *   copy of the data in the original block, but the block should be
2331
     *   resized to at least 'siz' bytes.  Returns a pointer to the new
2332
     *   block, which may move to a new memory location.
2333
     *   
2334
     *   If we move the memory to a new location, we are responsible for
2335
     *   freeing the old block of memory that the variable part occupied,
2336
     *   if necessary.
2337
     *   
2338
     *   We do not need to worry about informing the object header of any
2339
     *   change to the address of the variable part; the caller is
2340
     *   responsible for making any necessary changes in the object header
2341
     *   based on our return value.  
2342
     */
2343
    virtual void *realloc_mem(size_t siz, void *varpart,
2344
                              CVmObject *obj) = 0;
2345
2346
    /*
2347
     *   Free an object in the heap.  The object header may no longer be
2348
     *   valid after this function returns, so the heap manager should not
2349
     *   store the object header pointer after this function returns.  
2350
     */
2351
    virtual void free_mem(void *varpart) = 0;
2352
2353
#if 0
2354
    /* 
2355
     *   This is not currently used by the heap implementation (and doesn't
2356
     *   even have any theoretical reason to exist at the moment, since the
2357
     *   adoption of a non-moveable heap policy and the removal of
2358
     *   move_var_part()), so it's been removed to avoid the unnecessary
2359
     *   overhead of calling an empty method. 
2360
     */
2361
    
2362
    /*
2363
     *   Receive notification of the completion of a garbage collection
2364
     *   pass.  If the heap manager is capable of closing gaps in memory
2365
     *   by moving objects around, this is a good time to perform this
2366
     *   work, since we have just deleted all unreachable objects.
2367
     *   
2368
     *   If any object moves during processing here, we must call the
2369
     *   associated CVmObject's move_var_part() routine to tell it about
2370
     *   its new location.
2371
     *   
2372
     *   This routine isn't required to do anything at all.  It's simply
2373
     *   provided as a notification for heap managers that can take
2374
     *   advantage of the opportunity to compact the heap.  
2375
     */
2376
    virtual void finish_gc_pass() = 0;
2377
#endif
2378
};
2379
2380
2381
/* ------------------------------------------------------------------------ */
2382
/*
2383
 *   Simple variable-size object heap implementation.  This implementation
2384
 *   uses the normal C heap manager (malloc and free) to manage the heap.
2385
 */
2386
2387
/*
2388
 *   block header - we use the header to keep track of the size of the
2389
 *   object's data area
2390
 */
2391
struct CVmVarHeapMallocHdr
2392
{
2393
    /* size of the object */
2394
    size_t siz_;
2395
};
2396
2397
/*
2398
 *   heap implementation 
2399
 */
2400
class CVmVarHeapMalloc: public CVmVarHeap
2401
{
2402
public:
2403
    CVmVarHeapMalloc() { }
2404
    ~CVmVarHeapMalloc() { }
2405
2406
    /* initialize */
2407
    void init(VMG0_) { }
2408
2409
    /* terminate */
2410
    void terminate() { }
2411
2412
    /* allocate memory */
2413
    void *alloc_mem(size_t siz, CVmObject *)
2414
    {
2415
        CVmVarHeapMallocHdr *hdr;
2416
        
2417
        /* allocate space for the block plus the header */
2418
        hdr = (CVmVarHeapMallocHdr *)
2419
              t3malloc(siz + sizeof(CVmVarHeapMallocHdr));
2420
2421
        /* set up the header */
2422
        hdr->siz_ = siz;
2423
2424
        /* return the start of the part immediately after the header */
2425
        return (void *)(hdr + 1);
2426
    }
2427
2428
    /* reallocate memory */
2429
    void *realloc_mem(size_t siz, void *varpart, CVmObject *)
2430
    {
2431
        CVmVarHeapMallocHdr *hdr;
2432
2433
        /* 
2434
         *   get the original header, which immediately precedes the
2435
         *   original variable part in memory 
2436
         */
2437
        hdr = ((CVmVarHeapMallocHdr *)varpart) - 1;
2438
2439
        /* 
2440
         *   Reallocate it - the header is the actual memory block as far
2441
         *   as malloc was concerned, so realloc that.  Note that we must
2442
         *   add in the space needed for our header in the resized block.  
2443
         */
2444
        hdr = (CVmVarHeapMallocHdr *)
2445
              t3realloc(hdr, siz + sizeof(CVmVarHeapMallocHdr));
2446
2447
        /* adjust the size of the block in the header */
2448
        hdr->siz_ = siz;
2449
2450
        /* return the part immediately after the header */
2451
        return (void *)(hdr + 1);
2452
    }
2453
2454
    /* free memory */
2455
    void free_mem(void *varpart)
2456
    {
2457
        CVmVarHeapMallocHdr *hdr;
2458
2459
        /* 
2460
         *   get the original header, which immediately precedes the
2461
         *   original variable part in memory 
2462
         */
2463
        hdr = ((CVmVarHeapMallocHdr *)varpart) - 1;
2464
2465
        /* 
2466
         *   free the header, which is the actual memory block as far as
2467
         *   malloc was concerned 
2468
         */
2469
        t3free(hdr);
2470
    }
2471
2472
#if 0
2473
    /* removed with the removal of move_var_part() */
2474
    
2475
    /* 
2476
     *   complete garbage collection pass - we don't have to do anything
2477
     *   here, since we can't move objects around to consolidate free
2478
     *   space 
2479
     */
2480
    void finish_gc_pass() { }
2481
#endif
2482
    
2483
private:
2484
};
2485
2486
/* ------------------------------------------------------------------------ */
2487
/*
2488
 *   Hybrid cell-based and malloc-based heap allocator.  This heap manager
2489
 *   uses arrays of fixed-size blocks to allocate small objects, and falls
2490
 *   back on malloc for allocating large objects.  Small-block allocations
2491
 *   and frees are fast, require very little memory overhead, and minimize
2492
 *   heap fragmentation by packing large blocks of fixed-size items into
2493
 *   arrays, then suballocating out of free lists built from the arrays.  
2494
 */
2495
2496
/*
2497
 *   Each item we allocate from a small-object array has a header that
2498
 *   points back to the array's master list.  This is necessary so that we
2499
 *   can put the object back in the appropriate free list when it is
2500
 *   deleted.  
2501
 */
2502
struct CVmVarHeapHybrid_hdr
2503
{
2504
    /* 
2505
     *   the block interface that allocated this object -- we use this
2506
     *   when we free the object so that we can call the free() routine in
2507
     *   the block manager that originally did the allocation 
2508
     */
2509
    class CVmVarHeapHybrid_block *block;
2510
};
2511
2512
/*
2513
 *   Hybrid heap allocator - sub-block interface.  Each small-object cell
2514
 *   list is represented by one of these objects, as is the fallback
2515
 *   malloc allocator.  
2516
 */
2517
class CVmVarHeapHybrid_block
2518
{
2519
public:
2520
    /* allocate memory */
2521
    virtual struct CVmVarHeapHybrid_hdr *alloc(size_t siz) = 0;
2522
2523
    /* free memory */
2524
    virtual void free(struct CVmVarHeapHybrid_hdr *) = 0;
2525
2526
    /*
2527
     *   Reallocate memory.  If necessary, allocate new memory, copy the
2528
     *   data to the new memory, and delete the old memory.  We receive
2529
     *   the heap manager as an argument so that we can call it to
2530
     *   allocate new memory if necessary.  
2531
     */
2532
    virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz,
2533
                          class CVmObject *obj) = 0;
2534
};
2535
2536
/*
2537
 *   Malloc suballocator 
2538
 */
2539
class CVmVarHeapHybrid_malloc: public CVmVarHeapHybrid_block
2540
{
2541
public:
2542
    /* allocate memory */
2543
    virtual struct CVmVarHeapHybrid_hdr *alloc(size_t siz)
2544
    {
2545
        CVmVarHeapHybrid_hdr *ptr;
2546
        
2547
        /* adjust the size to add in the required header */
2548
        siz = osrndsz(siz + sizeof(CVmVarHeapHybrid_hdr));
2549
        
2550
        /* allocate directly via the default system heap manager */
2551
        ptr = (CVmVarHeapHybrid_hdr *)t3malloc(siz);
2552
2553
        /* fill in the header */
2554
        ptr->block = this;
2555
2556
        /* return the new block */
2557
        return ptr;
2558
    }
2559
2560
    /* release memory */
2561
    virtual void free(CVmVarHeapHybrid_hdr *mem)
2562
    {
2563
        /* release the memory directly to the default system heap manager */
2564
        t3free(mem);
2565
    }
2566
2567
    /* reallocate memory */
2568
    virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz,
2569
                          CVmObject *)
2570
    {
2571
        CVmVarHeapHybrid_hdr *ptr;
2572
        
2573
        /* adjust the new size to add in the required header */
2574
        siz = osrndsz(siz + sizeof(CVmVarHeapHybrid_hdr));
2575
2576
        /* reallocate the block */
2577
        ptr = (CVmVarHeapHybrid_hdr *)t3realloc(mem, siz);
2578
2579
        /* fill in the header in the new block */
2580
        ptr->block = this;
2581
2582
        /* return the caller-visible part of the new block */
2583
        return (void *)(ptr + 1);
2584
    }
2585
};
2586
2587
/*
2588
 *   Small-object array list head.  We suballocate small objects from
2589
 *   these arrays.  We maintain one of these objects for each distinct
2590
 *   cell size; this object manages all of the storage for blocks of the
2591
 *   cell size.  
2592
 */
2593
class CVmVarHeapHybrid_head: public CVmVarHeapHybrid_block
2594
{
2595
public:
2596
    CVmVarHeapHybrid_head(class CVmVarHeapHybrid *mem_mgr,
2597
                          size_t cell_size, size_t page_count)
2598
    {
2599
        /* remember our memory manager */
2600
        mem_mgr_ = mem_mgr;
2601
        
2602
        /* remember our cell size and number of items per array */
2603
        cell_size_ = cell_size;
2604
        page_count_ = page_count;
2605
2606
        /* we have nothing in our free list yet */
2607
        first_free_ = 0;
2608
    }
2609
    
2610
    /* allocate an object from my pool, expanding the pool if necessary */
2611
    CVmVarHeapHybrid_hdr *alloc(size_t siz);
2612
2613
    /* free a cell */
2614
    void free(CVmVarHeapHybrid_hdr *mem);
2615
2616
    /* reallocate */
2617
    virtual void *realloc(struct CVmVarHeapHybrid_hdr *mem, size_t siz,
2618
                          class CVmObject *obj);
2619
2620
    /* get the cell size for this cell manager */
2621
    size_t get_cell_size() const { return cell_size_; }
2622
2623
private:
2624
    /* size of each cell in the array */
2625
    size_t cell_size_;
2626
2627
    /* number of items we allocate per array */
2628
    size_t page_count_;
2629
2630
    /* head of the free list of cells in this array */
2631
    void *first_free_;
2632
2633
    /* our memory manager */
2634
    CVmVarHeapHybrid *mem_mgr_;
2635
};
2636
2637
/*
2638
 *   Small-object array list block.  We dynamically allocate these array
2639
 *   blocks as needed to hold blocks of a particular size.  
2640
 */
2641
struct CVmVarHeapHybrid_array
2642
{
2643
    /* next array in the master list */
2644
    CVmVarHeapHybrid_array *next_array;
2645
2646
    /* 
2647
     *   memory for allocation (we over-allocate the structure to make
2648
     *   room for some number of our fixed-size cells) 
2649
     */
2650
    char mem[1];
2651
};
2652
2653
/*
2654
 *   heap implementation 
2655
 */
2656
class CVmVarHeapHybrid: public CVmVarHeap
2657
{
2658
    friend class CVmVarHeapHybrid_head;
2659
    
2660
public:
2661
    CVmVarHeapHybrid();
2662
    ~CVmVarHeapHybrid();
2663
2664
    /* initialize */
2665
    void init(VMG0_) { }
2666
2667
    /* terminate */
2668
    void terminate() { }
2669
2670
    /* allocate memory */
2671
    void *alloc_mem(size_t siz, CVmObject *obj);
2672
2673
    /* reallocate memory */
2674
    void *realloc_mem(size_t siz, void *varpart, CVmObject *obj);
2675
2676
    /* free memory */
2677
    void free_mem(void *varpart);
2678
2679
#if 0
2680
    /* removed with the removal of move_var_part() */
2681
    
2682
    /* 
2683
     *   complete garbage collection pass - we don't have to do anything
2684
     *   here, since we can't move objects around to consolidate free
2685
     *   space 
2686
     */
2687
    void finish_gc_pass() { }
2688
#endif
2689
    
2690
private:
2691
    /* 
2692
     *   Head of list of arrays.  We keep this list so that we can delete
2693
     *   all of the arrays when we delete this heap manager object itself.
2694
     */
2695
    CVmVarHeapHybrid_array *first_array_;
2696
2697
    /* 
2698
     *   Array of cell-based subheap managers.  This array will be ordered
2699
     *   from smallest to largest, so we can search it for the best fit to
2700
     *   a requested size. 
2701
     */
2702
    CVmVarHeapHybrid_head **cell_heaps_;
2703
2704
    /* number of cell heap managers */
2705
    size_t cell_heap_cnt_;
2706
2707
    /*
2708
     *   Our fallback malloc heap manager.  We'll use this allocator for
2709
     *   any blocks that we can't allocate from one of our cell-based
2710
     *   memory managers. 
2711
     */
2712
    CVmVarHeapHybrid_malloc *malloc_heap_;
2713
};
2714
2715
/* ------------------------------------------------------------------------ */
2716
/*
2717
 *   Memory Manager - this is the primary interface to the object memory
2718
 *   subsystem.  
2719
 */
2720
class CVmMemory
2721
{
2722
public:
2723
    /* create the memory manager, using a given variable-size heap */
2724
    CVmMemory(VMG_ CVmVarHeap *varheap);
2725
2726
    /* delete the memory manager */
2727
    ~CVmMemory()
2728
    {
2729
        /* tell the variable-size heap to disengage */
2730
        varheap_->terminate();
2731
    }
2732
2733
    /* get the variable heap manager */
2734
    CVmVarHeap *get_var_heap() const { return varheap_; }
2735
2736
private:
2737
    /* variable-size object heap */
2738
    CVmVarHeap *varheap_;
2739
2740
    /* our constant pool manager */
2741
    class CVmPool *constant_pool_;
2742
};
2743
2744
/* ------------------------------------------------------------------------ */
2745
/*
2746
 *   Allocate a new object ID 
2747
 */
2748
inline vm_obj_id_t vm_new_id(VMG_ int in_root_set)
2749
{
2750
    /* ask the global object table to allocate a new ID */
2751
    return G_obj_table->alloc_obj(vmg_ in_root_set);
2752
}
2753
2754
/*
2755
 *   Allocate a new object ID, setting GC characteristics 
2756
 */
2757
inline vm_obj_id_t vm_new_id(VMG_ int in_root_set, int can_have_refs,
2758
                             int can_have_weak_refs)
2759
{
2760
    /* ask the global object table to allocate a new ID */
2761
    return G_obj_table->alloc_obj(vmg_ in_root_set, can_have_refs,
2762
                                  can_have_weak_refs);
2763
}
2764
2765
/*
2766
 *   Given an object ID, get a pointer to the object 
2767
 */
2768
inline CVmObject *vm_objp(VMG_ vm_obj_id_t id)
2769
{
2770
    /* ask the global object table to translate the ID */
2771
    return G_obj_table->get_obj(id);
2772
}
2773
2774
#endif /* VMOBJ_H */
2775
2776
/*
2777
 *   Register the root object class
2778
 */
2779
VM_REGISTER_METACLASS(CVmObject)