debug.c
1 #include <stdio.h>
2 #include <string.h>
3 
4 #include <kuroko/debug.h>
5 #include <kuroko/vm.h>
6 #include <kuroko/util.h>
7 #include <kuroko/compiler.h>
8 
9 #include "private.h"
10 #include "opcode_enum.h"
11 
12 #ifndef KRK_DISABLE_DEBUG
13 #define NOOP (void)0
14 
22 void krk_debug_dumpStack(FILE * file, KrkCallFrame * frame) {
23  size_t i = 0;
24  if (!frame) frame = &krk_currentThread.frames[krk_currentThread.frameCount-1];
25  for (KrkValue * slot = krk_currentThread.stack; slot < krk_currentThread.stackTop; slot++) {
26  fprintf(file, "[%c", frame->slots == i ? '*' : ' ');
27 
28  for (size_t x = krk_currentThread.frameCount; x > 0; x--) {
29  if (krk_currentThread.frames[x-1].slots > i) continue;
31  size_t relative = i - f->slots;
32 
33  /* Figure out the name of this value */
34  int found = 0;
35  for (size_t j = 0; j < f->closure->function->localNameCount; ++j) {
36  if (relative == f->closure->function->localNames[j].id
37  /* Only display this name if it's currently valid */
38  && f->closure->function->localNames[j].birthday <= (size_t)(f->ip - f->closure->function->chunk.code)
39  && f->closure->function->localNames[j].deathday >= (size_t)(f->ip - f->closure->function->chunk.code)
40  ) {
41  fprintf(file, "%s=", f->closure->function->localNames[j].name->chars);
42  found = 1;
43  break;
44  }
45  }
46  if (found) break;
47  }
48 
49  krk_printValueSafe(file, *slot);
50  fprintf(file, " ]");
51  i++;
52  }
53  if (i == frame->slots) {
54  fprintf(file, " * ");
55  }
56  fprintf(file, "\n");
57 }
58 
59 
60 void krk_disassembleCodeObject(FILE * f, KrkCodeObject * func, const char * name) {
61  KrkChunk * chunk = &func->chunk;
62  /* Function header */
63  fprintf(f, "<%s(", name);
64  int j = 0;
65  for (int i = 0; i < func->potentialPositionals; ++i) {
66  fprintf(f,"%s",func->localNames[j].name->chars);
67  if (j + 1 < func->totalArguments) fprintf(f,",");
68  j++;
69  }
70  if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS) {
71  fprintf(f,"*%s",func->localNames[j].name->chars);
72  if (j + 1 < func->totalArguments) fprintf(f,",");
73  j++;
74  }
75  for (int i = 0; i < func->keywordArgs; ++i) {
76  fprintf(f,"%s=",func->localNames[j].name->chars);
77  if (j + 1 < func->totalArguments) fprintf(f,",");
78  j++;
79  }
80  if (func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_KWS) {
81  fprintf(f,"**%s",func->localNames[j].name->chars);
82  }
83  fprintf(f, ") from %s>\n", chunk->filename->chars);
84  for (size_t offset = 0; offset < chunk->count;) {
85  offset = krk_disassembleInstruction(f, func, offset);
86  }
87 }
88 
89 static inline const char * opcodeClean(const char * opc) {
90  return &opc[3];
91 }
92 
93 static int isJumpTarget(KrkCodeObject * func, size_t startPoint) {
94  KrkChunk * chunk = &func->chunk;
95  size_t offset = 0;
96 
97 #define SIMPLE(opc) case opc: size = 1; break;
98 #define CONSTANT(opc,more) case opc: { size_t constant __attribute__((unused)) = chunk->code[offset + 1]; size = 2; more; break; } \
99  case opc ## _LONG: { size_t constant __attribute__((unused)) = (chunk->code[offset + 1] << 16) | \
100  (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
101 #define OPERANDB(opc,more) case opc: { size = 2; more; break; }
102 #define OPERAND(opc,more) OPERANDB(opc,more) \
103  case opc ## _LONG: { size = 4; more; break; }
104 #define JUMP(opc,sign) case opc: { uint16_t jump = (chunk->code[offset + 1] << 8) | (chunk->code[offset + 2]); \
105  if ((size_t)(offset + 3 sign jump) == startPoint) return 1; \
106  size = 3; break; }
107 #define CLOSURE_MORE \
108  KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
109  for (size_t j = 0; j < function->upvalueCount; ++j) { \
110  int isLocal = chunk->code[offset++ + size]; \
111  offset++; \
112  if (isLocal & 2) { \
113  offset += 2; \
114  } \
115  }
116 #define EXPAND_ARGS_MORE
117 #define LOCAL_MORE
118 #define FORMAT_VALUE_MORE
119 
120  while (offset < chunk->count) {
121  uint8_t opcode = chunk->code[offset];
122  size_t size = 0;
123  switch (opcode) {
124 #include "opcodes.h"
125  }
126  offset += size;
127  }
128  return 0;
129 #undef SIMPLE
130 #undef OPERANDB
131 #undef OPERAND
132 #undef CONSTANT
133 #undef JUMP
134 #undef CLOSURE_MORE
135 #undef LOCAL_MORE
136 #undef EXPAND_ARGS_MORE
137 #undef FORMAT_VALUE_MORE
138 }
139 
140 #define OPARGS FILE * f, const char * fullName, size_t * size, size_t * offset, KrkCodeObject * func, KrkChunk * chunk
141 #define OPARG_VALS f,fullName,size,offset,func,chunk
142 
143 static void _print_opcode(OPARGS) {
144  fprintf(f, "%-16s ", opcodeClean(fullName));
145 }
146 
147 static void _simple(OPARGS) {
148  _print_opcode(OPARG_VALS);
149  fprintf(f, " ");
150  *size = 1;
151 }
152 
153 static void _constant(OPARGS, int isLong, void (*more)(OPARGS, size_t constant)) {
154  _print_opcode(OPARG_VALS);
155  size_t constant = isLong ? (chunk->code[*offset + 1] << 16) | (chunk->code[*offset + 2] << 8) | (chunk->code[*offset + 3]) : chunk->code[*offset + 1];
156  fprintf(f, "%4d ", (int)constant);
157  krk_printValueSafe(f, chunk->constants.values[constant]);
158  *size = isLong ? 4 : 2;
159  if (more) more(OPARG_VALS, constant);
160 }
161 
162 static void _operand(OPARGS, int isLong, void (*more)(OPARGS, size_t constant)) {
163  _print_opcode(OPARG_VALS);
164  uint32_t operand = isLong ? (chunk->code[*offset + 1] << 16) | (chunk->code[*offset + 2] << 8) | (chunk->code[*offset + 3]) : chunk->code[*offset + 1];
165  fprintf(f, "%4d", (int)operand);
166  *size = isLong ? 4 : 2;
167  if (more) more(OPARG_VALS, operand);
168 }
169 
170 static void _jump(OPARGS, int sign) {
171  _print_opcode(OPARG_VALS);
172  uint16_t jump = (chunk->code[*offset + 1] << 8) | (chunk->code[*offset + 2]);
173  fprintf(f, "%4d (to %d)", (int)jump, (int)(*offset + 3 + sign * jump));
174  *size = 3;
175 }
176 
177 #undef NOOP
178 #define NOOP (NULL)
179 #define SIMPLE(opc) case opc: _simple(f,#opc,&size,&offset,func,chunk); break;
180 #define CONSTANT(opc,more) case opc: _constant(f,#opc,&size,&offset,func,chunk,0,more); break; \
181  case opc ## _LONG: _constant(f,#opc "_LONG",&size,&offset,func,chunk,1,more); break;
182 #define OPERAND(opc,more) case opc: _operand(f,#opc,&size,&offset,func,chunk,0,more); break; \
183  case opc ## _LONG: _operand(f,#opc "_LONG",&size,&offset,func,chunk,1,more); break;
184 #define JUMP(opc,sign) case opc: _jump(f,#opc,&size,&offset,func,chunk,sign 1); break;
185 
186 #define CLOSURE_MORE _closure_more
187 
188 static void _closure_more(OPARGS, size_t constant) {
189  KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]);
190  fprintf(f, " ");
191  for (size_t j = 0; j < function->upvalueCount; ++j) {
192  int isLocal = chunk->code[(*offset)++ + *size];
193  int index = chunk->code[(*offset)++ + *size];
194  if (isLocal & 2) {
195  index = (index << 16) | (chunk->code[*offset + *size] << 8) | chunk->code[*offset + 1 + *size];
196  offset += 2;
197  }
198  if (isLocal & 1) {
199  for (size_t i = 0; i < func->localNameCount; ++i) {
200  if (func->localNames[i].id == (size_t)index && func->localNames[i].birthday <= *offset && func->localNames[i].deathday >= *offset) {
201  fprintf(f, "%s", func->localNames[i].name->chars);
202  break;
203  }
204  }
205  } else if (isLocal & 4) {
206  fprintf(f, "classcell");
207  } else { fprintf(f, "upvalue<%d>", index); }
208  if (j + 1 != function->upvalueCount) fprintf(f, ", ");
209  }
210 }
211 
212 #define EXPAND_ARGS_MORE _expand_args_more
213 
214 static void _expand_args_more(OPARGS, size_t operand) {
215  fprintf(f, " (%s)", operand == 0 ? "singleton" : (operand == 1 ? "list" : "dict"));
216 }
217 
218 #define FORMAT_VALUE_MORE _format_value_more
219 
220 static void _format_value_more(OPARGS, size_t operand) {
221  if (operand != 0) {
222  int hasThing = 0;
223  fprintf(f, " (");
224  if (operand & FORMAT_OP_EQ) { fprintf(f, "eq"); hasThing = 1; }
225  if (operand & FORMAT_OP_STR) { fprintf(f, "%sstr", hasThing ? ", " : ""); hasThing = 1; }
226  if (operand & FORMAT_OP_REPR) { fprintf(f, "%srepr", hasThing ? ", " : ""); hasThing = 1; }
227  if (operand & FORMAT_OP_FORMAT) { fprintf(f, "%swith format", hasThing ? ", " : ""); }
228  fprintf(f, ")");
229  }
230 }
231 
232 #define LOCAL_MORE _local_more
233 static void _local_more(OPARGS, size_t operand) {
234  for (size_t i = 0; i < func->localNameCount; ++i) {
235  if (func->localNames[i].id == operand && func->localNames[i].birthday <= *offset && func->localNames[i].deathday >= *offset) {
236  fprintf(f, " (%s", func->localNames[i].name->chars);
237  if ((short int) operand < func->potentialPositionals) {
238  fprintf(f, ", arg");
239  } else if ((short int)operand < func->potentialPositionals + func->keywordArgs + !!(func->obj.flags & KRK_OBJ_FLAGS_CODEOBJECT_COLLECTS_ARGS)) {
240  fprintf(f, ", kwarg");
241  }
242  fprintf(f, ")");
243  break;
244  }
245  }
246 }
247 
248 size_t krk_disassembleInstruction(FILE * f, KrkCodeObject * func, size_t offset) {
249  KrkChunk * chunk = &func->chunk;
250  if (offset > 0 && krk_lineNumber(chunk, offset) == krk_lineNumber(chunk, offset - 1)) {
251  fprintf(f, " ");
252  } else {
253  if (offset > 0) fprintf(f,"\n");
254  fprintf(f, "%4d ", (int)krk_lineNumber(chunk, offset));
255  }
256  if (isJumpTarget(func,offset)) {
257  fprintf(f, " >> ");
258  } else {
259  fprintf(f, " ");
260  }
261  fprintf(f, "%4u ", (unsigned int)offset);
262  uint8_t opcode = chunk->code[offset];
263  size_t size = 1;
264 
265  switch (opcode) {
266 #include "opcodes.h"
267  default:
268  fprintf(f, "Unknown opcode: %02x", opcode);
269  }
270 
271  /* Birthdays - Local names that have become valid from this instruction */
272  for (size_t i = 0; i < func->localNameCount; ++i) {
273  if (func->localNames[i].birthday >= offset && func->localNames[i].birthday < offset + size) {
274  fprintf(f, " +%s", func->localNames[i].name->chars);
275  }
276  }
277 
278  /* Deathdays - Local names that are no longer valid as of this instruction */
279  for (size_t i = 0; i < func->localNameCount; ++i) {
280  if (func->localNames[i].deathday >= offset && func->localNames[i].deathday < offset + size) {
281  fprintf(f, " -%s", func->localNames[i].name->chars);
282  }
283  }
284 
285  fprintf(f,"\n");
286 
287  return offset + size;
288 }
289 #undef SIMPLE
290 #undef OPERANDB
291 #undef OPERAND
292 #undef CONSTANT
293 #undef JUMP
294 #undef CLOSURE_MORE
295 #undef LOCAL_MORE
296 #undef EXPAND_ARGS_MORE
297 #undef FORMAT_VALUE_MORE
298 #undef NOOP
299 
301  KrkCodeObject * inFunction;
302  size_t offset;
303  int flags;
304  uint8_t originalOpcode;
305 };
306 
307 #define MAX_BREAKPOINTS 32
309  int breakpointsCount;
310  KrkDebugCallback debuggerHook;
311 
312  /* XXX This was previously thread-local; it probably should still be
313  * specific to an individual thread... but we don't really do
314  * much thread debugging, so... */
315  int repeatStack_top;
316  int repeatStack_bottom;
317  int thisWasForced;
318 
319  struct BreakpointEntry breakpoints[MAX_BREAKPOINTS];
320 };
321 
322 int krk_debug_addBreakpointCodeOffset(KrkCodeObject * target, size_t offset, int flags) {
323  int index = vm.dbgState->breakpointsCount;
324  if (vm.dbgState->breakpointsCount == MAX_BREAKPOINTS) {
325  /* See if any are available */
326  for (int i = 0; i < MAX_BREAKPOINTS; ++i) {
327  if (vm.dbgState->breakpoints[i].inFunction == NULL) {
328  index = i;
329  break;
330  }
331  }
332  if (index == vm.dbgState->breakpointsCount) {
333  return -1;
334  }
335  } else {
336  index = vm.dbgState->breakpointsCount++;
337  }
338 
339  vm.dbgState->breakpoints[index].inFunction = target;
340  vm.dbgState->breakpoints[index].offset = offset;
341  vm.dbgState->breakpoints[index].originalOpcode = target->chunk.code[offset];
342  vm.dbgState->breakpoints[index].flags = flags;
343  target->chunk.code[offset] = OP_BREAKPOINT;
344 
345  return index;
346 }
347 
348 int krk_debug_addBreakpointFileLine(KrkString * filename, size_t line, int flags) {
349 
350  KrkCodeObject * target = NULL;
351 
352  /* Examine all code objects to find one that matches the requested
353  * filename and line number... */
354  KrkObj * object = vm.objects;
355  while (object) {
356  if (object->type == KRK_OBJ_CODEOBJECT) {
357  KrkChunk * chunk = &((KrkCodeObject*)object)->chunk;
358  if (filename == chunk->filename) {
359  /* We have a candidate. */
360  if (krk_lineNumber(chunk, 0) <= line &&
361  krk_lineNumber(chunk,chunk->count) >= line) {
362  target = (KrkCodeObject*)object;
363  break;
364  }
365  }
366  }
367  object = object->next;
368  }
369 
370  /* No matching function was found... */
371  if (!target) return -1;
372 
373  /* Find the right offset in this function */
374 
375  size_t offset = 0;
376  for (size_t i = 0; i < target->chunk.linesCount; ++i) {
377  if (target->chunk.lines[i].line > line) break;
378  if (target->chunk.lines[i].line == line) {
379  offset = target->chunk.lines[i].startOffset;
380  break;
381  }
382  offset = target->chunk.lines[i].startOffset;
383  }
384 
385  return krk_debug_addBreakpointCodeOffset(target, offset, flags);
386 }
387 
388 int krk_debug_enableBreakpoint(int breakIndex) {
389  if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
390  return 1;
391  vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] = OP_BREAKPOINT;
392  return 0;
393 }
394 KRK_Function(enablebreakpoint) {
395  CHECK_ARG(0,int,krk_integer_type,breakIndex);
396  if (krk_debug_enableBreakpoint(breakIndex))
397  return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
398  return NONE_VAL();
399 }
400 
401 int krk_debug_disableBreakpoint(int breakIndex) {
402  if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
403  return 1;
404  vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] =
405  vm.dbgState->breakpoints[breakIndex].originalOpcode;
406  if (breakIndex == vm.dbgState->repeatStack_top) {
407  vm.dbgState->repeatStack_top = -1;
408  }
409  return 0;
410 }
411 KRK_Function(disablebreakpoint) {
412  CHECK_ARG(0,int,krk_integer_type,breakIndex);
413  if (krk_debug_disableBreakpoint(breakIndex))
414  return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
415  return NONE_VAL();
416 }
417 
418 int krk_debug_removeBreakpoint(int breakIndex) {
419  if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount || vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
420  return 1;
421  krk_debug_disableBreakpoint(breakIndex);
422  vm.dbgState->breakpoints[breakIndex].inFunction = NULL;
423  while (vm.dbgState->breakpointsCount && vm.dbgState->breakpoints[vm.dbgState->breakpointsCount-1].inFunction == NULL) {
424  vm.dbgState->breakpointsCount--;
425  }
426  return 0;
427 }
428 KRK_Function(delbreakpoint) {
429  CHECK_ARG(0,int,krk_integer_type,breakIndex);
430  if (krk_debug_removeBreakpoint(breakIndex))
431  return krk_runtimeError(vm.exceptions->indexError, "invalid breakpoint id");
432  return NONE_VAL();
433 }
434 
435 KRK_Function(addbreakpoint) {
436  FUNCTION_TAKES_EXACTLY(2);
437  CHECK_ARG(1,int,krk_integer_type,lineNo);
438 
439  int flags = KRK_BREAKPOINT_NORMAL;
440 
441  if (hasKw) {
442  KrkValue flagsValue = NONE_VAL();
443  if (krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("flags")), &flagsValue)) {
444  if (!IS_INTEGER(flagsValue))
445  return TYPE_ERROR(int,flagsValue);
446  flags = AS_INTEGER(flagsValue);
447  }
448  }
449 
450  int result;
451  if (IS_STRING(argv[0])) {
452  result = krk_debug_addBreakpointFileLine(AS_STRING(argv[0]), lineNo, flags);
453  } else {
454  KrkCodeObject * target = NULL;
455  if (IS_CLOSURE(argv[0])) {
456  target = AS_CLOSURE(argv[0])->function;
457  } else if (IS_BOUND_METHOD(argv[0]) && IS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))) {
458  target = AS_CLOSURE(OBJECT_VAL(AS_BOUND_METHOD(argv[0])->method))->function;
459  } else if (IS_codeobject(argv[0])) {
460  target = AS_codeobject(argv[0]);
461  } else {
462  return TYPE_ERROR(function or method or filename,argv[0]);
463  }
464  /* Figure out what instruction this should be on */
465  size_t last = 0;
466  for (size_t i = 0; i < target->chunk.linesCount; ++i) {
467  if (target->chunk.lines[i].line > (size_t)lineNo) break;
468  if (target->chunk.lines[i].line == (size_t)lineNo) {
469  last = target->chunk.lines[i].startOffset;
470  break;
471  }
472  last = target->chunk.lines[i].startOffset;
473  }
474  result = krk_debug_addBreakpointCodeOffset(target,last,flags);
475  }
476 
477  if (result < 0)
478  return krk_runtimeError(vm.exceptions->baseException, "Could not add breakpoint.");
479 
480  return INTEGER_VAL(result);
481 }
482 
483 /*
484  * Begin debugger utility functions.
485  *
486  * These functions are exported for use in debuggers that
487  * are registered with krk_registerDebugger(...);
488  */
489 
498  int flagsBefore = krk_currentThread.flags;
501 
502  krk_runtimeError(vm.exceptions->baseException, "(breakpoint)");
504 
506  krk_currentThread.flags = flagsBefore;
507 }
508 
510  krk_currentThread.flags |= KRK_THREAD_SINGLE_STEP;
511 }
512 
514  krk_currentThread.flags &= ~(KRK_THREAD_SINGLE_STEP);
515 }
516 
518  if (!vm.dbgState->debuggerHook)
519  abort();
520 
521  if (vm.dbgState->repeatStack_top != -1) {
522  /* Re-enable stored repeat breakpoint */
523  krk_debug_enableBreakpoint(vm.dbgState->repeatStack_top);
524  }
525 
526  vm.dbgState->repeatStack_top = vm.dbgState->repeatStack_bottom;
527  vm.dbgState->repeatStack_bottom = -1;
528 
529  if (!vm.dbgState->thisWasForced) {
530  int result = vm.dbgState->debuggerHook(frame);
531  switch (result) {
532  case KRK_DEBUGGER_CONTINUE:
534  break;
535  case KRK_DEBUGGER_ABORT:
536  abort();
537  break;
538  case KRK_DEBUGGER_STEP:
540  break;
541  case KRK_DEBUGGER_QUIT:
542  exit(0);
543  break;
544  case KRK_DEBUGGER_RAISE:
545  krk_runtimeError(vm.exceptions->baseException, "raise from debugger");
546  break;
547  }
548  } else {
549  /* If we weren't asked to step to the next breakpoint, we need to disable single stepping. */
551  vm.dbgState->thisWasForced = 0;
552  }
553 
554  /* If the top of the repeat stack is an index, we need to ensure we re-enable that breakpoint */
555  if (vm.dbgState->repeatStack_top != -1 && !(krk_currentThread.flags & KRK_THREAD_SINGLE_STEP)) {
556  vm.dbgState->thisWasForced = 1;
558  }
559 
560  return 0;
561 }
562 
564  if (vm.dbgState->debuggerHook) return 1;
565  vm.dbgState->debuggerHook = hook;
566  return 0;
567 }
568 
569 int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject ** funcOut, size_t * offsetOut, int * flagsOut, int * enabled) {
570  if (breakIndex < 0 || breakIndex >= vm.dbgState->breakpointsCount)
571  return -1;
572  if (vm.dbgState->breakpoints[breakIndex].inFunction == NULL)
573  return -2;
574 
575  if (funcOut) *funcOut = vm.dbgState->breakpoints[breakIndex].inFunction;
576  if (offsetOut) *offsetOut = vm.dbgState->breakpoints[breakIndex].offset;
577  if (flagsOut) *flagsOut = vm.dbgState->breakpoints[breakIndex].flags;
578  if (enabled) *enabled = (vm.dbgState->breakpoints[breakIndex].inFunction->chunk.code[vm.dbgState->breakpoints[breakIndex].offset] == OP_BREAKPOINT) || breakIndex == vm.dbgState->repeatStack_top;
579 
580  return 0;
581 }
582 
584  int index = -1;
585 
587  KrkCodeObject * callee = frame->closure->function;
588  size_t offset = (frame->ip - 1) - callee->chunk.code;
589 
590  for (int i = 0; i < vm.dbgState->breakpointsCount; ++i) {
591  if (vm.dbgState->breakpoints[i].inFunction == callee && vm.dbgState->breakpoints[i].offset == offset) {
592  index = i;
593  }
594  }
595 
596  /* A breakpoint instruction without an associated breakpoint entry?
597  * This must be an invalid block of bytecode - just bail! */
598  if (index == -1) {
599  abort();
600  }
601 
602  /* Restore the instruction to its original state. If the debugger
603  * wants to break here again it can set the breakpoint again */
604  callee->chunk.code[offset] = vm.dbgState->breakpoints[index].originalOpcode;
605 
606  /* If this was a single-shot, we can remove it. */
607  if (vm.dbgState->breakpoints[index].flags == KRK_BREAKPOINT_ONCE) {
609  } else if (vm.dbgState->breakpoints[index].flags == KRK_BREAKPOINT_REPEAT) {
610  vm.dbgState->repeatStack_bottom = index;
611  }
612 
613  /* Rewind to rerun this instruction. */
614  frame->ip--;
615 
616  return krk_debuggerHook(frame);
617 }
618 
622 KRK_Function(dis) {
623  FUNCTION_TAKES_EXACTLY(1);
624 
625  if (IS_CLOSURE(argv[0])) {
626  KrkCodeObject * func = AS_CLOSURE(argv[0])->function;
627  krk_disassembleCodeObject(stdout, func, func->name ? func->name->chars : "<unnamed>");
628  } else if (IS_codeobject(argv[0])) {
629  krk_disassembleCodeObject(stdout, AS_codeobject(argv[0]), AS_codeobject(argv[0])->name ? AS_codeobject(argv[0])->name->chars : "<unnamed>");
630  } else if (IS_BOUND_METHOD(argv[0])) {
631  if (AS_BOUND_METHOD(argv[0])->method->type == KRK_OBJ_CLOSURE) {
632  KrkCodeObject * func = ((KrkClosure*)AS_BOUND_METHOD(argv[0])->method)->function;
633  const char * methodName = func->name ? func->name->chars : "<unnamed>";
634  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);
635  size_t allocSize = strlen(methodName) + strlen(typeName) + 2;
636  char * tmp = malloc(allocSize);
637  snprintf(tmp, allocSize, "%s.%s", typeName, methodName);
638  krk_disassembleCodeObject(stdout, func, tmp);
639  free(tmp);
640  } else {
641  krk_runtimeError(vm.exceptions->typeError, "Can not disassemble built-in method of '%T'", AS_BOUND_METHOD(argv[0])->receiver);
642  }
643  } else if (IS_CLASS(argv[0])) {
644  KrkValue code;
645  if (krk_tableGet(&AS_CLASS(argv[0])->methods, OBJECT_VAL(S("__func__")), &code) && IS_CLOSURE(code)) {
646  KrkCodeObject * func = AS_CLOSURE(code)->function;
647  krk_disassembleCodeObject(stdout, func, AS_CLASS(argv[0])->name->chars);
648  }
649  /* TODO Methods! */
650  } else {
651  krk_runtimeError(vm.exceptions->typeError, "Don't know how to disassemble '%T'", argv[0]);
652  }
653 
654  return NONE_VAL();
655 }
656 
657 KRK_Function(build) {
658  FUNCTION_TAKES_AT_LEAST(1);
659  FUNCTION_TAKES_AT_MOST(2);
660  CHECK_ARG(0,str,KrkString*,code);
661  char * fileName = "<source>";
662  if (argc > 1) {
663  CHECK_ARG(1,str,KrkString*,filename);
664  fileName = filename->chars;
665  }
666 
667  /* Unset module */
668  krk_push(OBJECT_VAL(krk_currentThread.module));
670  krk_currentThread.module = NULL;
671  KrkCodeObject * c = krk_compile(code->chars,fileName);
672  krk_currentThread.module = module;
673  krk_pop();
674  if (c) return OBJECT_VAL(c);
675  else return NONE_VAL();
676 }
677 
678 #define NOOP (void)0
679 #define SIMPLE(opc) case opc: size = 1; break;
680 #define CONSTANT(opc,more) case opc: { constant = chunk->code[offset + 1]; size = 2; more; break; } \
681  case opc ## _LONG: { constant = (chunk->code[offset + 1] << 16) | \
682  (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
683 #define OPERAND(opc,more) case opc: { operand = chunk->code[offset + 1]; size = 2; more; break; } \
684  case opc ## _LONG: { operand = (chunk->code[offset + 1] << 16) | \
685  (chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); size = 4; more; break; }
686 #define JUMP(opc,sign) case opc: { jump = 0 sign ((chunk->code[offset + 1] << 8) | (chunk->code[offset + 2])); \
687  size = 3; break; }
688 #define CLOSURE_MORE \
689  KrkCodeObject * function = AS_codeobject(chunk->constants.values[constant]); \
690  size_t baseOffset = offset; \
691  for (size_t j = 0; j < function->upvalueCount; ++j) { \
692  int isLocal = chunk->code[baseOffset++ + size]; \
693  baseOffset++; \
694  if (isLocal & 2) { \
695  baseOffset += 2; \
696  } \
697  } \
698  size += baseOffset - offset;
699 #define EXPAND_ARGS_MORE
700 #define FORMAT_VALUE_MORE
701 #define LOCAL_MORE local = operand;
702 static KrkValue _examineInternal(KrkCodeObject* func) {
703  KrkValue output = krk_list_of(0,NULL,0);
704  krk_push(output);
705 
706  KrkChunk * chunk = &func->chunk;
707  size_t offset = 0;
708  while (offset < chunk->count) {
709  uint8_t opcode = chunk->code[offset];
710  size_t size = 0;
711  ssize_t constant = -1;
712  ssize_t jump = 0;
713  ssize_t operand = -1;
714  ssize_t local = -1;
715  switch (opcode) {
716 #include "opcodes.h"
717  }
718 
719  KrkTuple * newTuple = krk_newTuple(3);
720  krk_push(OBJECT_VAL(newTuple));
721  newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(opcode);
722  newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(size);
723  if (constant != -1) {
724  newTuple->values.values[newTuple->values.count++] = chunk->constants.values[constant];
725  } else if (jump != 0) {
726  newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(jump);
727  } else if (local != -1) {
728  newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand); /* Just in case */
729  for (size_t i = 0; i < func->localNameCount; ++i) {
730  if (func->localNames[i].id == (size_t)local && func->localNames[i].birthday <= offset && func->localNames[i].deathday >= offset) {
731  newTuple->values.values[newTuple->values.count-1] = OBJECT_VAL(func->localNames[i].name);
732  break;
733  }
734  }
735  } else if (operand != -1) {
736  newTuple->values.values[newTuple->values.count++] = INTEGER_VAL(operand);
737  } else {
738  newTuple->values.values[newTuple->values.count++] = NONE_VAL();
739  }
740  krk_writeValueArray(AS_LIST(output), krk_peek(0));
741  krk_pop();
742 
743  if (size == 0) {
744  abort();
745  }
746 
747  offset += size;
748  }
749 
750  return krk_pop();
751 }
752 KRK_Function(examine) {
753  FUNCTION_TAKES_EXACTLY(1);
754  CHECK_ARG(0,codeobject,KrkCodeObject*,func);
755  return _examineInternal(func);
756 }
757 
758 #undef SIMPLE
759 #undef OPERANDB
760 #undef OPERAND
761 #undef CONSTANT
762 #undef JUMP
763 #undef CLOSURE_MORE
764 #undef LOCAL_MORE
765 #undef EXPAND_ARGS_MORE
766 #undef FORMAT_VALUE_MORE
767 
769  KrkInstance * module = krk_newInstance(vm.baseClasses->moduleClass);
770  krk_attachNamedObject(&vm.modules, "dis", (KrkObj*)module);
771  krk_attachNamedObject(&module->fields, "__name__", (KrkObj*)S("dis"));
772  krk_attachNamedValue(&module->fields, "__file__", NONE_VAL());
773 
774  vm.dbgState = calloc(1, sizeof(struct DebuggerState));
775  vm.dbgState->repeatStack_top = -1;
776  vm.dbgState->repeatStack_bottom = -1;
777 
778  KRK_DOC(module,
779  "@brief Provides tools for disassembling bytecode.\n\n"
780  "### Code Disassembly in Kuroko\n\n"
781  "The @c dis module contains functions for dealing with _code objects_ which "
782  "represent the compiled bytecode of a Kuroko function. The bytecode compilation "
783  "process is entirely static and bytecode analysis can be performed without calling "
784  "into the VM to run dynamic code.\n\n"
785  "### Debugger Breakpoints\n\n"
786  "Kuroko interpreters can provide a debugger hook through the C API's "
787  "@ref krk_debug_registerCallback() function. Breakpoints can be managed both "
788  "from the C API and from this module's @ref addbreakpoint, @ref delbreakpoint, "
789  "@ref enablebreakpoint, and @ref disablebreakpoint methods."
790  );
791 
792  KRK_DOC(BIND_FUNC(module, dis),
793  "@brief Disassemble an object.\n"
794  "@arguments obj\n\n"
795  "Dumps a disassembly of the bytecode in the code object associated with @p obj. "
796  "If @p obj can not be disassembled, a @ref TypeError is raised.");
797 
798  KRK_DOC(BIND_FUNC(module, build),
799  "@brief Compile a string to a code object.\n"
800  "@arguments code\n\n"
801  "Compiles the string @p code and returns a code object. If a syntax "
802  "error is encountered, it will be raised.");
803 
804  KRK_DOC(BIND_FUNC(module, examine),
805  "@brief Convert a code object to a list of instructions.\n"
806  "@arguments func\n\n"
807  "Examines the code object @p func and returns a list representation of its instructions. "
808  "Each instruction entry is a tuple of the opcode, total instruction size in bytes, and "
809  "the operand of the argument, either as an integer for jump offsets, the actual value for "
810  "constant operands, or the name of a local or global variable if available.");
811 
812  KRK_DOC(BIND_FUNC(module, addbreakpoint),
813  "@brief Attach a breakpoint to a code object.\n"
814  "@arguments func, line\n\n"
815  "@p func may be a filename string, or a function, method, or code object. Returns "
816  "the new breakpoint index, or raises @ref Exception if a breakpoint code not be added.");
817 
818  KRK_DOC(BIND_FUNC(module, delbreakpoint),
819  "@brief Delete a breakpoint.\n"
820  "@arguments handle\n\n"
821  "Delete the breakpoint specified by @p handle, disabling it if it was enabled. "
822  "May raise @ref IndexError if @p handle is not a valid breakpoint handle.");
823 
824  KRK_DOC(BIND_FUNC(module, enablebreakpoint),
825  "@brief Enable a breakpoint.\n"
826  "@arguments handle\n\n"
827  "Enable the breakpoint specified by @p handle. May raise @ref IndexError if "
828  "@p handle is not a valid breakpoint handle.");
829 
830  KRK_DOC(BIND_FUNC(module, disablebreakpoint),
831  "@brief Disable a breakpoint.\n"
832  "@arguments handle\n\n"
833  "Disable the breakpoint specified by @p handle. May raise @ref IndexError if "
834  "@p handle is not a valid breakpoint handle.");
835 
836  krk_attachNamedValue(&module->fields, "BREAKPOINT_ONCE", INTEGER_VAL(KRK_BREAKPOINT_ONCE));
837  krk_attachNamedValue(&module->fields, "BREAKPOINT_REPEAT", INTEGER_VAL(KRK_BREAKPOINT_REPEAT));
838 
839 #define OPCODE(opc) krk_attachNamedValue(&module->fields, #opc, INTEGER_VAL(opc));
840 #define SIMPLE(opc) OPCODE(opc)
841 #define CONSTANT(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
842 #define OPERAND(opc,more) OPCODE(opc) OPCODE(opc ## _LONG)
843 #define JUMP(opc,sign) OPCODE(opc)
844 #include "opcodes.h"
845 #undef SIMPLE
846 #undef OPERANDB
847 #undef OPERAND
848 #undef CONSTANT
849 #undef JUMP
850 }
851 
852 #endif
KrkCodeObject * krk_compile(const char *src, char *fileName)
Compile a source string to bytecode.
Definition: compiler.c:3986
Exported methods for the source compiler.
Functions for debugging bytecode execution.
#define KRK_BREAKPOINT_ONCE
Definition: debug.h:227
int krk_debug_addBreakpointCodeOffset(KrkCodeObject *codeObject, size_t offset, int flags)
Add a breakpoint to the given code object.
Definition: debug.c:322
void krk_debug_disableSingleStep(void)
Disable single stepping in the current thread.
Definition: debug.c:513
int krk_debuggerHook(KrkCallFrame *frame)
Called by the VM on single step.
Definition: debug.c:517
int krk_debug_examineBreakpoint(int breakIndex, KrkCodeObject **funcOut, size_t *offsetOut, int *flagsOut, int *enabledOut)
Retreive information on a breakpoint.
Definition: debug.c:569
int(* KrkDebugCallback)(KrkCallFrame *frame)
Function pointer for a debugger hook. krk_debug_registerCallback()
Definition: debug.h:73
int krk_debug_enableBreakpoint(int breakpointId)
Enable a breakpoint.
Definition: debug.c:388
int krk_debugBreakpointHandler(void)
Called by the VM when a breakpoint is encountered.
Definition: debug.c:583
void krk_debug_dumpTraceback(void)
Safely dump a traceback to stderr.
Definition: debug.c:497
void krk_debug_enableSingleStep(void)
Enable single stepping in the current thread.
Definition: debug.c:509
int krk_debug_removeBreakpoint(int breakpointId)
Remove a breakpoint from the breakpoint table.
Definition: debug.c:418
int krk_debug_registerCallback(KrkDebugCallback hook)
Register a debugger callback.
Definition: debug.c:563
int krk_debug_disableBreakpoint(int breakpointId)
Disable a breakpoint.
Definition: debug.c:401
size_t krk_disassembleInstruction(FILE *f, KrkCodeObject *func, size_t offset)
Print a disassembly of a single opcode instruction.
Definition: debug.c:248
int krk_debug_addBreakpointFileLine(KrkString *filename, size_t line, int flags)
Add a breakpoint to the given line of a file.
Definition: debug.c:348
#define KRK_BREAKPOINT_REPEAT
Definition: debug.h:228
void krk_disassembleCodeObject(FILE *f, KrkCodeObject *func, const char *name)
Print a disassembly of 'func' to the stream 'f'.
Definition: debug.c:60
#define KRK_BREAKPOINT_NORMAL
Definition: debug.h:226
void krk_debug_dumpStack(FILE *f, KrkCallFrame *frame)
Print the elements on the stack.
Definition: debug.c:22
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:445
void krk_dumpTraceback(void)
If there is an active exception, print a traceback to stderr.
Definition: exceptions.c:351
Internal header.
Represents a managed call state in a VM thread.
Definition: vm.h:46
size_t slots
Definition: vm.h:49
KrkClosure * closure
Definition: vm.h:47
uint8_t * ip
Definition: vm.h:48
Opcode chunk of a code object.
Definition: chunk.h:36
size_t krk_lineNumber(KrkChunk *chunk, size_t offset)
Obtain the line number for a byte offset into a bytecode chunk.
Definition: chunk.c:74
Function object.
Definition: object.h:169
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
Definition: object.h:171
Code object.
Definition: object.h:144
unsigned short potentialPositionals
Precalculated positional arguments for complex argument processing.
Definition: object.h:148
KrkChunk chunk
Bytecode data.
Definition: object.h:151
KrkLocalEntry * localNames
Stores the names of local variables used in the function, for debugging.
Definition: object.h:158
unsigned short keywordArgs
Arity of keyword (default) arguments.
Definition: object.h:147
KrkObj obj
Base.
Definition: object.h:145
size_t localNameCount
Number of entries in localNames.
Definition: object.h:157
KrkString * name
Name of the function.
Definition: object.h:152
unsigned short totalArguments
Total argument cells we can fill in complex argument processing.
Definition: object.h:149
An object of a class.
Definition: object.h:255
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:339
KrkTable fields
Attributes table.
Definition: object.h:258
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
Definition: obj_list.c:30
size_t birthday
Instruction offset that this local name became valid on.
Definition: object.h:131
KrkString * name
Name of the local.
Definition: object.h:133
size_t id
Local ID as used by opcodes; offset from the frame's stack base.
Definition: object.h:130
size_t deathday
Instruction offset that this local name becomes invalid on.
Definition: object.h:132
The most basic object type.
Definition: object.h:41
uint16_t flags
General object flags, mostly related to garbage collection.
Definition: object.h:43
uint16_t type
Tag indicating core type.
Definition: object.h:42
struct KrkObj * next
Invasive linked list of all objects in the VM.
Definition: object.h:45
Immutable sequence of Unicode codepoints.
Definition: object.h:93
char * chars
UTF8 canonical data.
Definition: object.h:97
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
Definition: table.c:178
void krk_attachNamedObject(KrkTable *table, const char name[], KrkObj *obj)
Attach an object to an attribute table.
Definition: vm.c:839
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:825
KrkValue currentException
Definition: vm.h:173
size_t frameCount
Definition: vm.h:165
KrkValue * stack
Definition: vm.h:167
KrkValue * stackTop
Definition: vm.h:168
KrkCallFrame * frames
Definition: vm.h:164
int flags
Definition: vm.h:174
KrkInstance * module
Definition: vm.h:172
Immutable sequence of arbitrary values.
Definition: object.h:297
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:299
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:353
KrkValue * values
Definition: value.h:73
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Definition: value.c:17
size_t count
Definition: value.h:72
Stack reference or primative value.
const char * krk_typeName(KrkValue value)
Get the name of the type of a value.
Definition: vm.c:1023
void krk_printValueSafe(FILE *f, KrkValue value)
Print a value without calling the VM.
Definition: value.c:52
Utilities for creating native bindings.
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:292
Core API for the bytecode virtual machine.
#define vm
Convenience macro for namespacing.
Definition: vm.h:267
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:170
threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
void krk_module_init_dis(void)
Initialize the built-in 'dis' module.
Definition: debug.c:768
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:157
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:178