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