1#include <stdarg.h>
2#include <string.h>
3
4#include "wren.h"
5#include "wren_common.h"
6#include "wren_compiler.h"
7#include "wren_core.h"
8#include "wren_debug.h"
9#include "wren_primitive.h"
10#include "wren_vm.h"
11
12#if WREN_OPT_META
13 #include "wren_opt_meta.h"
14#endif
15#if WREN_OPT_RANDOM
16 #include "wren_opt_random.h"
17#endif
18
19#if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC
20 #include <time.h>
21 #include <stdio.h>
22#endif
23
24// The behavior of realloc() when the size is 0 is implementation defined. It
25// may return a non-NULL pointer which must not be dereferenced but nevertheless
26// should be freed. To prevent that, we avoid calling realloc() with a zero
27// size.
28static void* defaultReallocate(void* ptr, size_t newSize, void* _)
29{
30 if (newSize == 0)
31 {
32 free(ptr);
33 return NULL;
34 }
35
36 return realloc(ptr, newSize);
37}
38
39int wrenGetVersionNumber()
40{
41 return WREN_VERSION_NUMBER;
42}
43
44void wrenInitConfiguration(WrenConfiguration* config)
45{
46 config->reallocateFn = defaultReallocate;
47 config->resolveModuleFn = NULL;
48 config->loadModuleFn = NULL;
49 config->bindForeignMethodFn = NULL;
50 config->bindForeignClassFn = NULL;
51 config->writeFn = NULL;
52 config->errorFn = NULL;
53 config->initialHeapSize = 1024 * 1024 * 10;
54 config->minHeapSize = 1024 * 1024;
55 config->heapGrowthPercent = 50;
56 config->userData = NULL;
57}
58
59WrenVM* wrenNewVM(WrenConfiguration* config)
60{
61 WrenReallocateFn reallocate = defaultReallocate;
62 void* userData = NULL;
63 if (config != NULL) {
64 userData = config->userData;
65 reallocate = config->reallocateFn ? config->reallocateFn : defaultReallocate;
66 }
67
68 WrenVM* vm = (WrenVM*)reallocate(NULL, sizeof(*vm), userData);
69 memset(vm, 0, sizeof(WrenVM));
70
71 // Copy the configuration if given one.
72 if (config != NULL)
73 {
74 memcpy(&vm->config, config, sizeof(WrenConfiguration));
75
76 // We choose to set this after copying,
77 // rather than modifying the user config pointer
78 vm->config.reallocateFn = reallocate;
79 }
80 else
81 {
82 wrenInitConfiguration(&vm->config);
83 }
84
85 // TODO: Should we allocate and free this during a GC?
86 vm->grayCount = 0;
87 // TODO: Tune this.
88 vm->grayCapacity = 4;
89 vm->gray = (Obj**)reallocate(NULL, vm->grayCapacity * sizeof(Obj*), userData);
90 vm->nextGC = vm->config.initialHeapSize;
91
92 wrenSymbolTableInit(&vm->methodNames);
93
94 vm->modules = wrenNewMap(vm);
95 wrenInitializeCore(vm);
96 return vm;
97}
98
99void wrenFreeVM(WrenVM* vm)
100{
101 ASSERT(vm->methodNames.count > 0, "VM appears to have already been freed.");
102
103 // Free all of the GC objects.
104 Obj* obj = vm->first;
105 while (obj != NULL)
106 {
107 Obj* next = obj->next;
108 wrenFreeObj(vm, obj);
109 obj = next;
110 }
111
112 // Free up the GC gray set.
113 vm->gray = (Obj**)vm->config.reallocateFn(vm->gray, 0, vm->config.userData);
114
115 // Tell the user if they didn't free any handles. We don't want to just free
116 // them here because the host app may still have pointers to them that they
117 // may try to use. Better to tell them about the bug early.
118 ASSERT(vm->handles == NULL, "All handles have not been released.");
119
120 wrenSymbolTableClear(vm, &vm->methodNames);
121
122 DEALLOCATE(vm, vm);
123}
124
125void wrenCollectGarbage(WrenVM* vm)
126{
127#if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC
128 printf("-- gc --\n");
129
130 size_t before = vm->bytesAllocated;
131 double startTime = (double)clock() / CLOCKS_PER_SEC;
132#endif
133
134 // Mark all reachable objects.
135
136 // Reset this. As we mark objects, their size will be counted again so that
137 // we can track how much memory is in use without needing to know the size
138 // of each *freed* object.
139 //
140 // This is important because when freeing an unmarked object, we don't always
141 // know how much memory it is using. For example, when freeing an instance,
142 // we need to know its class to know how big it is, but its class may have
143 // already been freed.
144 vm->bytesAllocated = 0;
145
146 wrenGrayObj(vm, (Obj*)vm->modules);
147
148 // Temporary roots.
149 for (int i = 0; i < vm->numTempRoots; i++)
150 {
151 wrenGrayObj(vm, vm->tempRoots[i]);
152 }
153
154 // The current fiber.
155 wrenGrayObj(vm, (Obj*)vm->fiber);
156
157 // The handles.
158 for (WrenHandle* handle = vm->handles;
159 handle != NULL;
160 handle = handle->next)
161 {
162 wrenGrayValue(vm, handle->value);
163 }
164
165 // Any object the compiler is using (if there is one).
166 if (vm->compiler != NULL) wrenMarkCompiler(vm, vm->compiler);
167
168 // Method names.
169 wrenBlackenSymbolTable(vm, &vm->methodNames);
170
171 // Now that we have grayed the roots, do a depth-first search over all of the
172 // reachable objects.
173 wrenBlackenObjects(vm);
174
175 // Collect the white objects.
176 Obj** obj = &vm->first;
177 while (*obj != NULL)
178 {
179 if (!((*obj)->isDark))
180 {
181 // This object wasn't reached, so remove it from the list and free it.
182 Obj* unreached = *obj;
183 *obj = unreached->next;
184 wrenFreeObj(vm, unreached);
185 }
186 else
187 {
188 // This object was reached, so unmark it (for the next GC) and move on to
189 // the next.
190 (*obj)->isDark = false;
191 obj = &(*obj)->next;
192 }
193 }
194
195 // Calculate the next gc point, this is the current allocation plus
196 // a configured percentage of the current allocation.
197 vm->nextGC = vm->bytesAllocated + ((vm->bytesAllocated * vm->config.heapGrowthPercent) / 100);
198 if (vm->nextGC < vm->config.minHeapSize) vm->nextGC = vm->config.minHeapSize;
199
200#if WREN_DEBUG_TRACE_MEMORY || WREN_DEBUG_TRACE_GC
201 double elapsed = ((double)clock() / CLOCKS_PER_SEC) - startTime;
202 // Explicit cast because size_t has different sizes on 32-bit and 64-bit and
203 // we need a consistent type for the format string.
204 printf("GC %lu before, %lu after (%lu collected), next at %lu. Took %.3fms.\n",
205 (unsigned long)before,
206 (unsigned long)vm->bytesAllocated,
207 (unsigned long)(before - vm->bytesAllocated),
208 (unsigned long)vm->nextGC,
209 elapsed*1000.0);
210#endif
211}
212
213void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize)
214{
215#if WREN_DEBUG_TRACE_MEMORY
216 // Explicit cast because size_t has different sizes on 32-bit and 64-bit and
217 // we need a consistent type for the format string.
218 printf("reallocate %p %lu -> %lu\n",
219 memory, (unsigned long)oldSize, (unsigned long)newSize);
220#endif
221
222 // If new bytes are being allocated, add them to the total count. If objects
223 // are being completely deallocated, we don't track that (since we don't
224 // track the original size). Instead, that will be handled while marking
225 // during the next GC.
226 vm->bytesAllocated += newSize - oldSize;
227
228#if WREN_DEBUG_GC_STRESS
229 // Since collecting calls this function to free things, make sure we don't
230 // recurse.
231 if (newSize > 0) wrenCollectGarbage(vm);
232#else
233 if (newSize > 0 && vm->bytesAllocated > vm->nextGC) wrenCollectGarbage(vm);
234#endif
235
236 return vm->config.reallocateFn(memory, newSize, vm->config.userData);
237}
238
239// Captures the local variable [local] into an [Upvalue]. If that local is
240// already in an upvalue, the existing one will be used. (This is important to
241// ensure that multiple closures closing over the same variable actually see
242// the same variable.) Otherwise, it will create a new open upvalue and add it
243// the fiber's list of upvalues.
244static ObjUpvalue* captureUpvalue(WrenVM* vm, ObjFiber* fiber, Value* local)
245{
246 // If there are no open upvalues at all, we must need a new one.
247 if (fiber->openUpvalues == NULL)
248 {
249 fiber->openUpvalues = wrenNewUpvalue(vm, local);
250 return fiber->openUpvalues;
251 }
252
253 ObjUpvalue* prevUpvalue = NULL;
254 ObjUpvalue* upvalue = fiber->openUpvalues;
255
256 // Walk towards the bottom of the stack until we find a previously existing
257 // upvalue or pass where it should be.
258 while (upvalue != NULL && upvalue->value > local)
259 {
260 prevUpvalue = upvalue;
261 upvalue = upvalue->next;
262 }
263
264 // Found an existing upvalue for this local.
265 if (upvalue != NULL && upvalue->value == local) return upvalue;
266
267 // We've walked past this local on the stack, so there must not be an
268 // upvalue for it already. Make a new one and link it in in the right
269 // place to keep the list sorted.
270 ObjUpvalue* createdUpvalue = wrenNewUpvalue(vm, local);
271 if (prevUpvalue == NULL)
272 {
273 // The new one is the first one in the list.
274 fiber->openUpvalues = createdUpvalue;
275 }
276 else
277 {
278 prevUpvalue->next = createdUpvalue;
279 }
280
281 createdUpvalue->next = upvalue;
282 return createdUpvalue;
283}
284
285// Closes any open upvalues that have been created for stack slots at [last]
286// and above.
287static void closeUpvalues(ObjFiber* fiber, Value* last)
288{
289 while (fiber->openUpvalues != NULL &&
290 fiber->openUpvalues->value >= last)
291 {
292 ObjUpvalue* upvalue = fiber->openUpvalues;
293
294 // Move the value into the upvalue itself and point the upvalue to it.
295 upvalue->closed = *upvalue->value;
296 upvalue->value = &upvalue->closed;
297
298 // Remove it from the open upvalue list.
299 fiber->openUpvalues = upvalue->next;
300 }
301}
302
303// Looks up a foreign method in [moduleName] on [className] with [signature].
304//
305// This will try the host's foreign method binder first. If that fails, it
306// falls back to handling the built-in modules.
307static WrenForeignMethodFn findForeignMethod(WrenVM* vm,
308 const char* moduleName,
309 const char* className,
310 bool isStatic,
311 const char* signature)
312{
313 WrenForeignMethodFn method = NULL;
314
315 if (vm->config.bindForeignMethodFn != NULL)
316 {
317 method = vm->config.bindForeignMethodFn(vm, moduleName, className, isStatic,
318 signature);
319 }
320
321 // If the host didn't provide it, see if it's an optional one.
322 if (method == NULL)
323 {
324#if WREN_OPT_META
325 if (strcmp(moduleName, "meta") == 0)
326 {
327 method = wrenMetaBindForeignMethod(vm, className, isStatic, signature);
328 }
329#endif
330#if WREN_OPT_RANDOM
331 if (strcmp(moduleName, "random") == 0)
332 {
333 method = wrenRandomBindForeignMethod(vm, className, isStatic, signature);
334 }
335#endif
336 }
337
338 return method;
339}
340
341// Defines [methodValue] as a method on [classObj].
342//
343// Handles both foreign methods where [methodValue] is a string containing the
344// method's signature and Wren methods where [methodValue] is a function.
345//
346// Aborts the current fiber if the method is a foreign method that could not be
347// found.
348static void bindMethod(WrenVM* vm, int methodType, int symbol,
349 ObjModule* module, ObjClass* classObj, Value methodValue)
350{
351 const char* className = classObj->name->value;
352 if (methodType == CODE_METHOD_STATIC) classObj = classObj->obj.classObj;
353
354 Method method;
355 if (IS_STRING(methodValue))
356 {
357 const char* name = AS_CSTRING(methodValue);
358 method.type = METHOD_FOREIGN;
359 method.as.foreign = findForeignMethod(vm, module->name->value,
360 className,
361 methodType == CODE_METHOD_STATIC,
362 name);
363
364 if (method.as.foreign == NULL)
365 {
366 vm->fiber->error = wrenStringFormat(vm,
367 "Could not find foreign method '@' for class $ in module '$'.",
368 methodValue, classObj->name->value, module->name->value);
369 return;
370 }
371 }
372 else
373 {
374 method.as.closure = AS_CLOSURE(methodValue);
375 method.type = METHOD_BLOCK;
376
377 // Patch up the bytecode now that we know the superclass.
378 wrenBindMethodCode(classObj, method.as.closure->fn);
379 }
380
381 wrenBindMethod(vm, classObj, symbol, method);
382}
383
384static void callForeign(WrenVM* vm, ObjFiber* fiber,
385 WrenForeignMethodFn foreign, int numArgs)
386{
387 ASSERT(vm->apiStack == NULL, "Cannot already be in foreign call.");
388 vm->apiStack = fiber->stackTop - numArgs;
389
390 foreign(vm);
391
392 // Discard the stack slots for the arguments and temporaries but leave one
393 // for the result.
394 fiber->stackTop = vm->apiStack + 1;
395
396 vm->apiStack = NULL;
397}
398
399// Handles the current fiber having aborted because of an error.
400//
401// Walks the call chain of fibers, aborting each one until it hits a fiber that
402// handles the error. If none do, tells the VM to stop.
403static void runtimeError(WrenVM* vm)
404{
405 ASSERT(wrenHasError(vm->fiber), "Should only call this after an error.");
406
407 ObjFiber* current = vm->fiber;
408 Value error = current->error;
409
410 while (current != NULL)
411 {
412 // Every fiber along the call chain gets aborted with the same error.
413 current->error = error;
414
415 // If the caller ran this fiber using "try", give it the error and stop.
416 if (current->state == FIBER_TRY)
417 {
418 // Make the caller's try method return the error message.
419 current->caller->stackTop[-1] = vm->fiber->error;
420 vm->fiber = current->caller;
421 return;
422 }
423
424 // Otherwise, unhook the caller since we will never resume and return to it.
425 ObjFiber* caller = current->caller;
426 current->caller = NULL;
427 current = caller;
428 }
429
430 // If we got here, nothing caught the error, so show the stack trace.
431 wrenDebugPrintStackTrace(vm);
432 vm->fiber = NULL;
433 vm->apiStack = NULL;
434}
435
436// Aborts the current fiber with an appropriate method not found error for a
437// method with [symbol] on [classObj].
438static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
439{
440 vm->fiber->error = wrenStringFormat(vm, "@ does not implement '$'.",
441 OBJ_VAL(classObj->name), vm->methodNames.data[symbol]->value);
442}
443
444// Looks up the previously loaded module with [name].
445//
446// Returns `NULL` if no module with that name has been loaded.
447static ObjModule* getModule(WrenVM* vm, Value name)
448{
449 Value moduleValue = wrenMapGet(vm->modules, name);
450 return !IS_UNDEFINED(moduleValue) ? AS_MODULE(moduleValue) : NULL;
451}
452
453static ObjClosure* compileInModule(WrenVM* vm, Value name, const char* source,
454 bool isExpression, bool printErrors)
455{
456 // See if the module has already been loaded.
457 ObjModule* module = getModule(vm, name);
458 if (module == NULL)
459 {
460 module = wrenNewModule(vm, AS_STRING(name));
461
462 // It's possible for the wrenMapSet below to resize the modules map,
463 // and trigger a GC while doing so. When this happens it will collect
464 // the module we've just created. Once in the map it is safe.
465 wrenPushRoot(vm, (Obj*)module);
466
467 // Store it in the VM's module registry so we don't load the same module
468 // multiple times.
469 wrenMapSet(vm, vm->modules, name, OBJ_VAL(module));
470
471 wrenPopRoot(vm);
472
473 // Implicitly import the core module.
474 ObjModule* coreModule = getModule(vm, NULL_VAL);
475 for (int i = 0; i < coreModule->variables.count; i++)
476 {
477 wrenDefineVariable(vm, module,
478 coreModule->variableNames.data[i]->value,
479 coreModule->variableNames.data[i]->length,
480 coreModule->variables.data[i], NULL);
481 }
482 }
483
484 ObjFn* fn = wrenCompile(vm, module, source, isExpression, printErrors);
485 if (fn == NULL)
486 {
487 // TODO: Should we still store the module even if it didn't compile?
488 return NULL;
489 }
490
491 // Functions are always wrapped in closures.
492 wrenPushRoot(vm, (Obj*)fn);
493 ObjClosure* closure = wrenNewClosure(vm, fn);
494 wrenPopRoot(vm); // fn.
495
496 return closure;
497}
498
499// Verifies that [superclassValue] is a valid object to inherit from. That
500// means it must be a class and cannot be the class of any built-in type.
501//
502// Also validates that it doesn't result in a class with too many fields and
503// the other limitations foreign classes have.
504//
505// If successful, returns `null`. Otherwise, returns a string for the runtime
506// error message.
507static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue,
508 int numFields)
509{
510 // Make sure the superclass is a class.
511 if (!IS_CLASS(superclassValue))
512 {
513 return wrenStringFormat(vm,
514 "Class '@' cannot inherit from a non-class object.",
515 name);
516 }
517
518 // Make sure it doesn't inherit from a sealed built-in type. Primitive methods
519 // on these classes assume the instance is one of the other Obj___ types and
520 // will fail horribly if it's actually an ObjInstance.
521 ObjClass* superclass = AS_CLASS(superclassValue);
522 if (superclass == vm->classClass ||
523 superclass == vm->fiberClass ||
524 superclass == vm->fnClass || // Includes OBJ_CLOSURE.
525 superclass == vm->listClass ||
526 superclass == vm->mapClass ||
527 superclass == vm->rangeClass ||
528 superclass == vm->stringClass ||
529 superclass == vm->boolClass ||
530 superclass == vm->nullClass ||
531 superclass == vm->numClass)
532 {
533 return wrenStringFormat(vm,
534 "Class '@' cannot inherit from built-in class '@'.",
535 name, OBJ_VAL(superclass->name));
536 }
537
538 if (superclass->numFields == -1)
539 {
540 return wrenStringFormat(vm,
541 "Class '@' cannot inherit from foreign class '@'.",
542 name, OBJ_VAL(superclass->name));
543 }
544
545 if (numFields == -1 && superclass->numFields > 0)
546 {
547 return wrenStringFormat(vm,
548 "Foreign class '@' may not inherit from a class with fields.",
549 name);
550 }
551
552 if (superclass->numFields + numFields > MAX_FIELDS)
553 {
554 return wrenStringFormat(vm,
555 "Class '@' may not have more than 255 fields, including inherited "
556 "ones.", name);
557 }
558
559 return NULL_VAL;
560}
561
562static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
563{
564 WrenForeignClassMethods methods;
565 methods.allocate = NULL;
566 methods.finalize = NULL;
567
568 // Check the optional built-in module first so the host can override it.
569
570 if (vm->config.bindForeignClassFn != NULL)
571 {
572 methods = vm->config.bindForeignClassFn(vm, module->name->value,
573 classObj->name->value);
574 }
575
576 // If the host didn't provide it, see if it's a built in optional module.
577 if (methods.allocate == NULL && methods.finalize == NULL)
578 {
579#if WREN_OPT_RANDOM
580 if (strcmp(module->name->value, "random") == 0)
581 {
582 methods = wrenRandomBindForeignClass(vm, module->name->value,
583 classObj->name->value);
584 }
585#endif
586 }
587
588 Method method;
589 method.type = METHOD_FOREIGN;
590
591 // Add the symbol even if there is no allocator so we can ensure that the
592 // symbol itself is always in the symbol table.
593 int symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<allocate>", 10);
594 if (methods.allocate != NULL)
595 {
596 method.as.foreign = methods.allocate;
597 wrenBindMethod(vm, classObj, symbol, method);
598 }
599
600 // Add the symbol even if there is no finalizer so we can ensure that the
601 // symbol itself is always in the symbol table.
602 symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<finalize>", 10);
603 if (methods.finalize != NULL)
604 {
605 method.as.foreign = (WrenForeignMethodFn)methods.finalize;
606 wrenBindMethod(vm, classObj, symbol, method);
607 }
608}
609
610// Completes the process for creating a new class.
611//
612// The class attributes instance and the class itself should be on the
613// top of the fiber's stack.
614//
615// This process handles moving the attribute data for a class from
616// compile time to runtime, since it now has all the attributes associated
617// with a class, including for methods.
618static void endClass(WrenVM* vm)
619{
620 // Pull the attributes and class off the stack
621 Value attributes = vm->fiber->stackTop[-2];
622 Value classValue = vm->fiber->stackTop[-1];
623
624 // Remove the stack items
625 vm->fiber->stackTop -= 2;
626
627 ObjClass* classObj = AS_CLASS(classValue);
628 classObj->attributes = attributes;
629}
630
631// Creates a new class.
632//
633// If [numFields] is -1, the class is a foreign class. The name and superclass
634// should be on top of the fiber's stack. After calling this, the top of the
635// stack will contain the new class.
636//
637// Aborts the current fiber if an error occurs.
638static void createClass(WrenVM* vm, int numFields, ObjModule* module)
639{
640 // Pull the name and superclass off the stack.
641 Value name = vm->fiber->stackTop[-2];
642 Value superclass = vm->fiber->stackTop[-1];
643
644 // We have two values on the stack and we are going to leave one, so discard
645 // the other slot.
646 vm->fiber->stackTop--;
647
648 vm->fiber->error = validateSuperclass(vm, name, superclass, numFields);
649 if (wrenHasError(vm->fiber)) return;
650
651 ObjClass* classObj = wrenNewClass(vm, AS_CLASS(superclass), numFields,
652 AS_STRING(name));
653 vm->fiber->stackTop[-1] = OBJ_VAL(classObj);
654
655 if (numFields == -1) bindForeignClass(vm, classObj, module);
656}
657
658static void createForeign(WrenVM* vm, ObjFiber* fiber, Value* stack)
659{
660 ObjClass* classObj = AS_CLASS(stack[0]);
661 ASSERT(classObj->numFields == -1, "Class must be a foreign class.");
662
663 // TODO: Don't look up every time.
664 int symbol = wrenSymbolTableFind(&vm->methodNames, "<allocate>", 10);
665 ASSERT(symbol != -1, "Should have defined <allocate> symbol.");
666
667 ASSERT(classObj->methods.count > symbol, "Class should have allocator.");
668 Method* method = &classObj->methods.data[symbol];
669 ASSERT(method->type == METHOD_FOREIGN, "Allocator should be foreign.");
670
671 // Pass the constructor arguments to the allocator as well.
672 ASSERT(vm->apiStack == NULL, "Cannot already be in foreign call.");
673 vm->apiStack = stack;
674
675 method->as.foreign(vm);
676
677 vm->apiStack = NULL;
678}
679
680void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign)
681{
682 // TODO: Don't look up every time.
683 int symbol = wrenSymbolTableFind(&vm->methodNames, "<finalize>", 10);
684 ASSERT(symbol != -1, "Should have defined <finalize> symbol.");
685
686 // If there are no finalizers, don't finalize it.
687 if (symbol == -1) return;
688
689 // If the class doesn't have a finalizer, bail out.
690 ObjClass* classObj = foreign->obj.classObj;
691 if (symbol >= classObj->methods.count) return;
692
693 Method* method = &classObj->methods.data[symbol];
694 if (method->type == METHOD_NONE) return;
695
696 ASSERT(method->type == METHOD_FOREIGN, "Finalizer should be foreign.");
697
698 WrenFinalizerFn finalizer = (WrenFinalizerFn)method->as.foreign;
699 finalizer(foreign->data);
700}
701
702// Let the host resolve an imported module name if it wants to.
703static Value resolveModule(WrenVM* vm, Value name)
704{
705 // If the host doesn't care to resolve, leave the name alone.
706 if (vm->config.resolveModuleFn == NULL) return name;
707
708 ObjFiber* fiber = vm->fiber;
709 ObjFn* fn = fiber->frames[fiber->numFrames - 1].closure->fn;
710 ObjString* importer = fn->module->name;
711
712 const char* resolved = vm->config.resolveModuleFn(vm, importer->value,
713 AS_CSTRING(name));
714 if (resolved == NULL)
715 {
716 vm->fiber->error = wrenStringFormat(vm,
717 "Could not resolve module '@' imported from '@'.",
718 name, OBJ_VAL(importer));
719 return NULL_VAL;
720 }
721
722 // If they resolved to the exact same string, we don't need to copy it.
723 if (resolved == AS_CSTRING(name)) return name;
724
725 // Copy the string into a Wren String object.
726 name = wrenNewString(vm, resolved);
727 DEALLOCATE(vm, (char*)resolved);
728 return name;
729}
730
731static Value importModule(WrenVM* vm, Value name)
732{
733 name = resolveModule(vm, name);
734
735 // If the module is already loaded, we don't need to do anything.
736 Value existing = wrenMapGet(vm->modules, name);
737 if (!IS_UNDEFINED(existing)) return existing;
738
739 wrenPushRoot(vm, AS_OBJ(name));
740
741 WrenLoadModuleResult result = {0};
742 const char* source = NULL;
743
744 // Let the host try to provide the module.
745 if (vm->config.loadModuleFn != NULL)
746 {
747 result = vm->config.loadModuleFn(vm, AS_CSTRING(name));
748 }
749
750 // If the host didn't provide it, see if it's a built in optional module.
751 if (result.source == NULL)
752 {
753 result.onComplete = NULL;
754 ObjString* nameString = AS_STRING(name);
755#if WREN_OPT_META
756 if (strcmp(nameString->value, "meta") == 0) result.source = wrenMetaSource();
757#endif
758#if WREN_OPT_RANDOM
759 if (strcmp(nameString->value, "random") == 0) result.source = wrenRandomSource();
760#endif
761 }
762
763 if (result.source == NULL)
764 {
765 vm->fiber->error = wrenStringFormat(vm, "Could not load module '@'.", name);
766 wrenPopRoot(vm); // name.
767 return NULL_VAL;
768 }
769
770 ObjClosure* moduleClosure = compileInModule(vm, name, result.source, false, true);
771
772 // Now that we're done, give the result back in case there's cleanup to do.
773 if(result.onComplete) result.onComplete(vm, AS_CSTRING(name), result);
774
775 if (moduleClosure == NULL)
776 {
777 vm->fiber->error = wrenStringFormat(vm,
778 "Could not compile module '@'.", name);
779 wrenPopRoot(vm); // name.
780 return NULL_VAL;
781 }
782
783 wrenPopRoot(vm); // name.
784
785 // Return the closure that executes the module.
786 return OBJ_VAL(moduleClosure);
787}
788
789static Value getModuleVariable(WrenVM* vm, ObjModule* module,
790 Value variableName)
791{
792 ObjString* variable = AS_STRING(variableName);
793 uint32_t variableEntry = wrenSymbolTableFind(&module->variableNames,
794 variable->value,
795 variable->length);
796
797 // It's a runtime error if the imported variable does not exist.
798 if (variableEntry != UINT32_MAX)
799 {
800 return module->variables.data[variableEntry];
801 }
802
803 vm->fiber->error = wrenStringFormat(vm,
804 "Could not find a variable named '@' in module '@'.",
805 variableName, OBJ_VAL(module->name));
806 return NULL_VAL;
807}
808
809inline static bool checkArity(WrenVM* vm, Value value, int numArgs)
810{
811 ASSERT(IS_CLOSURE(value), "Receiver must be a closure.");
812 ObjFn* fn = AS_CLOSURE(value)->fn;
813
814 // We only care about missing arguments, not extras. The "- 1" is because
815 // numArgs includes the receiver, the function itself, which we don't want to
816 // count.
817 if (numArgs - 1 >= fn->arity) return true;
818
819 vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
820 return false;
821}
822
823
824// The main bytecode interpreter loop. This is where the magic happens. It is
825// also, as you can imagine, highly performance critical.
826static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
827{
828 // Remember the current fiber so we can find it if a GC happens.
829 vm->fiber = fiber;
830 fiber->state = FIBER_ROOT;
831
832 // Hoist these into local variables. They are accessed frequently in the loop
833 // but assigned less frequently. Keeping them in locals and updating them when
834 // a call frame has been pushed or popped gives a large speed boost.
835 register CallFrame* frame;
836 register Value* stackStart;
837 register uint8_t* ip;
838 register ObjFn* fn;
839
840 // These macros are designed to only be invoked within this function.
841 #define PUSH(value) (*fiber->stackTop++ = value)
842 #define POP() (*(--fiber->stackTop))
843 #define DROP() (fiber->stackTop--)
844 #define PEEK() (*(fiber->stackTop - 1))
845 #define PEEK2() (*(fiber->stackTop - 2))
846 #define READ_BYTE() (*ip++)
847 #define READ_SHORT() (ip += 2, (uint16_t)((ip[-2] << 8) | ip[-1]))
848
849 // Use this before a CallFrame is pushed to store the local variables back
850 // into the current one.
851 #define STORE_FRAME() frame->ip = ip
852
853 // Use this after a CallFrame has been pushed or popped to refresh the local
854 // variables.
855 #define LOAD_FRAME() \
856 do \
857 { \
858 frame = &fiber->frames[fiber->numFrames - 1]; \
859 stackStart = frame->stackStart; \
860 ip = frame->ip; \
861 fn = frame->closure->fn; \
862 } while (false)
863
864 // Terminates the current fiber with error string [error]. If another calling
865 // fiber is willing to catch the error, transfers control to it, otherwise
866 // exits the interpreter.
867 #define RUNTIME_ERROR() \
868 do \
869 { \
870 STORE_FRAME(); \
871 runtimeError(vm); \
872 if (vm->fiber == NULL) return WREN_RESULT_RUNTIME_ERROR; \
873 fiber = vm->fiber; \
874 LOAD_FRAME(); \
875 DISPATCH(); \
876 } while (false)
877
878 #if WREN_DEBUG_TRACE_INSTRUCTIONS
879 // Prints the stack and instruction before each instruction is executed.
880 #define DEBUG_TRACE_INSTRUCTIONS() \
881 do \
882 { \
883 wrenDumpStack(fiber); \
884 wrenDumpInstruction(vm, fn, (int)(ip - fn->code.data)); \
885 } while (false)
886 #else
887 #define DEBUG_TRACE_INSTRUCTIONS() do { } while (false)
888 #endif
889
890 #if WREN_COMPUTED_GOTO
891
892 static void* dispatchTable[] = {
893 #define OPCODE(name, _) &&code_##name,
894 #include "wren_opcodes.h"
895 #undef OPCODE
896 };
897
898 #define INTERPRET_LOOP DISPATCH();
899 #define CASE_CODE(name) code_##name
900
901 #define DISPATCH() \
902 do \
903 { \
904 DEBUG_TRACE_INSTRUCTIONS(); \
905 goto *dispatchTable[instruction = (Code)READ_BYTE()]; \
906 } while (false)
907
908 #else
909
910 #define INTERPRET_LOOP \
911 loop: \
912 DEBUG_TRACE_INSTRUCTIONS(); \
913 switch (instruction = (Code)READ_BYTE())
914
915 #define CASE_CODE(name) case CODE_##name
916 #define DISPATCH() goto loop
917
918 #endif
919
920 LOAD_FRAME();
921
922 Code instruction;
923 INTERPRET_LOOP
924 {
925 CASE_CODE(LOAD_LOCAL_0):
926 CASE_CODE(LOAD_LOCAL_1):
927 CASE_CODE(LOAD_LOCAL_2):
928 CASE_CODE(LOAD_LOCAL_3):
929 CASE_CODE(LOAD_LOCAL_4):
930 CASE_CODE(LOAD_LOCAL_5):
931 CASE_CODE(LOAD_LOCAL_6):
932 CASE_CODE(LOAD_LOCAL_7):
933 CASE_CODE(LOAD_LOCAL_8):
934 PUSH(stackStart[instruction - CODE_LOAD_LOCAL_0]);
935 DISPATCH();
936
937 CASE_CODE(LOAD_LOCAL):
938 PUSH(stackStart[READ_BYTE()]);
939 DISPATCH();
940
941 CASE_CODE(LOAD_FIELD_THIS):
942 {
943 uint8_t field = READ_BYTE();
944 Value receiver = stackStart[0];
945 ASSERT(IS_INSTANCE(receiver), "Receiver should be instance.");
946 ObjInstance* instance = AS_INSTANCE(receiver);
947 ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field.");
948 PUSH(instance->fields[field]);
949 DISPATCH();
950 }
951
952 CASE_CODE(POP): DROP(); DISPATCH();
953 CASE_CODE(NULL): PUSH(NULL_VAL); DISPATCH();
954 CASE_CODE(FALSE): PUSH(FALSE_VAL); DISPATCH();
955 CASE_CODE(TRUE): PUSH(TRUE_VAL); DISPATCH();
956
957 CASE_CODE(STORE_LOCAL):
958 stackStart[READ_BYTE()] = PEEK();
959 DISPATCH();
960
961 CASE_CODE(CONSTANT):
962 PUSH(fn->constants.data[READ_SHORT()]);
963 DISPATCH();
964
965 {
966 // The opcodes for doing method and superclass calls share a lot of code.
967 // However, doing an if() test in the middle of the instruction sequence
968 // to handle the bit that is special to super calls makes the non-super
969 // call path noticeably slower.
970 //
971 // Instead, we do this old school using an explicit goto to share code for
972 // everything at the tail end of the call-handling code that is the same
973 // between normal and superclass calls.
974 int numArgs;
975 int symbol;
976
977 Value* args;
978 ObjClass* classObj;
979
980 Method* method;
981
982 CASE_CODE(CALL_0):
983 CASE_CODE(CALL_1):
984 CASE_CODE(CALL_2):
985 CASE_CODE(CALL_3):
986 CASE_CODE(CALL_4):
987 CASE_CODE(CALL_5):
988 CASE_CODE(CALL_6):
989 CASE_CODE(CALL_7):
990 CASE_CODE(CALL_8):
991 CASE_CODE(CALL_9):
992 CASE_CODE(CALL_10):
993 CASE_CODE(CALL_11):
994 CASE_CODE(CALL_12):
995 CASE_CODE(CALL_13):
996 CASE_CODE(CALL_14):
997 CASE_CODE(CALL_15):
998 CASE_CODE(CALL_16):
999 // Add one for the implicit receiver argument.
1000 numArgs = instruction - CODE_CALL_0 + 1;
1001 symbol = READ_SHORT();
1002
1003 // The receiver is the first argument.
1004 args = fiber->stackTop - numArgs;
1005 classObj = wrenGetClassInline(vm, args[0]);
1006 goto completeCall;
1007
1008 CASE_CODE(SUPER_0):
1009 CASE_CODE(SUPER_1):
1010 CASE_CODE(SUPER_2):
1011 CASE_CODE(SUPER_3):
1012 CASE_CODE(SUPER_4):
1013 CASE_CODE(SUPER_5):
1014 CASE_CODE(SUPER_6):
1015 CASE_CODE(SUPER_7):
1016 CASE_CODE(SUPER_8):
1017 CASE_CODE(SUPER_9):
1018 CASE_CODE(SUPER_10):
1019 CASE_CODE(SUPER_11):
1020 CASE_CODE(SUPER_12):
1021 CASE_CODE(SUPER_13):
1022 CASE_CODE(SUPER_14):
1023 CASE_CODE(SUPER_15):
1024 CASE_CODE(SUPER_16):
1025 // Add one for the implicit receiver argument.
1026 numArgs = instruction - CODE_SUPER_0 + 1;
1027 symbol = READ_SHORT();
1028
1029 // The receiver is the first argument.
1030 args = fiber->stackTop - numArgs;
1031
1032 // The superclass is stored in a constant.
1033 classObj = AS_CLASS(fn->constants.data[READ_SHORT()]);
1034 goto completeCall;
1035
1036 completeCall:
1037 // If the class's method table doesn't include the symbol, bail.
1038 if (symbol >= classObj->methods.count ||
1039 (method = &classObj->methods.data[symbol])->type == METHOD_NONE)
1040 {
1041 methodNotFound(vm, classObj, symbol);
1042 RUNTIME_ERROR();
1043 }
1044
1045 switch (method->type)
1046 {
1047 case METHOD_PRIMITIVE:
1048 if (method->as.primitive(vm, args))
1049 {
1050 // The result is now in the first arg slot. Discard the other
1051 // stack slots.
1052 fiber->stackTop -= numArgs - 1;
1053 } else {
1054 // An error, fiber switch, or call frame change occurred.
1055 STORE_FRAME();
1056
1057 // If we don't have a fiber to switch to, stop interpreting.
1058 fiber = vm->fiber;
1059 if (fiber == NULL) return WREN_RESULT_SUCCESS;
1060 if (wrenHasError(fiber)) RUNTIME_ERROR();
1061 LOAD_FRAME();
1062 }
1063 break;
1064
1065 case METHOD_FUNCTION_CALL:
1066 if (!checkArity(vm, args[0], numArgs)) {
1067 RUNTIME_ERROR();
1068 break;
1069 }
1070
1071 STORE_FRAME();
1072 method->as.primitive(vm, args);
1073 LOAD_FRAME();
1074 break;
1075
1076 case METHOD_FOREIGN:
1077 callForeign(vm, fiber, method->as.foreign, numArgs);
1078 if (wrenHasError(fiber)) RUNTIME_ERROR();
1079 break;
1080
1081 case METHOD_BLOCK:
1082 STORE_FRAME();
1083 wrenCallFunction(vm, fiber, (ObjClosure*)method->as.closure, numArgs);
1084 LOAD_FRAME();
1085 break;
1086
1087 case METHOD_NONE:
1088 UNREACHABLE();
1089 break;
1090 }
1091 DISPATCH();
1092 }
1093
1094 CASE_CODE(LOAD_UPVALUE):
1095 {
1096 ObjUpvalue** upvalues = frame->closure->upvalues;
1097 PUSH(*upvalues[READ_BYTE()]->value);
1098 DISPATCH();
1099 }
1100
1101 CASE_CODE(STORE_UPVALUE):
1102 {
1103 ObjUpvalue** upvalues = frame->closure->upvalues;
1104 *upvalues[READ_BYTE()]->value = PEEK();
1105 DISPATCH();
1106 }
1107
1108 CASE_CODE(LOAD_MODULE_VAR):
1109 PUSH(fn->module->variables.data[READ_SHORT()]);
1110 DISPATCH();
1111
1112 CASE_CODE(STORE_MODULE_VAR):
1113 fn->module->variables.data[READ_SHORT()] = PEEK();
1114 DISPATCH();
1115
1116 CASE_CODE(STORE_FIELD_THIS):
1117 {
1118 uint8_t field = READ_BYTE();
1119 Value receiver = stackStart[0];
1120 ASSERT(IS_INSTANCE(receiver), "Receiver should be instance.");
1121 ObjInstance* instance = AS_INSTANCE(receiver);
1122 ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field.");
1123 instance->fields[field] = PEEK();
1124 DISPATCH();
1125 }
1126
1127 CASE_CODE(LOAD_FIELD):
1128 {
1129 uint8_t field = READ_BYTE();
1130 Value receiver = POP();
1131 ASSERT(IS_INSTANCE(receiver), "Receiver should be instance.");
1132 ObjInstance* instance = AS_INSTANCE(receiver);
1133 ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field.");
1134 PUSH(instance->fields[field]);
1135 DISPATCH();
1136 }
1137
1138 CASE_CODE(STORE_FIELD):
1139 {
1140 uint8_t field = READ_BYTE();
1141 Value receiver = POP();
1142 ASSERT(IS_INSTANCE(receiver), "Receiver should be instance.");
1143 ObjInstance* instance = AS_INSTANCE(receiver);
1144 ASSERT(field < instance->obj.classObj->numFields, "Out of bounds field.");
1145 instance->fields[field] = PEEK();
1146 DISPATCH();
1147 }
1148
1149 CASE_CODE(JUMP):
1150 {
1151 uint16_t offset = READ_SHORT();
1152 ip += offset;
1153 DISPATCH();
1154 }
1155
1156 CASE_CODE(LOOP):
1157 {
1158 // Jump back to the top of the loop.
1159 uint16_t offset = READ_SHORT();
1160 ip -= offset;
1161 DISPATCH();
1162 }
1163
1164 CASE_CODE(JUMP_IF):
1165 {
1166 uint16_t offset = READ_SHORT();
1167 Value condition = POP();
1168
1169 if (wrenIsFalsyValue(condition)) ip += offset;
1170 DISPATCH();
1171 }
1172
1173 CASE_CODE(AND):
1174 {
1175 uint16_t offset = READ_SHORT();
1176 Value condition = PEEK();
1177
1178 if (wrenIsFalsyValue(condition))
1179 {
1180 // Short-circuit the right hand side.
1181 ip += offset;
1182 }
1183 else
1184 {
1185 // Discard the condition and evaluate the right hand side.
1186 DROP();
1187 }
1188 DISPATCH();
1189 }
1190
1191 CASE_CODE(OR):
1192 {
1193 uint16_t offset = READ_SHORT();
1194 Value condition = PEEK();
1195
1196 if (wrenIsFalsyValue(condition))
1197 {
1198 // Discard the condition and evaluate the right hand side.
1199 DROP();
1200 }
1201 else
1202 {
1203 // Short-circuit the right hand side.
1204 ip += offset;
1205 }
1206 DISPATCH();
1207 }
1208
1209 CASE_CODE(CLOSE_UPVALUE):
1210 // Close the upvalue for the local if we have one.
1211 closeUpvalues(fiber, fiber->stackTop - 1);
1212 DROP();
1213 DISPATCH();
1214
1215 CASE_CODE(RETURN):
1216 {
1217 Value result = POP();
1218 fiber->numFrames--;
1219
1220 // Close any upvalues still in scope.
1221 closeUpvalues(fiber, stackStart);
1222
1223 // If the fiber is complete, end it.
1224 if (fiber->numFrames == 0)
1225 {
1226 // See if there's another fiber to return to. If not, we're done.
1227 if (fiber->caller == NULL)
1228 {
1229 // Store the final result value at the beginning of the stack so the
1230 // C API can get it.
1231 fiber->stack[0] = result;
1232 fiber->stackTop = fiber->stack + 1;
1233 return WREN_RESULT_SUCCESS;
1234 }
1235
1236 ObjFiber* resumingFiber = fiber->caller;
1237 fiber->caller = NULL;
1238 fiber = resumingFiber;
1239 vm->fiber = resumingFiber;
1240
1241 // Store the result in the resuming fiber.
1242 fiber->stackTop[-1] = result;
1243 }
1244 else
1245 {
1246 // Store the result of the block in the first slot, which is where the
1247 // caller expects it.
1248 stackStart[0] = result;
1249
1250 // Discard the stack slots for the call frame (leaving one slot for the
1251 // result).
1252 fiber->stackTop = frame->stackStart + 1;
1253 }
1254
1255 LOAD_FRAME();
1256 DISPATCH();
1257 }
1258
1259 CASE_CODE(CONSTRUCT):
1260 ASSERT(IS_CLASS(stackStart[0]), "'this' should be a class.");
1261 stackStart[0] = wrenNewInstance(vm, AS_CLASS(stackStart[0]));
1262 DISPATCH();
1263
1264 CASE_CODE(FOREIGN_CONSTRUCT):
1265 ASSERT(IS_CLASS(stackStart[0]), "'this' should be a class.");
1266 createForeign(vm, fiber, stackStart);
1267 if (wrenHasError(fiber)) RUNTIME_ERROR();
1268 DISPATCH();
1269
1270 CASE_CODE(CLOSURE):
1271 {
1272 // Create the closure and push it on the stack before creating upvalues
1273 // so that it doesn't get collected.
1274 ObjFn* function = AS_FN(fn->constants.data[READ_SHORT()]);
1275 ObjClosure* closure = wrenNewClosure(vm, function);
1276 PUSH(OBJ_VAL(closure));
1277
1278 // Capture upvalues, if any.
1279 for (int i = 0; i < function->numUpvalues; i++)
1280 {
1281 uint8_t isLocal = READ_BYTE();
1282 uint8_t index = READ_BYTE();
1283 if (isLocal)
1284 {
1285 // Make an new upvalue to close over the parent's local variable.
1286 closure->upvalues[i] = captureUpvalue(vm, fiber,
1287 frame->stackStart + index);
1288 }
1289 else
1290 {
1291 // Use the same upvalue as the current call frame.
1292 closure->upvalues[i] = frame->closure->upvalues[index];
1293 }
1294 }
1295 DISPATCH();
1296 }
1297
1298 CASE_CODE(END_CLASS):
1299 {
1300 endClass(vm);
1301 if (wrenHasError(fiber)) RUNTIME_ERROR();
1302 DISPATCH();
1303 }
1304
1305 CASE_CODE(CLASS):
1306 {
1307 createClass(vm, READ_BYTE(), NULL);
1308 if (wrenHasError(fiber)) RUNTIME_ERROR();
1309 DISPATCH();
1310 }
1311
1312 CASE_CODE(FOREIGN_CLASS):
1313 {
1314 createClass(vm, -1, fn->module);
1315 if (wrenHasError(fiber)) RUNTIME_ERROR();
1316 DISPATCH();
1317 }
1318
1319 CASE_CODE(METHOD_INSTANCE):
1320 CASE_CODE(METHOD_STATIC):
1321 {
1322 uint16_t symbol = READ_SHORT();
1323 ObjClass* classObj = AS_CLASS(PEEK());
1324 Value method = PEEK2();
1325 bindMethod(vm, instruction, symbol, fn->module, classObj, method);
1326 if (wrenHasError(fiber)) RUNTIME_ERROR();
1327 DROP();
1328 DROP();
1329 DISPATCH();
1330 }
1331
1332 CASE_CODE(END_MODULE):
1333 {
1334 vm->lastModule = fn->module;
1335 PUSH(NULL_VAL);
1336 DISPATCH();
1337 }
1338
1339 CASE_CODE(IMPORT_MODULE):
1340 {
1341 // Make a slot on the stack for the module's fiber to place the return
1342 // value. It will be popped after this fiber is resumed. Store the
1343 // imported module's closure in the slot in case a GC happens when
1344 // invoking the closure.
1345 PUSH(importModule(vm, fn->constants.data[READ_SHORT()]));
1346 if (wrenHasError(fiber)) RUNTIME_ERROR();
1347
1348 // If we get a closure, call it to execute the module body.
1349 if (IS_CLOSURE(PEEK()))
1350 {
1351 STORE_FRAME();
1352 ObjClosure* closure = AS_CLOSURE(PEEK());
1353 wrenCallFunction(vm, fiber, closure, 1);
1354 LOAD_FRAME();
1355 }
1356 else
1357 {
1358 // The module has already been loaded. Remember it so we can import
1359 // variables from it if needed.
1360 vm->lastModule = AS_MODULE(PEEK());
1361 }
1362
1363 DISPATCH();
1364 }
1365
1366 CASE_CODE(IMPORT_VARIABLE):
1367 {
1368 Value variable = fn->constants.data[READ_SHORT()];
1369 ASSERT(vm->lastModule != NULL, "Should have already imported module.");
1370 Value result = getModuleVariable(vm, vm->lastModule, variable);
1371 if (wrenHasError(fiber)) RUNTIME_ERROR();
1372
1373 PUSH(result);
1374 DISPATCH();
1375 }
1376
1377 CASE_CODE(END):
1378 // A CODE_END should always be preceded by a CODE_RETURN. If we get here,
1379 // the compiler generated wrong code.
1380 UNREACHABLE();
1381 }
1382
1383 // We should only exit this function from an explicit return from CODE_RETURN
1384 // or a runtime error.
1385 UNREACHABLE();
1386 return WREN_RESULT_RUNTIME_ERROR;
1387
1388 #undef READ_BYTE
1389 #undef READ_SHORT
1390}
1391
1392WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature)
1393{
1394 ASSERT(signature != NULL, "Signature cannot be NULL.");
1395
1396 int signatureLength = (int)strlen(signature);
1397 ASSERT(signatureLength > 0, "Signature cannot be empty.");
1398
1399 // Count the number parameters the method expects.
1400 int numParams = 0;
1401 if (signature[signatureLength - 1] == ')')
1402 {
1403 for (int i = signatureLength - 1; i > 0 && signature[i] != '('; i--)
1404 {
1405 if (signature[i] == '_') numParams++;
1406 }
1407 }
1408
1409 // Count subscript arguments.
1410 if (signature[0] == '[')
1411 {
1412 for (int i = 0; i < signatureLength && signature[i] != ']'; i++)
1413 {
1414 if (signature[i] == '_') numParams++;
1415 }
1416 }
1417
1418 // Add the signatue to the method table.
1419 int method = wrenSymbolTableEnsure(vm, &vm->methodNames,
1420 signature, signatureLength);
1421
1422 // Create a little stub function that assumes the arguments are on the stack
1423 // and calls the method.
1424 ObjFn* fn = wrenNewFunction(vm, NULL, numParams + 1);
1425
1426 // Wrap the function in a closure and then in a handle. Do this here so it
1427 // doesn't get collected as we fill it in.
1428 WrenHandle* value = wrenMakeHandle(vm, OBJ_VAL(fn));
1429 value->value = OBJ_VAL(wrenNewClosure(vm, fn));
1430
1431 wrenByteBufferWrite(vm, &fn->code, (uint8_t)(CODE_CALL_0 + numParams));
1432 wrenByteBufferWrite(vm, &fn->code, (method >> 8) & 0xff);
1433 wrenByteBufferWrite(vm, &fn->code, method & 0xff);
1434 wrenByteBufferWrite(vm, &fn->code, CODE_RETURN);
1435 wrenByteBufferWrite(vm, &fn->code, CODE_END);
1436 wrenIntBufferFill(vm, &fn->debug->sourceLines, 0, 5);
1437 wrenFunctionBindName(vm, fn, signature, signatureLength);
1438
1439 return value;
1440}
1441
1442WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method)
1443{
1444 ASSERT(method != NULL, "Method cannot be NULL.");
1445 ASSERT(IS_CLOSURE(method->value), "Method must be a method handle.");
1446 ASSERT(vm->fiber != NULL, "Must set up arguments for call first.");
1447 ASSERT(vm->apiStack != NULL, "Must set up arguments for call first.");
1448 ASSERT(vm->fiber->numFrames == 0, "Can not call from a foreign method.");
1449
1450 ObjClosure* closure = AS_CLOSURE(method->value);
1451
1452 ASSERT(vm->fiber->stackTop - vm->fiber->stack >= closure->fn->arity,
1453 "Stack must have enough arguments for method.");
1454
1455 // Clear the API stack. Now that wrenCall() has control, we no longer need
1456 // it. We use this being non-null to tell if re-entrant calls to foreign
1457 // methods are happening, so it's important to clear it out now so that you
1458 // can call foreign methods from within calls to wrenCall().
1459 vm->apiStack = NULL;
1460
1461 // Discard any extra temporary slots. We take for granted that the stub
1462 // function has exactly one slot for each argument.
1463 vm->fiber->stackTop = &vm->fiber->stack[closure->fn->maxSlots];
1464
1465 wrenCallFunction(vm, vm->fiber, closure, 0);
1466 WrenInterpretResult result = runInterpreter(vm, vm->fiber);
1467
1468 // If the call didn't abort, then set up the API stack to point to the
1469 // beginning of the stack so the host can access the call's return value.
1470 if (vm->fiber != NULL) vm->apiStack = vm->fiber->stack;
1471
1472 return result;
1473}
1474
1475WrenHandle* wrenMakeHandle(WrenVM* vm, Value value)
1476{
1477 if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1478
1479 // Make a handle for it.
1480 WrenHandle* handle = ALLOCATE(vm, WrenHandle);
1481 handle->value = value;
1482
1483 if (IS_OBJ(value)) wrenPopRoot(vm);
1484
1485 // Add it to the front of the linked list of handles.
1486 if (vm->handles != NULL) vm->handles->prev = handle;
1487 handle->prev = NULL;
1488 handle->next = vm->handles;
1489 vm->handles = handle;
1490
1491 return handle;
1492}
1493
1494void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle)
1495{
1496 ASSERT(handle != NULL, "Handle cannot be NULL.");
1497
1498 // Update the VM's head pointer if we're releasing the first handle.
1499 if (vm->handles == handle) vm->handles = handle->next;
1500
1501 // Unlink it from the list.
1502 if (handle->prev != NULL) handle->prev->next = handle->next;
1503 if (handle->next != NULL) handle->next->prev = handle->prev;
1504
1505 // Clear it out. This isn't strictly necessary since we're going to free it,
1506 // but it makes for easier debugging.
1507 handle->prev = NULL;
1508 handle->next = NULL;
1509 handle->value = NULL_VAL;
1510 DEALLOCATE(vm, handle);
1511}
1512
1513WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
1514 const char* source)
1515{
1516 ObjClosure* closure = wrenCompileSource(vm, module, source, false, true);
1517 if (closure == NULL) return WREN_RESULT_COMPILE_ERROR;
1518
1519 wrenPushRoot(vm, (Obj*)closure);
1520 ObjFiber* fiber = wrenNewFiber(vm, closure);
1521 wrenPopRoot(vm); // closure.
1522 vm->apiStack = NULL;
1523
1524 return runInterpreter(vm, fiber);
1525}
1526
1527ObjClosure* wrenCompileSource(WrenVM* vm, const char* module, const char* source,
1528 bool isExpression, bool printErrors)
1529{
1530 Value nameValue = NULL_VAL;
1531 if (module != NULL)
1532 {
1533 nameValue = wrenNewString(vm, module);
1534 wrenPushRoot(vm, AS_OBJ(nameValue));
1535 }
1536
1537 ObjClosure* closure = compileInModule(vm, nameValue, source,
1538 isExpression, printErrors);
1539
1540 if (module != NULL) wrenPopRoot(vm); // nameValue.
1541 return closure;
1542}
1543
1544Value wrenGetModuleVariable(WrenVM* vm, Value moduleName, Value variableName)
1545{
1546 ObjModule* module = getModule(vm, moduleName);
1547 if (module == NULL)
1548 {
1549 vm->fiber->error = wrenStringFormat(vm, "Module '@' is not loaded.",
1550 moduleName);
1551 return NULL_VAL;
1552 }
1553
1554 return getModuleVariable(vm, module, variableName);
1555}
1556
1557Value wrenFindVariable(WrenVM* vm, ObjModule* module, const char* name)
1558{
1559 int symbol = wrenSymbolTableFind(&module->variableNames, name, strlen(name));
1560 return module->variables.data[symbol];
1561}
1562
1563int wrenDeclareVariable(WrenVM* vm, ObjModule* module, const char* name,
1564 size_t length, int line)
1565{
1566 if (module->variables.count == MAX_MODULE_VARS) return -2;
1567
1568 // Implicitly defined variables get a "value" that is the line where the
1569 // variable is first used. We'll use that later to report an error on the
1570 // right line.
1571 wrenValueBufferWrite(vm, &module->variables, NUM_VAL(line));
1572 return wrenSymbolTableAdd(vm, &module->variableNames, name, length);
1573}
1574
1575int wrenDefineVariable(WrenVM* vm, ObjModule* module, const char* name,
1576 size_t length, Value value, int* line)
1577{
1578 if (module->variables.count == MAX_MODULE_VARS) return -2;
1579
1580 if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
1581
1582 // See if the variable is already explicitly or implicitly declared.
1583 int symbol = wrenSymbolTableFind(&module->variableNames, name, length);
1584
1585 if (symbol == -1)
1586 {
1587 // Brand new variable.
1588 symbol = wrenSymbolTableAdd(vm, &module->variableNames, name, length);
1589 wrenValueBufferWrite(vm, &module->variables, value);
1590 }
1591 else if (IS_NUM(module->variables.data[symbol]))
1592 {
1593 // An implicitly declared variable's value will always be a number.
1594 // Now we have a real definition.
1595 if(line) *line = (int)AS_NUM(module->variables.data[symbol]);
1596 module->variables.data[symbol] = value;
1597
1598 // If this was a localname we want to error if it was
1599 // referenced before this definition.
1600 if (wrenIsLocalName(name)) symbol = -3;
1601 }
1602 else
1603 {
1604 // Already explicitly declared.
1605 symbol = -1;
1606 }
1607
1608 if (IS_OBJ(value)) wrenPopRoot(vm);
1609
1610 return symbol;
1611}
1612
1613// TODO: Inline?
1614void wrenPushRoot(WrenVM* vm, Obj* obj)
1615{
1616 ASSERT(obj != NULL, "Can't root NULL.");
1617 ASSERT(vm->numTempRoots < WREN_MAX_TEMP_ROOTS, "Too many temporary roots.");
1618
1619 vm->tempRoots[vm->numTempRoots++] = obj;
1620}
1621
1622void wrenPopRoot(WrenVM* vm)
1623{
1624 ASSERT(vm->numTempRoots > 0, "No temporary roots to release.");
1625 vm->numTempRoots--;
1626}
1627
1628int wrenGetSlotCount(WrenVM* vm)
1629{
1630 if (vm->apiStack == NULL) return 0;
1631
1632 return (int)(vm->fiber->stackTop - vm->apiStack);
1633}
1634
1635void wrenEnsureSlots(WrenVM* vm, int numSlots)
1636{
1637 // If we don't have a fiber accessible, create one for the API to use.
1638 if (vm->apiStack == NULL)
1639 {
1640 vm->fiber = wrenNewFiber(vm, NULL);
1641 vm->apiStack = vm->fiber->stack;
1642 }
1643
1644 int currentSize = (int)(vm->fiber->stackTop - vm->apiStack);
1645 if (currentSize >= numSlots) return;
1646
1647 // Grow the stack if needed.
1648 int needed = (int)(vm->apiStack - vm->fiber->stack) + numSlots;
1649 wrenEnsureStack(vm, vm->fiber, needed);
1650
1651 vm->fiber->stackTop = vm->apiStack + numSlots;
1652}
1653
1654// Ensures that [slot] is a valid index into the API's stack of slots.
1655static void validateApiSlot(WrenVM* vm, int slot)
1656{
1657 ASSERT(slot >= 0, "Slot cannot be negative.");
1658 ASSERT(slot < wrenGetSlotCount(vm), "Not that many slots.");
1659}
1660
1661// Gets the type of the object in [slot].
1662WrenType wrenGetSlotType(WrenVM* vm, int slot)
1663{
1664 validateApiSlot(vm, slot);
1665 if (IS_BOOL(vm->apiStack[slot])) return WREN_TYPE_BOOL;
1666 if (IS_NUM(vm->apiStack[slot])) return WREN_TYPE_NUM;
1667 if (IS_FOREIGN(vm->apiStack[slot])) return WREN_TYPE_FOREIGN;
1668 if (IS_LIST(vm->apiStack[slot])) return WREN_TYPE_LIST;
1669 if (IS_MAP(vm->apiStack[slot])) return WREN_TYPE_MAP;
1670 if (IS_NULL(vm->apiStack[slot])) return WREN_TYPE_NULL;
1671 if (IS_STRING(vm->apiStack[slot])) return WREN_TYPE_STRING;
1672
1673 return WREN_TYPE_UNKNOWN;
1674}
1675
1676bool wrenGetSlotBool(WrenVM* vm, int slot)
1677{
1678 validateApiSlot(vm, slot);
1679 ASSERT(IS_BOOL(vm->apiStack[slot]), "Slot must hold a bool.");
1680
1681 return AS_BOOL(vm->apiStack[slot]);
1682}
1683
1684const char* wrenGetSlotBytes(WrenVM* vm, int slot, int* length)
1685{
1686 validateApiSlot(vm, slot);
1687 ASSERT(IS_STRING(vm->apiStack[slot]), "Slot must hold a string.");
1688
1689 ObjString* string = AS_STRING(vm->apiStack[slot]);
1690 *length = string->length;
1691 return string->value;
1692}
1693
1694double wrenGetSlotDouble(WrenVM* vm, int slot)
1695{
1696 validateApiSlot(vm, slot);
1697 ASSERT(IS_NUM(vm->apiStack[slot]), "Slot must hold a number.");
1698
1699 return AS_NUM(vm->apiStack[slot]);
1700}
1701
1702void* wrenGetSlotForeign(WrenVM* vm, int slot)
1703{
1704 validateApiSlot(vm, slot);
1705 ASSERT(IS_FOREIGN(vm->apiStack[slot]),
1706 "Slot must hold a foreign instance.");
1707
1708 return AS_FOREIGN(vm->apiStack[slot])->data;
1709}
1710
1711const char* wrenGetSlotString(WrenVM* vm, int slot)
1712{
1713 validateApiSlot(vm, slot);
1714 ASSERT(IS_STRING(vm->apiStack[slot]), "Slot must hold a string.");
1715
1716 return AS_CSTRING(vm->apiStack[slot]);
1717}
1718
1719WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot)
1720{
1721 validateApiSlot(vm, slot);
1722 return wrenMakeHandle(vm, vm->apiStack[slot]);
1723}
1724
1725// Stores [value] in [slot] in the foreign call stack.
1726static void setSlot(WrenVM* vm, int slot, Value value)
1727{
1728 validateApiSlot(vm, slot);
1729 vm->apiStack[slot] = value;
1730}
1731
1732void wrenSetSlotBool(WrenVM* vm, int slot, bool value)
1733{
1734 setSlot(vm, slot, BOOL_VAL(value));
1735}
1736
1737void wrenSetSlotBytes(WrenVM* vm, int slot, const char* bytes, size_t length)
1738{
1739 ASSERT(bytes != NULL, "Byte array cannot be NULL.");
1740 setSlot(vm, slot, wrenNewStringLength(vm, bytes, length));
1741}
1742
1743void wrenSetSlotDouble(WrenVM* vm, int slot, double value)
1744{
1745 setSlot(vm, slot, NUM_VAL(value));
1746}
1747
1748void* wrenSetSlotNewForeign(WrenVM* vm, int slot, int classSlot, size_t size)
1749{
1750 validateApiSlot(vm, slot);
1751 validateApiSlot(vm, classSlot);
1752 ASSERT(IS_CLASS(vm->apiStack[classSlot]), "Slot must hold a class.");
1753
1754 ObjClass* classObj = AS_CLASS(vm->apiStack[classSlot]);
1755 ASSERT(classObj->numFields == -1, "Class must be a foreign class.");
1756
1757 ObjForeign* foreign = wrenNewForeign(vm, classObj, size);
1758 vm->apiStack[slot] = OBJ_VAL(foreign);
1759
1760 return (void*)foreign->data;
1761}
1762
1763void wrenSetSlotNewList(WrenVM* vm, int slot)
1764{
1765 setSlot(vm, slot, OBJ_VAL(wrenNewList(vm, 0)));
1766}
1767
1768void wrenSetSlotNewMap(WrenVM* vm, int slot)
1769{
1770 setSlot(vm, slot, OBJ_VAL(wrenNewMap(vm)));
1771}
1772
1773void wrenSetSlotNull(WrenVM* vm, int slot)
1774{
1775 setSlot(vm, slot, NULL_VAL);
1776}
1777
1778void wrenSetSlotString(WrenVM* vm, int slot, const char* text)
1779{
1780 ASSERT(text != NULL, "String cannot be NULL.");
1781
1782 setSlot(vm, slot, wrenNewString(vm, text));
1783}
1784
1785void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle)
1786{
1787 ASSERT(handle != NULL, "Handle cannot be NULL.");
1788
1789 setSlot(vm, slot, handle->value);
1790}
1791
1792int wrenGetListCount(WrenVM* vm, int slot)
1793{
1794 validateApiSlot(vm, slot);
1795 ASSERT(IS_LIST(vm->apiStack[slot]), "Slot must hold a list.");
1796
1797 ValueBuffer elements = AS_LIST(vm->apiStack[slot])->elements;
1798 return elements.count;
1799}
1800
1801void wrenGetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
1802{
1803 validateApiSlot(vm, listSlot);
1804 validateApiSlot(vm, elementSlot);
1805 ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
1806
1807 ValueBuffer elements = AS_LIST(vm->apiStack[listSlot])->elements;
1808
1809 uint32_t usedIndex = wrenValidateIndex(elements.count, index);
1810 ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
1811
1812 vm->apiStack[elementSlot] = elements.data[usedIndex];
1813}
1814
1815void wrenSetListElement(WrenVM* vm, int listSlot, int index, int elementSlot)
1816{
1817 validateApiSlot(vm, listSlot);
1818 validateApiSlot(vm, elementSlot);
1819 ASSERT(IS_LIST(vm->apiStack[listSlot]), "Slot must hold a list.");
1820
1821 ObjList* list = AS_LIST(vm->apiStack[listSlot]);
1822
1823 uint32_t usedIndex = wrenValidateIndex(list->elements.count, index);
1824 ASSERT(usedIndex != UINT32_MAX, "Index out of bounds.");
1825
1826 list->elements.data[usedIndex] = vm->apiStack[elementSlot];
1827}
1828
1829void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
1830{
1831 validateApiSlot(vm, listSlot);
1832 validateApiSlot(vm, elementSlot);
1833 ASSERT(IS_LIST(vm->apiStack[listSlot]), "Must insert into a list.");
1834
1835 ObjList* list = AS_LIST(vm->apiStack[listSlot]);
1836
1837 // Negative indices count from the end.
1838 // We don't use wrenValidateIndex here because insert allows 1 past the end.
1839 if (index < 0) index = list->elements.count + 1 + index;
1840
1841 ASSERT(index <= list->elements.count, "Index out of bounds.");
1842
1843 wrenListInsert(vm, list, vm->apiStack[elementSlot], index);
1844}
1845
1846int wrenGetMapCount(WrenVM* vm, int slot)
1847{
1848 validateApiSlot(vm, slot);
1849 ASSERT(IS_MAP(vm->apiStack[slot]), "Slot must hold a map.");
1850
1851 ObjMap* map = AS_MAP(vm->apiStack[slot]);
1852 return map->count;
1853}
1854
1855bool wrenGetMapContainsKey(WrenVM* vm, int mapSlot, int keySlot)
1856{
1857 validateApiSlot(vm, mapSlot);
1858 validateApiSlot(vm, keySlot);
1859 ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
1860
1861 Value key = vm->apiStack[keySlot];
1862 ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
1863 if (!validateKey(vm, key)) return false;
1864
1865 ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
1866 Value value = wrenMapGet(map, key);
1867
1868 return !IS_UNDEFINED(value);
1869}
1870
1871void wrenGetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
1872{
1873 validateApiSlot(vm, mapSlot);
1874 validateApiSlot(vm, keySlot);
1875 validateApiSlot(vm, valueSlot);
1876 ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
1877
1878 ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
1879 Value value = wrenMapGet(map, vm->apiStack[keySlot]);
1880 if (IS_UNDEFINED(value)) {
1881 value = NULL_VAL;
1882 }
1883
1884 vm->apiStack[valueSlot] = value;
1885}
1886
1887void wrenSetMapValue(WrenVM* vm, int mapSlot, int keySlot, int valueSlot)
1888{
1889 validateApiSlot(vm, mapSlot);
1890 validateApiSlot(vm, keySlot);
1891 validateApiSlot(vm, valueSlot);
1892 ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Must insert into a map.");
1893
1894 Value key = vm->apiStack[keySlot];
1895 ASSERT(wrenMapIsValidKey(key), "Key must be a value type");
1896
1897 if (!validateKey(vm, key)) {
1898 return;
1899 }
1900
1901 Value value = vm->apiStack[valueSlot];
1902 ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
1903
1904 wrenMapSet(vm, map, key, value);
1905}
1906
1907void wrenRemoveMapValue(WrenVM* vm, int mapSlot, int keySlot,
1908 int removedValueSlot)
1909{
1910 validateApiSlot(vm, mapSlot);
1911 validateApiSlot(vm, keySlot);
1912 ASSERT(IS_MAP(vm->apiStack[mapSlot]), "Slot must hold a map.");
1913
1914 Value key = vm->apiStack[keySlot];
1915 if (!validateKey(vm, key)) {
1916 return;
1917 }
1918
1919 ObjMap* map = AS_MAP(vm->apiStack[mapSlot]);
1920 Value removed = wrenMapRemoveKey(vm, map, key);
1921 setSlot(vm, removedValueSlot, removed);
1922}
1923
1924void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
1925 int slot)
1926{
1927 ASSERT(module != NULL, "Module cannot be NULL.");
1928 ASSERT(name != NULL, "Variable name cannot be NULL.");
1929
1930 Value moduleName = wrenStringFormat(vm, "$", module);
1931 wrenPushRoot(vm, AS_OBJ(moduleName));
1932
1933 ObjModule* moduleObj = getModule(vm, moduleName);
1934 ASSERT(moduleObj != NULL, "Could not find module.");
1935
1936 wrenPopRoot(vm); // moduleName.
1937
1938 int variableSlot = wrenSymbolTableFind(&moduleObj->variableNames,
1939 name, strlen(name));
1940 ASSERT(variableSlot != -1, "Could not find variable.");
1941
1942 setSlot(vm, slot, moduleObj->variables.data[variableSlot]);
1943}
1944
1945bool wrenHasVariable(WrenVM* vm, const char* module, const char* name)
1946{
1947 ASSERT(module != NULL, "Module cannot be NULL.");
1948 ASSERT(name != NULL, "Variable name cannot be NULL.");
1949
1950 Value moduleName = wrenStringFormat(vm, "$", module);
1951 wrenPushRoot(vm, AS_OBJ(moduleName));
1952
1953 //We don't use wrenHasModule since we want to use the module object.
1954 ObjModule* moduleObj = getModule(vm, moduleName);
1955 ASSERT(moduleObj != NULL, "Could not find module.");
1956
1957 wrenPopRoot(vm); // moduleName.
1958
1959 int variableSlot = wrenSymbolTableFind(&moduleObj->variableNames,
1960 name, strlen(name));
1961
1962 return variableSlot != -1;
1963}
1964
1965bool wrenHasModule(WrenVM* vm, const char* module)
1966{
1967 ASSERT(module != NULL, "Module cannot be NULL.");
1968
1969 Value moduleName = wrenStringFormat(vm, "$", module);
1970 wrenPushRoot(vm, AS_OBJ(moduleName));
1971
1972 ObjModule* moduleObj = getModule(vm, moduleName);
1973
1974 wrenPopRoot(vm); // moduleName.
1975
1976 return moduleObj != NULL;
1977}
1978
1979void wrenAbortFiber(WrenVM* vm, int slot)
1980{
1981 validateApiSlot(vm, slot);
1982 vm->fiber->error = vm->apiStack[slot];
1983}
1984
1985void* wrenGetUserData(WrenVM* vm)
1986{
1987 return vm->config.userData;
1988}
1989
1990void wrenSetUserData(WrenVM* vm, void* userData)
1991{
1992 vm->config.userData = userData;
1993}
1994