1 module wren.common; 2 3 @nogc: 4 5 // The Wren semantic version number components. 6 enum WREN_VERSION_MAJOR = 0; 7 enum WREN_VERSION_MINOR = 4; 8 enum WREN_VERSION_PATCH = 0; 9 10 // A human-friendly string representation of the version. 11 enum WREN_VERSION_STRING = "0.4.0"; 12 13 // A monotonically increasing numeric representation of the version number. Use 14 // this if you want to do range checks over versions. 15 auto WREN_VERSION_NUMBER = WREN_VERSION_MAJOR * 1000000 + 16 WREN_VERSION_MINOR * 1000 + 17 WREN_VERSION_PATCH; 18 19 int wrenGetVersionNumber() { 20 return WREN_VERSION_NUMBER; 21 } 22 23 // These flags let you control some details of the interpreter's implementation. 24 // Usually they trade-off a bit of portability for speed. They default to the 25 // most efficient behavior. 26 27 // If true, then Wren uses a NaN-tagged double for its core value 28 // representation. Otherwise, it uses a larger more conventional struct. The 29 // former is significantly faster and more compact. The latter is useful for 30 // debugging and may be more portable. 31 // 32 // Defaults to on. 33 version(WrenNoNanTagging) 34 { 35 enum WREN_NAN_TAGGING = 0; 36 } 37 else 38 { 39 enum WREN_NAN_TAGGING = 1; 40 } 41 42 // Wren's "computed goto" functionality depends on a non-standard GCC extension, 43 // which allows you to take the pointer of labels defined within a loop. D, from what I know, 44 // does not support this, so we disable this functionality. 45 /* 46 // If true, the VM's interpreter loop uses computed gotos. See this for more: 47 // http://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Labels-as-Values.html 48 // Enabling this speeds up the main dispatch loop a bit, but requires compiler 49 // support. 50 // see https://bullno1.com/blog/switched-goto for alternative 51 // Defaults to true on supported compilers. 52 version(WrenNoComputedGoto) 53 { 54 enum WREN_COMPUTED_GOTO = 0; 55 } 56 else 57 { 58 enum WREN_COMPUTED_GOTO = 1; 59 } 60 */ 61 62 // The VM includes a number of optional modules. You can choose to include 63 // these or not. By default, they are all available. To disable one, set the 64 // corresponding `WREN_OPT_<name>` define to `0`. 65 version(WrenNoOptMeta) 66 { 67 enum WREN_OPT_META = 0; 68 } 69 else 70 { 71 enum WREN_OPT_META = 1; 72 } 73 74 version (WrenNoOptRandom) 75 { 76 enum WREN_OPT_RANDOM = 0; 77 } 78 else 79 { 80 enum WREN_OPT_RANDOM = 1; 81 } 82 83 // These flags are useful for debugging and hacking on Wren itself. They are not 84 // intended to be used for production code. They default to off. 85 86 // Set this to true to stress test the GC. It will perform a collection before 87 // every allocation. This is useful to ensure that memory is always correctly 88 // reachable. 89 version (WrenDebugGCStress) 90 { 91 enum WREN_DEBUG_GC_STRESS = 1; 92 } 93 else 94 { 95 enum WREN_DEBUG_GC_STRESS = 0; 96 } 97 98 // Set this to true to log memory operations as they occur. 99 version (WrenDebugTraceMemory) 100 { 101 enum WREN_DEBUG_TRACE_MEMORY = 1; 102 } 103 else 104 { 105 enum WREN_DEBUG_TRACE_MEMORY = 0; 106 } 107 108 // Set this to true to log garbage collections as they occur. 109 version (WrenDebugTraceGC) 110 { 111 enum WREN_DEBUG_TRACE_GC = 1; 112 } 113 else 114 { 115 enum WREN_DEBUG_TRACE_GC = 0; 116 } 117 118 // Set this to true to print out the compiled bytecode of each function. 119 version (WrenDebugDumpCompiledCode) 120 { 121 enum WREN_DEBUG_DUMP_COMPILED_CODE = 1; 122 } 123 else 124 { 125 enum WREN_DEBUG_DUMP_COMPILED_CODE = 0; 126 } 127 128 // Set this to trace each instruction as it's executed. 129 version (WrenDebugTraceInstructions) 130 { 131 enum WREN_DEBUG_TRACE_INSTRUCTIONS = 1; 132 } 133 else 134 { 135 enum WREN_DEBUG_TRACE_INSTRUCTIONS = 0; 136 } 137 138 // The maximum number of module-level variables that may be defined at one time. 139 // This limitation comes from the 16 bits used for the arguments to 140 // `CODE_LOAD_MODULE_VAR` and `CODE_STORE_MODULE_VAR`. 141 enum MAX_MODULE_VARS = 65536; 142 143 // The maximum number of arguments that can be passed to a method. Note that 144 // this limitation is hardcoded in other places in the VM, in particular, the 145 // `CODE_CALL_XX` instructions assume a certain maximum number. 146 enum MAX_PARAMETERS = 16; 147 148 // The maximum name of a method, not including the signature. This is an 149 // arbitrary but enforced maximum just so we know how long the method name 150 // strings need to be in the parser. 151 enum MAX_METHOD_NAME = 64; 152 153 // The maximum length of a method signature. Signatures look like: 154 // 155 // foo // Getter. 156 // foo() // No-argument method. 157 // foo(_) // One-argument method. 158 // foo(_,_) // Two-argument method. 159 // init foo() // Constructor initializer. 160 // 161 // The maximum signature length takes into account the longest method name, the 162 // maximum number of parameters with separators between them, "init ", and "()". 163 enum MAX_METHOD_SIGNATURE = MAX_METHOD_NAME + (MAX_PARAMETERS * 2) + 6; 164 165 // The maximum length of an identifier. The only real reason for this limitation 166 // is so that error messages mentioning variables can be stack allocated. 167 enum MAX_VARIABLE_NAME = 64; 168 169 // The maximum number of fields a class can have, including inherited fields. 170 // This is explicit in the bytecode since `CODE_CLASS` and `CODE_SUBCLASS` take 171 // a single byte for the number of fields. Note that it's 255 and not 256 172 // because creating a class takes the *number* of fields, not the *highest 173 // field index*. 174 enum MAX_FIELDS = 255; 175 176 // XXX: move this to `wren.vm`? 177 // Use the VM's allocator to allocate an object of [type]. 178 T* ALLOCATE(VM, T)(VM* vm) @nogc 179 { 180 import wren.vm : wrenReallocate; 181 return cast(typeof(return))wrenReallocate(vm, null, 0, T.sizeof); 182 } 183 184 // Use the VM's allocator to allocate an object of [mainType] containing a 185 // flexible array of [count] objects of [arrayType]. 186 T* ALLOCATE_FLEX(VM, T, ArrayType)(VM* vm, size_t count) @nogc 187 { 188 import std.traits : isArray; 189 import wren.vm : wrenReallocate; 190 T* obj = cast(T*)wrenReallocate(vm, null, 0, T.sizeof); 191 ArrayType* arr = cast(ArrayType*)wrenReallocate(vm, null, 0, ArrayType.sizeof * count); 192 193 // EEEEK. Since arrays differ in implementation, 194 // we can't just malloc the size of an object + the size of the array we want to 195 // allocate for. This is a little tricky way of getting around that -- 196 // but this WILL break if T has more then one array. 197 static foreach(_mem; __traits(allMembers, T)) {{ 198 alias member = __traits(getMember, T, _mem); 199 static if (isArray!(typeof(member))) { 200 alias ArrayElementType = typeof(member[0]); 201 static if (is(ArrayElementType == ArrayType)) { 202 __traits(child, obj, member) = arr[0 .. count]; 203 } 204 } 205 }} 206 return obj; 207 } 208 209 T* ALLOCATE_ARRAY(VM, T)(VM* vm, size_t count) @nogc 210 { 211 import wren.vm : wrenReallocate; 212 return cast(typeof(return))wrenReallocate(vm, null, 0, T.sizeof * count); 213 } 214 215 void DEALLOCATE(VM)(VM* vm, void* pointer) @nogc 216 { 217 import wren.vm : wrenReallocate; 218 wrenReallocate(vm, pointer, 0, 0); 219 } 220