1 module wren.value;
2 import wren.common;
3 import wren.math;
4 import wren.utils;
5 
6 @nogc:
7 // This defines the built-in types and their core representations in memory.
8 // Since Wren is dynamically typed, any variable can hold a value of any type,
9 // and the type can change at runtime. Implementing this efficiently is
10 // critical for performance.
11 //
12 // The main type exposed by this is [Value]. A C variable of that type is a
13 // storage location that can hold any Wren value. The stack, module variables,
14 // and instance fields are all implemented in C as variables of type Value.
15 //
16 // The built-in types for booleans, numbers, and null are unboxed: their value
17 // is stored directly in the Value, and copying a Value copies the value. Other
18 // types--classes, instances of classes, functions, lists, and strings--are all
19 // reference types. They are stored on the heap and the Value just stores a
20 // pointer to it. Copying the Value copies a reference to the same object. The
21 // Wren implementation calls these "Obj", or objects, though to a user, all
22 // values are objects.
23 //
24 // There is also a special singleton value "undefined". It is used internally
25 // but never appears as a real value to a user. It has two uses:
26 //
27 // - It is used to identify module variables that have been implicitly declared
28 //   by use in a forward reference but not yet explicitly declared. These only
29 //   exist during compilation and do not appear at runtime.
30 //
31 // - It is used to represent unused map entries in an ObjMap.
32 //
33 // There are two supported Value representations. The main one uses a technique
34 // called "NaN tagging" (explained in detail below) to store a number, any of
35 // the value types, or a pointer, all inside one double-precision floating
36 // point number. A larger, slower, Value type that uses a struct to store these
37 // is also supported, and is useful for debugging the VM.
38 //
39 // The representation is controlled by the `WREN_NAN_TAGGING` define. If that's
40 // defined, Nan tagging is used.
41 
42 // Identifies which specific type a heap-allocated object is.
43 enum ObjType {
44   OBJ_CLASS,
45   OBJ_CLOSURE,
46   OBJ_FIBER,
47   OBJ_FN,
48   OBJ_FOREIGN,
49   OBJ_INSTANCE,
50   OBJ_LIST,
51   OBJ_MAP,
52   OBJ_MODULE,
53   OBJ_RANGE,
54   OBJ_STRING,
55   OBJ_UPVALUE
56 }
57 
58 // Base struct for all heap-allocated objects.
59 struct Obj
60 {
61     ObjType type;
62     bool isDark;
63 
64     // The object's class.
65     ObjClass* classObj;
66 
67     // The next object in the linked list of all currently allocated objects.
68     Obj* next;
69 } 
70 
71 static if (WREN_NAN_TAGGING)
72 {
73     alias Value = ulong;
74 }
75 else
76 {
77     enum ValueType {
78         VAL_FALSE,
79         VAL_NULL,
80         VAL_NUM,
81         VAL_TRUE,
82         VAL_UNDEFINED,
83         VAL_OBJ
84     }
85 
86     struct Value {
87         ValueType type;
88         union AsValue {
89             double num;
90             Obj* obj;
91         }
92         AsValue as;
93     }
94 }
95 
96 mixin(DECLARE_BUFFER("Value", "Value"));
97 
98 // These macros cast a Value to one of the specific object types. These do *not*
99 // perform any validation, so must only be used after the Value has been
100 // ensured to be the right type.
101 ObjClass* AS_CLASS(Value value)
102 {
103     return cast(ObjClass*)AS_OBJ(value);
104 }
105 
106 ObjClosure* AS_CLOSURE(Value value)
107 {
108     return cast(ObjClosure*)AS_OBJ(value);
109 }
110 
111 ObjFiber* AS_FIBER(Value value)
112 {
113     return cast(ObjFiber*)AS_OBJ(value);
114 }
115 
116 ObjFn* AS_FN(Value value)
117 {
118     return cast(ObjFn*)AS_OBJ(value);
119 }
120 
121 ObjForeign* AS_FOREIGN(Value value)
122 {
123     return cast(ObjForeign*)AS_OBJ(value);
124 }
125 
126 ObjInstance* AS_INSTANCE(Value value)
127 {
128     return cast(ObjInstance*)AS_OBJ(value);
129 }
130 
131 ObjList* AS_LIST(Value value)
132 {
133     return cast(ObjList*)AS_OBJ(value);
134 }
135 
136 ObjMap* AS_MAP(Value value)
137 {
138     return cast(ObjMap*)AS_OBJ(value);
139 }
140 
141 ObjModule* AS_MODULE(Value value)
142 {
143     return cast(ObjModule*)AS_OBJ(value);
144 }
145 
146 double AS_NUM(Value value)
147 {
148     return wrenValueToNum(value);
149 }
150 
151 ObjRange* AS_RANGE(Value value)
152 {
153     return cast(ObjRange*)AS_OBJ(value);
154 }
155 
156 ObjString* AS_STRING(Value value)
157 {
158     return cast(ObjString*)AS_OBJ(value);
159 }
160 
161 const(char)* AS_CSTRING(Value value)
162 {
163     return (AS_STRING(value).value.ptr);
164 }
165 
166 // These macros promote a primitive C value to a full Wren Value. There are
167 // more defined below that are specific to the Nan tagged or other
168 // representation.
169 Value BOOL_VAL(bool value)
170 {
171     return value ? TRUE_VAL : FALSE_VAL;
172 }
173 
174 Value NUM_VAL(double num)
175 {
176     return wrenNumToValue(num);
177 }
178 
179 Value OBJ_VAL(T)(T* obj)
180 {
181     return wrenObjectToValue(cast(Obj*)obj);
182 }
183 
184 // These perform type tests on a Value, returning `true` if the Value is of the
185 // given type.
186 bool IS_BOOL(Value value) {
187     return wrenIsBool(value);
188 }
189 
190 bool IS_CLASS(Value value) {
191     return wrenIsObjType(value, ObjType.OBJ_CLASS);
192 }
193 
194 bool IS_CLOSURE(Value value) {
195     return wrenIsObjType(value, ObjType.OBJ_CLOSURE);
196 }
197 
198 bool IS_FIBER(Value value) {
199     return wrenIsObjType(value, ObjType.OBJ_FIBER);
200 }
201 
202 bool IS_FN(Value value) {
203     return wrenIsObjType(value, ObjType.OBJ_FN);
204 }
205 
206 bool IS_FOREIGN(Value value) {
207     return wrenIsObjType(value, ObjType.OBJ_FOREIGN);
208 }
209 
210 bool IS_INSTANCE(Value value) {
211     return wrenIsObjType(value, ObjType.OBJ_INSTANCE);
212 }
213 
214 bool IS_LIST(Value value) {
215     return wrenIsObjType(value, ObjType.OBJ_LIST);
216 }
217 
218 bool IS_MAP(Value value) {
219     return wrenIsObjType(value, ObjType.OBJ_MAP);
220 }
221 
222 bool IS_RANGE(Value value) {
223     return wrenIsObjType(value, ObjType.OBJ_RANGE);
224 }
225 
226 bool IS_STRING(Value value) {
227     return wrenIsObjType(value, ObjType.OBJ_STRING);
228 }
229 
230 // A heap-allocated string object.
231 struct ObjString
232 {
233     Obj obj;
234 
235     // Number of bytes in the string, not including the null terminator.
236     uint length;
237 
238     // The hash value of the string's contents.
239     uint hash;
240 
241     // Inline array of the string's bytes followed by a null terminator.
242     char[] value;
243 }
244 
245 mixin(DECLARE_BUFFER("String", "ObjString*"));
246 
247 alias SymbolTable = StringBuffer;
248 
249 // This import has to be here. Otherwise, we run into weird undefined identifier problems.
250 import wren.vm;
251 
252 // Initializes the symbol table.
253 void wrenSymbolTableInit(SymbolTable* symbols) @nogc
254 {
255     wrenStringBufferInit(symbols);
256 }
257 
258 // Frees all dynamically allocated memory used by the symbol table, but not the
259 // SymbolTable itself.
260 void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols) @nogc
261 {
262     wrenStringBufferClear(vm, symbols);
263 }
264 
265 // Adds name to the symbol table. Returns the index of it in the table.
266 int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols,
267                        const(char)* name, size_t length) @nogc
268 {
269     ObjString* symbol = AS_STRING(wrenNewStringLength(vm, name, length));
270   
271     wrenPushRoot(vm, &symbol.obj);
272     wrenStringBufferWrite(vm, symbols, symbol);
273     wrenPopRoot(vm);
274   
275     return symbols.count - 1;
276 }
277 
278 // Adds name to the symbol table. Returns the index of it in the table. Will
279 // use an existing symbol if already present.
280 int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
281                           const(char)* name, size_t length) @nogc
282 {
283     // See if the symbol is already defined.
284     int existing = wrenSymbolTableFind(symbols, name, length);
285     if (existing != -1) return existing;
286 
287     // New symbol, so add it.
288     return wrenSymbolTableAdd(vm, symbols, name, length);
289 }
290 
291 // Looks up name in the symbol table. Returns its index if found or -1 if not.
292 int wrenSymbolTableFind(const SymbolTable* symbols,
293                         const(char)* name, size_t length) @nogc
294 {
295     // See if the symbol is already defined.
296     // TODO: O(n). Do something better.
297     for (int i = 0; i < symbols.count; i++)
298     {
299         if (wrenStringEqualsCString(cast(ObjString*)symbols.data[i], name, length)) return i;
300     }
301 
302     return -1;
303 }
304 
305 void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable) @nogc
306 {
307     for (int i = 0; i < symbolTable.count; i++)
308     {
309         wrenGrayObj(vm, &symbolTable.data[i].obj);
310     }
311 
312     // Keep track of how much memory is still in use.
313     vm.bytesAllocated += symbolTable.capacity * (*symbolTable.data).sizeof;
314 }
315 
316 // The dynamically allocated data structure for a variable that has been used
317 // by a closure. Whenever a function accesses a variable declared in an
318 // enclosing function, it will get to it through this.
319 //
320 // An upvalue can be either "closed" or "open". An open upvalue points directly
321 // to a [Value] that is still stored on the fiber's stack because the local
322 // variable is still in scope in the function where it's declared.
323 //
324 // When that local variable goes out of scope, the upvalue pointing to it will
325 // be closed. When that happens, the value gets copied off the stack into the
326 // upvalue itself. That way, it can have a longer lifetime than the stack
327 // variable.
328 struct ObjUpvalue
329 {
330     // The object header. Note that upvalues have this because they are garbage
331     // collected, but they are not first class Wren objects.
332     Obj obj;
333 
334     // Pointer to the variable this upvalue is referencing.
335     Value* value;
336 
337     // If the upvalue is closed (i.e. the local variable it was pointing to has
338     // been popped off the stack) then the closed-over value will be hoisted out
339     // of the stack into here. [value] will then be changed to point to this.
340     Value closed;
341 
342     // Open upvalues are stored in a linked list by the fiber. This points to the
343     // next upvalue in that list.    
344     ObjUpvalue* next;
345 }
346 
347 
348 
349 // TODO: See if it's actually a perf improvement to have this in a separate
350 // struct instead of in ObjFn.
351 // Stores debugging information for a function used for things like stack
352 // traces.
353 struct FnDebug
354 {
355     // The name of the function. Heap allocated and owned by the FnDebug.
356     char* name;
357 
358     // An array of line numbers. There is one element in this array for each
359     // bytecode in the function's bytecode array. The value of that element is
360     // the line in the source code that generated that instruction.
361     IntBuffer sourceLines;
362 }
363 
364 // A loaded module and the top-level variables it defines.
365 //
366 // While this is an Obj and is managed by the GC, it never appears as a
367 // first-class object in Wren.
368 struct ObjModule
369 {
370     Obj obj;
371 
372     // The currently defined top-level variables.
373     ValueBuffer variables;
374 
375     // Symbol table for the names of all module variables. Indexes here directly
376     // correspond to entries in [variables].
377     SymbolTable variableNames;
378 
379     // The name of the module.
380     ObjString* name;
381 }
382 
383 // A function object. It wraps and owns the bytecode and other debug information
384 // for a callable chunk of code.
385 //
386 // Function objects are not passed around and invoked directly. Instead, they
387 // are always referenced by an [ObjClosure] which is the real first-class
388 // representation of a function. This isn't strictly necessary if they function
389 // has no upvalues, but lets the rest of the VM assume all called objects will
390 // be closures.
391 struct ObjFn
392 {
393     Obj obj;
394     
395     ByteBuffer code;
396     ValueBuffer constants;
397     
398     // The module where this function was defined.
399     ObjModule* module_;
400 
401     // The maximum number of stack slots this function may use.
402     int maxSlots;
403     
404     // The number of upvalues this function closes over.
405     int numUpvalues;
406     
407     // The number of parameters this function expects. Used to ensure that .call
408     // handles a mismatch between number of parameters and arguments. This will
409     // only be set for fns, and not ObjFns that represent methods or scripts.
410     int arity;
411     FnDebug* debug_;
412 }
413 
414 // An instance of a first-class function and the environment it has closed over.
415 // Unlike [ObjFn], this has captured the upvalues that the function accesses.
416 struct ObjClosure
417 {
418     Obj obj;
419 
420     // The function that this closure is an instance of.
421     ObjFn* fn;
422 
423     // The upvalues this function has closed over.
424     ObjUpvalue*[] upvalues;
425 }
426 
427 struct CallFrame
428 {
429     // Pointer to the current (really next-to-be-executed) instruction in the
430     // function's bytecode.
431     ubyte* ip;
432     
433     // The closure being executed.
434     ObjClosure* closure;
435     
436     // Pointer to the first stack slot used by this call frame. This will contain
437     // the receiver, followed by the function's parameters, then local variables
438     // and temporaries.
439     Value* stackStart;
440 }
441 
442 // Tracks how this fiber has been invoked, aside from the ways that can be
443 // detected from the state of other fields in the fiber.
444 enum FiberState
445 {
446     // The fiber is being run from another fiber using a call to `try()`.
447     FIBER_TRY,
448     
449     // The fiber was directly invoked by `runInterpreter()`. This means it's the
450     // initial fiber used by a call to `wrenCall()` or `wrenInterpret()`.
451     FIBER_ROOT,
452     
453     // The fiber is invoked some other way. If [caller] is `NULL` then the fiber
454     // was invoked using `call()`. If [numFrames] is zero, then the fiber has
455     // finished running and is done. If [numFrames] is one and that frame's `ip`
456     // points to the first byte of code, the fiber has not been started yet.
457     FIBER_OTHER,
458 }
459 
460 struct ObjFiber
461 {
462     Obj obj;
463     
464     // The stack of value slots. This is used for holding local variables and
465     // temporaries while the fiber is executing. It is heap-allocated and grown
466     // as needed.
467     Value* stack;
468     
469     // A pointer to one past the top-most value on the stack.
470     Value* stackTop;
471     
472     // The number of allocated slots in the stack array.
473     int stackCapacity;
474     
475     // The stack of call frames. This is a dynamic array that grows as needed but
476     // never shrinks.
477     CallFrame* frames;
478     
479     // The number of frames currently in use in [frames].
480     int numFrames;
481     
482     // The number of [frames] allocated.
483     int frameCapacity;
484     
485     // Pointer to the first node in the linked list of open upvalues that are
486     // pointing to values still on the stack. The head of the list will be the
487     // upvalue closest to the top of the stack, and then the list works downwards.
488     ObjUpvalue* openUpvalues;
489     
490     // The fiber that ran this one. If this fiber is yielded, control will resume
491     // to this one. May be `NULL`.
492     ObjFiber* caller;
493     
494     // If the fiber failed because of a runtime error, this will contain the
495     // error object. Otherwise, it will be null.
496     Value error;
497     
498     FiberState state;
499 }
500 
501 enum MethodType
502 {
503     // A primitive method implemented in C in the VM. Unlike foreign methods,
504     // this can directly manipulate the fiber's stack.
505     METHOD_PRIMITIVE,
506 
507     // A primitive that handles .call on Fn.
508     METHOD_FUNCTION_CALL,
509 
510     // A externally-defined C method.
511     METHOD_FOREIGN,
512 
513     // A normal user-defined method.
514     METHOD_BLOCK,
515     
516     // No method for the given symbol.
517     METHOD_NONE
518 }
519 
520 struct Method
521 {
522     MethodType type;
523 
524     // The method function itself. The [type] determines which field of the union
525     // is used.
526     union AsType
527     {
528         Primitive primitive;
529         WrenForeignMethodFn foreign;
530         ObjClosure* closure;
531     }
532 
533     AsType as;
534 }
535 
536 mixin(DECLARE_BUFFER("Method", "Method"));
537 
538 struct ObjClass
539 {
540     Obj obj;
541     ObjClass* superclass;
542 
543     // The number of fields needed for an instance of this class, including all
544     // of its superclass fields.
545     int numFields;
546 
547     // The table of methods that are defined in or inherited by this class.
548     // Methods are called by symbol, and the symbol directly maps to an index in
549     // this table. This makes method calls fast at the expense of empty cells in
550     // the list for methods the class doesn't support.
551     //
552     // You can think of it as a hash table that never has collisions but has a
553     // really low load factor. Since methods are pretty small (just a type and a
554     // pointer), this should be a worthwhile trade-off.
555     MethodBuffer methods;
556 
557     // The name of the class.
558     ObjString* name;
559     
560     // The ClassAttribute for the class, if any
561     Value attributes;
562 }
563 
564 struct ObjForeign
565 {
566     Obj obj;
567     ubyte[] data;
568 }
569 
570 struct ObjInstance
571 {
572     Obj obj;
573     Value[] fields;
574 }
575 
576 struct ObjList
577 {
578     Obj obj;
579     
580     // The elements in the list.
581     ValueBuffer elements;
582 }
583 
584 struct MapEntry
585 {
586     // The entry's key, or UNDEFINED_VAL if the entry is not in use.
587     Value key;
588 
589     // The value associated with the key. If the key is UNDEFINED_VAL, this will
590     // be false to indicate an open available entry or true to indicate a
591     // tombstone -- an entry that was previously in use but was then deleted.
592     Value value;
593 }
594 
595 // A hash table mapping keys to values.
596 //
597 // We use something very simple: open addressing with linear probing. The hash
598 // table is an array of entries. Each entry is a key-value pair. If the key is
599 // the special UNDEFINED_VAL, it indicates no value is currently in that slot.
600 // Otherwise, it's a valid key, and the value is the value associated with it.
601 //
602 // When entries are added, the array is dynamically scaled by GROW_FACTOR to
603 // keep the number of filled slots under MAP_LOAD_PERCENT. Likewise, if the map
604 // gets empty enough, it will be resized to a smaller array. When this happens,
605 // all existing entries are rehashed and re-added to the new array.
606 //
607 // When an entry is removed, its slot is replaced with a "tombstone". This is an
608 // entry whose key is UNDEFINED_VAL and whose value is TRUE_VAL. When probing
609 // for a key, we will continue past tombstones, because the desired key may be
610 // found after them if the key that was removed was part of a prior collision.
611 // When the array gets resized, all tombstones are discarded.
612 struct ObjMap
613 {
614     Obj obj;
615 
616     // The number of entries allocated.
617     uint capacity;
618 
619     // The number of entries in the map.
620     uint count;
621 
622     // Pointer to a contiguous array of [capacity] entries.
623     MapEntry* entries;
624 }
625 
626 struct ObjRange
627 {
628     Obj obj;
629 
630     // The beginning of the range.
631     double from;
632 
633     // The end of the range. May be greater or less than [from].
634     double to;
635 
636     // True if [to] is included in the range.
637     bool isInclusive;
638 }
639 
640 struct WrenHandle
641 {
642   Value value;
643 
644   WrenHandle* prev;
645   WrenHandle* next;
646 };
647 
648 // An IEEE 754 double-precision float is a 64-bit value with bits laid out like:
649 //
650 // 1 Sign bit
651 // | 11 Exponent bits
652 // | |          52 Mantissa (i.e. fraction) bits
653 // | |          |
654 // S[Exponent-][Mantissa------------------------------------------]
655 //
656 // The details of how these are used to represent numbers aren't really
657 // relevant here as long we don't interfere with them. The important bit is NaN.
658 //
659 // An IEEE double can represent a few magical values like NaN ("not a number"),
660 // Infinity, and -Infinity. A NaN is any value where all exponent bits are set:
661 //
662 //  v--NaN bits
663 // -11111111111----------------------------------------------------
664 //
665 // Here, "-" means "doesn't matter". Any bit sequence that matches the above is
666 // a NaN. With all of those "-", it obvious there are a *lot* of different
667 // bit patterns that all mean the same thing. NaN tagging takes advantage of
668 // this. We'll use those available bit patterns to represent things other than
669 // numbers without giving up any valid numeric values.
670 //
671 // NaN values come in two flavors: "signalling" and "quiet". The former are
672 // intended to halt execution, while the latter just flow through arithmetic
673 // operations silently. We want the latter. Quiet NaNs are indicated by setting
674 // the highest mantissa bit:
675 //
676 //             v--Highest mantissa bit
677 // -[NaN      ]1---------------------------------------------------
678 //
679 // If all of the NaN bits are set, it's not a number. Otherwise, it is.
680 // That leaves all of the remaining bits as available for us to play with. We
681 // stuff a few different kinds of things here: special singleton values like
682 // "true", "false", and "null", and pointers to objects allocated on the heap.
683 // We'll use the sign bit to distinguish singleton values from pointers. If
684 // it's set, it's a pointer.
685 //
686 // v--Pointer or singleton?
687 // S[NaN      ]1---------------------------------------------------
688 //
689 // For singleton values, we just enumerate the different values. We'll use the
690 // low bits of the mantissa for that, and only need a few:
691 //
692 //                                                 3 Type bits--v
693 // 0[NaN      ]1------------------------------------------------[T]
694 //
695 // For pointers, we are left with 51 bits of mantissa to store an address.
696 // That's more than enough room for a 32-bit address. Even 64-bit machines
697 // only actually use 48 bits for addresses, so we've got plenty. We just stuff
698 // the address right into the mantissa.
699 //
700 // Ta-da, double precision numbers, pointers, and a bunch of singleton values,
701 // all stuffed into a single 64-bit sequence. Even better, we don't have to
702 // do any masking or work to extract number values: they are unmodified. This
703 // means math on numbers is fast.
704 static if (WREN_NAN_TAGGING)
705 {
706     // A mask that selects the sign bit.
707     enum SIGN_BIT = cast(ulong)1 << 63;
708 
709     // The bits that must be set to indicate a quiet NaN.
710     enum QNAN = cast(ulong)0x7ffc000000000000;
711 
712     // If the NaN bits are set, it's not a number.
713     bool IS_NUM(Value value)
714     {
715         return (((value) & QNAN) != QNAN);
716     }
717 
718     // An object pointer is a NaN with a set sign bit.
719     bool IS_OBJ(Value value)
720     {
721         return (((value) & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT));
722     }
723 
724     bool IS_FALSE(Value value)
725     {
726         return ((value) == FALSE_VAL);
727     }
728 
729     bool IS_NULL(Value value)
730     {
731         return ((value) == NULL_VAL);
732     }
733 
734     bool IS_UNDEFINED(Value value)
735     {
736         return ((value) == UNDEFINED_VAL);
737     }
738 
739     // Masks out the tag bits used to identify the singleton value.
740     enum MASK_TAG = 7;
741 
742     // Tag values for the different singleton values.
743     enum TAG_NAN = 0;
744     enum TAG_NULL = 1;
745     enum TAG_FALSE = 2;
746     enum TAG_TRUE = 3;
747     enum TAG_UNDEFINED = 4;
748     enum TAG_UNUSED2 = 5;
749     enum TAG_UNUSED3 = 6;
750     enum TAG_UNUSED4 = 7;
751 
752     // Value . 0 or 1.
753     bool AS_BOOL(Value value)
754     {
755         return ((value) == TRUE_VAL);
756     }
757 
758     // Value . Obj*.
759     Obj* AS_OBJ(Value value)
760     {
761         return (cast(Obj*)cast(ulong)((value) & ~(SIGN_BIT | QNAN)));
762     } 
763 
764     // Singleton values.
765     enum NULL_VAL = (cast(Value)cast(ulong)(QNAN | TAG_NULL));
766     enum FALSE_VAL = (cast(Value)cast(ulong)(QNAN | TAG_FALSE));
767     enum TRUE_VAL = (cast(Value)cast(ulong)(QNAN | TAG_TRUE));
768     enum UNDEFINED_VAL = (cast(Value)cast(ulong)(QNAN | TAG_UNDEFINED));
769 
770     int GET_TAG(Value value)
771     {
772         return (cast(int)((value) & MASK_TAG));
773     }
774 }
775 else
776 {
777     // Value . 0 or 1.
778     bool AS_BOOL(Value value)
779     {
780         return ((value).type == ValueType.VAL_TRUE);
781     }
782 
783     // Value . Obj*.
784     Obj* AS_OBJ(Value value)
785     {
786         return ((value).as.obj);
787     }
788 
789     // Determines if [value] is a garbage-collected object or not.
790     bool IS_OBJ(Value value)
791     {
792         return ((value).type == ValueType.VAL_OBJ);
793     }
794 
795     bool IS_FALSE(Value value)
796     {
797         return ((value).type == ValueType.VAL_FALSE);
798     }
799 
800     bool IS_NULL(Value value)
801     {
802         return ((value).type == ValueType.VAL_NULL);
803     }
804 
805     bool IS_NUM(Value value)
806     {
807         return ((value).type == ValueType.VAL_NUM);
808     }
809 
810     bool IS_UNDEFINED(Value value)
811     {
812         return ((value).type == ValueType.VAL_UNDEFINED);
813     }
814 
815     // Singleton values.
816     enum FALSE_VAL = Value(ValueType.VAL_FALSE);
817     enum NULL_VAL = Value(ValueType.VAL_NULL);
818     enum TRUE_VAL = Value(ValueType.VAL_TRUE);
819     enum UNDEFINED_VAL = Value(ValueType.VAL_UNDEFINED);
820 }
821 
822 // Returns true if [a] and [b] are strictly the same value. This is identity
823 // for object values, and value equality for unboxed values.
824 static bool wrenValuesSame(Value a, Value b)
825 {
826     static if (WREN_NAN_TAGGING)
827     {
828         return a == b;
829     }
830     else
831     {
832         if (a.type != b.type) return false;
833         if (a.type == ValueType.VAL_NUM) return a.as.num == b.as.num;
834         return a.as.obj == b.as.obj;
835     }
836 }
837 
838 // Returns true if [value] is a bool. Do not call this directly, instead use
839 // [IS_BOOL].
840 static bool wrenIsBool(Value value)
841 {
842     static if (WREN_NAN_TAGGING)
843     {
844         return value == TRUE_VAL || value == FALSE_VAL;
845     }
846     else
847     {
848         return value.type == ValueType.VAL_FALSE || value.type == ValueType.VAL_TRUE;
849     }
850 }
851 
852 // Returns true if [value] is an object of type [type]. Do not call this
853 // directly, instead use the [IS___] macro for the type in question.
854 static bool wrenIsObjType(Value value, ObjType type)
855 {
856     return IS_OBJ(value) && AS_OBJ(value).type == type;
857 }
858 
859 // Converts the raw object pointer [obj] to a [Value].
860 static Value wrenObjectToValue(Obj* obj)
861 {
862     static if (WREN_NAN_TAGGING)
863     {
864         return cast(Value)(SIGN_BIT | QNAN | cast(ulong)(obj));
865     }
866     else
867     {
868         Value value;
869         value.type = ValueType.VAL_OBJ;
870         value.as.obj = obj;
871         return value;
872     }
873 }
874 
875 // Interprets [value] as a [double].
876 static double wrenValueToNum(Value value)
877 {
878     static if (WREN_NAN_TAGGING)
879     {
880         return wrenDoubleFromBits(value);
881     }
882     else
883     {
884         return value.as.num;
885     }
886 }
887 
888 // Converts [num] to a [Value].
889 static Value wrenNumToValue(double num)
890 {
891     static if (WREN_NAN_TAGGING)
892     {
893         return wrenDoubleToBits(num);
894     }
895     else
896     {
897         Value value;
898         value.type = ValueType.VAL_NUM;
899         value.as.num = num;
900         return value;
901     }
902 }
903 
904 // Validates that [arg] is a valid object for use as a map key. Returns true if
905 // it is and returns false otherwise. Use validateKey usually, for a runtime error.
906 // This separation exists to aid the API in surfacing errors to the developer as well.
907 static bool wrenMapIsValidKey(Value arg)
908 {
909     return IS_BOOL(arg)
910       || IS_CLASS(arg)
911       || IS_NULL(arg)
912       || IS_NUM(arg)
913       || IS_RANGE(arg)
914       || IS_STRING(arg);
915 }
916 
917 // TODO: Tune these.
918 // The initial (and minimum) capacity of a non-empty list or map object.
919 enum MIN_CAPACITY = 16;
920 
921 // The rate at which a collection's capacity grows when the size exceeds the
922 // current capacity. The new capacity will be determined by *multiplying* the
923 // old capacity by this. Growing geometrically is necessary to ensure that
924 // adding to a collection has O(1) amortized complexity.
925 enum GROW_FACTOR = 2;
926 
927 // The maximum percentage of map entries that can be filled before the map is
928 // grown. A lower load takes more memory but reduces collisions which makes
929 // lookup faster.
930 enum MAP_LOAD_PERCENT = 75;
931 
932 // The number of call frames initially allocated when a fiber is created. Making
933 // this smaller makes fibers use less memory (at first) but spends more time
934 // reallocating when the call stack grows.
935 enum INITIAL_CALL_FRAMES = 4;
936 
937 static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj)
938 {
939     obj.type = type;
940     obj.isDark = false;
941     obj.classObj = classObj;
942     obj.next = vm.first;
943     vm.first = obj;
944 }
945 
946 // Creates a new "raw" class. It has no metaclass or superclass whatsoever.
947 // This is only used for bootstrapping the initial Object and Class classes,
948 // which are a little special.
949 ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
950 {
951     ObjClass* classObj = ALLOCATE!(WrenVM, ObjClass)(vm);
952     initObj(vm, &classObj.obj, ObjType.OBJ_CLASS, null);
953     classObj.superclass = null;
954     classObj.numFields = numFields;
955     classObj.name = name;
956     classObj.attributes = NULL_VAL;
957 
958     wrenPushRoot(vm, cast(Obj*)classObj);
959     wrenMethodBufferInit(&classObj.methods);
960     wrenPopRoot(vm);
961 
962     return classObj;
963 }
964 
965 // Makes [superclass] the superclass of [subclass], and causes subclass to
966 // inherit its methods. This should be called before any methods are defined
967 // on subclass.
968 void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass)
969 {
970     assert(superclass != null, "Must have superclass");
971 
972     subclass.superclass = superclass;
973 
974     // Include the superclass in the total number of fields.
975     if (subclass.numFields != -1)
976     {
977         subclass.numFields += superclass.numFields;
978     }
979     else
980     {
981         assert(superclass.numFields == 0, 
982                 "A foreign class cannot inherit from a class with fields.");
983     }
984 
985     // Inherit methods from its superclass.
986     for (int i = 0; i < superclass.methods.count; i++)
987     {
988         wrenBindMethod(vm, subclass, i, superclass.methods.data[i]);
989     } 
990 }
991 
992 // Creates a new class object as well as its associated metaclass.
993 ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
994                             ObjString* name)
995 {
996     // Create the metaclass.
997     Value metaclassName = wrenStringFormat(vm, "@ metaclass".ptr, OBJ_VAL(name));
998     wrenPushRoot(vm, AS_OBJ(metaclassName));
999 
1000     ObjClass* metaclass = wrenNewSingleClass(vm, 0, AS_STRING(metaclassName));
1001     metaclass.obj.classObj = vm.classClass;
1002 
1003     wrenPopRoot(vm);
1004 
1005     // Make sure the metaclass isn't collected when we allocate the class.
1006     wrenPushRoot(vm, cast(Obj*)metaclass);
1007 
1008     // Metaclasses always inherit Class and do not parallel the non-metaclass
1009     // hierarchy.
1010     wrenBindSuperclass(vm, metaclass, vm.classClass);
1011 
1012     ObjClass* classObj = wrenNewSingleClass(vm, numFields, name);
1013 
1014     // Make sure the class isn't collected while the inherited methods are being
1015     // bound.
1016     wrenPushRoot(vm, cast(Obj*)classObj);
1017 
1018     classObj.obj.classObj = metaclass;
1019     wrenBindSuperclass(vm, classObj, superclass);
1020 
1021     wrenPopRoot(vm);
1022     wrenPopRoot(vm);
1023 
1024     return classObj;
1025 }
1026 
1027 void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method)
1028 {
1029     // Make sure the buffer is big enough to contain the symbol's index.
1030     if (symbol >= classObj.methods.count)
1031     {
1032         Method noMethod;
1033         noMethod.type = MethodType.METHOD_NONE;
1034         wrenMethodBufferFill(vm, &classObj.methods, noMethod,
1035                              symbol - classObj.methods.count + 1);
1036     }
1037 
1038     classObj.methods.data[symbol] = method;
1039 }
1040 
1041 // Creates a new closure object that invokes [fn]. Allocates room for its
1042 // upvalues, but assumes outside code will populate it.
1043 ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn)
1044 {
1045     ObjClosure* closure = ALLOCATE_FLEX!(WrenVM, ObjClosure, ObjUpvalue*)(vm, fn.numUpvalues);
1046 
1047     initObj(vm, &closure.obj, ObjType.OBJ_CLOSURE, vm.fnClass);
1048 
1049     closure.fn = fn;
1050 
1051     // Clear the upvalue array. We need to do this in case a GC is triggered
1052     // after the closure is created but before the upvalue array is populated.
1053     for (int i = 0; i < fn.numUpvalues; i++) closure.upvalues[i] = null;
1054 
1055     return closure; 
1056 }
1057 
1058 // Adds a new [CallFrame] to [fiber] invoking [closure] whose stack starts at
1059 // [stackStart].
1060 void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure, Value* stackStart)
1061 {
1062     assert(fiber.frameCapacity > fiber.numFrames, "No memory for call frame");
1063 
1064     CallFrame* frame = &fiber.frames[fiber.numFrames++];
1065     frame.stackStart = stackStart;
1066     frame.closure = closure;
1067     frame.ip = closure.fn.code.data;
1068 }
1069 
1070 // Creates a new fiber object that will invoke [closure].
1071 ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure)
1072 {
1073     // Allocate the arrays before the fiber in case it triggers a GC.
1074     CallFrame* frames = ALLOCATE_ARRAY!(WrenVM, CallFrame)(vm, INITIAL_CALL_FRAMES);
1075 
1076     // Add one slot for the unused implicit receiver slot that the compiler
1077     // assumes all functions have.
1078     int stackCapacity = closure == null 
1079         ? 1
1080         : wrenPowerOf2Ceil(closure.fn.maxSlots + 1);
1081     Value* stack = ALLOCATE_ARRAY!(WrenVM, Value)(vm, stackCapacity);
1082 
1083     ObjFiber* fiber = ALLOCATE!(WrenVM, ObjFiber)(vm);
1084     initObj(vm, &fiber.obj, ObjType.OBJ_FIBER, vm.fiberClass);
1085 
1086     fiber.stack = stack;
1087     fiber.stackTop = fiber.stack;
1088     fiber.stackCapacity = stackCapacity;
1089 
1090     fiber.frames = frames;
1091     fiber.frameCapacity = INITIAL_CALL_FRAMES;
1092     fiber.numFrames = 0;
1093 
1094     fiber.openUpvalues = null;
1095     fiber.caller = null;
1096     fiber.error = NULL_VAL;
1097     fiber.state = FiberState.FIBER_OTHER;
1098 
1099     if (closure != null)
1100     {
1101         // Initialize the first call frame.
1102         wrenAppendCallFrame(vm, fiber, closure, fiber.stack);
1103 
1104         // The first slot always holds the closure.
1105         fiber.stackTop[0] = OBJ_VAL(closure);
1106         fiber.stackTop++;
1107     }
1108 
1109     return fiber;
1110 }
1111 
1112 // Ensures [fiber]'s stack has at least [needed] slots.
1113 void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed)
1114 {
1115     if (fiber.stackCapacity >= needed) return;
1116 
1117     int capacity = wrenPowerOf2Ceil(needed);
1118 
1119     Value* oldStack = fiber.stack;
1120     fiber.stack = cast(Value*)wrenReallocate(vm, fiber.stack,
1121                                              Value.sizeof * fiber.stackCapacity,
1122                                              Value.sizeof * capacity);
1123     fiber.stackCapacity = capacity;
1124 
1125     // If the reallocation moves the stack, then we need to recalculate every
1126     // pointer that points into the old stack to into the same relative distance
1127     // in the new stack. We have to be a little careful about how these are
1128     // calculated because pointer subtraction is only well-defined within a
1129     // single array, hence the slightly redundant-looking arithmetic below.
1130     if (fiber.stack != oldStack)
1131     {
1132         // Top of the stack.
1133         if (vm.apiStack >= oldStack && vm.apiStack <= fiber.stackTop)
1134         {
1135             vm.apiStack = fiber.stack + (vm.apiStack - oldStack);
1136         }
1137 
1138         // Stack pointer for each call frame.
1139         for (int i = 0; i < fiber.numFrames; i++)
1140         {
1141             CallFrame* frame = &fiber.frames[i];
1142             frame.stackStart = fiber.stack + (frame.stackStart - oldStack);
1143         }
1144 
1145         // Open upvalues.
1146         for (ObjUpvalue* upvalue = fiber.openUpvalues;
1147              upvalue != null;
1148              upvalue = upvalue.next)
1149         {
1150             upvalue.value = fiber.stack + (upvalue.value - oldStack);
1151         }
1152 
1153         fiber.stackTop = fiber.stack + (fiber.stackTop - oldStack);
1154     }
1155 }
1156 
1157 static bool wrenHasError(ObjFiber* fiber)
1158 {
1159     return !IS_NULL(fiber.error);
1160 }
1161 
1162 ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size)
1163 {
1164     import core.stdc.string : memset;
1165     ObjForeign* object = ALLOCATE_FLEX!(WrenVM, ObjForeign, ubyte)(vm, size);
1166     initObj(vm, &object.obj, ObjType.OBJ_FOREIGN, classObj);
1167 
1168     // Zero out the bytes.
1169     memset(object.data.ptr, 0, size);
1170     return object;
1171 }
1172 
1173 // Creates a new empty function. Before being used, it must have code,
1174 // constants, etc. added to it.
1175 ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module_, int maxSlots)
1176 {
1177     FnDebug* debug_ = ALLOCATE!(WrenVM, FnDebug)(vm);
1178     debug_.name = null;
1179     wrenIntBufferInit(&debug_.sourceLines);
1180 
1181     ObjFn* fn = ALLOCATE!(WrenVM, ObjFn)(vm);
1182     initObj(vm, &fn.obj, ObjType.OBJ_FN, vm.fnClass);
1183 
1184     wrenValueBufferInit(&fn.constants);
1185     wrenByteBufferInit(&fn.code);
1186     fn.module_ = module_;
1187     fn.maxSlots = maxSlots;
1188     fn.numUpvalues = 0;
1189     fn.arity = 0;
1190     fn.debug_ = debug_;
1191 
1192     return fn;
1193 }
1194 
1195 void wrenFunctionBindName(WrenVM* vm, ObjFn* fn, const(char)* name, int length)
1196 {
1197     import core.stdc.string : memcpy;
1198     fn.debug_.name = ALLOCATE_ARRAY!(WrenVM, char)(vm, length + 1);
1199     memcpy(fn.debug_.name, name, length);
1200     fn.debug_.name[length] = '\0';
1201 }
1202 
1203 // Creates a new instance of the given [classObj].
1204 Value wrenNewInstance(WrenVM* vm, ObjClass* classObj)
1205 {
1206     ObjInstance* instance = ALLOCATE_FLEX!(WrenVM, ObjInstance, Value)(vm, classObj.numFields);
1207     initObj(vm, &instance.obj, ObjType.OBJ_INSTANCE, classObj);
1208 
1209     // Initialize fields to null.
1210     for (int i = 0; i < classObj.numFields; i++)
1211     {
1212         instance.fields[i] = NULL_VAL;
1213     }
1214 
1215     return OBJ_VAL(instance);
1216 }
1217 
1218 // Creates a new list with [numElements] elements (which are left
1219 // uninitialized.)
1220 ObjList* wrenNewList(WrenVM* vm, uint numElements)
1221 {
1222     // Allocate this before the list object in case it triggers a GC which would
1223     // free the list.
1224     Value* elements = null;
1225     if (numElements > 0)
1226     {
1227         elements = ALLOCATE_ARRAY!(WrenVM, Value)(vm, numElements);
1228     }
1229 
1230     ObjList* list = ALLOCATE!(WrenVM, ObjList)(vm);
1231     initObj(vm, &list.obj, ObjType.OBJ_LIST, vm.listClass);
1232     list.elements.capacity = numElements;
1233     list.elements.count = numElements;
1234     list.elements.data = elements;
1235     return list;
1236 }
1237 
1238 // Inserts [value] in [list] at [index], shifting down the other elements.
1239 void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint index)
1240 {
1241     if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1242 
1243     // Add a slot at the end of the list.
1244     wrenValueBufferWrite(vm, &list.elements, NULL_VAL);
1245 
1246     if (IS_OBJ(value)) wrenPopRoot(vm);
1247 
1248     // Shift the existing elements down.
1249     for (uint i = list.elements.count - 1; i > index; i--)
1250     {
1251         list.elements.data[i] = list.elements.data[i - 1];
1252     }
1253 
1254     // Store the new element.
1255     list.elements.data[index] = value;
1256 }
1257 
1258 // Searches for [value] in [list], returns the index or -1 if not found.
1259 int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value)
1260 {
1261     int count = list.elements.count;
1262     for (int i = 0; i < count; i++)
1263     {
1264         Value item = list.elements.data[i];
1265         if(wrenValuesEqual(item, value)) {
1266             return i;
1267         }
1268     }
1269     return -1;
1270 }
1271 
1272 Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint index)
1273 {
1274     Value removed = list.elements.data[index];
1275 
1276     if (IS_OBJ(removed)) wrenPushRoot(vm, AS_OBJ(removed));
1277 
1278     // Shift items up.
1279     for (int i = index; i < list.elements.count - 1; i++)
1280     {
1281         list.elements.data[i] = list.elements.data[i + 1];
1282     }
1283 
1284     // If we have too much excess capacity, shrink it.
1285     if (list.elements.capacity / GROW_FACTOR >= list.elements.count)
1286     {
1287         list.elements.data = cast(Value*)wrenReallocate(vm, list.elements.data,
1288             Value.sizeof * list.elements.capacity,
1289             Value.sizeof * (list.elements.capacity / GROW_FACTOR));
1290         list.elements.capacity /= GROW_FACTOR;
1291     }
1292 
1293     if (IS_OBJ(removed)) wrenPopRoot(vm);
1294 
1295     list.elements.count--;
1296     return removed;
1297 }
1298 
1299 // Creates a new empty map.
1300 ObjMap* wrenNewMap(WrenVM* vm)
1301 {
1302     ObjMap* map = ALLOCATE!(WrenVM, ObjMap)(vm);
1303     initObj(vm, &map.obj, ObjType.OBJ_MAP, vm.mapClass);
1304     map.capacity = 0;
1305     map.count = 0;
1306     map.entries = null;
1307     return map;
1308 }
1309 
1310 static uint hashBits(ulong hash)
1311 {
1312     // From v8's ComputeLongHash() which in turn cites:
1313     // Thomas Wang, Integer Hash Functions.
1314     // http://www.concentric.net/~Ttwang/tech/inthash.htm
1315     hash = ~hash + (hash << 18);  // hash = (hash << 18) - hash - 1;
1316     hash = hash ^ (hash >> 31);
1317     hash = hash * 21;  // hash = (hash + (hash << 2)) + (hash << 4);
1318     hash = hash ^ (hash >> 11);
1319     hash = hash + (hash << 6);
1320     hash = hash ^ (hash >> 22);
1321     return cast(uint)(hash & 0x3fffffff);
1322 }
1323 
1324 // Generates a hash code for [num].
1325 static uint hashNumber(double num)
1326 {
1327     // Hash the raw bits of the value.
1328     return hashBits(wrenDoubleToBits(num));
1329 }
1330 
1331 // Generates a hash code for [object].
1332 static uint hashObject(Obj* object)
1333 {
1334     switch (object.type)
1335     {
1336         case ObjType.OBJ_CLASS:
1337             // Classes just use their name.
1338             return hashObject(cast(Obj*)(cast(ObjClass*)object).name);
1339         
1340         // Allow bare (non-closure) functions so that we can use a map to find
1341         // existing constants in a function's constant table. This is only used
1342         // internally. Since user code never sees a non-closure function, they
1343         // cannot use them as map keys.
1344         case ObjType.OBJ_FN:
1345         {
1346             ObjFn* fn = cast(ObjFn*)object;
1347             return hashNumber(fn.arity) ^ hashNumber(fn.code.count);
1348         }
1349 
1350         case ObjType.OBJ_RANGE:
1351         {
1352             ObjRange* range = cast(ObjRange*)object;
1353             return hashNumber(range.from) ^ hashNumber(range.to);
1354         }
1355 
1356         case ObjType.OBJ_STRING:
1357             return (cast(ObjString*)object).hash;
1358 
1359         default:
1360             assert(false, "Only immutable objects can be hashed.");
1361     }
1362 }
1363 
1364 // Generates a hash code for [value], which must be one of the built-in
1365 // immutable types: null, bool, class, num, range, or string.
1366 static uint hashValue(Value value)
1367 {
1368     // TODO: We'll probably want to randomize this at some point.
1369     static if (WREN_NAN_TAGGING)
1370     {
1371         if (IS_OBJ(value)) return hashObject(AS_OBJ(value));
1372 
1373         // Hash the raw bits of the unboxed value.
1374         return hashBits(value);
1375     }
1376     else
1377     {
1378         switch (value.type) with(ValueType)
1379         {
1380             case VAL_FALSE: return 0;
1381             case VAL_NULL:  return 1;
1382             case VAL_NUM:   return hashNumber(AS_NUM(value));
1383             case VAL_TRUE:  return 2;
1384             case VAL_OBJ:   return hashObject(AS_OBJ(value));
1385             default:        assert(0, "Unreachable?");
1386         }
1387     }
1388 }
1389 
1390 // Looks for an entry with [key] in an array of [capacity] [entries].
1391 //
1392 // If found, sets [result] to point to it and returns `true`. Otherwise,
1393 // returns `false` and points [result] to the entry where the key/value pair
1394 // should be inserted.
1395 static bool findEntry(MapEntry* entries, uint capacity, Value key,
1396                       MapEntry** result)
1397 {
1398     // If there is no entry array (an empty map), we definitely won't find it.
1399     if (capacity == 0) return false;
1400     
1401     // Figure out where to insert it in the table. Use open addressing and
1402     // basic linear probing.
1403     uint startIndex = hashValue(key) % capacity;
1404     uint index = startIndex;
1405     
1406     // If we pass a tombstone and don't end up finding the key, its entry will
1407     // be re-used for the insert.
1408     MapEntry* tombstone = null;
1409     
1410     // Walk the probe sequence until we've tried every slot.
1411     do
1412     {
1413         MapEntry* entry = &entries[index];
1414         
1415         if (IS_UNDEFINED(entry.key))
1416         {
1417         // If we found an empty slot, the key is not in the table. If we found a
1418         // slot that contains a deleted key, we have to keep looking.
1419         if (IS_FALSE(entry.value))
1420         {
1421             // We found an empty slot, so we've reached the end of the probe
1422             // sequence without finding the key. If we passed a tombstone, then
1423             // that's where we should insert the item, otherwise, put it here at
1424             // the end of the sequence.
1425             *result = tombstone != null ? tombstone : entry;
1426             return false;
1427         }
1428         else
1429         {
1430             // We found a tombstone. We need to keep looking in case the key is
1431             // after it, but we'll use this entry as the insertion point if the
1432             // key ends up not being found.
1433             if (tombstone == null) tombstone = entry;
1434         }
1435         }
1436         else if (wrenValuesEqual(entry.key, key))
1437         {
1438         // We found the key.
1439         *result = entry;
1440         return true;
1441         }
1442         
1443         // Try the next slot.
1444         index = (index + 1) % capacity;
1445     }
1446     while (index != startIndex);
1447     
1448     // If we get here, the table is full of tombstones. Return the first one we
1449     // found.
1450     assert(tombstone != null, "Map should have tombstones or empty entries.");
1451     *result = tombstone;
1452     return false;
1453 }
1454 
1455 // Inserts [key] and [value] in the array of [entries] with the given
1456 // [capacity].
1457 //
1458 // Returns `true` if this is the first time [key] was added to the map.
1459 static bool insertEntry(MapEntry* entries, uint capacity,
1460                         Value key, Value value)
1461 {
1462     assert(entries != null, "Should ensure capacity before inserting.");
1463     
1464     MapEntry* entry;
1465     if (findEntry(entries, capacity, key, &entry))
1466     {
1467         // Already present, so just replace the value.
1468         entry.value = value;
1469         return false;
1470     }
1471     else
1472     {
1473         entry.key = key;
1474         entry.value = value;
1475         return true;
1476     }
1477 }
1478 
1479 // Updates [map]'s entry array to [capacity].
1480 static void resizeMap(WrenVM* vm, ObjMap* map, uint capacity)
1481 {
1482     // Create the new empty hash table.
1483     MapEntry* entries = ALLOCATE_ARRAY!(WrenVM, MapEntry)(vm, capacity);
1484     for (uint i = 0; i < capacity; i++)
1485     {
1486         entries[i].key = UNDEFINED_VAL;
1487         entries[i].value = FALSE_VAL;
1488     }
1489 
1490     // Re-add the existing entries.
1491     if (map.capacity > 0)
1492     {
1493         for (uint i = 0; i < map.capacity; i++)
1494         {
1495         MapEntry* entry = &map.entries[i];
1496         
1497         // Don't copy empty entries or tombstones.
1498         if (IS_UNDEFINED(entry.key)) continue;
1499 
1500         insertEntry(entries, capacity, entry.key, entry.value);
1501         }
1502     }
1503 
1504     // Replace the array.
1505     DEALLOCATE(vm, map.entries);
1506     map.entries = entries;
1507     map.capacity = capacity;
1508 }
1509 
1510 // Looks up [key] in [map]. If found, returns the value. Otherwise, returns
1511 // `UNDEFINED_VAL`.
1512 Value wrenMapGet(ObjMap* map, Value key)
1513 {
1514     MapEntry* entry;
1515     if (findEntry(map.entries, map.capacity, key, &entry)) return entry.value;
1516 
1517     return UNDEFINED_VAL;
1518 }
1519 
1520 // Associates [key] with [value] in [map].
1521 void wrenMapSet(WrenVM* vm, ObjMap* map, Value key, Value value)
1522 {
1523     // If the map is getting too full, make room first.
1524     if (map.count + 1 > map.capacity * MAP_LOAD_PERCENT / 100)
1525     {
1526         // Figure out the new hash table size.
1527         uint capacity = map.capacity * GROW_FACTOR;
1528         if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
1529 
1530         resizeMap(vm, map, capacity);
1531     }
1532 
1533     if (insertEntry(map.entries, map.capacity, key, value))
1534     {
1535         // A new key was added.
1536         map.count++;
1537     }
1538 }
1539 
1540 void wrenMapClear(WrenVM* vm, ObjMap* map)
1541 {
1542     DEALLOCATE(vm, map.entries);
1543     map.entries = null;
1544     map.capacity = 0;
1545     map.count = 0;
1546 }
1547 
1548 // Removes [key] from [map], if present. Returns the value for the key if found
1549 // or `NULL_VAL` otherwise.
1550 Value wrenMapRemoveKey(WrenVM* vm, ObjMap* map, Value key)
1551 {
1552     MapEntry* entry;
1553     if (!findEntry(map.entries, map.capacity, key, &entry)) return NULL_VAL;
1554 
1555     // Remove the entry from the map. Set this value to true, which marks it as a
1556     // deleted slot. When searching for a key, we will stop on empty slots, but
1557     // continue past deleted slots.
1558     Value value = entry.value;
1559     entry.key = UNDEFINED_VAL;
1560     entry.value = TRUE_VAL;
1561 
1562     if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1563 
1564     map.count--;
1565 
1566     if (map.count == 0)
1567     {
1568         // Removed the last item, so free the array.
1569         wrenMapClear(vm, map);
1570     }
1571     else if (map.capacity > MIN_CAPACITY &&
1572             map.count < map.capacity / GROW_FACTOR * MAP_LOAD_PERCENT / 100)
1573     {
1574         uint capacity = map.capacity / GROW_FACTOR;
1575         if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
1576 
1577         // The map is getting empty, so shrink the entry array back down.
1578         // TODO: Should we do this less aggressively than we grow?
1579         resizeMap(vm, map, capacity);
1580     }
1581 
1582     if (IS_OBJ(value)) wrenPopRoot(vm);
1583     return value;
1584 }
1585 
1586 // Creates a new module.
1587 ObjModule* wrenNewModule(WrenVM* vm, ObjString* name)
1588 {
1589   ObjModule* module_ = ALLOCATE!(WrenVM, ObjModule)(vm);
1590 
1591   // Modules are never used as first-class objects, so don't need a class.
1592   initObj(vm, cast(Obj*)module_, ObjType.OBJ_MODULE, null);
1593 
1594   wrenPushRoot(vm, cast(Obj*)module_);
1595 
1596   wrenSymbolTableInit(&module_.variableNames);
1597   wrenValueBufferInit(&module_.variables);
1598 
1599   module_.name = name;
1600 
1601   wrenPopRoot(vm);
1602   return module_;
1603 }
1604 
1605 // Creates a new range from [from] to [to].
1606 Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive)
1607 {
1608   ObjRange* range = ALLOCATE!(WrenVM, ObjRange)(vm);
1609   initObj(vm, &range.obj, ObjType.OBJ_RANGE, vm.rangeClass);
1610   range.from = from;
1611   range.to = to;
1612   range.isInclusive = isInclusive;
1613 
1614   return OBJ_VAL(range);
1615 }
1616 
1617 // Creates a new string object with a null-terminated buffer large enough to
1618 // hold a string of [length] but does not fill in the bytes.
1619 //
1620 // The caller is expected to fill in the buffer and then calculate the string's
1621 // hash.
1622 static ObjString* allocateString(WrenVM* vm, size_t length)
1623 {
1624     ObjString* str = ALLOCATE_FLEX!(WrenVM, ObjString, char)(vm, length + 1);
1625     initObj(vm, &str.obj, ObjType.OBJ_STRING, vm.stringClass);
1626     str.length = cast(int)length;
1627     str.value[length] = '\0';
1628 
1629     return str;
1630 }
1631 
1632 static void hashString(ObjString* str)
1633 {
1634     // FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/
1635     uint hash = 2166136261u;
1636 
1637     // This is O(n) on the length of the string, but we only call this when a new
1638     // string is created. Since the creation is also O(n) (to copy/initialize all
1639     // the bytes), we allow this here.
1640     for (uint i = 0; i < str.length; i++)
1641     {
1642         hash ^= str.value[i];
1643         hash *= 16777619;
1644     }
1645 
1646     str.hash = hash;
1647 }
1648 
1649 // Creates a new string object and copies [text] into it.
1650 //
1651 // [text] must be non-NULL.
1652 Value wrenNewString(WrenVM* vm, const(char)* text)
1653 {
1654     import core.stdc.string : strlen;
1655     return wrenNewStringLength(vm, text, strlen(text));
1656 }
1657 
1658 // Creates a new string object of [length] and copies [text] into it.
1659 //
1660 // [text] may be NULL if [length] is zero.
1661 Value wrenNewStringLength(WrenVM* vm, const(char)* text, size_t length)
1662 {
1663     import core.stdc.string : memcpy;
1664     // Allow NULL if the string is empty since byte buffers don't allocate any
1665     // characters for a zero-length string.
1666     assert(length == 0 || text != null, "Unexpected null string.");
1667 
1668     ObjString* str = allocateString(vm, length);
1669 
1670     // Copy the string (if given one).
1671     if (length > 0 && text != null) memcpy(str.value.ptr, text, length);
1672 
1673     hashString(str);
1674     return OBJ_VAL(str);
1675 }
1676 
1677 // Creates a new string object from [text], which should be a bare C string
1678 // literal. This determines the length of the string automatically at compile
1679 // time based on the size of the character array (-1 for the terminating '\0').
1680 Value CONST_STRING(WrenVM* vm, const(char)[] text)
1681 {
1682     return wrenNewStringLength(vm, text.ptr, text.length);
1683 }
1684 
1685 // Creates a new string object by taking a range of characters from [source].
1686 // The range starts at [start], contains [count] bytes, and increments by
1687 // [step].
1688 Value wrenNewStringFromRange(WrenVM* vm, ObjString* source, int start,
1689                                  uint count, int step)
1690 {
1691     ubyte* from = cast(ubyte*)source.value;
1692     int length = 0;
1693     for (uint i = 0; i < count; i++)
1694     {
1695         length += wrenUtf8DecodeNumBytes(from[start + i * step]);
1696     }
1697 
1698     ObjString* result = allocateString(vm, length);
1699     result.value[length] = '\0';
1700 
1701     ubyte* to = cast(ubyte*)result.value;
1702     for (uint i = 0; i < count; i++)
1703     {
1704         int index = start + i * step;
1705         int codePoint = wrenUtf8Decode(from + index, source.length - index);
1706 
1707         if (codePoint != -1)
1708         {
1709             to += wrenUtf8Encode(codePoint, to);
1710         }
1711     }
1712 
1713     hashString(result);
1714     return OBJ_VAL(result);
1715 }
1716 
1717 // Produces a string representation of [value].
1718 Value wrenNumToString(WrenVM* vm, double value)
1719 {
1720     import std.math : isNaN, isInfinity;
1721     import core.stdc.stdio : sprintf;
1722     if (isNaN(value))
1723     {
1724         return CONST_STRING(vm, "nan");
1725     }
1726 
1727     if (isInfinity(value))
1728     {
1729         if (value > 0.0)
1730         {
1731             return CONST_STRING(vm, "infinity");
1732         }
1733         else
1734         {
1735             return CONST_STRING(vm, "-infinity");
1736         }
1737     }
1738 
1739     // This is large enough to hold any double converted to a string using
1740     // "%.14g". Example:
1741     //
1742     //     -1.12345678901234e-1022
1743     //
1744     // So we have:
1745     //
1746     // + 1 char for sign
1747     // + 1 char for digit
1748     // + 1 char for "."
1749     // + 14 chars for decimal digits
1750     // + 1 char for "e"
1751     // + 1 char for "-" or "+"
1752     // + 4 chars for exponent
1753     // + 1 char for "\0"
1754     // = 24
1755     char[24] buffer;
1756     int length = sprintf(buffer.ptr, "%.14g", value);
1757     return wrenNewStringLength(vm, buffer.ptr, length);
1758 }
1759 
1760 // Creates a new string containing the UTF-8 encoding of [value].
1761 Value wrenStringFromCodePoint(WrenVM* vm, int value)
1762 {
1763     int length = wrenUtf8EncodeNumBytes(value);
1764     assert(length != 0, "Value out of range");
1765 
1766     ObjString* str = allocateString(vm, length);
1767 
1768     wrenUtf8Encode(value, cast(ubyte*)str.value);
1769     hashString(str);
1770 
1771     return OBJ_VAL(str);
1772 }
1773 
1774 // Creates a new string from the integer representation of a byte
1775 Value wrenStringFromByte(WrenVM *vm, ubyte value)
1776 {
1777     int length = 1;
1778     ObjString* str = allocateString(vm, length);
1779     str.value[0] = value;
1780     hashString(str);
1781     return OBJ_VAL(str);
1782 }
1783 
1784 // Creates a new formatted string from [format] and any additional arguments
1785 // used in the format string.
1786 //
1787 // This is a very restricted flavor of formatting, intended only for internal
1788 // use by the VM. Two formatting characters are supported, each of which reads
1789 // the next argument as a certain type:
1790 //
1791 // $ - A C string.
1792 // @ - A Wren string object.
1793 extern(C) Value wrenStringFormat(WrenVM* vm, const(char)* format, ...)
1794 {
1795     import core.stdc.stdarg : va_start, va_arg, va_end, va_list, va_copy;
1796     import core.stdc.string : strlen, memcpy;
1797 
1798     // Calculate the length of the result string. Do this up front so we can
1799     // create the final string with a single allocation.
1800     // Work-around nasty compiler bug here. Apparently va_args doesn't get reset.
1801     va_list v1;
1802     va_start(v1, format);
1803     va_list v2;
1804     va_copy(v2, v1);
1805     size_t totalLength = 0;
1806 
1807     for (const(char)* c = format; *c != '\0'; c++)
1808     {
1809         switch (*c)
1810         {
1811             case '$':
1812                 totalLength += strlen(va_arg!(const(char*))(v2));
1813                 break;
1814 
1815             case '@':
1816                 totalLength += AS_STRING(va_arg!(Value)(v2)).length;
1817                 break;
1818 
1819             default:
1820                 // Any other character is interpreted literally.
1821                 totalLength++;
1822         }
1823     }
1824     va_end(v2);
1825 
1826     // Concatenate the string.
1827     ObjString* result = allocateString(vm, totalLength);
1828 
1829     //va_start(_argptr, format);
1830     char* start = result.value.ptr;
1831     for (const(char)* c = format; *c != '\0'; c++)
1832     {
1833         switch (*c)
1834         {
1835             case '$':
1836             {
1837                 const(char)* str = va_arg!(const(char)*)(v1);
1838                 size_t length = strlen(str);
1839                 memcpy(start, str, length);
1840                 start += length;
1841                 break;
1842             }
1843 
1844             case '@':
1845             {
1846                 Value v = va_arg!(Value)(v1);
1847                 ObjString* str = AS_STRING(v);
1848                 memcpy(start, str.value.ptr, str.length);
1849                 start += str.length;
1850                 break;
1851             }
1852 
1853             default:
1854                 // Any other character is interpreted literally.
1855                 *start++ = *c;
1856             }
1857     }
1858     va_end(v1);
1859 
1860     hashString(result);
1861 
1862     return OBJ_VAL(result);
1863 }
1864 
1865 // Creates a new string containing the code point in [string] starting at byte
1866 // [index]. If [index] points into the middle of a UTF-8 sequence, returns an
1867 // empty string.
1868 Value wrenStringCodePointAt(WrenVM* vm, ObjString* string_, uint index)
1869 {
1870     assert(index < string_.length, "Index out of bounds.");
1871 
1872     int codePoint = wrenUtf8Decode(cast(ubyte*)string_.value + index,
1873                                     string_.length - index);
1874     if (codePoint == -1)
1875     {
1876         // If it isn't a valid UTF-8 sequence, treat it as a single raw byte.
1877         char[2] bytes;
1878         bytes[0] = string_.value[index];
1879         bytes[1] = '\0';
1880         return wrenNewStringLength(vm, bytes.ptr, 1);
1881     }
1882 
1883     return wrenStringFromCodePoint(vm, codePoint);
1884 }
1885 
1886 // Uses the Boyer-Moore-Horspool string matching algorithm.
1887 // Search for the first occurence of [needle] within [haystack] and returns its
1888 // zero-based offset. Returns `UINT32_MAX` if [haystack] does not contain
1889 // [needle].
1890 uint wrenStringFind(ObjString* haystack, ObjString* needle, uint start)
1891 {
1892     // Edge case: An empty needle is always found.
1893     if (needle.length == 0) return start;
1894 
1895     // If the needle goes past the haystack it won't be found.
1896     if (start + needle.length > haystack.length) return uint.max;
1897 
1898     // If the startIndex is too far it also won't be found.
1899     if (start >= haystack.length) return uint.max;
1900 
1901     // Pre-calculate the shift table. For each character (8-bit value), we
1902     // determine how far the search window can be advanced if that character is
1903     // the last character in the haystack where we are searching for the needle
1904     // and the needle doesn't match there.
1905     uint[ubyte.max] shift;
1906     uint needleEnd = needle.length - 1;
1907 
1908     // By default, we assume the character is not the needle at all. In that case
1909     // case, if a match fails on that character, we can advance one whole needle
1910     // width since.
1911     for (uint index = 0; index < ubyte.max; index++)
1912     {
1913         shift[index] = needle.length;
1914     }
1915 
1916     // Then, for every character in the needle, determine how far it is from the
1917     // end. If a match fails on that character, we can advance the window such
1918     // that it the last character in it lines up with the last place we could
1919     // find it in the needle.
1920     for (uint index = 0; index < needleEnd; index++)
1921     {
1922         char c = needle.value[index];
1923         shift[cast(ubyte)c] = needleEnd - index;
1924     }
1925 
1926     // Slide the needle across the haystack, looking for the first match or
1927     // stopping if the needle goes off the end.
1928     char lastChar = needle.value[needleEnd];
1929     uint range = haystack.length - needle.length;
1930 
1931     for (uint index = start; index <= range; )
1932     {
1933         import core.stdc.string : memcmp;
1934         // Compare the last character in the haystack's window to the last character
1935         // in the needle. If it matches, see if the whole needle matches.
1936         char c = haystack.value[index + needleEnd];
1937         if (lastChar == c &&
1938             memcmp(haystack.value.ptr + index, needle.value.ptr, needleEnd) == 0)
1939         {
1940             // Found a match.
1941             return index;
1942         }
1943 
1944         // Otherwise, slide the needle forward.
1945         index += shift[cast(ubyte)c];
1946     }
1947 
1948     // Not found.
1949     return uint.max;
1950 }
1951 
1952 // Returns true if [a] and [b] represent the same string.
1953 static bool wrenStringEqualsCString(ObjString* a, const(char)* b, size_t length)
1954 {
1955     import core.stdc.string : memcmp;
1956     return a.length == length && memcmp(a.value.ptr, b, length) == 0;
1957 }
1958 
1959 // Creates a new open upvalue pointing to [value] on the stack.
1960 ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value)
1961 {
1962     ObjUpvalue* upvalue = ALLOCATE!(WrenVM, ObjUpvalue)(vm);
1963 
1964     // Upvalues are never used as first-class objects, so don't need a class.
1965     initObj(vm, &upvalue.obj, ObjType.OBJ_UPVALUE, null);
1966 
1967     upvalue.value = value;
1968     upvalue.closed = NULL_VAL;
1969     upvalue.next = null;
1970 
1971     return upvalue;
1972 }
1973 
1974 // Mark [obj] as reachable and still in use. This should only be called
1975 // during the sweep phase of a garbage collection.
1976 void wrenGrayObj(WrenVM* vm, Obj* obj)
1977 {
1978     if (obj == null) return;
1979 
1980     // Stop if the object is already darkened so we don't get stuck in a cycle.
1981     if (obj.isDark) return;
1982 
1983     // It's been reached.
1984     obj.isDark = true;
1985 
1986     // Add it to the gray list so it can be recursively explored for
1987     // more marks later.
1988     if (vm.grayCount >= vm.grayCapacity)
1989     {
1990         vm.grayCapacity = vm.grayCount * 2;
1991         vm.gray = cast(Obj**)vm.config.reallocateFn(vm.gray,
1992                                                 vm.grayCapacity * (Obj*).sizeof,
1993                                                 vm.config.userData);
1994     }
1995 
1996     vm.gray[vm.grayCount++] = obj;
1997 }
1998 
1999 // Mark [value] as reachable and still in use. This should only be called
2000 // during the sweep phase of a garbage collection.
2001 void wrenGrayValue(WrenVM* vm, Value value)
2002 {
2003     if (!IS_OBJ(value)) return;
2004     wrenGrayObj(vm, AS_OBJ(value));
2005 }
2006 
2007 // Mark the values in [buffer] as reachable and still in use. This should only
2008 // be called during the sweep phase of a garbage collection.
2009 void wrenGrayBuffer(WrenVM* vm, ValueBuffer* buffer)
2010 {
2011     for (int i = 0; i < buffer.count; i++)
2012     {
2013         wrenGrayValue(vm, buffer.data[i]);
2014     }
2015 }
2016 
2017 static void blackenClass(WrenVM* vm, ObjClass* classObj)
2018 {
2019     // The metaclass.
2020     wrenGrayObj(vm, cast(Obj*)classObj.obj.classObj);
2021 
2022     // The superclass.
2023     wrenGrayObj(vm, cast(Obj*)classObj.superclass);
2024 
2025     // Method function objects.
2026     for (int i = 0; i < classObj.methods.count; i++)
2027     {
2028         if (classObj.methods.data[i].type == MethodType.METHOD_BLOCK)
2029         {
2030         wrenGrayObj(vm, cast(Obj*)classObj.methods.data[i].as.closure);
2031         }
2032     }
2033 
2034     wrenGrayObj(vm, cast(Obj*)classObj.name);
2035 
2036     if(!IS_NULL(classObj.attributes)) wrenGrayObj(vm, AS_OBJ(classObj.attributes));
2037 
2038     // Keep track of how much memory is still in use.
2039     vm.bytesAllocated += ObjClass.sizeof;
2040     vm.bytesAllocated += classObj.methods.capacity * Method.sizeof;
2041 }
2042 
2043 static void blackenClosure(WrenVM* vm, ObjClosure* closure)
2044 {
2045     // Mark the function.
2046     wrenGrayObj(vm, cast(Obj*)closure.fn);
2047 
2048     // Mark the upvalues.
2049     for (int i = 0; i < closure.fn.numUpvalues; i++)
2050     {
2051         wrenGrayObj(vm, cast(Obj*)closure.upvalues[i]);
2052     }
2053 
2054     // Keep track of how much memory is still in use.
2055     vm.bytesAllocated += ObjClosure.sizeof;
2056     vm.bytesAllocated += (ObjUpvalue*).sizeof * closure.fn.numUpvalues;
2057 }
2058 
2059 static void blackenFiber(WrenVM* vm, ObjFiber* fiber)
2060 {
2061     // Stack functions.
2062 
2063     for (int i = 0; i < fiber.numFrames; i++)
2064     {
2065         wrenGrayObj(vm, cast(Obj*)fiber.frames[i].closure);
2066     }
2067 
2068     // Stack variables.
2069     for (Value* slot = fiber.stack; slot < fiber.stackTop; slot++)
2070     {
2071         wrenGrayValue(vm, *slot);
2072     }
2073 
2074     // Open upvalues.
2075     ObjUpvalue* upvalue = fiber.openUpvalues;
2076     while (upvalue != null)
2077     {
2078         wrenGrayObj(vm, cast(Obj*)upvalue);
2079         upvalue = upvalue.next;
2080     }
2081 
2082     // The caller.
2083     wrenGrayObj(vm, cast(Obj*)fiber.caller);
2084     wrenGrayValue(vm, fiber.error);
2085 
2086     // Keep track of how much memory is still in use.
2087     vm.bytesAllocated += ObjFiber.sizeof;
2088     vm.bytesAllocated += fiber.frameCapacity * CallFrame.sizeof;
2089     vm.bytesAllocated += fiber.stackCapacity * Value.sizeof;
2090 }
2091 
2092 static void blackenFn(WrenVM* vm, ObjFn* fn)
2093 {
2094   // Mark the constants.
2095   wrenGrayBuffer(vm, &fn.constants);
2096 
2097   // Keep track of how much memory is still in use.
2098   vm.bytesAllocated += ObjFn.sizeof;
2099   vm.bytesAllocated += ubyte.sizeof * fn.code.capacity;
2100   vm.bytesAllocated += Value.sizeof * fn.constants.capacity;
2101   
2102   // The debug line number buffer.
2103   vm.bytesAllocated += int.sizeof * fn.code.capacity;
2104   // TODO: What about the function name?
2105 }
2106 
2107 static void blackenForeign(WrenVM* vm, ObjForeign* foreign)
2108 {
2109   // TODO: Keep track of how much memory the foreign object uses. We can store
2110   // this in each foreign object, but it will balloon the size. We may not want
2111   // that much overhead. One option would be to let the foreign class register
2112   // a C function that returns a size for the object. That way the VM doesn't
2113   // always have to explicitly store it.
2114 }
2115 
2116 static void blackenInstance(WrenVM* vm, ObjInstance* instance)
2117 {
2118   wrenGrayObj(vm, cast(Obj*)instance.obj.classObj);
2119 
2120   // Mark the fields.
2121   for (int i = 0; i < instance.obj.classObj.numFields; i++)
2122   {
2123     wrenGrayValue(vm, instance.fields[i]);
2124   }
2125 
2126   // Keep track of how much memory is still in use.
2127   vm.bytesAllocated += ObjInstance.sizeof;
2128   vm.bytesAllocated += Value.sizeof * instance.obj.classObj.numFields;
2129 }
2130 
2131 static void blackenList(WrenVM* vm, ObjList* list)
2132 {
2133   // Mark the elements.
2134   wrenGrayBuffer(vm, &list.elements);
2135 
2136   // Keep track of how much memory is still in use.
2137   vm.bytesAllocated += ObjList.sizeof;
2138   vm.bytesAllocated += Value.sizeof * list.elements.capacity;
2139 }
2140 
2141 static void blackenMap(WrenVM* vm, ObjMap* map)
2142 {
2143   // Mark the entries.
2144   for (uint i = 0; i < map.capacity; i++)
2145   {
2146     MapEntry* entry = &map.entries[i];
2147     if (IS_UNDEFINED(entry.key)) continue;
2148 
2149     wrenGrayValue(vm, entry.key);
2150     wrenGrayValue(vm, entry.value);
2151   }
2152 
2153   // Keep track of how much memory is still in use.
2154   vm.bytesAllocated += ObjMap.sizeof;
2155   vm.bytesAllocated += MapEntry.sizeof * map.capacity;
2156 }
2157 
2158 static void blackenModule(WrenVM* vm, ObjModule* module_)
2159 {
2160   // Top-level variables.
2161   for (int i = 0; i < module_.variables.count; i++)
2162   {
2163     wrenGrayValue(vm, module_.variables.data[i]);
2164   }
2165 
2166   wrenBlackenSymbolTable(vm, &module_.variableNames);
2167 
2168   wrenGrayObj(vm, cast(Obj*)module_.name);
2169 
2170   // Keep track of how much memory is still in use.
2171   vm.bytesAllocated += ObjModule.sizeof;
2172 }
2173 
2174 static void blackenRange(WrenVM* vm, ObjRange* range)
2175 {
2176   // Keep track of how much memory is still in use.
2177   vm.bytesAllocated += ObjRange.sizeof;
2178 }
2179 
2180 static void blackenString(WrenVM* vm, ObjString* str)
2181 {
2182   // Keep track of how much memory is still in use.
2183   vm.bytesAllocated += ObjString.sizeof + str.length + 1;
2184 }
2185 
2186 static void blackenUpvalue(WrenVM* vm, ObjUpvalue* upvalue)
2187 {
2188     // Mark the closed-over object (in case it is closed).
2189     wrenGrayValue(vm, upvalue.closed);
2190 
2191     // Keep track of how much memory is still in use.
2192     vm.bytesAllocated += ObjUpvalue.sizeof;
2193 }
2194 
2195 static void blackenObject(WrenVM* vm, Obj* obj)
2196 {
2197     static if (WREN_DEBUG_TRACE_MEMORY)
2198     {
2199         import core.stdc.stdio : printf;
2200         import wren.dbg : wrenDumpValue;
2201         printf("mark ");
2202         wrenDumpValue(OBJ_VAL(obj));
2203         printf(" @ %p\n", obj);
2204     }
2205 
2206     // Traverse the object's fields.
2207     switch (obj.type) with(ObjType)
2208     {
2209         case OBJ_CLASS:    blackenClass(   vm, cast(ObjClass*)   obj); break;
2210         case OBJ_CLOSURE:  blackenClosure( vm, cast(ObjClosure*) obj); break;
2211         case OBJ_FIBER:    blackenFiber(   vm, cast(ObjFiber*)   obj); break;
2212         case OBJ_FN:       blackenFn(      vm, cast(ObjFn*)      obj); break;
2213         case OBJ_FOREIGN:  blackenForeign( vm, cast(ObjForeign*) obj); break;
2214         case OBJ_INSTANCE: blackenInstance(vm, cast(ObjInstance*)obj); break;
2215         case OBJ_LIST:     blackenList(    vm, cast(ObjList*)    obj); break;
2216         case OBJ_MAP:      blackenMap(     vm, cast(ObjMap*)     obj); break;
2217         case OBJ_MODULE:   blackenModule(  vm, cast(ObjModule*)  obj); break;
2218         case OBJ_RANGE:    blackenRange(   vm, cast(ObjRange*)   obj); break;
2219         case OBJ_STRING:   blackenString(  vm, cast(ObjString*)  obj); break;
2220         case OBJ_UPVALUE:  blackenUpvalue( vm, cast(ObjUpvalue*) obj); break;
2221         default: assert(0, "Unexpected object type");
2222     }
2223 }
2224 
2225 // Processes every object in the gray stack until all reachable objects have
2226 // been marked. After that, all objects are either white (freeable) or black
2227 // (in use and fully traversed).
2228 void wrenBlackenObjects(WrenVM* vm)
2229 {
2230     while (vm.grayCount > 0)
2231     {
2232         // Pop an item from the gray stack.
2233         Obj* obj = vm.gray[--vm.grayCount];
2234         blackenObject(vm, obj);
2235     }
2236 }
2237 
2238 // Releases all memory owned by [obj], including [obj] itself.
2239 void wrenFreeObj(WrenVM* vm, Obj* obj)
2240 {
2241     static if (WREN_DEBUG_TRACE_MEMORY)
2242     {
2243         import core.stdc.stdio;
2244         import wren.dbg : wrenDumpValue;
2245         printf("free ");
2246         wrenDumpValue(OBJ_VAL(obj));
2247         printf (" @ %p\n", obj);
2248     }
2249 
2250     switch (obj.type)
2251     {
2252         case ObjType.OBJ_CLASS: {
2253             wrenMethodBufferClear(vm, &(cast(ObjClass*)obj).methods);
2254             break;
2255         }
2256         
2257         case ObjType.OBJ_FIBER: {
2258             ObjFiber* fiber = cast(ObjFiber*)obj;
2259             DEALLOCATE(vm, fiber.frames);
2260             DEALLOCATE(vm, fiber.stack);
2261             break;
2262         }
2263         
2264         case ObjType.OBJ_FN: {
2265             ObjFn* fn = cast(ObjFn*)obj;
2266             wrenValueBufferClear(vm, &fn.constants);
2267             wrenByteBufferClear(vm, &fn.code);
2268             wrenIntBufferClear(vm, &fn.debug_.sourceLines);
2269             DEALLOCATE(vm, fn.debug_.name);
2270             DEALLOCATE(vm, fn.debug_);
2271             break;
2272         }
2273 
2274         case ObjType.OBJ_FOREIGN:
2275             assert(0, "stub");
2276         
2277         case ObjType.OBJ_LIST:
2278             wrenValueBufferClear(vm, &(cast(ObjList*)obj).elements);
2279             break;
2280         
2281         case ObjType.OBJ_MAP:
2282             DEALLOCATE(vm, (cast(ObjMap*)obj).entries);
2283             break;
2284         
2285         case ObjType.OBJ_MODULE:
2286             wrenSymbolTableClear(vm, &(cast(ObjModule*)obj).variableNames);
2287             wrenValueBufferClear(vm, &(cast(ObjModule*)obj).variables);
2288             break;
2289         
2290         case ObjType.OBJ_CLOSURE:
2291         case ObjType.OBJ_INSTANCE:
2292         case ObjType.OBJ_RANGE:
2293         case ObjType.OBJ_STRING:
2294         case ObjType.OBJ_UPVALUE:
2295             break;
2296         
2297         default:
2298             assert(0, "Unexpected object type");       
2299     }
2300 }
2301 
2302 // Returns true if [a] and [b] are equivalent. Immutable values (null, bools,
2303 // numbers, ranges, and strings) are equal if they have the same data. All
2304 // other values are equal if they are identical objects.
2305 bool wrenValuesEqual(Value a, Value b)
2306 {
2307     if (wrenValuesSame(a, b)) return true;
2308 
2309     // If we get here, it's only possible for two heap-allocated immutable objects
2310     // to be equal.
2311     if (!IS_OBJ(a) || !IS_OBJ(b)) return false;
2312 
2313     Obj* aObj = AS_OBJ(a);
2314     Obj* bObj = AS_OBJ(b);
2315 
2316     // Must be the same type.
2317     if (aObj.type != bObj.type) return false;
2318 
2319     switch (aObj.type)
2320     {
2321         case ObjType.OBJ_RANGE:
2322         {
2323             ObjRange* aRange = cast(ObjRange*)aObj;
2324             ObjRange* bRange = cast(ObjRange*)bObj;
2325             return aRange.from == bRange.from &&
2326                     aRange.to == bRange.to &&
2327                     aRange.isInclusive == bRange.isInclusive;
2328         }
2329 
2330         case ObjType.OBJ_STRING:
2331         {
2332             ObjString* aString = cast(ObjString*)aObj;
2333             ObjString* bString = cast(ObjString*)bObj;
2334             return aString.hash == bString.hash &&
2335                 wrenStringEqualsCString(aString, bString.value.ptr, bString.length);
2336         }
2337 
2338         default:
2339             // All other types are only equal if they are same, which they aren't if
2340             // we get here.
2341             return false;
2342     }
2343 }