9 #include "../private.h"
10 #include "../opcode_enum.h"
12 #ifndef KRK_DISABLE_DEBUG
14 KRK_Function(enablebreakpoint) {
16 if (!
krk_parseArgs(
"i",(
const char*[]){
"breakpoint"}, &breakIndex))
return NONE_VAL();
22 KRK_Function(disablebreakpoint) {
24 if (!
krk_parseArgs(
"i",(
const char*[]){
"breakpoint"}, &breakIndex))
return NONE_VAL();
30 KRK_Function(delbreakpoint) {
32 if (!
krk_parseArgs(
"i",(
const char*[]){
"breakpoint"}, &breakIndex))
return NONE_VAL();
38 KRK_Function(addbreakpoint) {
42 if (!
krk_parseArgs(
"Vi|i",(
const char*[]){
"func",
"lineno",
"flags"}, &func, &lineNo, &flags))
return NONE_VAL();
45 if (IS_STRING(func)) {
49 if (IS_CLOSURE(func)) {
50 target = AS_CLOSURE(func)->function;
51 }
else if (IS_BOUND_METHOD(func) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))) {
52 target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))->function;
53 }
else if (IS_codeobject(func)) {
54 target = AS_codeobject(func);
56 return TYPE_ERROR(
function or method or filename,func);
60 for (
size_t i = 0; i < target->
chunk.linesCount; ++i) {
61 if (target->
chunk.lines[i].line > (
size_t)lineNo)
break;
62 if (target->
chunk.lines[i].line == (
size_t)lineNo) {
63 last = target->
chunk.lines[i].startOffset;
66 last = target->
chunk.lines[i].startOffset;
74 return INTEGER_VAL(result);
83 if (!
krk_parseArgs(
"V",(
const char*[]){
"func"},&funcVal))
return NONE_VAL();
85 if (IS_CLOSURE(funcVal)) {
88 }
else if (IS_codeobject(funcVal)) {
89 krk_disassembleCodeObject(stdout, AS_codeobject(funcVal), AS_codeobject(funcVal)->name ? AS_codeobject(funcVal)->name->chars :
"<unnamed>");
90 }
else if (IS_BOUND_METHOD(funcVal)) {
91 if (AS_BOUND_METHOD(funcVal)->method->type == KRK_OBJ_CLOSURE) {
93 const char * methodName = func->
name ? func->
name->
chars :
"<unnamed>";
94 const char * typeName = IS_CLASS(AS_BOUND_METHOD(funcVal)->receiver) ? AS_CLASS(AS_BOUND_METHOD(funcVal)->receiver)->name->chars :
krk_typeName(AS_BOUND_METHOD(funcVal)->receiver);
95 size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
96 char * tmp = malloc(allocSize);
97 snprintf(tmp, allocSize,
"%s.%s", typeName, methodName);
101 krk_runtimeError(
vm.exceptions->typeError,
"Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(funcVal)->receiver);
103 }
else if (IS_CLASS(funcVal)) {
105 if (
krk_tableGet(&AS_CLASS(funcVal)->methods,
vm.specialMethodNames[METHOD_FUNC], &code) && IS_CLOSURE(code)) {
111 krk_runtimeError(
vm.exceptions->typeError,
"Don't know how to disassemble '%T'", funcVal);
117 KRK_Function(build) {
119 char * fileName =
"<source>";
120 if (!
krk_parseArgs(
"s|s", (
const char*[]){
"code",
"filename"}, &code, &fileName))
return NONE_VAL();
129 if (c)
return OBJECT_VAL(c);
130 else return NONE_VAL();
134 #define SIMPLE(opc) case opc: size = 1; break;
135 #define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
136 case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
137 (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
138 #define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
139 case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
140 (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
141 #define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
143 #define COMPLICATED(opc,more) case opc: size = 1; more; break;
144 #define OVERLONG_JUMP_MORE size = 3; jump = (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])
145 #define CLOSURE_MORE \
146 KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
147 size_t baseOffset = offset; \
148 for (size_t j = 0; j < function->upvalueCount; ++j) { \
149 int isLocal = chunk->code[baseOffset++ + size]; \
155 size += baseOffset - offset;
156 #define EXPAND_ARGS_MORE
157 #define FORMAT_VALUE_MORE
158 #define LOCAL_MORE local = operand;
165 while (offset < chunk->count) {
166 uint8_t opcode = chunk->code[offset];
168 ssize_t constant = -1;
170 ssize_t operand = -1;
180 if (constant != -1) {
182 }
else if (jump != 0) {
184 }
else if (local != -1) {
192 }
else if (operand != -1) {
209 KRK_Function(examine) {
211 if (!
krk_parseArgs(
"O!",(
const char*[]){
"func"}, KRK_BASE_CLASS(codeobject), &func))
return NONE_VAL();
212 return _examineInternal(func);
221 #undef OVERLONG_JUMP_MORE
224 #undef EXPAND_ARGS_MORE
225 #undef FORMAT_VALUE_MORE
227 KRK_Function(ip_to_expression) {
231 if (!
krk_parseArgs(
"VN",(
const char*[]){
"func",
"ip"}, &func, &ip))
return NONE_VAL();
235 if (IS_CLOSURE(func)) actual = AS_CLOSURE(func)->function;
236 else if (IS_codeobject(func)) actual = AS_codeobject(func);
237 else if (IS_BOUND_METHOD(func) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(func)->method))) actual = ((
KrkClosure*)AS_BOUND_METHOD(func)->method)->
function;
238 else return krk_runtimeError(
vm.exceptions->typeError,
"func must be a managed function, method, or codeobject, not '%T'", func);
241 uint8_t start, midStart, midEnd, end;
262 #ifndef KRK_DISABLE_DEBUG
264 "@brief Provides tools for disassembling bytecode.\n\n"
265 "### Code Disassembly in Kuroko\n\n"
266 "The @c dis module contains functions for dealing with _code objects_ which "
267 "represent the compiled bytecode of a Kuroko function. The bytecode compilation "
268 "process is entirely static and bytecode analysis can be performed without calling "
269 "into the VM to run dynamic code.\n\n"
270 "### Debugger Breakpoints\n\n"
271 "Kuroko interpreters can provide a debugger hook through the C API's "
272 "@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
273 "from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
274 "@ref enablebreakpoint, and @ref disablebreakpoint methods."
277 KRK_DOC(BIND_FUNC(module, dis),
278 "@brief Disassemble an object.\n"
280 "Dumps a disassembly of the bytecode in the code object associated with @p obj. "
281 "If @p obj can not be disassembled, a @ref TypeError is raised.");
283 KRK_DOC(BIND_FUNC(module, build),
284 "@brief Compile a string to a code object.\n"
285 "@arguments code\n\n"
286 "Compiles the string @p code and returns a code object. If a syntax "
287 "error is encountered, it will be raised.");
289 KRK_DOC(BIND_FUNC(module, examine),
290 "@brief Convert a code object to a list of instructions.\n"
291 "@arguments func\n\n"
292 "Examines the code object @p func and returns a list representation of its instructions. "
293 "Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
294 "the operand of the argument, either as an integer for jump offsets, the actual value for "
295 "constant operands, or the name of a local or global variable if available.");
297 KRK_DOC(BIND_FUNC(module, addbreakpoint),
298 "@brief Attach a breakpoint to a code object.\n"
299 "@arguments func, line\n\n"
300 "@p func may be a filename string, or a function, method, or code object. Returns "
301 "the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
303 KRK_DOC(BIND_FUNC(module, delbreakpoint),
304 "@brief Delete a breakpoint.\n"
305 "@arguments handle\n\n"
306 "Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
307 "May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
309 KRK_DOC(BIND_FUNC(module, enablebreakpoint),
310 "@brief Enable a breakpoint.\n"
311 "@arguments handle\n\n"
312 "Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
313 "@p handle is not a valid breakpoint handle.");
315 KRK_DOC(BIND_FUNC(module, disablebreakpoint),
316 "@brief Disable a breakpoint.\n"
317 "@arguments handle\n\n"
318 "Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
319 "@p handle is not a valid breakpoint handle.");
321 KRK_DOC(BIND_FUNC(module, ip_to_expression),
322 "@brief Map an IP in a codeobject or function to an expression span.\n"
323 "@arguments func,ip\n\n"
324 "For various reasons, the instruction pointer @p ip must be the last byte of an opcode.");
329 #define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
330 #define SIMPLE(opc) OPCODE(opc)
331 #define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
332 #define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
333 #define JUMP(opc,sign) OPCODE(opc)
334 #define COMPLICATED(opc,more) OPCODE(opc)
343 if (runAs && !strcmp(runAs->chars,
"__main__")) {
350 "def disrec(code, seen):\n"
351 " let next = [code]\n"
353 " let co = next[0]\n"
356 " for inst,size,operand in dis.examine(co):\n"
357 " if isinstance(operand,codeobject) and operand not in seen and operand not in next:\n"
358 " next.append(operand)\n"
362 "if (len(kuroko.argv) < 2):\n"
363 " print(\"Usage: kuroko -m dis FILE\")\n"
366 "for file in kuroko.argv[1:]:\n"
367 " with fileio.open(file,'r') as f:\n"
368 " let result = dis.build(f.read(), file)\n"
369 " disrec(result,set())\n",
KrkCodeObject * krk_compile(const char *src, const char *fileName)
Compile a source string to bytecode.
Exported methods for the source compiler.
Functions for debugging bytecode execution.
#define KRK_BREAKPOINT_ONCE
int krk_debug_addBreakpointCodeOffset(KrkCodeObject *codeObject, size_t offset, int flags)
Add a breakpoint to the given code object.
int krk_debug_enableBreakpoint(int breakpointId)
Enable a breakpoint.
int krk_debug_removeBreakpoint(int breakpointId)
Remove a breakpoint from the breakpoint table.
int krk_debug_disableBreakpoint(int breakpointId)
Disable a breakpoint.
int krk_debug_addBreakpointFileLine(KrkString *filename, size_t line, int flags)
Add a breakpoint to the given line of a file.
#define KRK_BREAKPOINT_REPEAT
void krk_disassembleCodeObject(FILE *f, KrkCodeObject *func, const char *name)
Print a disassembly of 'func' to the stream 'f'.
#define KRK_BREAKPOINT_NORMAL
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Opcode chunk of a code object.
size_t krk_lineNumber(KrkChunk *chunk, size_t offset)
Obtain the line number for a byte offset into a bytecode chunk.
KrkChunk chunk
Bytecode data.
KrkLocalEntry * localNames
Stores the names of local variables used in the function, for debugging.
int krk_debug_expressionUnderline(const KrkCodeObject *codeobject, uint8_t *start, uint8_t *midStart, uint8_t *endStart, uint8_t *end, size_t instruction)
Extract expression mapping from chunk.
size_t localNameCount
Number of entries in localNames.
KrkString * name
Name of the function.
KrkTable fields
Attributes table.
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
size_t birthday
Instruction offset that this local name became valid on.
KrkString * name
Name of the local.
size_t id
Local ID as used by opcodes; offset from the frame's stack base.
size_t deathday
Instruction offset that this local name becomes invalid on.
The most basic object type.
char * chars
UTF8 canonical data.
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Immutable sequence of arbitrary values.
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Stack reference or primative value.
const char * krk_typeName(KrkValue value)
Get the name of the type of a value.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define vm
Convenience macro for namespacing.
KrkValue krk_pop(void)
Pop the top of the stack.
KrkValue krk_interpret(const char *src, const char *fromFile)
Compile and execute a source code input.
void krk_push(KrkValue value)
Push a stack value.
KrkInstance * krk_startModule(const char *name)
Set up a new module object in the current thread.
KrkValue krk_peek(int distance)
Peek down from the top of the stack.