Split off 'dis' as well
This commit is contained in:
parent
8425ed5a3b
commit
eb105690b5
@ -1,28 +0,0 @@
|
||||
'''
|
||||
@brief Called when 'dis' is run as the main module (-m dis)
|
||||
'''
|
||||
# Get the real dis
|
||||
import dis
|
||||
|
||||
def disrec(code, seen):
|
||||
let next = [code]
|
||||
while next:
|
||||
let co = next[0]
|
||||
next = next[1:]
|
||||
dis.dis(co)
|
||||
for inst,size,operand in dis.examine(co):
|
||||
if isinstance(operand,codeobject) and operand not in seen and operand not in next:
|
||||
next.append(operand)
|
||||
if next:
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import kuroko
|
||||
if (len(kuroko.argv) < 2):
|
||||
print("Usage: kuroko -m dis FILE")
|
||||
return 1
|
||||
import fileio
|
||||
for file in kuroko.argv[1:]:
|
||||
with fileio.open(file,'r') as f:
|
||||
let result = dis.build(f.read(), file)
|
||||
disrec(result,set())
|
318
src/debug.c
318
src/debug.c
@ -297,28 +297,6 @@ size_t krk_disassembleInstruction(FILE * f, KrkCodeObject * func, size_t offset)
|
||||
#undef FORMAT_VALUE_MORE
|
||||
#undef NOOP
|
||||
|
||||
struct BreakpointEntry {
|
||||
KrkCodeObject * inFunction;
|
||||
size_t offset;
|
||||
int flags;
|
||||
uint8_t originalOpcode;
|
||||
};
|
||||
|
||||
#define MAX_BREAKPOINTS 32
|
||||
struct DebuggerState {
|
||||
int breakpointsCount;
|
||||
KrkDebugCallback debuggerHook;
|
||||
|
||||
/* XXX This was previously thread-local; it probably should still be
|
||||
* specific to an individual thread... but we don't really do
|
||||
* much thread debugging, so... */
|
||||
int repeatStack_top;
|
||||
int repeatStack_bottom;
|
||||
int thisWasForced;
|
||||
|
||||
struct BreakpointEntry breakpoints[MAX_BREAKPOINTS];
|
||||
};
|
||||
|
||||
int krk_debug_addBreakpointCodeOffset(KrkCodeObject * target, size_t offset, int flags) {
|
||||
int index = vm.dbgState->breakpointsCount;
|
||||
if (vm.dbgState->breakpointsCount == MAX_BREAKPOINTS) {
|
||||
@ -391,12 +369,6 @@ int krk_debug_enableBreakpoint(int breakIndex) {
|
||||
vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] = OP_BREAKPOINT;
|
||||
return 0;
|
||||
}
|
||||
KRK_Function(enablebreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_enableBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
int krk_debug_disableBreakpoint(int breakIndex) {
|
||||
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
||||
@ -408,12 +380,6 @@ int krk_debug_disableBreakpoint(int breakIndex) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
KRK_Function(disablebreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_disableBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
int krk_debug_removeBreakpoint(int breakIndex) {
|
||||
if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
|
||||
@ -425,61 +391,6 @@ int krk_debug_removeBreakpoint(int breakIndex) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
KRK_Function(delbreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_removeBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(addbreakpoint) {
|
||||
FUNCTION_TAKES_EXACTLY(2);
|
||||
CHECK_ARG(1,int,krk_integer_type,lineNo);
|
||||
|
||||
int flags = KRK_BREAKPOINT_NORMAL;
|
||||
|
||||
if (hasKw) {
|
||||
KrkValue flagsValue = NONE_VAL();
|
||||
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("flags")), &flagsValue)) {
|
||||
if (!IS_INTEGER(flagsValue))
|
||||
return TYPE_ERROR(int,flagsValue);
|
||||
flags = AS_INTEGER(flagsValue);
|
||||
}
|
||||
}
|
||||
|
||||
int result;
|
||||
if (IS_STRING(argv[0])) {
|
||||
result = krk_debug_addBreakpointFileLine(AS_STRING(argv[0]), lineNo, flags);
|
||||
} else {
|
||||
KrkCodeObject * target = NULL;
|
||||
if (IS_CLOSURE(argv[0])) {
|
||||
target = AS_CLOSURE(argv[0])->function;
|
||||
} else if (IS_BOUND_METHOD(argv[0]) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))) {
|
||||
target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))->function;
|
||||
} else if (IS_codeobject(argv[0])) {
|
||||
target = AS_codeobject(argv[0]);
|
||||
} else {
|
||||
return TYPE_ERROR(function or method or filename,argv[0]);
|
||||
}
|
||||
/* Figure out what instruction this should be on */
|
||||
size_t last = 0;
|
||||
for (size_t i = 0; i < target->chunk.linesCount; ++i) {
|
||||
if (target->chunk.lines[i].line > (size_t)lineNo) break;
|
||||
if (target->chunk.lines[i].line == (size_t)lineNo) {
|
||||
last = target->chunk.lines[i].startOffset;
|
||||
break;
|
||||
}
|
||||
last = target->chunk.lines[i].startOffset;
|
||||
}
|
||||
result = krk_debug_addBreakpointCodeOffset(target,last,flags);
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
return krk_runtimeError(vm.exceptions->baseException, "Could not add breakpoint.");
|
||||
|
||||
return INTEGER_VAL(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin debugger utility functions.
|
||||
*
|
||||
@ -616,237 +527,10 @@ int krk_debugBreakpointHandler(void) {
|
||||
return krk_debuggerHook(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* dis.dis(object)
|
||||
*/
|
||||
KRK_Function(dis) {
|
||||
FUNCTION_TAKES_EXACTLY(1);
|
||||
|
||||
if (IS_CLOSURE(argv[0])) {
|
||||
KrkCodeObject * func = AS_CLOSURE(argv[0])->function;
|
||||
krk_disassembleCodeObject(stdout, func, func->name ? func->name->chars : "<unnamed>");
|
||||
} else if (IS_codeobject(argv[0])) {
|
||||
krk_disassembleCodeObject(stdout, AS_codeobject(argv[0]), AS_codeobject(argv[0])->name ? AS_codeobject(argv[0])->name->chars : "<unnamed>");
|
||||
} else if (IS_BOUND_METHOD(argv[0])) {
|
||||
if (AS_BOUND_METHOD(argv[0])->method->type == KRK_OBJ_CLOSURE) {
|
||||
KrkCodeObject * func = ((KrkClosure*)AS_BOUND_METHOD(argv[0])->method)->function;
|
||||
const char * methodName = func->name ? func->name->chars : "<unnamed>";
|
||||
const char * typeName = IS_CLASS(AS_BOUND_METHOD(argv[0])->receiver) ? AS_CLASS(AS_BOUND_METHOD(argv[0])->receiver)->name->chars : krk_typeName(AS_BOUND_METHOD(argv[0])->receiver);
|
||||
size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
|
||||
char * tmp = malloc(allocSize);
|
||||
snprintf(tmp, allocSize, "%s.%s", typeName, methodName);
|
||||
krk_disassembleCodeObject(stdout, func, tmp);
|
||||
free(tmp);
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions->typeError, "Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(argv[0])->receiver);
|
||||
}
|
||||
} else if (IS_CLASS(argv[0])) {
|
||||
KrkValue code;
|
||||
if (krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
|
||||
KrkCodeObject * func = AS_CLOSURE(code)->function;
|
||||
krk_disassembleCodeObject(stdout, func, AS_CLASS(argv[0])->name->chars);
|
||||
}
|
||||
/* TODO Methods! */
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions->typeError, "Don't know how to disassemble '%T'", argv[0]);
|
||||
}
|
||||
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(build) {
|
||||
FUNCTION_TAKES_AT_LEAST(1);
|
||||
FUNCTION_TAKES_AT_MOST(2);
|
||||
CHECK_ARG(0,str,KrkString*,code);
|
||||
char * fileName = "<source>";
|
||||
if (argc > 1) {
|
||||
CHECK_ARG(1,str,KrkString*,filename);
|
||||
fileName = filename->chars;
|
||||
}
|
||||
|
||||
/* Unset module */
|
||||
krk_push(OBJECT_VAL(krk_currentThread.module));
|
||||
KrkInstance * module = krk_currentThread.module;
|
||||
krk_currentThread.module = NULL;
|
||||
KrkCodeObject * c = krk_compile(code->chars,fileName);
|
||||
krk_currentThread.module = module;
|
||||
krk_pop();
|
||||
if (c) return OBJECT_VAL(c);
|
||||
else return NONE_VAL();
|
||||
}
|
||||
|
||||
#define NOOP (void)0
|
||||
#define SIMPLE(opc) case opc: size = 1; break;
|
||||
#define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||
case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
|
||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||
#define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||
case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
|
||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||
#define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
|
||||
size = 3; break; }
|
||||
#define CLOSURE_MORE \
|
||||
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
||||
size_t baseOffset = offset; \
|
||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||
int isLocal = chunk->code[baseOffset++ + size]; \
|
||||
baseOffset++; \
|
||||
if (isLocal & 2) { \
|
||||
baseOffset += 2; \
|
||||
} \
|
||||
} \
|
||||
size += baseOffset - offset;
|
||||
#define EXPAND_ARGS_MORE
|
||||
#define FORMAT_VALUE_MORE
|
||||
#define LOCAL_MORE local = operand;
|
||||
static KrkValue _examineInternal(KrkCodeObject* func) {
|
||||
KrkValue output = krk_list_of(0,NULL,0);
|
||||
krk_push(output);
|
||||
|
||||
KrkChunk * chunk = &func->chunk;
|
||||
size_t offset = 0;
|
||||
while (offset < chunk->count) {
|
||||
uint8_t opcode = chunk->code[offset];
|
||||
size_t size = 0;
|
||||
ssize_t constant = -1;
|
||||
ssize_t jump = 0;
|
||||
ssize_t operand = -1;
|
||||
ssize_t local = -1;
|
||||
switch (opcode) {
|
||||
#include "opcodes.h"
|
||||
}
|
||||
|
||||
KrkTuple * newTuple = krk_newTuple(3);
|
||||
krk_push(OBJECT_VAL(newTuple));
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(opcode);
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(size);
|
||||
if (constant != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = chunk->constants.values[constant];
|
||||
} else if (jump != 0) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
|
||||
} else if (local != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
|
||||
for (size_t i = 0; i < func->localNameCount; ++i) {
|
||||
if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
|
||||
newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (operand != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand);
|
||||
} else {
|
||||
newTuple->values.values[newTuple->values.count++] = NONE_VAL();
|
||||
}
|
||||
krk_writeValueArray(AS_LIST(output), krk_peek(0));
|
||||
krk_pop();
|
||||
|
||||
if (size == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return krk_pop();
|
||||
}
|
||||
KRK_Function(examine) {
|
||||
FUNCTION_TAKES_EXACTLY(1);
|
||||
CHECK_ARG(0,codeobject,KrkCodeObject*,func);
|
||||
return _examineInternal(func);
|
||||
}
|
||||
|
||||
#undef SIMPLE
|
||||
#undef OPERANDB
|
||||
#undef OPERAND
|
||||
#undef CONSTANT
|
||||
#undef JUMP
|
||||
#undef CLOSURE_MORE
|
||||
#undef LOCAL_MORE
|
||||
#undef EXPAND_ARGS_MORE
|
||||
#undef FORMAT_VALUE_MORE
|
||||
|
||||
void krk_module_init_dis(void) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_attachNamedObject(&vm.modules, "dis", (KrkObj*)module);
|
||||
krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("dis"));
|
||||
krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
|
||||
|
||||
void krk_debug_init(void) {
|
||||
vm.dbgState = calloc(1, sizeof(struct DebuggerState));
|
||||
vm.dbgState->repeatStack_top = -1;
|
||||
vm.dbgState->repeatStack_bottom = -1;
|
||||
|
||||
KRK_DOC(module,
|
||||
"@brief Provides tools for disassembling bytecode.\n\n"
|
||||
"### Code Disassembly in Kuroko\n\n"
|
||||
"The @c dis module contains functions for dealing with _code objects_ which "
|
||||
"represent the compiled bytecode of a Kuroko function. The bytecode compilation "
|
||||
"process is entirely static and bytecode analysis can be performed without calling "
|
||||
"into the VM to run dynamic code.\n\n"
|
||||
"### Debugger Breakpoints\n\n"
|
||||
"Kuroko interpreters can provide a debugger hook through the C API's "
|
||||
"@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
|
||||
"from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
|
||||
"@ref enablebreakpoint, and @ref disablebreakpoint methods."
|
||||
);
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, dis),
|
||||
"@brief Disassemble an object.\n"
|
||||
"@arguments obj\n\n"
|
||||
"Dumps a disassembly of the bytecode in the code object associated with @p obj. "
|
||||
"If @p obj can not be disassembled, a @ref TypeError is raised.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, build),
|
||||
"@brief Compile a string to a code object.\n"
|
||||
"@arguments code\n\n"
|
||||
"Compiles the string @p code and returns a code object. If a syntax "
|
||||
"error is encountered, it will be raised.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, examine),
|
||||
"@brief Convert a code object to a list of instructions.\n"
|
||||
"@arguments func\n\n"
|
||||
"Examines the code object @p func and returns a list representation of its instructions. "
|
||||
"Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
|
||||
"the operand of the argument, either as an integer for jump offsets, the actual value for "
|
||||
"constant operands, or the name of a local or global variable if available.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, addbreakpoint),
|
||||
"@brief Attach a breakpoint to a code object.\n"
|
||||
"@arguments func, line\n\n"
|
||||
"@p func may be a filename string, or a function, method, or code object. Returns "
|
||||
"the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, delbreakpoint),
|
||||
"@brief Delete a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
|
||||
"May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, enablebreakpoint),
|
||||
"@brief Enable a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||
"@p handle is not a valid breakpoint handle.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, disablebreakpoint),
|
||||
"@brief Disable a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||
"@p handle is not a valid breakpoint handle.");
|
||||
|
||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_ONCE", INTEGER_VAL(KRK_BREAKPOINT_ONCE));
|
||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_REPEAT", INTEGER_VAL(KRK_BREAKPOINT_REPEAT));
|
||||
|
||||
#define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
|
||||
#define SIMPLE(opc) OPCODE(opc)
|
||||
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||
#define JUMP(opc,sign) OPCODE(opc)
|
||||
#include "opcodes.h"
|
||||
#undef SIMPLE
|
||||
#undef OPERANDB
|
||||
#undef OPERAND
|
||||
#undef CONSTANT
|
||||
#undef JUMP
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -206,6 +206,11 @@ extern int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject ** funcOut,
|
||||
*/
|
||||
extern void krk_debug_dumpStack(FILE * f, KrkCallFrame * frame);
|
||||
|
||||
/**
|
||||
* @brief Initialize debugger state. Call exactly once per VM.
|
||||
*/
|
||||
extern void krk_debug_init(void);
|
||||
|
||||
/**
|
||||
* @def KRK_BREAKPOINT_NORMAL
|
||||
*
|
||||
|
349
src/modules/module_dis.c
Normal file
349
src/modules/module_dis.c
Normal file
@ -0,0 +1,349 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <kuroko/debug.h>
|
||||
#include <kuroko/vm.h>
|
||||
#include <kuroko/util.h>
|
||||
#include <kuroko/compiler.h>
|
||||
|
||||
#include "../private.h"
|
||||
#include "../opcode_enum.h"
|
||||
|
||||
#ifndef KRK_DISABLE_DEBUG
|
||||
|
||||
KRK_Function(enablebreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_enableBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(disablebreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_disableBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(delbreakpoint) {
|
||||
CHECK_ARG(0,int,krk_integer_type,breakIndex);
|
||||
if (krk_debug_removeBreakpoint(breakIndex))
|
||||
return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(addbreakpoint) {
|
||||
FUNCTION_TAKES_EXACTLY(2);
|
||||
CHECK_ARG(1,int,krk_integer_type,lineNo);
|
||||
|
||||
int flags = KRK_BREAKPOINT_NORMAL;
|
||||
|
||||
if (hasKw) {
|
||||
KrkValue flagsValue = NONE_VAL();
|
||||
if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("flags")), &flagsValue)) {
|
||||
if (!IS_INTEGER(flagsValue))
|
||||
return TYPE_ERROR(int,flagsValue);
|
||||
flags = AS_INTEGER(flagsValue);
|
||||
}
|
||||
}
|
||||
|
||||
int result;
|
||||
if (IS_STRING(argv[0])) {
|
||||
result = krk_debug_addBreakpointFileLine(AS_STRING(argv[0]), lineNo, flags);
|
||||
} else {
|
||||
KrkCodeObject * target = NULL;
|
||||
if (IS_CLOSURE(argv[0])) {
|
||||
target = AS_CLOSURE(argv[0])->function;
|
||||
} else if (IS_BOUND_METHOD(argv[0]) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))) {
|
||||
target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))->function;
|
||||
} else if (IS_codeobject(argv[0])) {
|
||||
target = AS_codeobject(argv[0]);
|
||||
} else {
|
||||
return TYPE_ERROR(function or method or filename,argv[0]);
|
||||
}
|
||||
/* Figure out what instruction this should be on */
|
||||
size_t last = 0;
|
||||
for (size_t i = 0; i < target->chunk.linesCount; ++i) {
|
||||
if (target->chunk.lines[i].line > (size_t)lineNo) break;
|
||||
if (target->chunk.lines[i].line == (size_t)lineNo) {
|
||||
last = target->chunk.lines[i].startOffset;
|
||||
break;
|
||||
}
|
||||
last = target->chunk.lines[i].startOffset;
|
||||
}
|
||||
result = krk_debug_addBreakpointCodeOffset(target,last,flags);
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
return krk_runtimeError(vm.exceptions->baseException, "Could not add breakpoint.");
|
||||
|
||||
return INTEGER_VAL(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* dis.dis(object)
|
||||
*/
|
||||
KRK_Function(dis) {
|
||||
FUNCTION_TAKES_EXACTLY(1);
|
||||
|
||||
if (IS_CLOSURE(argv[0])) {
|
||||
KrkCodeObject * func = AS_CLOSURE(argv[0])->function;
|
||||
krk_disassembleCodeObject(stdout, func, func->name ? func->name->chars : "<unnamed>");
|
||||
} else if (IS_codeobject(argv[0])) {
|
||||
krk_disassembleCodeObject(stdout, AS_codeobject(argv[0]), AS_codeobject(argv[0])->name ? AS_codeobject(argv[0])->name->chars : "<unnamed>");
|
||||
} else if (IS_BOUND_METHOD(argv[0])) {
|
||||
if (AS_BOUND_METHOD(argv[0])->method->type == KRK_OBJ_CLOSURE) {
|
||||
KrkCodeObject * func = ((KrkClosure*)AS_BOUND_METHOD(argv[0])->method)->function;
|
||||
const char * methodName = func->name ? func->name->chars : "<unnamed>";
|
||||
const char * typeName = IS_CLASS(AS_BOUND_METHOD(argv[0])->receiver) ? AS_CLASS(AS_BOUND_METHOD(argv[0])->receiver)->name->chars : krk_typeName(AS_BOUND_METHOD(argv[0])->receiver);
|
||||
size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
|
||||
char * tmp = malloc(allocSize);
|
||||
snprintf(tmp, allocSize, "%s.%s", typeName, methodName);
|
||||
krk_disassembleCodeObject(stdout, func, tmp);
|
||||
free(tmp);
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions->typeError, "Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(argv[0])->receiver);
|
||||
}
|
||||
} else if (IS_CLASS(argv[0])) {
|
||||
KrkValue code;
|
||||
if (krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
|
||||
KrkCodeObject * func = AS_CLOSURE(code)->function;
|
||||
krk_disassembleCodeObject(stdout, func, AS_CLASS(argv[0])->name->chars);
|
||||
}
|
||||
/* TODO Methods! */
|
||||
} else {
|
||||
krk_runtimeError(vm.exceptions->typeError, "Don't know how to disassemble '%T'", argv[0]);
|
||||
}
|
||||
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KRK_Function(build) {
|
||||
FUNCTION_TAKES_AT_LEAST(1);
|
||||
FUNCTION_TAKES_AT_MOST(2);
|
||||
CHECK_ARG(0,str,KrkString*,code);
|
||||
char * fileName = "<source>";
|
||||
if (argc > 1) {
|
||||
CHECK_ARG(1,str,KrkString*,filename);
|
||||
fileName = filename->chars;
|
||||
}
|
||||
|
||||
/* Unset module */
|
||||
krk_push(OBJECT_VAL(krk_currentThread.module));
|
||||
KrkInstance * module = krk_currentThread.module;
|
||||
krk_currentThread.module = NULL;
|
||||
KrkCodeObject * c = krk_compile(code->chars,fileName);
|
||||
krk_currentThread.module = module;
|
||||
krk_pop();
|
||||
if (c) return OBJECT_VAL(c);
|
||||
else return NONE_VAL();
|
||||
}
|
||||
|
||||
#define NOOP (void)0
|
||||
#define SIMPLE(opc) case opc: size = 1; break;
|
||||
#define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||
case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
|
||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||
#define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
|
||||
case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
|
||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
|
||||
#define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
|
||||
size = 3; break; }
|
||||
#define CLOSURE_MORE \
|
||||
KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
|
||||
size_t baseOffset = offset; \
|
||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||
int isLocal = chunk->code[baseOffset++ + size]; \
|
||||
baseOffset++; \
|
||||
if (isLocal & 2) { \
|
||||
baseOffset += 2; \
|
||||
} \
|
||||
} \
|
||||
size += baseOffset - offset;
|
||||
#define EXPAND_ARGS_MORE
|
||||
#define FORMAT_VALUE_MORE
|
||||
#define LOCAL_MORE local = operand;
|
||||
static KrkValue _examineInternal(KrkCodeObject* func) {
|
||||
KrkValue output = krk_list_of(0,NULL,0);
|
||||
krk_push(output);
|
||||
|
||||
KrkChunk * chunk = &func->chunk;
|
||||
size_t offset = 0;
|
||||
while (offset < chunk->count) {
|
||||
uint8_t opcode = chunk->code[offset];
|
||||
size_t size = 0;
|
||||
ssize_t constant = -1;
|
||||
ssize_t jump = 0;
|
||||
ssize_t operand = -1;
|
||||
ssize_t local = -1;
|
||||
switch (opcode) {
|
||||
#include "opcodes.h"
|
||||
}
|
||||
|
||||
KrkTuple * newTuple = krk_newTuple(3);
|
||||
krk_push(OBJECT_VAL(newTuple));
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(opcode);
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(size);
|
||||
if (constant != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = chunk->constants.values[constant];
|
||||
} else if (jump != 0) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
|
||||
} else if (local != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
|
||||
for (size_t i = 0; i < func->localNameCount; ++i) {
|
||||
if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
|
||||
newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (operand != -1) {
|
||||
newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand);
|
||||
} else {
|
||||
newTuple->values.values[newTuple->values.count++] = NONE_VAL();
|
||||
}
|
||||
krk_writeValueArray(AS_LIST(output), krk_peek(0));
|
||||
krk_pop();
|
||||
|
||||
if (size == 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
return krk_pop();
|
||||
}
|
||||
KRK_Function(examine) {
|
||||
FUNCTION_TAKES_EXACTLY(1);
|
||||
CHECK_ARG(0,codeobject,KrkCodeObject*,func);
|
||||
return _examineInternal(func);
|
||||
}
|
||||
|
||||
#undef SIMPLE
|
||||
#undef OPERANDB
|
||||
#undef OPERAND
|
||||
#undef CONSTANT
|
||||
#undef JUMP
|
||||
#undef CLOSURE_MORE
|
||||
#undef LOCAL_MORE
|
||||
#undef EXPAND_ARGS_MORE
|
||||
#undef FORMAT_VALUE_MORE
|
||||
|
||||
#endif
|
||||
|
||||
KrkValue krk_module_onload_dis(KrkString * runAs) {
|
||||
#ifndef KRK_DISABLE_DEBUG
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
KRK_DOC(module,
|
||||
"@brief Provides tools for disassembling bytecode.\n\n"
|
||||
"### Code Disassembly in Kuroko\n\n"
|
||||
"The @c dis module contains functions for dealing with _code objects_ which "
|
||||
"represent the compiled bytecode of a Kuroko function. The bytecode compilation "
|
||||
"process is entirely static and bytecode analysis can be performed without calling "
|
||||
"into the VM to run dynamic code.\n\n"
|
||||
"### Debugger Breakpoints\n\n"
|
||||
"Kuroko interpreters can provide a debugger hook through the C API's "
|
||||
"@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
|
||||
"from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
|
||||
"@ref enablebreakpoint, and @ref disablebreakpoint methods."
|
||||
);
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, dis),
|
||||
"@brief Disassemble an object.\n"
|
||||
"@arguments obj\n\n"
|
||||
"Dumps a disassembly of the bytecode in the code object associated with @p obj. "
|
||||
"If @p obj can not be disassembled, a @ref TypeError is raised.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, build),
|
||||
"@brief Compile a string to a code object.\n"
|
||||
"@arguments code\n\n"
|
||||
"Compiles the string @p code and returns a code object. If a syntax "
|
||||
"error is encountered, it will be raised.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, examine),
|
||||
"@brief Convert a code object to a list of instructions.\n"
|
||||
"@arguments func\n\n"
|
||||
"Examines the code object @p func and returns a list representation of its instructions. "
|
||||
"Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
|
||||
"the operand of the argument, either as an integer for jump offsets, the actual value for "
|
||||
"constant operands, or the name of a local or global variable if available.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, addbreakpoint),
|
||||
"@brief Attach a breakpoint to a code object.\n"
|
||||
"@arguments func, line\n\n"
|
||||
"@p func may be a filename string, or a function, method, or code object. Returns "
|
||||
"the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, delbreakpoint),
|
||||
"@brief Delete a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
|
||||
"May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, enablebreakpoint),
|
||||
"@brief Enable a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||
"@p handle is not a valid breakpoint handle.");
|
||||
|
||||
KRK_DOC(BIND_FUNC(module, disablebreakpoint),
|
||||
"@brief Disable a breakpoint.\n"
|
||||
"@arguments handle\n\n"
|
||||
"Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
|
||||
"@p handle is not a valid breakpoint handle.");
|
||||
|
||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_ONCE", INTEGER_VAL(KRK_BREAKPOINT_ONCE));
|
||||
krk_attachNamedValue(&module->fields, "BREAKPOINT_REPEAT", INTEGER_VAL(KRK_BREAKPOINT_REPEAT));
|
||||
|
||||
#define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
|
||||
#define SIMPLE(opc) OPCODE(opc)
|
||||
#define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||
#define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
|
||||
#define JUMP(opc,sign) OPCODE(opc)
|
||||
#include "opcodes.h"
|
||||
#undef SIMPLE
|
||||
#undef OPERANDB
|
||||
#undef OPERAND
|
||||
#undef CONSTANT
|
||||
#undef JUMP
|
||||
|
||||
if (runAs && !strcmp(runAs->chars,"__main__")) {
|
||||
/* Force `dis` into the module table early */
|
||||
krk_attachNamedObject(&vm.modules, "dis", (KrkObj*)module);
|
||||
/* Start executing additional code */
|
||||
krk_startModule("_dis");
|
||||
krk_interpret(
|
||||
"import dis\n"
|
||||
"print(dir(dis))\n"
|
||||
"def disrec(code, seen):\n"
|
||||
" let next = [code]\n"
|
||||
" while next:\n"
|
||||
" let co = next[0]\n"
|
||||
" next = next[1:]\n"
|
||||
" dis.dis(co)\n"
|
||||
" for inst,size,operand in dis.examine(co):\n"
|
||||
" if isinstance(operand,codeobject) and operand not in seen and operand not in next:\n"
|
||||
" next.append(operand)\n"
|
||||
" if next:\n"
|
||||
" print()\n"
|
||||
"import kuroko\n"
|
||||
"if (len(kuroko.argv) < 2):\n"
|
||||
" print(\"Usage: kuroko -m dis FILE\")\n"
|
||||
" return 1\n"
|
||||
"import fileio\n"
|
||||
"for file in kuroko.argv[1:]:\n"
|
||||
" with fileio.open(file,'r') as f:\n"
|
||||
" let result = dis.build(f.read(), file)\n"
|
||||
" disrec(result,set())\n",
|
||||
"_dis"
|
||||
);
|
||||
}
|
||||
|
||||
return krk_pop();
|
||||
#else
|
||||
return krk_runtimeError(vm.exceptions->notImplementedError, "debugger support is disabled");
|
||||
#endif
|
||||
}
|
@ -148,7 +148,7 @@ MATH_IS(isnan)
|
||||
|
||||
#define bind(name) krk_defineNative(&module->fields, #name, _math_ ## name)
|
||||
|
||||
KrkValue krk_module_onload_math(void) {
|
||||
KrkValue krk_module_onload_math(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -617,7 +617,7 @@ KRK_Method(stat_result,__repr__) {
|
||||
return krk_pop();
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_os(void) {
|
||||
KrkValue krk_module_onload_os(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -58,7 +58,7 @@ KRK_Function(seed) {
|
||||
return NONE_VAL();
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_random(void) {
|
||||
KrkValue krk_module_onload_random(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -513,7 +513,7 @@ KRK_Method(socket,proto) {
|
||||
return INTEGER_VAL(self->proto);
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_socket(void) {
|
||||
KrkValue krk_module_onload_socket(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -40,7 +40,7 @@ KRK_Function(S_ISSOCK) {
|
||||
}
|
||||
#endif
|
||||
|
||||
KrkValue krk_module_onload_stat(void) {
|
||||
KrkValue krk_module_onload_stat(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -38,7 +38,7 @@ KRK_Function(time) {
|
||||
return FLOATING_VAL(out);
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_time(void) {
|
||||
KrkValue krk_module_onload_time(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
KRK_DOC(module, "@brief Provides timekeeping functions.");
|
||||
|
@ -35,7 +35,7 @@ KRK_Function(timeit) {
|
||||
return FLOATING_VAL(after-before);
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_timeit(void) {
|
||||
KrkValue krk_module_onload_timeit(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
|
||||
|
@ -17,7 +17,7 @@ KRK_Function(wcwidth) {
|
||||
return INTEGER_VAL(wcwidth(codepoint));
|
||||
}
|
||||
|
||||
KrkValue krk_module_onload_wcwidth(void) {
|
||||
KrkValue krk_module_onload_wcwidth(KrkString * runAs) {
|
||||
KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
|
||||
krk_push(OBJECT_VAL(module));
|
||||
KRK_DOC(module, "Character widths.");
|
||||
|
@ -67,3 +67,28 @@ struct ParsedFormatSpec {
|
||||
* This is the "sdbm" hash. I've been using it in various places for many years,
|
||||
* and this specific version apparently traces to gawk. */
|
||||
#define krk_hash_advance(hash,c) do { hash = (int)(c) + (hash << 6) + (hash << 16) - hash; } while (0)
|
||||
|
||||
#ifndef KRK_DISABLE_DEBUG
|
||||
#include <kuroko/debug.h>
|
||||
struct BreakpointEntry {
|
||||
KrkCodeObject * inFunction;
|
||||
size_t offset;
|
||||
int flags;
|
||||
uint8_t originalOpcode;
|
||||
};
|
||||
|
||||
#define MAX_BREAKPOINTS 32
|
||||
struct DebuggerState {
|
||||
int breakpointsCount;
|
||||
KrkDebugCallback debuggerHook;
|
||||
|
||||
/* XXX This was previously thread-local; it probably should still be
|
||||
* specific to an individual thread... but we don't really do
|
||||
* much thread debugging, so... */
|
||||
int repeatStack_top;
|
||||
int repeatStack_bottom;
|
||||
int thisWasForced;
|
||||
|
||||
struct BreakpointEntry breakpoints[MAX_BREAKPOINTS];
|
||||
};
|
||||
#endif
|
||||
|
7
src/vm.c
7
src/vm.c
@ -971,14 +971,15 @@ void krk_initVM(int flags) {
|
||||
krk_module_init_gc();
|
||||
krk_module_init_fileio();
|
||||
#endif
|
||||
#ifndef KRK_DISABLE_DEBUG
|
||||
krk_module_init_dis();
|
||||
#endif
|
||||
#ifndef KRK_DISABLE_THREADS
|
||||
krk_module_init_threading();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef KRK_DISABLE_DEBUG
|
||||
krk_debug_init();
|
||||
#endif
|
||||
|
||||
|
||||
/* The VM is now ready to start executing code. */
|
||||
krk_resetStack();
|
||||
|
Loading…
Reference in New Issue
Block a user