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 */
get_metaclass_reg()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      */
is_of_metaclass(class CVmMetaclass * meta)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      */
create_instance(VMG_ vm_obj_id_t self,const uchar ** pc_ptr,uint argc)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      */
has_finalizer(VMG_ vm_obj_id_t)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      */
invoke_finalizer(VMG_ vm_obj_id_t)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      */
is_class_object(VMG_ vm_obj_id_t)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      */
get_superclass_count(VMG_ vm_obj_id_t)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      */
provides_props(VMG0_)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      */
enum_props(VMG_ vm_obj_id_t self,void (* cb)(VMG_ void * ctx,vm_obj_id_t self,vm_prop_id_t prop,const vm_val_t * val),void * cbctx)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      */
discard_undo(VMG_ struct CVmUndoRecord *)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      */
post_load_init(VMG_ vm_obj_id_t)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      */
reload_from_image(VMG_ vm_obj_id_t self,const char * ptr,size_t siz)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      */
reset_to_image(VMG_ vm_obj_id_t)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      */
is_changed_since_load()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      */
equals(VMG_ vm_obj_id_t self,const vm_val_t * val,int)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      */
compare_to(VMG_ vm_obj_id_t,const vm_val_t *)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         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      */
calc_hash(VMG_ vm_obj_id_t self,int)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      */
add_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *)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      */
sub_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *)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 */
mul_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *)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 */
div_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *)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 */
neg_val(VMG_ vm_val_t *,vm_obj_id_t self)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      */
get_coll_addsub_rhs_ele_cnt(VMG0_)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 */
get_coll_addsub_rhs_ele(VMG_ vm_val_t * retval,vm_obj_id_t self,size_t)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      */
index_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *)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      */
set_index_val(VMG_ vm_val_t *,vm_obj_id_t,const vm_val_t *,const vm_val_t *)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      */
cast_to_string(VMG_ vm_obj_id_t,vm_val_t *)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         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      */
get_as_list()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      */
get_as_string(VMG0_)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      */
rebuild_image(VMG_ char *,ulong)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      */
reserve_const_data(VMG_ class CVmConstMapper *,vm_obj_id_t)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      */
convert_to_const_data(VMG_ class CVmConstMapper *,vm_obj_id_t)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      */
get_convert_to_const_data_type()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      */
get_prop_check_argc(vm_val_t * val,uint * argc,const CVmNativeCodeDesc * desc)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      */
get_reg_idx()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      */
get_supermeta_count(VMG0_)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      */
set_metaclass_reg_index(uint idx)1086     void set_metaclass_reg_index(uint idx) { meta_reg_idx_ = idx; }
1087 
1088     /* most metaclasses are simply derived from Object */
get_supermeta_reg()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 */
get_meta_name()1114     const char *get_meta_name() const { return "root-object/030004"; }
1115 
1116     /* create an instance - this class cannot be instantiated */
create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)1117     vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
1118     {
1119         err_throw(VMERR_BAD_DYNAMIC_NEW);
1120         return VM_INVALID_OBJ;
1121     }
1122 
1123     /* create an object - this class cannot be instantiated */
create_for_image_load(VMG_ vm_obj_id_t id)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 */
create_for_restore(VMG_ vm_obj_id_t id)1128     void create_for_restore(VMG_ vm_obj_id_t id)
1129         { err_throw(VMERR_BAD_STATIC_NEW); }
1130 
1131     /* call a static property */
call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)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 */
get_supermeta_count(VMG0_)1141     int get_supermeta_count(VMG0_) const { return 0; }
get_supermeta(VMG_ int)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      */
is_meta_instance_of(VMG_ vm_obj_id_t obj)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 */
get_supermeta_reg()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 */
get_entry(ulong idx)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:
CVmObjGlobPage()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 
~CVmObjGlobPage()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      */
add_entry(vm_obj_id_t obj)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 */
get_vm_objCVmObjPageEntry1380     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      */
is_deletableCVmObjPageEntry1483     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      */
is_in_undoCVmObjPageEntry1495     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      */
is_saveableCVmObjPageEntry1518     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      */
is_persistentCVmObjPageEntry1535     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 */
CVmObjTable()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 */
get_obj(vm_obj_id_t 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 */
alloc_obj(VMG_ int in_root_set)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      */
alloc_obj_with_id(vm_obj_id_t id,int in_root_set)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_);
gc_pass_continue(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      */
is_obj_deletable(vm_obj_id_t obj)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      */
mark_obj_undo_rec(VMG_ vm_obj_id_t obj,struct CVmUndoRecord * undo_rec)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      */
remove_obj_stale_undo_weak_ref(VMG_ vm_obj_id_t obj,struct CVmUndoRecord * undo_rec)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      */
is_obj_in_undo(vm_obj_id_t obj)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      */
is_obj_deletable(const vm_val_t * val)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      */
is_obj_saveable(vm_obj_id_t obj)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      */
is_obj_persistent(vm_obj_id_t obj)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 */
is_obj_transient(vm_obj_id_t obj)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 */
set_obj_transient(vm_obj_id_t obj)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 */
set_obj_gc_characteristics(vm_obj_id_t obj,int can_have_refs,int can_have_weak_refs)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 */
is_obj_in_root_set(vm_obj_id_t obj)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      */
mark_all_refs(vm_obj_id_t obj,uint state)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      */
get_max_used_obj_id()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 */
is_obj_id_valid(vm_obj_id_t obj)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      */
get_obj_internal_state(vm_obj_id_t id)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 */
get_entry(vm_obj_id_t 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      */
add_to_gc_queue(vm_obj_id_t id,uint state)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 */
add_to_gc_queue(vm_obj_id_t id,CVmObjPageEntry * entry,uint state)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      */
add_to_finalize_queue(vm_obj_id_t id,CVmObjPageEntry * entry)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      */
alloc_check_gc(VMG_ int do_count)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      */
gc_set_init_conditions(vm_obj_id_t id,struct CVmObjPageEntry * entry)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:
~CVmVarHeap()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:
CVmVarHeapMalloc()2403     CVmVarHeapMalloc() { }
~CVmVarHeapMalloc()2404     ~CVmVarHeapMalloc() { }
2405 
2406     /* initialize */
init(VMG0_)2407     void init(VMG0_) { }
2408 
2409     /* terminate */
terminate()2410     void terminate() { }
2411 
2412     /* allocate memory */
alloc_mem(size_t siz,CVmObject *)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 */
realloc_mem(size_t siz,void * varpart,CVmObject *)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 */
free_mem(void * varpart)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 */
alloc(size_t siz)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 */
free(CVmVarHeapHybrid_hdr * mem)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 */
realloc(struct CVmVarHeapHybrid_hdr * mem,size_t siz,CVmObject *)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:
CVmVarHeapHybrid_head(class CVmVarHeapHybrid * mem_mgr,size_t cell_size,size_t page_count)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 */
get_cell_size()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 */
init(VMG0_)2665     void init(VMG0_) { }
2666 
2667     /* terminate */
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 */
~CVmMemory()2727     ~CVmMemory()
2728     {
2729         /* tell the variable-size heap to disengage */
2730         varheap_->terminate();
2731     }
2732 
2733     /* get the variable heap manager */
get_var_heap()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  */
vm_new_id(VMG_ int in_root_set)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  */
vm_new_id(VMG_ int in_root_set,int can_have_refs,int can_have_weak_refs)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  */
vm_objp(VMG_ vm_obj_id_t id)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)
2780