1 module wren.vm;
2 import wren.core;
3 import wren.common;
4 import wren.opcodes;
5 import wren.value;
6 import wren.utils;
7 
8 @nogc:
9 
10 // The type of a primitive function.
11 //
12 // Primitives are similar to foreign functions, but have more direct access to
13 // VM internals. It is passed the arguments in [args]. If it returns a value,
14 // it places it in `args[0]` and returns `true`. If it causes a runtime error
15 // or modifies the running fiber, it returns `false`.
16 alias Primitive = bool function(WrenVM* vm, Value* args);
17 
18 // A generic allocation function that handles all explicit memory management
19 // used by Wren. It's used like so:
20 //
21 // - To allocate new memory, [memory] is null and [newSize] is the desired
22 //   size. It should return the allocated memory or null on failure.
23 //
24 // - To attempt to grow an existing allocation, [memory] is the memory, and
25 //   [newSize] is the desired size. It should return [memory] if it was able to
26 //   grow it in place, or a new pointer if it had to move it.
27 //
28 // - To shrink memory, [memory] and [newSize] are the same as above but it will
29 //   always return [memory].
30 //
31 // - To free memory, [memory] will be the memory to free and [newSize] will be
32 //   zero. It should return null.
33 alias WrenReallocateFn = void* function(void* memory, size_t newSize, void* userData);
34 
35 // A function callable from Wren code, but implemented in C.
36 alias WrenForeignMethodFn = void function(WrenVM* vm);
37 
38 // A finalizer function for freeing resources owned by an instance of a foreign
39 // class. Unlike most foreign methods, finalizers do not have access to the VM
40 // and should not interact with it since it's in the middle of a garbage
41 // collection.
42 alias WrenFinalizerFn = void function(void* data);
43 
44 // Gives the host a chance to canonicalize the imported module name,
45 // potentially taking into account the (previously resolved) name of the module
46 // that contains the import. Typically, this is used to implement relative
47 // imports.
48 alias WrenResolveModuleFn = const(char)* function(WrenVM* vm, 
49                                     const(char)* importer, const(char)* name);
50 
51 // Called after loadModuleFn is called for module [name]. The original returned result
52 // is handed back to you in this callback, so that you can free memory if appropriate.
53 alias WrenLoadModuleCompleteFn = void function(WrenVM* vm, const(char)* name, WrenLoadModuleResult result);
54 
55 // The result of a loadModuleFn call. 
56 // [source] is the source code for the module, or null if the module is not found.
57 // [onComplete] an optional callback that will be called once Wren is done with the result.
58 struct WrenLoadModuleResult
59 {
60     const(char)* source;
61     WrenLoadModuleCompleteFn onComplete;
62     void* userData;
63 }
64 
65 // Loads and returns the source code for the module [name].
66 alias WrenLoadModuleFn = WrenLoadModuleResult function(WrenVM* vm, const(char)* name);
67 
68 // Returns a pointer to a foreign method on [className] in [module] with
69 // [signature].
70 alias WrenBindForeignMethodFn = WrenForeignMethodFn function(WrenVM* vm,
71     const(char)* module_, const(char)* className, bool isStatic,
72     const(char)* signature);
73 
74 // Displays a string of text to the user.
75 alias WrenWriteFn = void function(WrenVM* vm, const(char)* text);
76 
77 enum WrenErrorType
78 {
79     // A syntax or resolution error detected at compile time.
80     WREN_ERROR_COMPILE,
81 
82     // The error message for a runtime error.
83     WREN_ERROR_RUNTIME,
84 
85     // One entry of a runtime error's stack trace.
86     WREN_ERROR_STACK_TRACE
87 }
88 
89 // Reports an error to the user.
90 //
91 // An error detected during compile time is reported by calling this once with
92 // [type] `WREN_ERROR_COMPILE`, the resolved name of the [module] and [line]
93 // where the error occurs, and the compiler's error [message].
94 //
95 // A runtime error is reported by calling this once with [type]
96 // `WREN_ERROR_RUNTIME`, no [module] or [line], and the runtime error's
97 // [message]. After that, a series of [type] `WREN_ERROR_STACK_TRACE` calls are
98 // made for each line in the stack trace. Each of those has the resolved
99 // [module] and [line] where the method or function is defined and [message] is
100 // the name of the method or function.
101 alias WrenErrorFn = void function(WrenVM* vm, WrenErrorType type, const(char)* module_,
102     int line, const(char)* message);
103 
104 struct WrenForeignClassMethods
105 {
106     // The callback invoked when the foreign object is created.
107     //
108     // This must be provided. Inside the body of this, it must call
109     // [wrenSetSlotNewForeign()] exactly once.
110     WrenForeignMethodFn allocate;
111 
112     // The callback invoked when the garbage collector is about to collect a
113     // foreign object's memory.
114     //
115     // This may be `null` if the foreign class does not need to finalize.
116     WrenFinalizerFn finalize;
117 }
118 
119 alias WrenBindForeignClassFn = WrenForeignClassMethods function(
120     WrenVM* vm, const(char)* module_, const(char)* className);
121 
122 struct WrenConfiguration
123 {
124     // The callback Wren will use to allocate, reallocate, and deallocate memory.
125     //
126     // If `null`, defaults to a built-in function that uses `realloc` and `free`.
127     WrenReallocateFn reallocateFn;
128 
129     // The callback Wren uses to resolve a module name.
130     //
131     // Some host applications may wish to support "relative" imports, where the
132     // meaning of an import string depends on the module that contains it. To
133     // support that without baking any policy into Wren itself, the VM gives the
134     // host a chance to resolve an import string.
135     //
136     // Before an import is loaded, it calls this, passing in the name of the
137     // module that contains the import and the import string. The host app can
138     // look at both of those and produce a new "canonical" string that uniquely
139     // identifies the module. This string is then used as the name of the module
140     // going forward. It is what is passed to [loadModuleFn], how duplicate
141     // imports of the same module are detected, and how the module is reported in
142     // stack traces.
143     //
144     // If you leave this function null, then the original import string is
145     // treated as the resolved string.
146     //
147     // If an import cannot be resolved by the embedder, it should return null and
148     // Wren will report that as a runtime error.
149     //
150     // Wren will take ownership of the string you return and free it for you, so
151     // it should be allocated using the same allocation function you provide
152     // above.
153     WrenResolveModuleFn resolveModuleFn;
154 
155     // The callback Wren uses to load a module.
156     //
157     // Since Wren does not talk directly to the file system, it relies on the
158     // embedder to physically locate and read the source code for a module. The
159     // first time an import appears, Wren will call this and pass in the name of
160     // the module being imported. The method will return a result, which contains
161     // the source code for that module. Memory for the source is owned by the 
162     // host application, and can be freed using the onComplete callback.
163     //
164     // This will only be called once for any given module name. Wren caches the
165     // result internally so subsequent imports of the same module will use the
166     // previous source and not call this.
167     //
168     // If a module with the given name could not be found by the embedder, it
169     // should return null and Wren will report that as a runtime error.
170     WrenLoadModuleFn loadModuleFn;
171 
172     // The callback Wren uses to find a foreign method and bind it to a class.
173     //
174     // When a foreign method is declared in a class, this will be called with the
175     // foreign method's module, class, and signature when the class body is
176     // executed. It should return a pointer to the foreign function that will be
177     // bound to that method.
178     //
179     // If the foreign function could not be found, this should return null and
180     // Wren will report it as runtime error.
181     WrenBindForeignMethodFn bindForeignMethodFn;
182 
183     // The callback Wren uses to find a foreign class and get its foreign methods.
184     //
185     // When a foreign class is declared, this will be called with the class's
186     // module and name when the class body is executed. It should return the
187     // foreign functions uses to allocate and (optionally) finalize the bytes
188     // stored in the foreign object when an instance is created.
189     WrenBindForeignClassFn bindForeignClassFn;
190 
191     // The callback Wren uses to display text when `System.print()` or the other
192     // related functions are called.
193     //
194     // If this is `null`, Wren discards any printed text.
195     WrenWriteFn writeFn;
196 
197     // The callback Wren uses to report errors.
198     //
199     // When an error occurs, this will be called with the module name, line
200     // number, and an error message. If this is `null`, Wren doesn't report any
201     // errors.
202     WrenErrorFn errorFn;
203 
204     // The number of bytes Wren will allocate before triggering the first garbage
205     // collection.
206     //
207     // If zero, defaults to 10MB.
208     size_t initialHeapSize;
209 
210     // After a collection occurs, the threshold for the next collection is
211     // determined based on the number of bytes remaining in use. This allows Wren
212     // to shrink its memory usage automatically after reclaiming a large amount
213     // of memory.
214     //
215     // This can be used to ensure that the heap does not get too small, which can
216     // in turn lead to a large number of collections afterwards as the heap grows
217     // back to a usable size.
218     //
219     // If zero, defaults to 1MB.
220     size_t minHeapSize;
221 
222     // Wren will resize the heap automatically as the number of bytes
223     // remaining in use after a collection changes. This number determines the
224     // amount of additional memory Wren will use after a collection, as a
225     // percentage of the current heap size.
226     //
227     // For example, say that this is 50. After a garbage collection, when there
228     // are 400 bytes of memory still in use, the next collection will be triggered
229     // after a total of 600 bytes are allocated (including the 400 already in
230     // use.)
231     //
232     // Setting this to a smaller number wastes less memory, but triggers more
233     // frequent garbage collections.
234     //
235     // If zero, defaults to 50.
236     int heapGrowthPercent;
237 
238     // User-defined data associated with the VM.
239     void* userData;
240 }
241 
242 enum WrenInterpretResult
243 {
244     WREN_RESULT_SUCCESS,
245     WREN_RESULT_COMPILE_ERROR,
246     WREN_RESULT_RUNTIME_ERROR
247 }
248 
249 // The type of an object stored in a slot.
250 //
251 // This is not necessarily the object's *class*, but instead its low level
252 // representation type.
253 enum WrenType
254 {
255     WREN_TYPE_BOOL,
256     WREN_TYPE_NUM,
257     WREN_TYPE_FOREIGN,
258     WREN_TYPE_LIST,
259     WREN_TYPE_MAP,
260     WREN_TYPE_NULL,
261     WREN_TYPE_STRING,
262 
263     // The object is of a type that isn't accessible by the C API.
264     WREN_TYPE_UNKNOWN
265 }
266 
267 // The maximum number of temporary objects that can be made visible to the GC
268 // at one time.
269 enum WREN_MAX_TEMP_ROOTS = 8;
270 
271 struct WrenVM
272 {
273     ObjClass* boolClass;
274     ObjClass* classClass;
275     ObjClass* fiberClass;
276     ObjClass* fnClass;
277     ObjClass* listClass;
278     ObjClass* mapClass;
279     ObjClass* nullClass;
280     ObjClass* numClass;
281     ObjClass* objectClass;
282     ObjClass* rangeClass;
283     ObjClass* stringClass;
284 
285     // The fiber that is currently running.
286     ObjFiber* fiber;
287 
288     // The loaded modules. Each key is an ObjString (except for the main module,
289     // whose key is null) for the module's name and the value is the ObjModule
290     // for the module.
291     ObjMap* modules;
292     
293     // The most recently imported module. More specifically, the module whose
294     // code has most recently finished executing.
295     //
296     // Not treated like a GC root since the module is already in [modules].
297     ObjModule* lastModule;
298 
299     // Memory management data:
300 
301     // The number of bytes that are known to be currently allocated. Includes all
302     // memory that was proven live after the last GC, as well as any new bytes
303     // that were allocated since then. Does *not* include bytes for objects that
304     // were freed since the last GC.
305     size_t bytesAllocated;
306 
307     // The number of total allocated bytes that will trigger the next GC.
308     size_t nextGC;
309 
310     // The first object in the linked list of all currently allocated objects.
311     Obj* first;
312 
313     // The "gray" set for the garbage collector. This is the stack of unprocessed
314     // objects while a garbage collection pass is in process.
315     Obj** gray;
316     int grayCount;
317     int grayCapacity;
318 
319     // The list of temporary roots. This is for temporary or new objects that are
320     // not otherwise reachable but should not be collected.
321     //
322     // They are organized as a stack of pointers stored in this array. This
323     // implies that temporary roots need to have stack semantics: only the most
324     // recently pushed object can be released.
325     Obj*[WREN_MAX_TEMP_ROOTS] tempRoots;
326 
327     int numTempRoots;
328     
329     // Pointer to the first node in the linked list of active handles or null if
330     // there are none.
331     WrenHandle* handles;
332     
333     // Pointer to the bottom of the range of stack slots available for use from
334     // the C API. During a foreign method, this will be in the stack of the fiber
335     // that is executing a method.
336     //
337     // If not in a foreign method, this is initially null. If the user requests
338     // slots by calling wrenEnsureSlots(), a stack is created and this is
339     // initialized.
340     Value* apiStack;
341 
342     WrenConfiguration config;
343     
344     // Compiler and debugger data:
345 
346     // The compiler that is currently compiling code. This is used so that heap
347     // allocated objects used by the compiler can be found if a GC is kicked off
348     // in the middle of a compile.
349     import wren.compiler : Compiler;
350     Compiler* compiler;
351 
352     // There is a single global symbol table for all method names on all classes.
353     // Method calls are dispatched directly by index in this table.
354     SymbolTable methodNames;
355 }
356 
357 // The behavior of realloc() when the size is 0 is implementation defined. It
358 // may return a non-null pointer which must not be dereferenced but nevertheless
359 // should be freed. To prevent that, we avoid calling realloc() with a zero
360 // size.
361 static void* defaultReallocate(void* ptr, size_t newSize, void* _)
362 {
363     import core.stdc.stdlib : free, realloc;
364 
365     if (newSize == 0)
366     {
367         free(ptr);
368         return null;
369     }
370     
371     return realloc(ptr, newSize);
372 }
373 
374 void wrenInitConfiguration(WrenConfiguration* config)
375 {
376     config.reallocateFn = &defaultReallocate;
377     config.resolveModuleFn = null;
378     config.loadModuleFn = null;
379     config.bindForeignMethodFn = null;
380     config.bindForeignClassFn = null;
381     config.writeFn = null;
382     config.errorFn = null;
383     config.initialHeapSize = 1024 * 1024 * 10;
384     config.minHeapSize = 1024 * 1024;
385     config.heapGrowthPercent = 50;
386     config.userData = null;
387 }
388 
389 WrenVM* wrenNewVM(WrenConfiguration* config)
390 {
391     import core.stdc.string : memset, memcpy;
392     WrenReallocateFn reallocate = &defaultReallocate;
393     void* userData = null;
394     if (config != null) {
395         userData = config.userData;
396         reallocate = config.reallocateFn ? config.reallocateFn : &defaultReallocate;
397     }
398 
399     WrenVM* vm = cast(WrenVM*)reallocate(null, WrenVM.sizeof, userData);
400     memset(vm, 0, WrenVM.sizeof);
401 
402     // Copy the configuration if given one.
403     if (config != null)
404     {
405         memcpy(&vm.config, config, WrenConfiguration.sizeof);
406         // We choose to set this after copying, 
407         // rather than modifying the user config pointer
408         vm.config.reallocateFn = reallocate;
409     }
410     else
411     {
412         wrenInitConfiguration(&vm.config);
413     }
414 
415     // TODO: Should we allocate and free this during a GC?
416     vm.grayCount = 0;
417     // TODO: Tune this.
418     vm.grayCapacity = 4;
419     vm.gray = cast(Obj**)reallocate(null, vm.grayCapacity * (Obj*).sizeof, userData);
420     vm.nextGC = vm.config.initialHeapSize;
421 
422     wrenSymbolTableInit(&vm.methodNames);
423 
424     vm.modules = wrenNewMap(vm);
425     wrenInitializeCore(vm);
426     return vm;
427 }
428 
429 void wrenFreeVM(WrenVM* vm)
430 {
431     assert(vm.methodNames.count > 0, "VM appears to have already been freed.");
432 
433     // Free all of the GC objects.
434     Obj* obj = vm.first;
435     while (obj != null)
436     {
437         Obj* next = obj.next;
438         wrenFreeObj(vm, obj);
439         obj = next;
440     }
441 
442     // Free up the GC gray set.
443     vm.gray = cast(Obj**)vm.config.reallocateFn(vm.gray, 0, vm.config.userData);
444 
445     // Tell the user if they didn't free any handles. We don't want to just free
446     // them here because the host app may still have pointers to them that they
447     // may try to use. Better to tell them about the bug early.
448     assert(vm.handles == null, "All handles have not been released.");
449 
450     wrenSymbolTableClear(vm, &vm.methodNames);
451 
452     DEALLOCATE(vm, vm);
453 }
454 
455 void wrenCollectGarbage(WrenVM* vm)
456 {
457     static if (WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC)
458     {
459         import core.stdc.stdio : printf;
460         import core.stdc.time;
461         printf("-- gc --\n");
462 
463         size_t before = vm.bytesAllocated;
464         double startTime = cast(double)clock() / CLOCKS_PER_SEC;
465     }
466 
467     // Mark all reachable objects.
468 
469     // Reset this. As we mark objects, their size will be counted again so that
470     // we can track how much memory is in use without needing to know the size
471     // of each *freed* object.
472     //
473     // This is important because when freeing an unmarked object, we don't always
474     // know how much memory it is using. For example, when freeing an instance,
475     // we need to know its class to know how big it is, but its class may have
476     // already been freed.
477     vm.bytesAllocated = 0;
478 
479     wrenGrayObj(vm, cast(Obj*)vm.modules);
480 
481     // Temporary roots.
482     for (int i = 0; i < vm.numTempRoots; i++)
483     {
484         wrenGrayObj(vm, vm.tempRoots[i]);
485     }
486 
487     // The current fiber.
488     wrenGrayObj(vm, cast(Obj*)vm.fiber);
489 
490     // The handles.
491     for (WrenHandle* handle = vm.handles;
492         handle != null;
493         handle = handle.next)
494     {
495         wrenGrayValue(vm, handle.value);
496     }
497 
498     // Any object the compiler is using (if there is one).
499     import wren.compiler : wrenMarkCompiler;
500     if (vm.compiler != null) wrenMarkCompiler(vm, vm.compiler);
501 
502     // Method names.
503     wrenBlackenSymbolTable(vm, &vm.methodNames);
504 
505     // Now that we have grayed the roots, do a depth-first search over all of the
506     // reachable objects.
507     wrenBlackenObjects(vm);
508 
509     // Collect the white objects.
510     Obj** obj = &vm.first;
511     while (*obj != null)
512     {
513         if (!((*obj).isDark))
514         {
515             // This object wasn't reached, so remove it from the list and free it.
516             Obj* unreached = *obj;
517             *obj = unreached.next;
518             wrenFreeObj(vm, unreached);
519         }
520         else
521         {
522             // This object was reached, so unmark it (for the next GC) and move on to
523             // the next.
524             (*obj).isDark = false;
525             obj = &(*obj).next;
526         }
527     }
528 
529     // Calculate the next gc point, this is the current allocation plus
530     // a configured percentage of the current allocation.
531     vm.nextGC = vm.bytesAllocated + ((vm.bytesAllocated * vm.config.heapGrowthPercent) / 100);
532     if (vm.nextGC < vm.config.minHeapSize) vm.nextGC = vm.config.minHeapSize;
533 
534     static if (WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC)
535     {
536         double elapsed = (cast(double)clock / CLOCKS_PER_SEC) - startTime;
537         // Explicit cast because size_t has different sizes on 32-bit and 64-bit and
538         // we need a consistent type for the format string.
539         printf("GC %lu before, %lu after (%lu collected), next at %lu. Took %.3fms.\n",
540             cast(ulong)before,
541             cast(ulong)vm.bytesAllocated,
542             cast(ulong)(before - vm.bytesAllocated),
543             cast(ulong)vm.nextGC,
544             elapsed*1000.0);
545     }
546 }
547 
548 // A generic allocation function that handles all explicit memory management.
549 // It's used like so:
550 //
551 // - To allocate new memory, [memory] is null and [oldSize] is zero. It should
552 //   return the allocated memory or null on failure.
553 //
554 // - To attempt to grow an existing allocation, [memory] is the memory,
555 //   [oldSize] is its previous size, and [newSize] is the desired size.
556 //   It should return [memory] if it was able to grow it in place, or a new
557 //   pointer if it had to move it.
558 //
559 // - To shrink memory, [memory], [oldSize], and [newSize] are the same as above
560 //   but it will always return [memory].
561 //
562 // - To free memory, [memory] will be the memory to free and [newSize] and
563 //   [oldSize] will be zero. It should return null.
564 void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize)
565 {
566     static if (WREN_DEBUG_TRACE_MEMORY)
567     {
568         import core.stdc.stdio : printf;
569         printf("reallocate %p %lu . %lu",
570                 memory, cast(ulong)oldSize, cast(ulong)newSize);
571     }
572 
573     // If new bytes are being allocated, add them to the total count. If objects
574     // are being completely deallocated, we don't track that (since we don't
575     // track the original size). Instead, that will be handled while marking
576     // during the next GC.
577     vm.bytesAllocated += newSize - oldSize;
578 
579     static if (WREN_DEBUG_GC_STRESS)
580     {
581         // Since collecting calls this function to free things, make sure we don't
582         // recurse.
583         if (newSize > 0) wrenCollectGarbage(vm);
584     }
585     else
586     {
587         if (newSize > 0 && vm.bytesAllocated > vm.nextGC) wrenCollectGarbage(vm);
588     }
589 
590     return vm.config.reallocateFn(memory, newSize, vm.config.userData);
591 }
592 
593 // Captures the local variable [local] into an [Upvalue]. If that local is
594 // already in an upvalue, the existing one will be used. (This is important to
595 // ensure that multiple closures closing over the same variable actually see
596 // the same variable.) Otherwise, it will create a new open upvalue and add it
597 // the fiber's list of upvalues.
598 static ObjUpvalue* captureUpvalue(WrenVM* vm, ObjFiber* fiber, Value* local)
599 {
600     // If there are no open upvalues at all, we must need a new one.
601     if (fiber.openUpvalues == null)
602     {
603         fiber.openUpvalues = wrenNewUpvalue(vm, local);
604         return fiber.openUpvalues;
605     }
606 
607     ObjUpvalue* prevUpvalue = null;
608     ObjUpvalue* upvalue = fiber.openUpvalues;
609 
610     // Walk towards the bottom of the stack until we find a previously existing
611     // upvalue or pass where it should be.
612     while (upvalue != null && upvalue.value > local)
613     {
614         prevUpvalue = upvalue;
615         upvalue = upvalue.next;
616     }
617 
618     // Found an existing upvalue for this local.
619     if (upvalue != null && upvalue.value == local) return upvalue;
620 
621     // We've walked past this local on the stack, so there must not be an
622     // upvalue for it already. Make a new one and link it in in the right
623     // place to keep the list sorted.
624     ObjUpvalue* createdUpvalue = wrenNewUpvalue(vm, local);
625     if (prevUpvalue == null)
626     {
627         // The new one is the first one in the list.
628         fiber.openUpvalues = createdUpvalue;
629     }
630     else
631     {
632         prevUpvalue.next = createdUpvalue;
633     }
634 
635     createdUpvalue.next = upvalue;
636     return createdUpvalue;
637 }
638 
639 // Closes any open upvalues that have been created for stack slots at [last]
640 // and above.
641 static void closeUpvalues(ObjFiber* fiber, Value* last)
642 {
643     while (fiber.openUpvalues != null &&
644             fiber.openUpvalues.value >= last)
645     {
646         ObjUpvalue* upvalue = fiber.openUpvalues;
647 
648         // Move the value into the upvalue itself and point the upvalue to it.
649         upvalue.closed = *upvalue.value;
650         upvalue.value = &upvalue.closed;
651 
652         // Remove it from the open upvalue list.
653         fiber.openUpvalues = upvalue.next;
654     }
655 }
656 
657 // Looks up a foreign method in [moduleName] on [className] with [signature].
658 //
659 // This will try the host's foreign method binder first. If that fails, it
660 // falls back to handling the built-in modules.
661 static WrenForeignMethodFn findForeignMethod(WrenVM* vm,
662                                              const char* moduleName,
663                                              const char* className,
664                                              bool isStatic,
665                                              const char* signature)
666 {
667     WrenForeignMethodFn method = null;
668     
669     if (vm.config.bindForeignMethodFn != null)
670     {
671         method = vm.config.bindForeignMethodFn(vm, moduleName, className, isStatic,
672                                                 signature);
673     }
674     
675     // If the host didn't provide it, see if it's an optional one.
676     if (method == null)
677     {
678         import core.stdc.string : strcmp;
679         static if (WREN_OPT_META) {
680             if (strcmp(moduleName, "meta") == 0)
681             {
682                 import wren.optional.meta : wrenMetaBindForeignMethod;
683                 method = wrenMetaBindForeignMethod(vm, className, isStatic, signature);
684             }
685         }
686         static if (WREN_OPT_RANDOM) {
687             if (strcmp(moduleName, "random") == 0)
688             {
689                 import wren.optional.random : wrenRandomBindForeignMethod;
690                 method = wrenRandomBindForeignMethod(vm, className, isStatic, signature);
691             }
692         }
693     }
694 
695   return method;
696 }
697 
698 // Defines [methodValue] as a method on [classObj].
699 //
700 // Handles both foreign methods where [methodValue] is a string containing the
701 // method's signature and Wren methods where [methodValue] is a function.
702 //
703 // Aborts the current fiber if the method is a foreign method that could not be
704 // found.
705 static void bindMethod(WrenVM* vm, int methodType, int symbol,
706                        ObjModule* module_, ObjClass* classObj, Value methodValue)
707 {
708     const(char)* className = classObj.name.value.ptr;
709     if (methodType == Code.CODE_METHOD_STATIC) classObj = classObj.obj.classObj;
710 
711     Method method;
712     if (IS_STRING(methodValue))
713     {
714         const(char)* name = AS_CSTRING(methodValue);
715         method.type = MethodType.METHOD_FOREIGN;
716         method.as.foreign = findForeignMethod(vm, module_.name.value.ptr,
717                                             className,
718                                             methodType == Code.CODE_METHOD_STATIC,
719                                             name);
720 
721         if (method.as.foreign == null)
722         {
723             vm.fiber.error = wrenStringFormat(vm,
724                 "Could not find foreign method '@' for class $ in module '$'.".ptr,
725                 methodValue, classObj.name.value.ptr, module_.name.value.ptr);
726             return;
727         }
728     }
729     else
730     {
731         import wren.compiler : wrenBindMethodCode;
732 
733         method.as.closure = AS_CLOSURE(methodValue);
734         method.type = MethodType.METHOD_BLOCK;
735 
736         // Patch up the bytecode now that we know the superclass.
737         wrenBindMethodCode(classObj, method.as.closure.fn);
738     }
739 
740     wrenBindMethod(vm, classObj, symbol, method);
741 }
742 
743 static void callForeign(WrenVM* vm, ObjFiber* fiber,
744                         WrenForeignMethodFn foreign, int numArgs)
745 {
746     assert(vm.apiStack == null, "Cannot already be in foreign call.");
747     vm.apiStack = fiber.stackTop - numArgs;
748 
749     foreign(vm);
750 
751     // Discard the stack slots for the arguments and temporaries but leave one
752     // for the result.
753     fiber.stackTop = vm.apiStack + 1;
754 
755     vm.apiStack = null;
756 }
757 
758 // Handles the current fiber having aborted because of an error.
759 //
760 // Walks the call chain of fibers, aborting each one until it hits a fiber that
761 // handles the error. If none do, tells the VM to stop.
762 static void runtimeError(WrenVM* vm)
763 {
764     assert(wrenHasError(vm.fiber), "Should only call this after an error.");
765 
766     ObjFiber* current = vm.fiber;
767     Value error = current.error;
768     
769     while (current != null)
770     {
771         // Every fiber along the call chain gets aborted with the same error.
772         current.error = error;
773 
774         // If the caller ran this fiber using "try", give it the error and stop.
775         if (current.state == FiberState.FIBER_TRY)
776         {
777             // Make the caller's try method return the error message.
778             current.caller.stackTop[-1] = vm.fiber.error;
779             vm.fiber = current.caller;
780             return;
781         }
782         
783         // Otherwise, unhook the caller since we will never resume and return to it.
784         ObjFiber* caller = current.caller;
785         current.caller = null;
786         current = caller;
787     }
788 
789     // If we got here, nothing caught the error, so show the stack trace.
790     import wren.dbg : wrenDebugPrintStackTrace;
791     wrenDebugPrintStackTrace(vm);
792     vm.fiber = null;
793     vm.apiStack = null;
794 }
795 
796 // Aborts the current fiber with an appropriate method not found error for a
797 // method with [symbol] on [classObj].
798 static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
799 {
800     vm.fiber.error = wrenStringFormat(vm, "@ does not implement '$'.".ptr,
801         OBJ_VAL(classObj.name), vm.methodNames.data[symbol].value.ptr);
802 }
803 
804 // Looks up the previously loaded module with [name].
805 //
806 // Returns `null` if no module with that name has been loaded.
807 static ObjModule* getModule(WrenVM* vm, Value name)
808 {
809     Value moduleValue = wrenMapGet(vm.modules, name);
810     return !IS_UNDEFINED(moduleValue) ? AS_MODULE(moduleValue) : null;
811 }
812 
813 static ObjClosure* compileInModule(WrenVM* vm, Value name, const char* source,
814                                    bool isExpression, bool printErrors)
815 {
816     // See if the module has already been loaded.
817     ObjModule* module_ = getModule(vm, name);
818     if (module_ == null)
819     {
820         module_ = wrenNewModule(vm, AS_STRING(name));
821 
822         // It's possible for the wrenMapSet below to resize the modules map,
823         // and trigger a GC while doing so. When this happens it will collect
824         // the module we've just created. Once in the map it is safe.
825         wrenPushRoot(vm, cast(Obj*)module_);
826 
827         // Store it in the VM's module registry so we don't load the same module
828         // multiple times.
829         wrenMapSet(vm, vm.modules, name, OBJ_VAL(module_));
830 
831         wrenPopRoot(vm);
832 
833         // Implicitly import the core module.
834         ObjModule* coreModule = getModule(vm, NULL_VAL);
835         for (int i = 0; i < coreModule.variables.count; i++)
836         {
837             wrenDefineVariable(vm, module_,
838                                 coreModule.variableNames.data[i].value.ptr,
839                                 coreModule.variableNames.data[i].length,
840                                 coreModule.variables.data[i], null);
841         }
842     }
843 
844     import wren.compiler : wrenCompile;
845     ObjFn* fn = wrenCompile(vm, module_, source, isExpression, printErrors);
846     if (fn == null)
847     {
848         // TODO: Should we still store the module even if it didn't compile?
849         return null;
850     }
851 
852     // Functions are always wrapped in closures.
853     wrenPushRoot(vm, cast(Obj*)fn);
854     ObjClosure* closure = wrenNewClosure(vm, fn);
855     wrenPopRoot(vm); // fn.
856 
857     return closure;
858 }
859 
860 // Verifies that [superclassValue] is a valid object to inherit from. That
861 // means it must be a class and cannot be the class of any built-in type.
862 //
863 // Also validates that it doesn't result in a class with too many fields and
864 // the other limitations foreign classes have.
865 //
866 // If successful, returns `null`. Otherwise, returns a string for the runtime
867 // error message.
868 static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue,
869                                 int numFields)
870 {
871   // Make sure the superclass is a class.
872   if (!IS_CLASS(superclassValue))
873   {
874     return wrenStringFormat(vm,
875         "Class '@' cannot inherit from a non-class object.".ptr,
876         name);
877   }
878 
879   // Make sure it doesn't inherit from a sealed built-in type. Primitive methods
880   // on these classes assume the instance is one of the other Obj___ types and
881   // will fail horribly if it's actually an ObjInstance.
882   ObjClass* superclass = AS_CLASS(superclassValue);
883   if (superclass == vm.classClass ||
884       superclass == vm.fiberClass ||
885       superclass == vm.fnClass || // Includes OBJ_CLOSURE.
886       superclass == vm.listClass ||
887       superclass == vm.mapClass ||
888       superclass == vm.rangeClass ||
889       superclass == vm.stringClass ||
890       superclass == vm.boolClass ||
891       superclass == vm.nullClass ||
892       superclass == vm.numClass)
893   {
894     return wrenStringFormat(vm,
895         "Class '@' cannot inherit from built-in class '@'.",
896         name, OBJ_VAL(superclass.name));
897   }
898 
899   if (superclass.numFields == -1)
900   {
901     return wrenStringFormat(vm,
902         "Class '@' cannot inherit from foreign class '@'.",
903         name, OBJ_VAL(superclass.name));
904   }
905 
906   if (numFields == -1 && superclass.numFields > 0)
907   {
908     return wrenStringFormat(vm,
909         "Foreign class '@' may not inherit from a class with fields.",
910         name);
911   }
912 
913   if (superclass.numFields + numFields > MAX_FIELDS)
914   {
915     return wrenStringFormat(vm,
916         "Class '@' may not have more than 255 fields, including inherited "
917         ~ "ones.", name);
918   }
919 
920   return NULL_VAL;
921 }
922 
923 static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module_)
924 {
925     WrenForeignClassMethods methods;
926     methods.allocate = null;
927     methods.finalize = null;
928     
929     // Check the optional built-in module first so the host can override it.
930     
931     if (vm.config.bindForeignClassFn != null)
932     {
933         methods = vm.config.bindForeignClassFn(vm, module_.name.value.ptr,
934                                                 classObj.name.value.ptr);
935     }
936 
937     // If the host didn't provide it, see if it's a built in optional module.
938     if (methods.allocate == null && methods.finalize == null)
939     {
940         static if (WREN_OPT_RANDOM) {
941             import core.stdc.string : strcmp;
942             if (strcmp(module_.name.value.ptr, "random") == 0)
943             {
944                 import wren.optional.random : wrenRandomBindForeignClass;
945                 methods = wrenRandomBindForeignClass(vm, module_.name.value.ptr,
946                                                 classObj.name.value.ptr);
947             }
948         }
949     }
950     
951     Method method;
952     method.type = MethodType.METHOD_FOREIGN;
953 
954     // Add the symbol even if there is no allocator so we can ensure that the
955     // symbol itself is always in the symbol table.
956     int symbol = wrenSymbolTableEnsure(vm, &vm.methodNames, "<allocate>", 10);
957     if (methods.allocate != null)
958     {
959         method.as.foreign = methods.allocate;
960         wrenBindMethod(vm, classObj, symbol, method);
961     }
962     
963     // Add the symbol even if there is no finalizer so we can ensure that the
964     // symbol itself is always in the symbol table.
965     symbol = wrenSymbolTableEnsure(vm, &vm.methodNames, "<finalize>", 10);
966     if (methods.finalize != null)
967     {
968         method.as.foreign = cast(WrenForeignMethodFn)methods.finalize;
969         wrenBindMethod(vm, classObj, symbol, method);
970     }
971 }
972 
973 // Completes the process for creating a new class.
974 //
975 // The class attributes instance and the class itself should be on the 
976 // top of the fiber's stack. 
977 //
978 // This process handles moving the attribute data for a class from
979 // compile time to runtime, since it now has all the attributes associated
980 // with a class, including for methods.
981 static void endClass(WrenVM* vm) 
982 {
983   // Pull the attributes and class off the stack
984   Value attributes = vm.fiber.stackTop[-2];
985   Value classValue = vm.fiber.stackTop[-1];
986 
987   // Remove the stack items
988   vm.fiber.stackTop -= 2;
989 
990   ObjClass* classObj = AS_CLASS(classValue);
991     classObj.attributes = attributes;
992 }
993 
994 // Creates a new class.
995 //
996 // If [numFields] is -1, the class is a foreign class. The name and superclass
997 // should be on top of the fiber's stack. After calling this, the top of the
998 // stack will contain the new class.
999 //
1000 // Aborts the current fiber if an error occurs.
1001 static void createClass(WrenVM* vm, int numFields, ObjModule* module_)
1002 {
1003     // Pull the name and superclass off the stack.
1004     Value name = vm.fiber.stackTop[-2];
1005     Value superclass = vm.fiber.stackTop[-1];
1006 
1007     // We have two values on the stack and we are going to leave one, so discard
1008     // the other slot.
1009     vm.fiber.stackTop--;
1010 
1011     vm.fiber.error = validateSuperclass(vm, name, superclass, numFields);
1012     if (wrenHasError(vm.fiber)) return;
1013 
1014     ObjClass* classObj = wrenNewClass(vm, AS_CLASS(superclass), numFields,
1015                                         AS_STRING(name));
1016     vm.fiber.stackTop[-1] = OBJ_VAL(classObj);
1017 
1018     if (numFields == -1) bindForeignClass(vm, classObj, module_);
1019 }
1020 
1021 static void createForeign(WrenVM* vm, ObjFiber* fiber, Value* stack)
1022 {
1023     ObjClass* classObj = AS_CLASS(stack[0]);
1024     assert(classObj.numFields == -1, "Class must be a foreign class.");
1025 
1026     // TODO: Don't look up every time.
1027     int symbol = wrenSymbolTableFind(&vm.methodNames, "<allocate>", 10);
1028     assert(symbol != -1, "Should have defined <allocate> symbol.");
1029 
1030     assert(classObj.methods.count > symbol, "Class should have allocator.");
1031     Method* method = &classObj.methods.data[symbol];
1032     assert(method.type == MethodType.METHOD_FOREIGN, "Allocator should be foreign.");
1033 
1034     // Pass the constructor arguments to the allocator as well.
1035     assert(vm.apiStack == null, "Cannot already be in foreign call.");
1036     vm.apiStack = stack;
1037 
1038     method.as.foreign(vm);
1039 
1040     vm.apiStack = null;
1041 }
1042 
1043 void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign)
1044 {
1045     // TODO: Don't look up every time.
1046     int symbol = wrenSymbolTableFind(&vm.methodNames, "<finalize>", 10);
1047     assert(symbol != -1, "Should have defined <finalize> symbol.");
1048 
1049     // If there are no finalizers, don't finalize it.
1050     if (symbol == -1) return;
1051 
1052     // If the class doesn't have a finalizer, bail out.
1053     ObjClass* classObj = foreign.obj.classObj;
1054     if (symbol >= classObj.methods.count) return;
1055 
1056     Method* method = &classObj.methods.data[symbol];
1057     if (method.type == MethodType.METHOD_NONE) return;
1058 
1059     assert(method.type == MethodType.METHOD_FOREIGN, "Finalizer should be foreign.");
1060 
1061     WrenFinalizerFn finalizer = cast(WrenFinalizerFn)method.as.foreign;
1062     finalizer(foreign.data.ptr);
1063 }
1064 
1065 // Let the host resolve an imported module name if it wants to.
1066 static Value resolveModule(WrenVM* vm, Value name)
1067 {
1068     // If the host doesn't care to resolve, leave the name alone.
1069     if (vm.config.resolveModuleFn == null) return name;
1070 
1071     ObjFiber* fiber = vm.fiber;
1072     ObjFn* fn = fiber.frames[fiber.numFrames - 1].closure.fn;
1073     ObjString* importer = fn.module_.name;
1074     
1075     const(char)* resolved = vm.config.resolveModuleFn(vm, importer.value.ptr,
1076                                                         AS_CSTRING(name));
1077     if (resolved == null)
1078     {
1079         vm.fiber.error = wrenStringFormat(vm,
1080             "Could not resolve module '@' imported from '@'.",
1081             name, OBJ_VAL(importer));
1082         return NULL_VAL;
1083     }
1084     
1085     // If they resolved to the exact same string, we don't need to copy it.
1086     if (resolved == AS_CSTRING(name)) return name;
1087 
1088     // Copy the string into a Wren String object.
1089     name = wrenNewString(vm, resolved);
1090     DEALLOCATE(vm, cast(char*)resolved);
1091     return name;
1092 }
1093 
1094 static Value importModule(WrenVM* vm, Value name)
1095 {
1096     name = resolveModule(vm, name);
1097     
1098     // If the module is already loaded, we don't need to do anything.
1099     Value existing = wrenMapGet(vm.modules, name);
1100     if (!IS_UNDEFINED(existing)) return existing;
1101 
1102     wrenPushRoot(vm, AS_OBJ(name));
1103 
1104     WrenLoadModuleResult result = WrenLoadModuleResult(null, null, null);
1105     const(char)* source = null;
1106     
1107     // Let the host try to provide the module.
1108     if (vm.config.loadModuleFn != null)
1109     {
1110         result = vm.config.loadModuleFn(vm, AS_CSTRING(name));
1111     }
1112     
1113     // If the host didn't provide it, see if it's a built in optional module.
1114     if (result.source == null)
1115     {
1116         import core.stdc.string : strcmp;
1117         result.onComplete = null;
1118         ObjString* nameString = AS_STRING(name);
1119         static if (WREN_OPT_META) {
1120             import wren.optional.meta : wrenMetaSource;
1121             if (strcmp(nameString.value.ptr, "meta") == 0) result.source = wrenMetaSource().ptr;
1122         }
1123         static if (WREN_OPT_RANDOM) {
1124             import wren.optional.random : wrenRandomSource;
1125             if (strcmp(nameString.value.ptr, "random") == 0) result.source = wrenRandomSource().ptr;
1126         }
1127     }
1128     
1129     if (result.source == null)
1130     {
1131         vm.fiber.error = wrenStringFormat(vm, "Could not load module '@'.", name);
1132         wrenPopRoot(vm); // name.
1133         return NULL_VAL;
1134     }
1135     
1136     ObjClosure* moduleClosure = compileInModule(vm, name, result.source, false, true);
1137     
1138     // Now that we're done, give the result back in case there's cleanup to do.
1139     if(result.onComplete) result.onComplete(vm, AS_CSTRING(name), result);
1140     
1141     if (moduleClosure == null)
1142     {
1143         vm.fiber.error = wrenStringFormat(vm,
1144                                             "Could not compile module '@'.", name);
1145         wrenPopRoot(vm); // name.
1146         return NULL_VAL;
1147     }
1148 
1149     wrenPopRoot(vm); // name.
1150 
1151     // Return the closure that executes the module.
1152     return OBJ_VAL(moduleClosure);
1153 }
1154 
1155 static Value getModuleVariable(WrenVM* vm, ObjModule* module_,
1156                                Value variableName)
1157 {
1158     ObjString* variable = AS_STRING(variableName);
1159     uint variableEntry = wrenSymbolTableFind(&module_.variableNames,
1160                                                 variable.value.ptr,
1161                                                 variable.length);
1162     
1163     // It's a runtime error if the imported variable does not exist.
1164     if (variableEntry != uint.max)
1165     {
1166         return module_.variables.data[variableEntry];
1167     }
1168     
1169     vm.fiber.error = wrenStringFormat(vm,
1170         "Could not find a variable named '@' in module '@'.",
1171         variableName, OBJ_VAL(module_.name));
1172     return NULL_VAL;
1173 }
1174 
1175 static bool checkArity(WrenVM* vm, Value value, int numArgs)
1176 {
1177     assert(IS_CLOSURE(value), "Receiver must be a closure.");
1178     ObjFn* fn = AS_CLOSURE(value).fn;
1179 
1180     // We only care about missing arguments, not extras. The "- 1" is because
1181     // numArgs includes the receiver, the function itself, which we don't want to
1182     // count.
1183     if (numArgs - 1 >= fn.arity) return true;
1184 
1185     vm.fiber.error = CONST_STRING(vm, "Function expects more arguments.");
1186     return false;
1187 }
1188 
1189 // The main bytecode interpreter loop. This is where the magic happens. It is
1190 // also, as you can imagine, highly performance critical.
1191 // Arg... thar be dragons here.
1192 static WrenInterpretResult runInterpreter(WrenVM* vm, ObjFiber* fiber)
1193 {
1194     // Remember the current fiber so we can find it if a GC happens.
1195     vm.fiber = fiber;
1196     fiber.state = FiberState.FIBER_ROOT;
1197 
1198     // Hoist these into local variables. They are accessed frequently in the loop
1199     // but assigned less frequently. Keeping them in locals and updating them when
1200     // a call frame has been pushed or popped gives a large speed boost.
1201     CallFrame* frame;
1202     Value* stackStart;
1203     ubyte* ip;
1204     ObjFn* fn;
1205 
1206     // These are a part of the CALL args,
1207     // but cannot be defined within the switch statement itself.
1208     int numArgs;
1209     int symbol;
1210     Value* args;
1211     ObjClass* classObj;
1212     Method* method;
1213 
1214     // These macros are designed to only be invoked within this function.
1215     pragma(inline, true)
1216     void PUSH(Value value) {
1217         *fiber.stackTop++ = value;
1218     }
1219 
1220     pragma(inline, true)
1221     Value POP() {
1222         return (*(--fiber.stackTop));
1223     }
1224 
1225     pragma(inline, true)
1226     void DROP() {
1227         fiber.stackTop--;
1228     }
1229 
1230     pragma(inline, true)
1231     Value PEEK() {
1232         return (*(fiber.stackTop - 1));
1233     }
1234 
1235     pragma(inline, true)
1236     Value PEEK2() {
1237         return (*(fiber.stackTop - 2));
1238     }
1239 
1240     pragma(inline, true)
1241     ubyte READ_BYTE() {
1242         return (*ip++);
1243     }
1244 
1245     pragma(inline, true)
1246     ushort READ_SHORT() {
1247         ip += 2;
1248         return cast(ushort)((ip[-2] << 8) | ip[-1]);
1249     }
1250 
1251     // Use this before a CallFrame is pushed to store the local variables back
1252     // into the current one.
1253     pragma(inline, true)
1254     void STORE_FRAME() {
1255         frame.ip = ip;
1256     }
1257 
1258     // Use this after a CallFrame has been pushed or popped to refresh the local
1259     // variables.
1260     pragma(inline, true)
1261     void LOAD_FRAME() {
1262         frame = &fiber.frames[fiber.numFrames - 1];
1263         stackStart = frame.stackStart;
1264         ip = frame.ip;
1265         fn = frame.closure.fn;
1266     }
1267 
1268     // Terminates the current fiber with error string [error]. If another calling
1269     // fiber is willing to catch the error, transfers control to it, otherwise
1270     // exits the interpreter.
1271     string RUNTIME_ERROR() {
1272         return q{
1273             STORE_FRAME();
1274             runtimeError(vm);
1275             if (vm.fiber == null) return WrenInterpretResult.WREN_RESULT_RUNTIME_ERROR;
1276             fiber = vm.fiber;
1277             LOAD_FRAME();
1278             goto loop;
1279         };
1280     }
1281 
1282     static if (WREN_DEBUG_TRACE_INSTRUCTIONS)
1283     {
1284         void DEBUG_TRACE_INSTRUCTION() {
1285             import wren.dbg : wrenDumpStack, wrenDumpInstruction;
1286             wrenDumpStack(fiber);
1287             wrenDumpInstruction(vm, fn, cast(int)(ip - fn.code.data));
1288         }
1289     }
1290 
1291     LOAD_FRAME();
1292     Code instruction;
1293 
1294 loop:
1295     static if (WREN_DEBUG_TRACE_INSTRUCTIONS)
1296     {
1297         DEBUG_TRACE_INSTRUCTION();
1298     }
1299     switch (instruction = cast(Code)READ_BYTE()) with(Code)
1300     {
1301         case CODE_LOAD_LOCAL_0:
1302         case CODE_LOAD_LOCAL_1:
1303         case CODE_LOAD_LOCAL_2:
1304         case CODE_LOAD_LOCAL_3:
1305         case CODE_LOAD_LOCAL_4:
1306         case CODE_LOAD_LOCAL_5:
1307         case CODE_LOAD_LOCAL_6:
1308         case CODE_LOAD_LOCAL_7:
1309         case CODE_LOAD_LOCAL_8:
1310             PUSH(stackStart[instruction - CODE_LOAD_LOCAL_0]);
1311             goto loop;
1312         
1313         case CODE_LOAD_LOCAL:
1314             PUSH(stackStart[READ_BYTE()]);
1315             goto loop;
1316 
1317         case CODE_LOAD_FIELD_THIS: {
1318             ubyte field = READ_BYTE();
1319             Value receiver = stackStart[0];
1320             assert(IS_INSTANCE(receiver), "Receiver should be instance.");
1321             ObjInstance* instance = AS_INSTANCE(receiver);
1322             assert(field < instance.obj.classObj.numFields, "Out of bounds field.");
1323             PUSH(instance.fields[field]);
1324             goto loop;
1325         }
1326         
1327         case CODE_POP:
1328             DROP();
1329             goto loop;
1330         
1331         case CODE_NULL:
1332             PUSH(NULL_VAL);
1333             goto loop;
1334 
1335         case CODE_FALSE:
1336             PUSH(FALSE_VAL);
1337             goto loop;
1338         
1339         case CODE_TRUE:
1340             PUSH(TRUE_VAL);
1341             goto loop;
1342 
1343         case CODE_STORE_LOCAL:
1344             stackStart[READ_BYTE()] = PEEK();
1345             goto loop;
1346         
1347         case CODE_CONSTANT:
1348             PUSH(fn.constants.data[READ_SHORT()]);
1349             goto loop;
1350 
1351         // The opcodes for doing method and superclass calls share a lot of code.
1352         // However, doing an if() test in the middle of the instruction sequence
1353         // to handle the bit that is special to super calls makes the non-super
1354         // call path noticeably slower.
1355         //
1356         // Instead, we do this old school using an explicit goto to share code for
1357         // everything at the tail end of the call-handling code that is the same
1358         // between normal and superclass calls.
1359 
1360         case CODE_CALL_0:
1361         case CODE_CALL_1:
1362         case CODE_CALL_2:
1363         case CODE_CALL_3:
1364         case CODE_CALL_4:
1365         case CODE_CALL_5:
1366         case CODE_CALL_6:
1367         case CODE_CALL_7:
1368         case CODE_CALL_8:
1369         case CODE_CALL_9:
1370         case CODE_CALL_10:
1371         case CODE_CALL_11:
1372         case CODE_CALL_12:
1373         case CODE_CALL_13:
1374         case CODE_CALL_14:
1375         case CODE_CALL_15:
1376         case CODE_CALL_16:
1377             // Add one for the implicit receiver argument.
1378             numArgs = instruction - CODE_CALL_0 + 1;
1379             symbol = READ_SHORT();
1380             
1381             // The receiver is the first argument.
1382             args = fiber.stackTop - numArgs;
1383             classObj = wrenGetClassInline(vm, args[0]);
1384             goto completeCall;
1385 
1386         case CODE_SUPER_0:
1387         case CODE_SUPER_1:
1388         case CODE_SUPER_2:
1389         case CODE_SUPER_3:
1390         case CODE_SUPER_4:
1391         case CODE_SUPER_5:
1392         case CODE_SUPER_6:
1393         case CODE_SUPER_7:
1394         case CODE_SUPER_8:
1395         case CODE_SUPER_9:
1396         case CODE_SUPER_10:
1397         case CODE_SUPER_11:
1398         case CODE_SUPER_12:
1399         case CODE_SUPER_13:
1400         case CODE_SUPER_14:
1401         case CODE_SUPER_15:
1402         case CODE_SUPER_16:
1403         {
1404             // Add one for the implicit receiver argument.
1405             numArgs = instruction - CODE_SUPER_0 + 1;
1406             symbol = READ_SHORT();
1407 
1408             // The receiver is the first argument.
1409             args = fiber.stackTop - numArgs;
1410 
1411             // The superclass is stored in a constant.
1412             classObj = AS_CLASS(fn.constants.data[READ_SHORT()]);
1413             goto completeCall;
1414 
1415         completeCall:
1416             {
1417                 // If the class's method table doesn't include the symbol, bail.
1418                 if (symbol >= classObj.methods.count ||
1419                     (method = &classObj.methods.data[symbol]).type == MethodType.METHOD_NONE)
1420                     {
1421                         methodNotFound(vm, classObj, symbol);
1422                         mixin(RUNTIME_ERROR);
1423                     }
1424 
1425                 switch (method.type) with(MethodType) {
1426                     case METHOD_PRIMITIVE: {
1427                         if (method.as.primitive(vm, args))
1428                         {
1429                             // The result is now in the first arg slot. Discard the other
1430                             // stack slots.
1431                             fiber.stackTop -= numArgs - 1;
1432                         } else {
1433                             // An error, fiber switch, or call frame change occurred.
1434                             STORE_FRAME();
1435 
1436                             // If we don't have a fiber to switch to, stop interpreting.
1437                             fiber = vm.fiber;
1438                             if (fiber == null) return WrenInterpretResult.WREN_RESULT_SUCCESS;
1439                             if (wrenHasError(fiber)) mixin(RUNTIME_ERROR);
1440                             LOAD_FRAME();
1441                         }
1442                         break;
1443                     }
1444 
1445                     case METHOD_FUNCTION_CALL: {
1446                         if (!checkArity(vm, args[0], numArgs)) {
1447                             mixin(RUNTIME_ERROR);
1448                         }
1449 
1450                         STORE_FRAME();
1451                         method.as.primitive(vm, args);
1452                         LOAD_FRAME();
1453                         break;
1454                     }
1455 
1456                     case METHOD_FOREIGN: {
1457                         callForeign(vm, fiber, method.as.foreign, numArgs);
1458                         if (wrenHasError(fiber)) {
1459                             mixin(RUNTIME_ERROR);
1460                         }
1461                         break;
1462                     }
1463 
1464                     case METHOD_BLOCK: {
1465                         STORE_FRAME();
1466                         wrenCallFunction(vm, fiber, cast(ObjClosure*)method.as.closure, numArgs);
1467                         LOAD_FRAME();
1468                         break;
1469                     }
1470                     default:
1471                         assert(0, "Unreachable");
1472 
1473                 }
1474                 goto loop;
1475             }
1476         }
1477 
1478         case CODE_LOAD_UPVALUE:
1479         {
1480             ObjUpvalue** upvalues = frame.closure.upvalues.ptr;
1481             PUSH(*upvalues[READ_BYTE()].value);
1482             goto loop;
1483         }
1484 
1485         case CODE_STORE_UPVALUE:
1486         {
1487             ObjUpvalue** upvalues = frame.closure.upvalues.ptr;
1488             *upvalues[READ_BYTE()].value = PEEK();
1489             goto loop;
1490         }
1491 
1492         case CODE_LOAD_MODULE_VAR:
1493             PUSH(fn.module_.variables.data[READ_SHORT()]);
1494             goto loop;
1495 
1496         case CODE_STORE_MODULE_VAR:
1497             fn.module_.variables.data[READ_SHORT()] = PEEK();
1498             goto loop;
1499 
1500         case CODE_STORE_FIELD_THIS:
1501         {
1502             ubyte field = READ_BYTE();
1503             Value receiver = stackStart[0];
1504             assert(IS_INSTANCE(receiver), "Receiver should be instance.");
1505             ObjInstance* instance = AS_INSTANCE(receiver);
1506             assert(field < instance.obj.classObj.numFields, "Out of bounds field.");
1507             instance.fields[field] = PEEK();
1508             goto loop;
1509         }
1510 
1511         case CODE_LOAD_FIELD:
1512         {
1513             ubyte field = READ_BYTE();
1514             Value receiver = POP();
1515             assert(IS_INSTANCE(receiver), "Receiver should be instance.");
1516             ObjInstance* instance = AS_INSTANCE(receiver);
1517             assert(field < instance.obj.classObj.numFields, "Out of bounds field.");
1518             PUSH(instance.fields[field]);
1519             goto loop;
1520         }
1521 
1522         case CODE_STORE_FIELD:
1523         {
1524             ubyte field = READ_BYTE();
1525             Value receiver = POP();
1526             assert(IS_INSTANCE(receiver), "Receiver should be instance.");
1527             ObjInstance* instance = AS_INSTANCE(receiver);
1528             assert(field < instance.obj.classObj.numFields, "Out of bounds field.");
1529             instance.fields[field] = PEEK();
1530             goto loop;
1531         }
1532 
1533         case CODE_JUMP:
1534         {
1535             ushort offset = READ_SHORT();
1536             ip += offset;
1537             goto loop;
1538         }
1539 
1540         case CODE_LOOP:
1541         {
1542             // Jump back to the top of the loop.
1543             ushort offset = READ_SHORT();
1544             ip -= offset;
1545             goto loop;
1546         }
1547 
1548         case CODE_JUMP_IF:
1549         {
1550             ushort offset = READ_SHORT();
1551             Value condition = POP();
1552 
1553             if (wrenIsFalsyValue(condition)) ip += offset;
1554             goto loop;
1555         }
1556 
1557         case CODE_AND:
1558         {
1559             ushort offset = READ_SHORT();
1560             Value condition = PEEK();
1561 
1562             if (wrenIsFalsyValue(condition))
1563             {
1564                 // Short-circuit the right hand side.
1565                 ip += offset;
1566             }
1567             else
1568             {
1569                 // Discard the condition and evaluate the right hand side.
1570                 DROP();
1571             }
1572             goto loop;
1573         }
1574 
1575         case CODE_OR:
1576         {
1577             ushort offset = READ_SHORT();
1578             Value condition = PEEK();
1579 
1580             if (wrenIsFalsyValue(condition))
1581             {
1582                 // Discard the condition and evaluate the right hand side.
1583                 DROP();
1584             }
1585             else
1586             {
1587                 // Short-circuit the right hand side.
1588                 ip += offset;
1589             }
1590             goto loop;
1591         }
1592 
1593         case CODE_CLOSE_UPVALUE:
1594         {
1595             // Close the upvalue for the local if we have one.
1596             closeUpvalues(fiber, fiber.stackTop - 1);
1597             DROP();
1598             goto loop;
1599         }
1600 
1601         case CODE_RETURN:
1602         {
1603             Value result = POP();
1604             fiber.numFrames--;
1605 
1606             // Close any upvalues still in scope.
1607             closeUpvalues(fiber, stackStart);
1608 
1609             // If the fiber is complete, end it.
1610             if (fiber.numFrames == 0)
1611             {
1612                 // See if there's another fiber to return to. If not, we're done.
1613                 if (fiber.caller == null)
1614                 {
1615                     // Store the final result value at the beginning of the stack so the
1616                     // C API can get it.
1617                     fiber.stack[0] = result;
1618                     fiber.stackTop = fiber.stack + 1;
1619                     return WrenInterpretResult.WREN_RESULT_SUCCESS;
1620                 }
1621 
1622                 ObjFiber* resumingFiber = fiber.caller;
1623                 fiber.caller = null;
1624                 fiber = resumingFiber;
1625                 vm.fiber = resumingFiber;
1626 
1627                 // Store the result in the resuming fiber.
1628                 fiber.stackTop[-1] = result;
1629             }
1630             else
1631             {
1632                 // Store the result of the block in the first slot, which is where
1633                 // the caller expects it.
1634                 stackStart[0] = result;
1635 
1636                 // Discard the stack slots for the call frame (leaving one slot for the
1637                 // result).
1638                 fiber.stackTop = frame.stackStart + 1;
1639             }
1640 
1641             LOAD_FRAME();
1642             goto loop;
1643         }
1644 
1645         case CODE_CONSTRUCT:
1646             assert(IS_CLASS(stackStart[0]), "'this' should be a class.");
1647             stackStart[0] = wrenNewInstance(vm, AS_CLASS(stackStart[0]));
1648             goto loop;
1649 
1650         case CODE_FOREIGN_CONSTRUCT:
1651             assert(IS_CLASS(stackStart[0]), "'this' should be a class.");
1652             createForeign(vm, fiber, stackStart);
1653             if (wrenHasError(fiber)) {
1654                 mixin(RUNTIME_ERROR);
1655             }
1656             goto loop;
1657 
1658         case CODE_CLOSURE:
1659         {
1660             // Create the closure and push it on the stack before creating upvalues
1661             // so that it doesn't get collected.
1662             ObjFn* fnC = AS_FN(fn.constants.data[READ_SHORT()]);
1663             ObjClosure* closure = wrenNewClosure(vm, fnC);
1664             PUSH(OBJ_VAL(closure));
1665 
1666             // Capture upvalues, if any.
1667             for (int i = 0; i < fnC.numUpvalues; i++)
1668             {
1669                 ubyte isLocal = READ_BYTE();
1670                 ubyte index = READ_BYTE();
1671                 if (isLocal)
1672                 {
1673                     // Make an new upvalue to close over the parent's local variable.
1674                     closure.upvalues[i] = captureUpvalue(vm, fiber, frame.stackStart + index);
1675                 }
1676                 else
1677                 {
1678                     // Use the same upvalue as the current call frame.
1679                     closure.upvalues[i] = frame.closure.upvalues[index];
1680                 }
1681             }
1682             goto loop;
1683         }
1684 
1685         case CODE_END_CLASS:
1686         {
1687             endClass(vm);
1688             if (wrenHasError(fiber))
1689             {
1690                 mixin(RUNTIME_ERROR);
1691             }
1692             goto loop;
1693         }
1694 
1695         case CODE_CLASS:
1696         {
1697             createClass(vm, READ_BYTE(), null);
1698             if (wrenHasError(fiber))
1699             {
1700                 mixin(RUNTIME_ERROR);
1701             }
1702             goto loop;
1703         }
1704 
1705         case CODE_FOREIGN_CLASS:
1706         {
1707             createClass(vm, -1, fn.module_);
1708             if (wrenHasError(fiber)) {
1709                 mixin(RUNTIME_ERROR);
1710             }
1711             goto loop;
1712         }
1713 
1714         case CODE_METHOD_INSTANCE:
1715         case CODE_METHOD_STATIC:
1716         {
1717             ushort methodSymbol = READ_SHORT();
1718             ObjClass* methodClassObj = AS_CLASS(PEEK());
1719             Value methodValue = PEEK2();
1720             bindMethod(vm, instruction, methodSymbol, fn.module_, methodClassObj, methodValue);
1721             if (wrenHasError(fiber)) {
1722                 mixin(RUNTIME_ERROR);
1723             }
1724             DROP();
1725             DROP();
1726             goto loop;
1727         }
1728 
1729         case CODE_END_MODULE:
1730         {
1731             vm.lastModule = fn.module_;
1732             PUSH(NULL_VAL);
1733             goto loop;
1734         }
1735 
1736         case CODE_IMPORT_MODULE:
1737         {
1738             // Make a slot on the stack for the module's fiber to place the return
1739             // value. It will be popped after this fiber is resumed. Store the
1740             // imported module's closure in the slot in case a GC happens when
1741             // invoking the closure.
1742             PUSH(importModule(vm, fn.constants.data[READ_SHORT()]));
1743             if (wrenHasError(fiber)) {
1744                 mixin(RUNTIME_ERROR);
1745             }
1746 
1747             // If we get a closure, call it to execute the module body.
1748             if (IS_CLOSURE(PEEK()))
1749             {
1750                 STORE_FRAME();
1751                 ObjClosure* closure = AS_CLOSURE(PEEK());
1752                 wrenCallFunction(vm, fiber, closure, 1);
1753                 LOAD_FRAME();
1754             }
1755             else
1756             {
1757                 // The module has already been loaded. Remember it so we can import
1758                 // variables from it if needed.
1759                 vm.lastModule = AS_MODULE(PEEK());
1760             }
1761 
1762             goto loop;
1763         }
1764 
1765         case CODE_IMPORT_VARIABLE:
1766         {
1767             Value variable = fn.constants.data[READ_SHORT()];
1768             assert(vm.lastModule != null, "Should have already imported module.");
1769             Value result = getModuleVariable(vm, vm.lastModule, variable);
1770             if (wrenHasError(fiber)) {
1771                 mixin(RUNTIME_ERROR);
1772             }
1773 
1774             PUSH(result);
1775             goto loop;
1776         }
1777 
1778         case CODE_END:
1779             assert(0, "Unreachable");
1780 
1781         default:
1782             assert(0, "Unhandled instruction");
1783     }
1784 }
1785 
1786 WrenHandle* wrenMakeCallHandle(WrenVM* vm, const(char)* signature)
1787 {
1788     import core.stdc.string : strlen;
1789     assert(signature != null, "Signature cannot be NULL.");
1790     
1791     int signatureLength = cast(int)strlen(signature);
1792     assert(signatureLength > 0, "Signature cannot be empty.");
1793     
1794     // Count the number parameters the method expects.
1795     int numParams = 0;
1796     if (signature[signatureLength - 1] == ')')
1797     {
1798         for (int i = signatureLength - 1; i > 0 && signature[i] != '('; i--)
1799         {
1800             if (signature[i] == '_') numParams++;
1801         }
1802     }
1803     
1804     // Count subscript arguments.
1805     if (signature[0] == '[')
1806     {
1807         for (int i = 0; i < signatureLength && signature[i] != ']'; i++)
1808         {
1809             if (signature[i] == '_') numParams++;
1810         }
1811     }
1812     
1813     // Add the signatue to the method table.
1814     int method =  wrenSymbolTableEnsure(vm, &vm.methodNames,
1815                                         signature, signatureLength);
1816     
1817     // Create a little stub function that assumes the arguments are on the stack
1818     // and calls the method.
1819     ObjFn* fn = wrenNewFunction(vm, null, numParams + 1);
1820     
1821     // Wrap the function in a closure and then in a handle. Do this here so it
1822     // doesn't get collected as we fill it in.
1823     WrenHandle* value = wrenMakeHandle(vm, OBJ_VAL(fn));
1824     value.value = OBJ_VAL(wrenNewClosure(vm, fn));
1825     
1826     wrenByteBufferWrite(vm, &fn.code, cast(ubyte)(Code.CODE_CALL_0 + numParams));
1827     wrenByteBufferWrite(vm, &fn.code, (method >> 8) & 0xff);
1828     wrenByteBufferWrite(vm, &fn.code, method & 0xff);
1829     wrenByteBufferWrite(vm, &fn.code, Code.CODE_RETURN);
1830     wrenByteBufferWrite(vm, &fn.code, Code.CODE_END);
1831     wrenIntBufferFill(vm, &fn.debug_.sourceLines, 0, 5);
1832     wrenFunctionBindName(vm, fn, signature, signatureLength);
1833 
1834     return value;
1835 }
1836 
1837 WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method)
1838 {
1839     assert(method != null, "Method cannot be NULL.");
1840     assert(IS_CLOSURE(method.value), "Method must be a method handle.");
1841     assert(vm.fiber != null, "Must set up arguments for call first.");
1842     assert(vm.apiStack != null, "Must set up arguments for call first.");
1843     assert(vm.fiber.numFrames == 0, "Can not call from a foreign method.");
1844     
1845     ObjClosure* closure = AS_CLOSURE(method.value);
1846     
1847     assert(vm.fiber.stackTop - vm.fiber.stack >= closure.fn.arity,
1848             "Stack must have enough arguments for method.");
1849     
1850     // Clear the API stack. Now that wrenCall() has control, we no longer need
1851     // it. We use this being non-null to tell if re-entrant calls to foreign
1852     // methods are happening, so it's important to clear it out now so that you
1853     // can call foreign methods from within calls to wrenCall().
1854     vm.apiStack = null;
1855 
1856     // Discard any extra temporary slots. We take for granted that the stub
1857     // function has exactly one slot for each argument.
1858     vm.fiber.stackTop = &vm.fiber.stack[closure.fn.maxSlots];
1859     
1860     wrenCallFunction(vm, vm.fiber, closure, 0);
1861     WrenInterpretResult result = runInterpreter(vm, vm.fiber);
1862     
1863     // If the call didn't abort, then set up the API stack to point to the
1864     // beginning of the stack so the host can access the call's return value.
1865     if (vm.fiber != null) vm.apiStack = vm.fiber.stack;
1866     
1867     return result;
1868 }
1869 
1870 WrenHandle* wrenMakeHandle(WrenVM* vm, Value value)
1871 {
1872     if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1873     
1874     // Make a handle for it.
1875     WrenHandle* handle = ALLOCATE!(WrenVM, WrenHandle)(vm);
1876     handle.value = value;
1877 
1878     if (IS_OBJ(value)) wrenPopRoot(vm);
1879 
1880     // Add it to the front of the linked list of handles.
1881     if (vm.handles != null) vm.handles.prev = handle;
1882     handle.prev = null;
1883     handle.next = vm.handles;
1884     vm.handles = handle;
1885     
1886     return handle;
1887 }
1888 
1889 void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle)
1890 {
1891     assert(handle != null, "Handle cannot be NULL.");
1892 
1893     // Update the VM's head pointer if we're releasing the first handle.
1894     if (vm.handles == handle) vm.handles = handle.next;
1895 
1896     // Unlink it from the list.
1897     if (handle.prev != null) handle.prev.next = handle.next;
1898     if (handle.next != null) handle.next.prev = handle.prev;
1899 
1900     // Clear it out. This isn't strictly necessary since we're going to free it,
1901     // but it makes for easier debugging.
1902     handle.prev = null;
1903     handle.next = null;
1904     handle.value = NULL_VAL;
1905     DEALLOCATE(vm, handle);
1906 }
1907 
1908 WrenInterpretResult wrenInterpret(WrenVM* vm, const(char)* module_,
1909                                   const(char)* source)
1910 {
1911     ObjClosure* closure = wrenCompileSource(vm, module_, source, false, true);
1912     if (closure == null) return WrenInterpretResult.WREN_RESULT_COMPILE_ERROR;
1913     
1914     wrenPushRoot(vm, cast(Obj*)closure);
1915     ObjFiber* fiber = wrenNewFiber(vm, closure);
1916     wrenPopRoot(vm); // closure.
1917     vm.apiStack = null;
1918 
1919     return runInterpreter(vm, fiber);
1920 }
1921 
1922 ObjClosure* wrenCompileSource(WrenVM* vm, const(char)* module_, const(char)* source,
1923                             bool isExpression, bool printErrors)
1924 {
1925     Value nameValue = NULL_VAL;
1926     if (module_ != null)
1927     {
1928         nameValue = wrenNewString(vm, module_);
1929         wrenPushRoot(vm, AS_OBJ(nameValue));
1930     }
1931     
1932     ObjClosure* closure = compileInModule(vm, nameValue, source,
1933                                             isExpression, printErrors);
1934 
1935     if (module_ != null) wrenPopRoot(vm); // nameValue.
1936     return closure;
1937 }
1938 
1939 Value wrenGetModuleVariable(WrenVM* vm, Value moduleName, Value variableName)
1940 {
1941     ObjModule* module_ = getModule(vm, moduleName);
1942     if (module_ == null)
1943     {
1944         vm.fiber.error = wrenStringFormat(vm, "Module '@' is not loaded.",
1945                                             moduleName);
1946         return NULL_VAL;
1947     }
1948     
1949     return getModuleVariable(vm, module_, variableName);
1950 }
1951 
1952 Value wrenFindVariable(WrenVM* vm, ObjModule* module_, const char* name)
1953 {
1954     import core.stdc.string : strlen;
1955     int symbol = wrenSymbolTableFind(&module_.variableNames, name, strlen(name));
1956     return module_.variables.data[symbol];
1957 }
1958 
1959 int wrenDeclareVariable(WrenVM* vm, ObjModule* module_, const char* name,
1960                         size_t length, int line)
1961 {
1962     if (module_.variables.count == MAX_MODULE_VARS) return -2;
1963 
1964     // Implicitly defined variables get a "value" that is the line where the
1965     // variable is first used. We'll use that later to report an error on the
1966     // right line.
1967     wrenValueBufferWrite(vm, &module_.variables, NUM_VAL(line));
1968     return wrenSymbolTableAdd(vm, &module_.variableNames, name, length);
1969 }
1970 
1971 int wrenDefineVariable(WrenVM* vm, ObjModule* module_, const char* name,
1972                        size_t length, Value value, int* line)
1973 {
1974     if (module_.variables.count == MAX_MODULE_VARS) return -2;
1975 
1976     if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1977 
1978     // See if the variable is already explicitly or implicitly declared.
1979     int symbol = wrenSymbolTableFind(&module_.variableNames, name, length);
1980 
1981     if (symbol == -1)
1982     {
1983         // Brand new variable.
1984         symbol = wrenSymbolTableAdd(vm, &module_.variableNames, name, length);
1985         wrenValueBufferWrite(vm, &module_.variables, value);
1986     }
1987     else if (IS_NUM(module_.variables.data[symbol]))
1988     {
1989         // An implicitly declared variable's value will always be a number.
1990         // Now we have a real definition.
1991         if(line) *line = cast(int)AS_NUM(module_.variables.data[symbol]);
1992         module_.variables.data[symbol] = value;
1993 
1994         // If this was a localname we want to error if it was 
1995         // referenced before this definition.
1996         if (wrenIsLocalName(name)) symbol = -3;
1997     }
1998     else
1999     {
2000         // Already explicitly declared.
2001         symbol = -1;
2002     }
2003 
2004     if (IS_OBJ(value)) wrenPopRoot(vm);
2005 
2006     return symbol;
2007 }
2008 
2009 static void wrenCallFunction(WrenVM* vm, ObjFiber* fiber,
2010                                     ObjClosure* closure, int numArgs)
2011 {
2012     // Grow the call frame array if needed.
2013     if (fiber.numFrames + 1 > fiber.frameCapacity)
2014     {
2015         int max = fiber.frameCapacity * 2;
2016         fiber.frames = cast(CallFrame*)wrenReallocate(vm, fiber.frames,
2017             CallFrame.sizeof * fiber.frameCapacity, CallFrame.sizeof * max);
2018         fiber.frameCapacity = max;
2019     }
2020     
2021     // Grow the stack if needed.
2022     int stackSize = cast(int)(fiber.stackTop - fiber.stack);
2023     int needed = stackSize + closure.fn.maxSlots;
2024     wrenEnsureStack(vm, fiber, needed);
2025     
2026     wrenAppendCallFrame(vm, fiber, closure, fiber.stackTop - numArgs);
2027 }
2028 
2029 // TODO: Inline?
2030 void wrenPushRoot(WrenVM* vm, Obj* obj)
2031 {
2032     assert(obj != null, "Cannot root null");
2033     assert(vm.numTempRoots < WREN_MAX_TEMP_ROOTS, "Too many temporary roots.");
2034 
2035     vm.tempRoots[vm.numTempRoots++] = obj;
2036 }
2037 
2038 void wrenPopRoot(WrenVM* vm)
2039 {
2040     assert(vm.numTempRoots > 0, "No temporary roots to release.");
2041     vm.numTempRoots--;
2042 }
2043 
2044 ObjClass* wrenGetClass(WrenVM* vm, Value value)
2045 {
2046     return wrenGetClassInline(vm, value);
2047 }
2048 
2049 // Returns the class of [value].
2050 //
2051 // Defined here instead of in wren_value.h because it's critical that this be
2052 // inlined. That means it must be defined in the header, but the wren_value.h
2053 // header doesn't have a full definitely of WrenVM yet.
2054 static ObjClass* wrenGetClassInline(WrenVM* vm, Value value)
2055 {
2056     if (IS_NUM(value)) return vm.numClass;
2057     if (IS_OBJ(value)) return AS_OBJ(value).classObj;
2058 
2059     static if (WREN_NAN_TAGGING) {
2060         switch (GET_TAG(value))
2061         {
2062             case TAG_FALSE:     return vm.boolClass;
2063             case TAG_NAN:       return vm.numClass;
2064             case TAG_NULL:      return vm.nullClass;
2065             case TAG_TRUE:      return vm.boolClass;
2066             case TAG_UNDEFINED: assert(0, "Unreachable");
2067             default: assert(0, "Unhandled tag?");
2068         }
2069     } else { 
2070         switch (value.type) with(ValueType)
2071         {
2072             case VAL_FALSE:     return vm.boolClass;
2073             case VAL_NULL:      return vm.nullClass;
2074             case VAL_NUM:       return vm.numClass;
2075             case VAL_TRUE:      return vm.boolClass;
2076             case VAL_OBJ:       return AS_OBJ(value).classObj;
2077             case VAL_UNDEFINED: assert(0, "Unreachable");
2078             default: assert(0, "Unreachable");
2079         }
2080     }
2081 }
2082 
2083 int wrenGetSlotCount(WrenVM* vm)
2084 {
2085     if (vm.apiStack == null) return 0;
2086   
2087     return cast(int)(vm.fiber.stackTop - vm.apiStack);
2088 }
2089 
2090 void wrenEnsureSlots(WrenVM* vm, int numSlots)
2091 {
2092     // If we don't have a fiber accessible, create one for the API to use.
2093     if (vm.apiStack == null)
2094     {
2095         vm.fiber = wrenNewFiber(vm, null);
2096         vm.apiStack = vm.fiber.stack;
2097     }
2098     
2099     int currentSize = cast(int)(vm.fiber.stackTop - vm.apiStack);
2100     if (currentSize >= numSlots) return;
2101     
2102     // Grow the stack if needed.
2103     int needed = cast(int)(vm.apiStack - vm.fiber.stack) + numSlots;
2104     wrenEnsureStack(vm, vm.fiber, needed);
2105     
2106     vm.fiber.stackTop = vm.apiStack + numSlots;
2107 }
2108 
2109 // Ensures that [slot] is a valid index into the API's stack of slots.
2110 static void validateApiSlot(WrenVM* vm, int slot)
2111 {
2112     assert(slot >= 0, "Slot cannot be negative.");
2113     assert(slot < wrenGetSlotCount(vm), "Not that many slots.");
2114 }
2115 
2116 // Gets the type of the object in [slot].
2117 WrenType wrenGetSlotType(WrenVM* vm, int slot)
2118 {
2119     validateApiSlot(vm, slot);
2120     if (IS_BOOL(vm.apiStack[slot])) return WrenType.WREN_TYPE_BOOL;
2121     if (IS_NUM(vm.apiStack[slot])) return WrenType.WREN_TYPE_NUM;
2122     if (IS_FOREIGN(vm.apiStack[slot])) return WrenType.WREN_TYPE_FOREIGN;
2123     if (IS_LIST(vm.apiStack[slot])) return WrenType.WREN_TYPE_LIST;
2124     if (IS_MAP(vm.apiStack[slot])) return WrenType.WREN_TYPE_MAP;
2125     if (IS_NULL(vm.apiStack[slot])) return WrenType.WREN_TYPE_NULL;
2126     if (IS_STRING(vm.apiStack[slot])) return WrenType.WREN_TYPE_STRING;
2127     
2128     return WrenType.WREN_TYPE_UNKNOWN;
2129 }
2130 
2131 bool wrenGetSlotBool(WrenVM* vm, int slot)
2132 {
2133     validateApiSlot(vm, slot);
2134     assert(IS_BOOL(vm.apiStack[slot]), "Slot must hold a bool.");
2135 
2136     return AS_BOOL(vm.apiStack[slot]);
2137 }
2138 
2139 const(char)* wrenGetSlotBytes(WrenVM* vm, int slot, int* length)
2140 {
2141     validateApiSlot(vm, slot);
2142     assert(IS_STRING(vm.apiStack[slot]), "Slot must hold a string.");
2143     
2144     ObjString* string_ = AS_STRING(vm.apiStack[slot]);
2145     *length = string_.length;
2146     return string_.value.ptr;
2147 }
2148 
2149 double wrenGetSlotDouble(WrenVM* vm, int slot)
2150 {
2151     validateApiSlot(vm, slot);
2152     assert(IS_NUM(vm.apiStack[slot]), "Slot must hold a number.");
2153 
2154     return AS_NUM(vm.apiStack[slot]);
2155 }
2156 
2157 void* wrenGetSlotForeign(WrenVM* vm, int slot)
2158 {
2159     validateApiSlot(vm, slot);
2160     assert(IS_FOREIGN(vm.apiStack[slot]),
2161             "Slot must hold a foreign instance.");
2162 
2163     return AS_FOREIGN(vm.apiStack[slot]).data.ptr;
2164 }
2165 
2166 const(char)* wrenGetSlotString(WrenVM* vm, int slot)
2167 {
2168     validateApiSlot(vm, slot);
2169     assert(IS_STRING(vm.apiStack[slot]), "Slot must hold a string.");
2170 
2171     return AS_CSTRING(vm.apiStack[slot]);
2172 }
2173 
2174 WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot)
2175 {
2176     validateApiSlot(vm, slot);
2177     return wrenMakeHandle(vm, vm.apiStack[slot]);
2178 }
2179 
2180 // Stores [value] in [slot] in the foreign call stack.
2181 static void setSlot(WrenVM* vm, int slot, Value value)
2182 {
2183     validateApiSlot(vm, slot);
2184     vm.apiStack[slot] = value;
2185 }
2186 
2187 void wrenSetSlotBool(WrenVM* vm, int slot, bool value)
2188 {
2189     setSlot(vm, slot, BOOL_VAL(value));
2190 }
2191 
2192 void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length)
2193 {
2194     assert(bytes != null, "Byte array cannot be NULL.");
2195     setSlot(vm, slot, wrenNewStringLength(vm, bytes, length));
2196 }
2197 
2198 void wrenSetSlotDouble(WrenVM* vm, int slot, double value)
2199 {
2200     setSlot(vm, slot, NUM_VAL(value));
2201 }
2202 
2203 void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size)
2204 {
2205     validateApiSlot(vm, slot);
2206     validateApiSlot(vm, classSlot);
2207     assert(IS_CLASS(vm.apiStack[classSlot]), "Slot must hold a class.");
2208     
2209     ObjClass* classObj = AS_CLASS(vm.apiStack[classSlot]);
2210     assert(classObj.numFields == -1, "Class must be a foreign class.");
2211     
2212     ObjForeign* foreign = wrenNewForeign(vm, classObj, size);
2213     vm.apiStack[slot] = OBJ_VAL(foreign);
2214     
2215     return cast(void*)foreign.data;
2216 }
2217 
2218 void wrenSetSlotNewList(WrenVM* vm, int slot)
2219 {
2220     setSlot(vm, slot, OBJ_VAL(wrenNewList(vm, 0)));
2221 }
2222 
2223 void wrenSetSlotNewMap(WrenVM* vm, int slot)
2224 {
2225     setSlot(vm, slot, OBJ_VAL(wrenNewMap(vm)));
2226 }
2227 
2228 void wrenSetSlotNull(WrenVM* vm, int slot)
2229 {
2230     setSlot(vm, slot, NULL_VAL);
2231 }
2232 
2233 void wrenSetSlotString(WrenVM* vm, int slot, const char* text)
2234 {
2235     assert(text != null, "String cannot be NULL.");
2236     
2237     setSlot(vm, slot, wrenNewString(vm, text));
2238 }
2239 
2240 void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle)
2241 {
2242     assert(handle != null, "Handle cannot be NULL.");
2243 
2244     setSlot(vm, slot, handle.value);
2245 }
2246 
2247 int wrenGetListCount(WrenVM* vm, int slot)
2248 {
2249     validateApiSlot(vm, slot);
2250     assert(IS_LIST(vm.apiStack[slot]), "Slot must hold a list.");
2251     
2252     ValueBuffer elements = AS_LIST(vm.apiStack[slot]).elements;
2253     return elements.count;
2254 }
2255 
2256 void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
2257 {
2258     validateApiSlot(vm, listSlot);
2259     validateApiSlot(vm, elementSlot);
2260     assert(IS_LIST(vm.apiStack[listSlot]), "Slot must hold a list.");
2261 
2262     ValueBuffer elements = AS_LIST(vm.apiStack[listSlot]).elements;
2263 
2264     uint usedIndex = wrenValidateIndex(elements.count, index);
2265     assert(usedIndex != uint.max, "Index out of bounds.");
2266 
2267     vm.apiStack[elementSlot] = elements.data[usedIndex];
2268 }
2269 
2270 void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
2271 {
2272     validateApiSlot(vm, listSlot);
2273     validateApiSlot(vm, elementSlot);
2274     assert(IS_LIST(vm.apiStack[listSlot]), "Slot must hold a list.");
2275 
2276     ObjList* list = AS_LIST(vm.apiStack[listSlot]);
2277 
2278     uint usedIndex = wrenValidateIndex(list.elements.count, index);
2279     assert(usedIndex != uint.max, "Index out of bounds.");
2280     
2281     list.elements.data[usedIndex] = vm.apiStack[elementSlot];
2282 }
2283 
2284 void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
2285 {
2286     validateApiSlot(vm, listSlot);
2287     validateApiSlot(vm, elementSlot);
2288     assert(IS_LIST(vm.apiStack[listSlot]), "Must insert into a list.");
2289     
2290     ObjList* list = AS_LIST(vm.apiStack[listSlot]);
2291     
2292     // Negative indices count from the end. 
2293     // We don't use wrenValidateIndex here because insert allows 1 past the end.
2294     if (index < 0) index = list.elements.count + 1 + index;
2295     
2296     assert(index <= list.elements.count, "Index out of bounds.");
2297     
2298     wrenListInsert(vm, list, vm.apiStack[elementSlot], index);
2299 }
2300 
2301 int wrenGetMapCount(WrenVM* vm, int slot)
2302 {
2303     validateApiSlot(vm, slot);
2304     assert(IS_MAP(vm.apiStack[slot]), "Slot must hold a map.");
2305 
2306     ObjMap* map = AS_MAP(vm.apiStack[slot]);
2307     return map.count;
2308 }
2309 
2310 bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot)
2311 {
2312     import wren.primitive : validateKey;
2313     validateApiSlot(vm, mapSlot);
2314     validateApiSlot(vm, keySlot);
2315     assert(IS_MAP(vm.apiStack[mapSlot]), "Slot must hold a map.");
2316 
2317     Value key = vm.apiStack[keySlot];
2318     assert(wrenMapIsValidKey(key), "Key must be a value type");
2319     if (!validateKey(vm, key)) return false;
2320 
2321     ObjMap* map = AS_MAP(vm.apiStack[mapSlot]);
2322     Value value = wrenMapGet(map, key);
2323 
2324     return !IS_UNDEFINED(value);
2325 }
2326 
2327 void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
2328 {
2329     validateApiSlot(vm, mapSlot);
2330     validateApiSlot(vm, keySlot);
2331     validateApiSlot(vm, valueSlot);
2332     assert(IS_MAP(vm.apiStack[mapSlot]), "Slot must hold a map.");
2333 
2334     ObjMap* map = AS_MAP(vm.apiStack[mapSlot]);
2335     Value value = wrenMapGet(map, vm.apiStack[keySlot]);
2336     if (IS_UNDEFINED(value)) {
2337         value = NULL_VAL;
2338     }
2339 
2340     vm.apiStack[valueSlot] = value;
2341 }
2342 
2343 void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
2344 {
2345     import wren.primitive : validateKey;
2346     validateApiSlot(vm, mapSlot);
2347     validateApiSlot(vm, keySlot);
2348     validateApiSlot(vm, valueSlot);
2349     assert(IS_MAP(vm.apiStack[mapSlot]), "Must insert into a map.");
2350     
2351     Value key = vm.apiStack[keySlot];
2352     assert(wrenMapIsValidKey(key), "Key must be a value type");
2353 
2354     if (!validateKey(vm, key)) {
2355         return;
2356     }
2357 
2358     Value value = vm.apiStack[valueSlot];
2359     ObjMap* map = AS_MAP(vm.apiStack[mapSlot]);
2360     
2361     wrenMapSet(vm, map, key, value);
2362 }
2363 
2364 void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot, 
2365                         int removedValueSlot)
2366 {
2367     import wren.primitive : validateKey;
2368     validateApiSlot(vm, mapSlot);
2369     validateApiSlot(vm, keySlot);
2370     assert(IS_MAP(vm.apiStack[mapSlot]), "Slot must hold a map.");
2371 
2372     Value key = vm.apiStack[keySlot];
2373     if (!validateKey(vm, key)) {
2374         return;
2375     }
2376 
2377     ObjMap* map = AS_MAP(vm.apiStack[mapSlot]);
2378     Value removed = wrenMapRemoveKey(vm, map, key);
2379     setSlot(vm, removedValueSlot, removed);
2380 }
2381 
2382 void wrenGetVariable(WrenVM* vm, const(char)* module_, const(char)* name,
2383                      int slot)
2384 {
2385     import core.stdc.string : strlen;
2386 
2387     assert(module_ != null, "Module cannot be NULL.");
2388     assert(name != null, "Variable name cannot be NULL.");  
2389 
2390     Value moduleName = wrenStringFormat(vm, "$", module_);
2391     wrenPushRoot(vm, AS_OBJ(moduleName));
2392     
2393     ObjModule* moduleObj = getModule(vm, moduleName);
2394     assert(moduleObj != null, "Could not find module.");
2395     
2396     wrenPopRoot(vm); // moduleName.
2397 
2398     int variableSlot = wrenSymbolTableFind(&moduleObj.variableNames,
2399                                             name, strlen(name));
2400     assert(variableSlot != -1, "Could not find variable.");
2401     
2402     setSlot(vm, slot, moduleObj.variables.data[variableSlot]);
2403 }
2404 
2405 bool wrenHasVariable(WrenVM* vm, const(char)* module_, const(char)* name)
2406 {
2407     import core.stdc.string : strlen;
2408 
2409     assert(module_ != null, "Module cannot be NULL.");
2410     assert(name != null, "Variable name cannot be NULL.");
2411 
2412     Value moduleName = wrenStringFormat(vm, "$", module_);
2413     wrenPushRoot(vm, AS_OBJ(moduleName));
2414 
2415     //We don't use wrenHasModule since we want to use the module object.
2416     ObjModule* moduleObj = getModule(vm, moduleName);
2417     assert(moduleObj != null, "Could not find module.");
2418 
2419     wrenPopRoot(vm); // moduleName.
2420 
2421     int variableSlot = wrenSymbolTableFind(&moduleObj.variableNames,
2422         name, strlen(name));
2423 
2424     return variableSlot != -1;
2425 }
2426 
2427 bool wrenHasModule(WrenVM* vm, const(char)* module_)
2428 {
2429     assert(module_ != null, "Module cannot be NULL.");
2430     
2431     Value moduleName = wrenStringFormat(vm, "$", module_);
2432     wrenPushRoot(vm, AS_OBJ(moduleName));
2433 
2434     ObjModule* moduleObj = getModule(vm, moduleName);
2435     
2436     wrenPopRoot(vm); // moduleName.
2437 
2438     return moduleObj != null;
2439 }
2440 
2441 void wrenAbortFiber(WrenVM* vm, int slot)
2442 {
2443     validateApiSlot(vm, slot);
2444     vm.fiber.error = vm.apiStack[slot];
2445 }
2446 
2447 void* wrenGetUserData(WrenVM* vm)
2448 {
2449 	return vm.config.userData;
2450 }
2451 
2452 void wrenSetUserData(WrenVM* vm, void* userData)
2453 {
2454 	vm.config.userData = userData;
2455 }
2456 
2457 static bool wrenIsLocalName(const(char)* name)
2458 {
2459     return name[0] >= 'a' && name[0] <= 'z';
2460 }
2461 
2462 static bool wrenIsFalsyValue(Value value)
2463 {
2464   return IS_FALSE(value) || IS_NULL(value);
2465 }