1#ifndef wren_vm_h
2#define wren_vm_h
3
4#include "wren_common.h"
5#include "wren_compiler.h"
6#include "wren_value.h"
7#include "wren_utils.h"
8
9// The maximum number of temporary objects that can be made visible to the GC
10// at one time.
11#define WREN_MAX_TEMP_ROOTS 8
12
13typedef enum
14{
15 #define OPCODE(name, _) CODE_##name,
16 #include "wren_opcodes.h"
17 #undef OPCODE
18} Code;
19
20// A handle to a value, basically just a linked list of extra GC roots.
21//
22// Note that even non-heap-allocated values can be stored here.
23struct WrenHandle
24{
25 Value value;
26
27 WrenHandle* prev;
28 WrenHandle* next;
29};
30
31struct WrenVM
32{
33 ObjClass* boolClass;
34 ObjClass* classClass;
35 ObjClass* fiberClass;
36 ObjClass* fnClass;
37 ObjClass* listClass;
38 ObjClass* mapClass;
39 ObjClass* nullClass;
40 ObjClass* numClass;
41 ObjClass* objectClass;
42 ObjClass* rangeClass;
43 ObjClass* stringClass;
44
45 // The fiber that is currently running.
46 ObjFiber* fiber;
47
48 // The loaded modules. Each key is an ObjString (except for the main module,
49 // whose key is null) for the module's name and the value is the ObjModule
50 // for the module.
51 ObjMap* modules;
52
53 // The most recently imported module. More specifically, the module whose
54 // code has most recently finished executing.
55 //
56 // Not treated like a GC root since the module is already in [modules].
57 ObjModule* lastModule;
58
59 // Memory management data:
60
61 // The number of bytes that are known to be currently allocated. Includes all
62 // memory that was proven live after the last GC, as well as any new bytes
63 // that were allocated since then. Does *not* include bytes for objects that
64 // were freed since the last GC.
65 size_t bytesAllocated;
66
67 // The number of total allocated bytes that will trigger the next GC.
68 size_t nextGC;
69
70 // The first object in the linked list of all currently allocated objects.
71 Obj* first;
72
73 // The "gray" set for the garbage collector. This is the stack of unprocessed
74 // objects while a garbage collection pass is in process.
75 Obj** gray;
76 int grayCount;
77 int grayCapacity;
78
79 // The list of temporary roots. This is for temporary or new objects that are
80 // not otherwise reachable but should not be collected.
81 //
82 // They are organized as a stack of pointers stored in this array. This
83 // implies that temporary roots need to have stack semantics: only the most
84 // recently pushed object can be released.
85 Obj* tempRoots[WREN_MAX_TEMP_ROOTS];
86
87 int numTempRoots;
88
89 // Pointer to the first node in the linked list of active handles or NULL if
90 // there are none.
91 WrenHandle* handles;
92
93 // Pointer to the bottom of the range of stack slots available for use from
94 // the C API. During a foreign method, this will be in the stack of the fiber
95 // that is executing a method.
96 //
97 // If not in a foreign method, this is initially NULL. If the user requests
98 // slots by calling wrenEnsureSlots(), a stack is created and this is
99 // initialized.
100 Value* apiStack;
101
102 WrenConfiguration config;
103
104 // Compiler and debugger data:
105
106 // The compiler that is currently compiling code. This is used so that heap
107 // allocated objects used by the compiler can be found if a GC is kicked off
108 // in the middle of a compile.
109 Compiler* compiler;
110
111 // There is a single global symbol table for all method names on all classes.
112 // Method calls are dispatched directly by index in this table.
113 SymbolTable methodNames;
114};
115
116// A generic allocation function that handles all explicit memory management.
117// It's used like so:
118//
119// - To allocate new memory, [memory] is NULL and [oldSize] is zero. It should
120// return the allocated memory or NULL on failure.
121//
122// - To attempt to grow an existing allocation, [memory] is the memory,
123// [oldSize] is its previous size, and [newSize] is the desired size.
124// It should return [memory] if it was able to grow it in place, or a new
125// pointer if it had to move it.
126//
127// - To shrink memory, [memory], [oldSize], and [newSize] are the same as above
128// but it will always return [memory].
129//
130// - To free memory, [memory] will be the memory to free and [newSize] and
131// [oldSize] will be zero. It should return NULL.
132void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize);
133
134// Invoke the finalizer for the foreign object referenced by [foreign].
135void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign);
136
137// Creates a new [WrenHandle] for [value].
138WrenHandle* wrenMakeHandle(WrenVM* vm, Value value);
139
140// Compile [source] in the context of [module] and wrap in a fiber that can
141// execute it.
142//
143// Returns NULL if a compile error occurred.
144ObjClosure* wrenCompileSource(WrenVM* vm, const char* module,
145 const char* source, bool isExpression,
146 bool printErrors);
147
148// Looks up a variable from a previously-loaded module.
149//
150// Aborts the current fiber if the module or variable could not be found.
151Value wrenGetModuleVariable(WrenVM* vm, Value moduleName, Value variableName);
152
153// Returns the value of the module-level variable named [name] in the main
154// module.
155Value wrenFindVariable(WrenVM* vm, ObjModule* module, const char* name);
156
157// Adds a new implicitly declared top-level variable named [name] to [module]
158// based on a use site occurring on [line].
159//
160// Does not check to see if a variable with that name is already declared or
161// defined. Returns the symbol for the new variable or -2 if there are too many
162// variables defined.
163int wrenDeclareVariable(WrenVM* vm, ObjModule* module, const char* name,
164 size_t length, int line);
165
166// Adds a new top-level variable named [name] to [module], and optionally
167// populates line with the line of the implicit first use (line can be NULL).
168//
169// Returns the symbol for the new variable, -1 if a variable with the given name
170// is already defined, or -2 if there are too many variables defined.
171// Returns -3 if this is a top-level lowercase variable (localname) that was
172// used before being defined.
173int wrenDefineVariable(WrenVM* vm, ObjModule* module, const char* name,
174 size_t length, Value value, int* line);
175
176// Pushes [closure] onto [fiber]'s callstack to invoke it. Expects [numArgs]
177// arguments (including the receiver) to be on the top of the stack already.
178static inline void wrenCallFunction(WrenVM* vm, ObjFiber* fiber,
179 ObjClosure* closure, int numArgs)
180{
181 // Grow the call frame array if needed.
182 if (fiber->numFrames + 1 > fiber->frameCapacity)
183 {
184 int max = fiber->frameCapacity * 2;
185 fiber->frames = (CallFrame*)wrenReallocate(vm, fiber->frames,
186 sizeof(CallFrame) * fiber->frameCapacity, sizeof(CallFrame) * max);
187 fiber->frameCapacity = max;
188 }
189
190 // Grow the stack if needed.
191 int stackSize = (int)(fiber->stackTop - fiber->stack);
192 int needed = stackSize + closure->fn->maxSlots;
193 wrenEnsureStack(vm, fiber, needed);
194
195 wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs);
196}
197
198// Marks [obj] as a GC root so that it doesn't get collected.
199void wrenPushRoot(WrenVM* vm, Obj* obj);
200
201// Removes the most recently pushed temporary root.
202void wrenPopRoot(WrenVM* vm);
203
204// Returns the class of [value].
205//
206// Defined here instead of in wren_value.h because it's critical that this be
207// inlined. That means it must be defined in the header, but the wren_value.h
208// header doesn't have a full definitely of WrenVM yet.
209static inline ObjClass* wrenGetClassInline(WrenVM* vm, Value value)
210{
211 if (IS_NUM(value)) return vm->numClass;
212 if (IS_OBJ(value)) return AS_OBJ(value)->classObj;
213
214#if WREN_NAN_TAGGING
215 switch (GET_TAG(value))
216 {
217 case TAG_FALSE: return vm->boolClass; break;
218 case TAG_NAN: return vm->numClass; break;
219 case TAG_NULL: return vm->nullClass; break;
220 case TAG_TRUE: return vm->boolClass; break;
221 case TAG_UNDEFINED: UNREACHABLE();
222 }
223#else
224 switch (value.type)
225 {
226 case VAL_FALSE: return vm->boolClass;
227 case VAL_NULL: return vm->nullClass;
228 case VAL_NUM: return vm->numClass;
229 case VAL_TRUE: return vm->boolClass;
230 case VAL_OBJ: return AS_OBJ(value)->classObj;
231 case VAL_UNDEFINED: UNREACHABLE();
232 }
233#endif
234
235 UNREACHABLE();
236 return NULL;
237}
238
239// Returns `true` if [name] is a local variable name (starts with a lowercase
240// letter).
241static inline bool wrenIsLocalName(const char* name)
242{
243 return name[0] >= 'a' && name[0] <= 'z';
244}
245
246static inline bool wrenIsFalsyValue(Value value)
247{
248 return IS_FALSE(value) || IS_NULL(value);
249}
250
251#endif
252