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