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