1#include <stdio.h>
2
3#include "wren_debug.h"
4
5void 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
45static 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
67void 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
102static 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
356int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i)
357{
358 return dumpInstruction(vm, fn, i, NULL);
359}
360
361void 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
379void 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