| 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 | |