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 | |
13 | typedef 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. |
23 | struct WrenHandle |
24 | { |
25 | Value value; |
26 | |
27 | WrenHandle* prev; |
28 | WrenHandle* next; |
29 | }; |
30 | |
31 | struct 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. |
132 | void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize); |
133 | |
134 | // Invoke the finalizer for the foreign object referenced by [foreign]. |
135 | void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign); |
136 | |
137 | // Creates a new [WrenHandle] for [value]. |
138 | WrenHandle* 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. |
144 | ObjClosure* 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. |
151 | Value wrenGetModuleVariable(WrenVM* vm, Value moduleName, Value variableName); |
152 | |
153 | // Returns the value of the module-level variable named [name] in the main |
154 | // module. |
155 | Value 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. |
163 | int 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. |
173 | int 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. |
178 | static 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. |
199 | void wrenPushRoot(WrenVM* vm, Obj* obj); |
200 | |
201 | // Removes the most recently pushed temporary root. |
202 | void 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. |
209 | static 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). |
241 | static inline bool wrenIsLocalName(const char* name) |
242 | { |
243 | return name[0] >= 'a' && name[0] <= 'z'; |
244 | } |
245 | |
246 | static inline bool wrenIsFalsyValue(Value value) |
247 | { |
248 | return IS_FALSE(value) || IS_NULL(value); |
249 | } |
250 | |
251 | #endif |
252 | |