388 lines
10 KiB
C
388 lines
10 KiB
C
#include <stdio.h>
|
|
|
|
#include "wren_debug.h"
|
|
|
|
void wrenDebugPrintStackTrace(WrenVM* vm)
|
|
{
|
|
// Bail if the host doesn't enable printing errors.
|
|
if (vm->config.errorFn == NULL) return;
|
|
|
|
ObjFiber* fiber = vm->fiber;
|
|
if (IS_STRING(fiber->error))
|
|
{
|
|
vm->config.errorFn(vm, WREN_ERROR_RUNTIME,
|
|
NULL, -1, AS_CSTRING(fiber->error));
|
|
}
|
|
else
|
|
{
|
|
// TODO: Print something a little useful here. Maybe the name of the error's
|
|
// class?
|
|
vm->config.errorFn(vm, WREN_ERROR_RUNTIME,
|
|
NULL, -1, "[error object]");
|
|
}
|
|
|
|
for (int i = fiber->numFrames - 1; i >= 0; i--)
|
|
{
|
|
CallFrame* frame = &fiber->frames[i];
|
|
ObjFn* fn = frame->closure->fn;
|
|
|
|
// Skip over stub functions for calling methods from the C API.
|
|
if (fn->module == NULL) continue;
|
|
|
|
// The built-in core module has no name. We explicitly omit it from stack
|
|
// traces since we don't want to highlight to a user the implementation
|
|
// detail of what part of the core module is written in C and what is Wren.
|
|
if (fn->module->name == NULL) continue;
|
|
|
|
// -1 because IP has advanced past the instruction that it just executed.
|
|
int line = fn->debug->sourceLines.data[frame->ip - fn->code.data - 1];
|
|
vm->config.errorFn(vm, WREN_ERROR_STACK_TRACE,
|
|
fn->module->name->value, line,
|
|
fn->debug->name);
|
|
}
|
|
}
|
|
|
|
static void dumpObject(Obj* obj)
|
|
{
|
|
switch (obj->type)
|
|
{
|
|
case OBJ_CLASS:
|
|
printf("[class %s %p]", ((ObjClass*)obj)->name->value, obj);
|
|
break;
|
|
case OBJ_CLOSURE: printf("[closure %p]", obj); break;
|
|
case OBJ_FIBER: printf("[fiber %p]", obj); break;
|
|
case OBJ_FN: printf("[fn %p]", obj); break;
|
|
case OBJ_FOREIGN: printf("[foreign %p]", obj); break;
|
|
case OBJ_INSTANCE: printf("[instance %p]", obj); break;
|
|
case OBJ_LIST: printf("[list %p]", obj); break;
|
|
case OBJ_MAP: printf("[map %p]", obj); break;
|
|
case OBJ_MODULE: printf("[module %p]", obj); break;
|
|
case OBJ_RANGE: printf("[range %p]", obj); break;
|
|
case OBJ_STRING: printf("%s", ((ObjString*)obj)->value); break;
|
|
case OBJ_UPVALUE: printf("[upvalue %p]", obj); break;
|
|
default: printf("[unknown object %d]", obj->type); break;
|
|
}
|
|
}
|
|
|
|
void wrenDumpValue(Value value)
|
|
{
|
|
#if WREN_NAN_TAGGING
|
|
if (IS_NUM(value))
|
|
{
|
|
printf("%.14g", AS_NUM(value));
|
|
}
|
|
else if (IS_OBJ(value))
|
|
{
|
|
dumpObject(AS_OBJ(value));
|
|
}
|
|
else
|
|
{
|
|
switch (GET_TAG(value))
|
|
{
|
|
case TAG_FALSE: printf("false"); break;
|
|
case TAG_NAN: printf("NaN"); break;
|
|
case TAG_NULL: printf("null"); break;
|
|
case TAG_TRUE: printf("true"); break;
|
|
case TAG_UNDEFINED: UNREACHABLE();
|
|
}
|
|
}
|
|
#else
|
|
switch (value.type)
|
|
{
|
|
case VAL_FALSE: printf("false"); break;
|
|
case VAL_NULL: printf("null"); break;
|
|
case VAL_NUM: printf("%.14g", AS_NUM(value)); break;
|
|
case VAL_TRUE: printf("true"); break;
|
|
case VAL_OBJ: dumpObject(AS_OBJ(value)); break;
|
|
case VAL_UNDEFINED: UNREACHABLE();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
|
{
|
|
int start = i;
|
|
uint8_t* bytecode = fn->code.data;
|
|
Code code = (Code)bytecode[i];
|
|
|
|
int line = fn->debug->sourceLines.data[i];
|
|
if (lastLine == NULL || *lastLine != line)
|
|
{
|
|
printf("%4d:", line);
|
|
if (lastLine != NULL) *lastLine = line;
|
|
}
|
|
else
|
|
{
|
|
printf(" ");
|
|
}
|
|
|
|
printf(" %04d ", i++);
|
|
|
|
#define READ_BYTE() (bytecode[i++])
|
|
#define READ_SHORT() (i += 2, (bytecode[i - 2] << 8) | bytecode[i - 1])
|
|
|
|
#define BYTE_INSTRUCTION(name) \
|
|
printf("%-16s %5d\n", name, READ_BYTE()); \
|
|
break; \
|
|
|
|
switch (code)
|
|
{
|
|
case CODE_CONSTANT:
|
|
{
|
|
int constant = READ_SHORT();
|
|
printf("%-16s %5d '", "CONSTANT", constant);
|
|
wrenDumpValue(fn->constants.data[constant]);
|
|
printf("'\n");
|
|
break;
|
|
}
|
|
|
|
case CODE_NULL: printf("NULL\n"); break;
|
|
case CODE_FALSE: printf("FALSE\n"); break;
|
|
case CODE_TRUE: printf("TRUE\n"); break;
|
|
|
|
case CODE_LOAD_LOCAL_0: printf("LOAD_LOCAL_0\n"); break;
|
|
case CODE_LOAD_LOCAL_1: printf("LOAD_LOCAL_1\n"); break;
|
|
case CODE_LOAD_LOCAL_2: printf("LOAD_LOCAL_2\n"); break;
|
|
case CODE_LOAD_LOCAL_3: printf("LOAD_LOCAL_3\n"); break;
|
|
case CODE_LOAD_LOCAL_4: printf("LOAD_LOCAL_4\n"); break;
|
|
case CODE_LOAD_LOCAL_5: printf("LOAD_LOCAL_5\n"); break;
|
|
case CODE_LOAD_LOCAL_6: printf("LOAD_LOCAL_6\n"); break;
|
|
case CODE_LOAD_LOCAL_7: printf("LOAD_LOCAL_7\n"); break;
|
|
case CODE_LOAD_LOCAL_8: printf("LOAD_LOCAL_8\n"); break;
|
|
|
|
case CODE_LOAD_LOCAL: BYTE_INSTRUCTION("LOAD_LOCAL");
|
|
case CODE_STORE_LOCAL: BYTE_INSTRUCTION("STORE_LOCAL");
|
|
case CODE_LOAD_UPVALUE: BYTE_INSTRUCTION("LOAD_UPVALUE");
|
|
case CODE_STORE_UPVALUE: BYTE_INSTRUCTION("STORE_UPVALUE");
|
|
|
|
case CODE_LOAD_MODULE_VAR:
|
|
{
|
|
int slot = READ_SHORT();
|
|
printf("%-16s %5d '%s'\n", "LOAD_MODULE_VAR", slot,
|
|
fn->module->variableNames.data[slot]->value);
|
|
break;
|
|
}
|
|
|
|
case CODE_STORE_MODULE_VAR:
|
|
{
|
|
int slot = READ_SHORT();
|
|
printf("%-16s %5d '%s'\n", "STORE_MODULE_VAR", slot,
|
|
fn->module->variableNames.data[slot]->value);
|
|
break;
|
|
}
|
|
|
|
case CODE_LOAD_FIELD_THIS: BYTE_INSTRUCTION("LOAD_FIELD_THIS");
|
|
case CODE_STORE_FIELD_THIS: BYTE_INSTRUCTION("STORE_FIELD_THIS");
|
|
case CODE_LOAD_FIELD: BYTE_INSTRUCTION("LOAD_FIELD");
|
|
case CODE_STORE_FIELD: BYTE_INSTRUCTION("STORE_FIELD");
|
|
|
|
case CODE_POP: printf("POP\n"); break;
|
|
|
|
case CODE_CALL_0:
|
|
case CODE_CALL_1:
|
|
case CODE_CALL_2:
|
|
case CODE_CALL_3:
|
|
case CODE_CALL_4:
|
|
case CODE_CALL_5:
|
|
case CODE_CALL_6:
|
|
case CODE_CALL_7:
|
|
case CODE_CALL_8:
|
|
case CODE_CALL_9:
|
|
case CODE_CALL_10:
|
|
case CODE_CALL_11:
|
|
case CODE_CALL_12:
|
|
case CODE_CALL_13:
|
|
case CODE_CALL_14:
|
|
case CODE_CALL_15:
|
|
case CODE_CALL_16:
|
|
{
|
|
int numArgs = bytecode[i - 1] - CODE_CALL_0;
|
|
int symbol = READ_SHORT();
|
|
printf("CALL_%-11d %5d '%s'\n", numArgs, symbol,
|
|
vm->methodNames.data[symbol]->value);
|
|
break;
|
|
}
|
|
|
|
case CODE_SUPER_0:
|
|
case CODE_SUPER_1:
|
|
case CODE_SUPER_2:
|
|
case CODE_SUPER_3:
|
|
case CODE_SUPER_4:
|
|
case CODE_SUPER_5:
|
|
case CODE_SUPER_6:
|
|
case CODE_SUPER_7:
|
|
case CODE_SUPER_8:
|
|
case CODE_SUPER_9:
|
|
case CODE_SUPER_10:
|
|
case CODE_SUPER_11:
|
|
case CODE_SUPER_12:
|
|
case CODE_SUPER_13:
|
|
case CODE_SUPER_14:
|
|
case CODE_SUPER_15:
|
|
case CODE_SUPER_16:
|
|
{
|
|
int numArgs = bytecode[i - 1] - CODE_SUPER_0;
|
|
int symbol = READ_SHORT();
|
|
int superclass = READ_SHORT();
|
|
printf("SUPER_%-10d %5d '%s' %5d\n", numArgs, symbol,
|
|
vm->methodNames.data[symbol]->value, superclass);
|
|
break;
|
|
}
|
|
|
|
case CODE_JUMP:
|
|
{
|
|
int offset = READ_SHORT();
|
|
printf("%-16s %5d to %d\n", "JUMP", offset, i + offset);
|
|
break;
|
|
}
|
|
|
|
case CODE_LOOP:
|
|
{
|
|
int offset = READ_SHORT();
|
|
printf("%-16s %5d to %d\n", "LOOP", offset, i - offset);
|
|
break;
|
|
}
|
|
|
|
case CODE_JUMP_IF:
|
|
{
|
|
int offset = READ_SHORT();
|
|
printf("%-16s %5d to %d\n", "JUMP_IF", offset, i + offset);
|
|
break;
|
|
}
|
|
|
|
case CODE_AND:
|
|
{
|
|
int offset = READ_SHORT();
|
|
printf("%-16s %5d to %d\n", "AND", offset, i + offset);
|
|
break;
|
|
}
|
|
|
|
case CODE_OR:
|
|
{
|
|
int offset = READ_SHORT();
|
|
printf("%-16s %5d to %d\n", "OR", offset, i + offset);
|
|
break;
|
|
}
|
|
|
|
case CODE_CLOSE_UPVALUE: printf("CLOSE_UPVALUE\n"); break;
|
|
case CODE_RETURN: printf("RETURN\n"); break;
|
|
|
|
case CODE_CLOSURE:
|
|
{
|
|
int constant = READ_SHORT();
|
|
printf("%-16s %5d ", "CLOSURE", constant);
|
|
wrenDumpValue(fn->constants.data[constant]);
|
|
printf(" ");
|
|
ObjFn* loadedFn = AS_FN(fn->constants.data[constant]);
|
|
for (int j = 0; j < loadedFn->numUpvalues; j++)
|
|
{
|
|
int isLocal = READ_BYTE();
|
|
int index = READ_BYTE();
|
|
if (j > 0) printf(", ");
|
|
printf("%s %d", isLocal ? "local" : "upvalue", index);
|
|
}
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
case CODE_CONSTRUCT: printf("CONSTRUCT\n"); break;
|
|
case CODE_FOREIGN_CONSTRUCT: printf("FOREIGN_CONSTRUCT\n"); break;
|
|
|
|
case CODE_CLASS:
|
|
{
|
|
int numFields = READ_BYTE();
|
|
printf("%-16s %5d fields\n", "CLASS", numFields);
|
|
break;
|
|
}
|
|
|
|
case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n"); break;
|
|
|
|
case CODE_METHOD_INSTANCE:
|
|
{
|
|
int symbol = READ_SHORT();
|
|
printf("%-16s %5d '%s'\n", "METHOD_INSTANCE", symbol,
|
|
vm->methodNames.data[symbol]->value);
|
|
break;
|
|
}
|
|
|
|
case CODE_METHOD_STATIC:
|
|
{
|
|
int symbol = READ_SHORT();
|
|
printf("%-16s %5d '%s'\n", "METHOD_STATIC", symbol,
|
|
vm->methodNames.data[symbol]->value);
|
|
break;
|
|
}
|
|
|
|
case CODE_END_MODULE:
|
|
printf("END_MODULE\n");
|
|
break;
|
|
|
|
case CODE_IMPORT_MODULE:
|
|
{
|
|
int name = READ_SHORT();
|
|
printf("%-16s %5d '", "IMPORT_MODULE", name);
|
|
wrenDumpValue(fn->constants.data[name]);
|
|
printf("'\n");
|
|
break;
|
|
}
|
|
|
|
case CODE_IMPORT_VARIABLE:
|
|
{
|
|
int variable = READ_SHORT();
|
|
printf("%-16s %5d '", "IMPORT_VARIABLE", variable);
|
|
wrenDumpValue(fn->constants.data[variable]);
|
|
printf("'\n");
|
|
break;
|
|
}
|
|
|
|
case CODE_END:
|
|
printf("END\n");
|
|
break;
|
|
|
|
default:
|
|
printf("UKNOWN! [%d]\n", bytecode[i - 1]);
|
|
break;
|
|
}
|
|
|
|
// Return how many bytes this instruction takes, or -1 if it's an END.
|
|
if (code == CODE_END) return -1;
|
|
return i - start;
|
|
|
|
#undef READ_BYTE
|
|
#undef READ_SHORT
|
|
}
|
|
|
|
int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i)
|
|
{
|
|
return dumpInstruction(vm, fn, i, NULL);
|
|
}
|
|
|
|
void wrenDumpCode(WrenVM* vm, ObjFn* fn)
|
|
{
|
|
printf("%s: %s\n",
|
|
fn->module->name == NULL ? "<core>" : fn->module->name->value,
|
|
fn->debug->name);
|
|
|
|
int i = 0;
|
|
int lastLine = -1;
|
|
for (;;)
|
|
{
|
|
int offset = dumpInstruction(vm, fn, i, &lastLine);
|
|
if (offset == -1) break;
|
|
i += offset;
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
void wrenDumpStack(ObjFiber* fiber)
|
|
{
|
|
printf("(fiber %p) ", fiber);
|
|
for (Value* slot = fiber->stack; slot < fiber->stackTop; slot++)
|
|
{
|
|
wrenDumpValue(*slot);
|
|
printf(" | ");
|
|
}
|
|
printf("\n");
|
|
}
|