1 | #include <stdio.h> |
2 | |
3 | #include "wren_debug.h" |
4 | |
5 | void wrenDebugPrintStackTrace(WrenVM* vm) |
6 | { |
7 | // Bail if the host doesn't enable printing errors. |
8 | if (vm->config.errorFn == NULL) return; |
9 | |
10 | ObjFiber* fiber = vm->fiber; |
11 | if (IS_STRING(fiber->error)) |
12 | { |
13 | vm->config.errorFn(vm, WREN_ERROR_RUNTIME, |
14 | NULL, -1, AS_CSTRING(fiber->error)); |
15 | } |
16 | else |
17 | { |
18 | // TODO: Print something a little useful here. Maybe the name of the error's |
19 | // class? |
20 | vm->config.errorFn(vm, WREN_ERROR_RUNTIME, |
21 | NULL, -1, "[error object]" ); |
22 | } |
23 | |
24 | for (int i = fiber->numFrames - 1; i >= 0; i--) |
25 | { |
26 | CallFrame* frame = &fiber->frames[i]; |
27 | ObjFn* fn = frame->closure->fn; |
28 | |
29 | // Skip over stub functions for calling methods from the C API. |
30 | if (fn->module == NULL) continue; |
31 | |
32 | // The built-in core module has no name. We explicitly omit it from stack |
33 | // traces since we don't want to highlight to a user the implementation |
34 | // detail of what part of the core module is written in C and what is Wren. |
35 | if (fn->module->name == NULL) continue; |
36 | |
37 | // -1 because IP has advanced past the instruction that it just executed. |
38 | int line = fn->debug->sourceLines.data[frame->ip - fn->code.data - 1]; |
39 | vm->config.errorFn(vm, WREN_ERROR_STACK_TRACE, |
40 | fn->module->name->value, line, |
41 | fn->debug->name); |
42 | } |
43 | } |
44 | |
45 | static void dumpObject(Obj* obj) |
46 | { |
47 | switch (obj->type) |
48 | { |
49 | case OBJ_CLASS: |
50 | printf("[class %s %p]" , ((ObjClass*)obj)->name->value, obj); |
51 | break; |
52 | case OBJ_CLOSURE: printf("[closure %p]" , obj); break; |
53 | case OBJ_FIBER: printf("[fiber %p]" , obj); break; |
54 | case OBJ_FN: printf("[fn %p]" , obj); break; |
55 | case OBJ_FOREIGN: printf("[foreign %p]" , obj); break; |
56 | case OBJ_INSTANCE: printf("[instance %p]" , obj); break; |
57 | case OBJ_LIST: printf("[list %p]" , obj); break; |
58 | case OBJ_MAP: printf("[map %p]" , obj); break; |
59 | case OBJ_MODULE: printf("[module %p]" , obj); break; |
60 | case OBJ_RANGE: printf("[range %p]" , obj); break; |
61 | case OBJ_STRING: printf("%s" , ((ObjString*)obj)->value); break; |
62 | case OBJ_UPVALUE: printf("[upvalue %p]" , obj); break; |
63 | default: printf("[unknown object %d]" , obj->type); break; |
64 | } |
65 | } |
66 | |
67 | void wrenDumpValue(Value value) |
68 | { |
69 | #if WREN_NAN_TAGGING |
70 | if (IS_NUM(value)) |
71 | { |
72 | printf("%.14g" , AS_NUM(value)); |
73 | } |
74 | else if (IS_OBJ(value)) |
75 | { |
76 | dumpObject(AS_OBJ(value)); |
77 | } |
78 | else |
79 | { |
80 | switch (GET_TAG(value)) |
81 | { |
82 | case TAG_FALSE: printf("false" ); break; |
83 | case TAG_NAN: printf("NaN" ); break; |
84 | case TAG_NULL: printf("null" ); break; |
85 | case TAG_TRUE: printf("true" ); break; |
86 | case TAG_UNDEFINED: UNREACHABLE(); |
87 | } |
88 | } |
89 | #else |
90 | switch (value.type) |
91 | { |
92 | case VAL_FALSE: printf("false" ); break; |
93 | case VAL_NULL: printf("null" ); break; |
94 | case VAL_NUM: printf("%.14g" , AS_NUM(value)); break; |
95 | case VAL_TRUE: printf("true" ); break; |
96 | case VAL_OBJ: dumpObject(AS_OBJ(value)); break; |
97 | case VAL_UNDEFINED: UNREACHABLE(); |
98 | } |
99 | #endif |
100 | } |
101 | |
102 | static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine) |
103 | { |
104 | int start = i; |
105 | uint8_t* bytecode = fn->code.data; |
106 | Code code = (Code)bytecode[i]; |
107 | |
108 | int line = fn->debug->sourceLines.data[i]; |
109 | if (lastLine == NULL || *lastLine != line) |
110 | { |
111 | printf("%4d:" , line); |
112 | if (lastLine != NULL) *lastLine = line; |
113 | } |
114 | else |
115 | { |
116 | printf(" " ); |
117 | } |
118 | |
119 | printf(" %04d " , i++); |
120 | |
121 | #define READ_BYTE() (bytecode[i++]) |
122 | #define READ_SHORT() (i += 2, (bytecode[i - 2] << 8) | bytecode[i - 1]) |
123 | |
124 | #define BYTE_INSTRUCTION(name) \ |
125 | printf("%-16s %5d\n", name, READ_BYTE()); \ |
126 | break |
127 | |
128 | switch (code) |
129 | { |
130 | case CODE_CONSTANT: |
131 | { |
132 | int constant = READ_SHORT(); |
133 | printf("%-16s %5d '" , "CONSTANT" , constant); |
134 | wrenDumpValue(fn->constants.data[constant]); |
135 | printf("'\n" ); |
136 | break; |
137 | } |
138 | |
139 | case CODE_NULL: printf("NULL\n" ); break; |
140 | case CODE_FALSE: printf("FALSE\n" ); break; |
141 | case CODE_TRUE: printf("TRUE\n" ); break; |
142 | |
143 | case CODE_LOAD_LOCAL_0: printf("LOAD_LOCAL_0\n" ); break; |
144 | case CODE_LOAD_LOCAL_1: printf("LOAD_LOCAL_1\n" ); break; |
145 | case CODE_LOAD_LOCAL_2: printf("LOAD_LOCAL_2\n" ); break; |
146 | case CODE_LOAD_LOCAL_3: printf("LOAD_LOCAL_3\n" ); break; |
147 | case CODE_LOAD_LOCAL_4: printf("LOAD_LOCAL_4\n" ); break; |
148 | case CODE_LOAD_LOCAL_5: printf("LOAD_LOCAL_5\n" ); break; |
149 | case CODE_LOAD_LOCAL_6: printf("LOAD_LOCAL_6\n" ); break; |
150 | case CODE_LOAD_LOCAL_7: printf("LOAD_LOCAL_7\n" ); break; |
151 | case CODE_LOAD_LOCAL_8: printf("LOAD_LOCAL_8\n" ); break; |
152 | |
153 | case CODE_LOAD_LOCAL: BYTE_INSTRUCTION("LOAD_LOCAL" ); |
154 | case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL" ); |
155 | case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE" ); |
156 | case CODE_STORE_UPVALUE: BYTE_INSTRUCTION("STORE_UPVALUE" ); |
157 | |
158 | case CODE_LOAD_MODULE_VAR: |
159 | { |
160 | int slot = READ_SHORT(); |
161 | printf("%-16s %5d '%s'\n" , "LOAD_MODULE_VAR" , slot, |
162 | fn->module->variableNames.data[slot]->value); |
163 | break; |
164 | } |
165 | |
166 | case CODE_STORE_MODULE_VAR: |
167 | { |
168 | int slot = READ_SHORT(); |
169 | printf("%-16s %5d '%s'\n" , "STORE_MODULE_VAR" , slot, |
170 | fn->module->variableNames.data[slot]->value); |
171 | break; |
172 | } |
173 | |
174 | case CODE_LOAD_FIELD_THIS: BYTE_INSTRUCTION("LOAD_FIELD_THIS" ); |
175 | case CODE_STORE_FIELD_THIS: BYTE_INSTRUCTION("STORE_FIELD_THIS" ); |
176 | case CODE_LOAD_FIELD: BYTE_INSTRUCTION("LOAD_FIELD" ); |
177 | case CODE_STORE_FIELD: BYTE_INSTRUCTION("STORE_FIELD" ); |
178 | |
179 | case CODE_POP: printf("POP\n" ); break; |
180 | |
181 | case CODE_CALL_0: |
182 | case CODE_CALL_1: |
183 | case CODE_CALL_2: |
184 | case CODE_CALL_3: |
185 | case CODE_CALL_4: |
186 | case CODE_CALL_5: |
187 | case CODE_CALL_6: |
188 | case CODE_CALL_7: |
189 | case CODE_CALL_8: |
190 | case CODE_CALL_9: |
191 | case CODE_CALL_10: |
192 | case CODE_CALL_11: |
193 | case CODE_CALL_12: |
194 | case CODE_CALL_13: |
195 | case CODE_CALL_14: |
196 | case CODE_CALL_15: |
197 | case CODE_CALL_16: |
198 | { |
199 | int numArgs = bytecode[i - 1] - CODE_CALL_0; |
200 | int symbol = READ_SHORT(); |
201 | printf("CALL_%-11d %5d '%s'\n" , numArgs, symbol, |
202 | vm->methodNames.data[symbol]->value); |
203 | break; |
204 | } |
205 | |
206 | case CODE_SUPER_0: |
207 | case CODE_SUPER_1: |
208 | case CODE_SUPER_2: |
209 | case CODE_SUPER_3: |
210 | case CODE_SUPER_4: |
211 | case CODE_SUPER_5: |
212 | case CODE_SUPER_6: |
213 | case CODE_SUPER_7: |
214 | case CODE_SUPER_8: |
215 | case CODE_SUPER_9: |
216 | case CODE_SUPER_10: |
217 | case CODE_SUPER_11: |
218 | case CODE_SUPER_12: |
219 | case CODE_SUPER_13: |
220 | case CODE_SUPER_14: |
221 | case CODE_SUPER_15: |
222 | case CODE_SUPER_16: |
223 | { |
224 | int numArgs = bytecode[i - 1] - CODE_SUPER_0; |
225 | int symbol = READ_SHORT(); |
226 | int superclass = READ_SHORT(); |
227 | printf("SUPER_%-10d %5d '%s' %5d\n" , numArgs, symbol, |
228 | vm->methodNames.data[symbol]->value, superclass); |
229 | break; |
230 | } |
231 | |
232 | case CODE_JUMP: |
233 | { |
234 | int offset = READ_SHORT(); |
235 | printf("%-16s %5d to %d\n" , "JUMP" , offset, i + offset); |
236 | break; |
237 | } |
238 | |
239 | case CODE_LOOP: |
240 | { |
241 | int offset = READ_SHORT(); |
242 | printf("%-16s %5d to %d\n" , "LOOP" , offset, i - offset); |
243 | break; |
244 | } |
245 | |
246 | case CODE_JUMP_IF: |
247 | { |
248 | int offset = READ_SHORT(); |
249 | printf("%-16s %5d to %d\n" , "JUMP_IF" , offset, i + offset); |
250 | break; |
251 | } |
252 | |
253 | case CODE_AND: |
254 | { |
255 | int offset = READ_SHORT(); |
256 | printf("%-16s %5d to %d\n" , "AND" , offset, i + offset); |
257 | break; |
258 | } |
259 | |
260 | case CODE_OR: |
261 | { |
262 | int offset = READ_SHORT(); |
263 | printf("%-16s %5d to %d\n" , "OR" , offset, i + offset); |
264 | break; |
265 | } |
266 | |
267 | case CODE_CLOSE_UPVALUE: printf("CLOSE_UPVALUE\n" ); break; |
268 | case CODE_RETURN: printf("RETURN\n" ); break; |
269 | |
270 | case CODE_CLOSURE: |
271 | { |
272 | int constant = READ_SHORT(); |
273 | printf("%-16s %5d " , "CLOSURE" , constant); |
274 | wrenDumpValue(fn->constants.data[constant]); |
275 | printf(" " ); |
276 | ObjFn* loadedFn = AS_FN(fn->constants.data[constant]); |
277 | for (int j = 0; j < loadedFn->numUpvalues; j++) |
278 | { |
279 | int isLocal = READ_BYTE(); |
280 | int index = READ_BYTE(); |
281 | if (j > 0) printf(", " ); |
282 | printf("%s %d" , isLocal ? "local" : "upvalue" , index); |
283 | } |
284 | printf("\n" ); |
285 | break; |
286 | } |
287 | |
288 | case CODE_CONSTRUCT: printf("CONSTRUCT\n" ); break; |
289 | case CODE_FOREIGN_CONSTRUCT: printf("FOREIGN_CONSTRUCT\n" ); break; |
290 | |
291 | case CODE_CLASS: |
292 | { |
293 | int numFields = READ_BYTE(); |
294 | printf("%-16s %5d fields\n" , "CLASS" , numFields); |
295 | break; |
296 | } |
297 | |
298 | case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n" ); break; |
299 | case CODE_END_CLASS: printf("END_CLASS\n" ); break; |
300 | |
301 | case CODE_METHOD_INSTANCE: |
302 | { |
303 | int symbol = READ_SHORT(); |
304 | printf("%-16s %5d '%s'\n" , "METHOD_INSTANCE" , symbol, |
305 | vm->methodNames.data[symbol]->value); |
306 | break; |
307 | } |
308 | |
309 | case CODE_METHOD_STATIC: |
310 | { |
311 | int symbol = READ_SHORT(); |
312 | printf("%-16s %5d '%s'\n" , "METHOD_STATIC" , symbol, |
313 | vm->methodNames.data[symbol]->value); |
314 | break; |
315 | } |
316 | |
317 | case CODE_END_MODULE: |
318 | printf("END_MODULE\n" ); |
319 | break; |
320 | |
321 | case CODE_IMPORT_MODULE: |
322 | { |
323 | int name = READ_SHORT(); |
324 | printf("%-16s %5d '" , "IMPORT_MODULE" , name); |
325 | wrenDumpValue(fn->constants.data[name]); |
326 | printf("'\n" ); |
327 | break; |
328 | } |
329 | |
330 | case CODE_IMPORT_VARIABLE: |
331 | { |
332 | int variable = READ_SHORT(); |
333 | printf("%-16s %5d '" , "IMPORT_VARIABLE" , variable); |
334 | wrenDumpValue(fn->constants.data[variable]); |
335 | printf("'\n" ); |
336 | break; |
337 | } |
338 | |
339 | case CODE_END: |
340 | printf("END\n" ); |
341 | break; |
342 | |
343 | default: |
344 | printf("UKNOWN! [%d]\n" , bytecode[i - 1]); |
345 | break; |
346 | } |
347 | |
348 | // Return how many bytes this instruction takes, or -1 if it's an END. |
349 | if (code == CODE_END) return -1; |
350 | return i - start; |
351 | |
352 | #undef READ_BYTE |
353 | #undef READ_SHORT |
354 | } |
355 | |
356 | int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i) |
357 | { |
358 | return dumpInstruction(vm, fn, i, NULL); |
359 | } |
360 | |
361 | void wrenDumpCode(WrenVM* vm, ObjFn* fn) |
362 | { |
363 | printf("%s: %s\n" , |
364 | fn->module->name == NULL ? "<core>" : fn->module->name->value, |
365 | fn->debug->name); |
366 | |
367 | int i = 0; |
368 | int lastLine = -1; |
369 | for (;;) |
370 | { |
371 | int offset = dumpInstruction(vm, fn, i, &lastLine); |
372 | if (offset == -1) break; |
373 | i += offset; |
374 | } |
375 | |
376 | printf("\n" ); |
377 | } |
378 | |
379 | void wrenDumpStack(ObjFiber* fiber) |
380 | { |
381 | printf("(fiber %p) " , fiber); |
382 | for (Value* slot = fiber->stack; slot < fiber->stackTop; slot++) |
383 | { |
384 | wrenDumpValue(*slot); |
385 | printf(" | " ); |
386 | } |
387 | printf("\n" ); |
388 | } |
389 | |