Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

colorfull debugs

  • Loading branch information...
commit 2319ff38518c228344932449a0a2c6922f3f83ec 1 parent a9f3edf
Joshua Thijssen jaytaph authored
5 debug.sh
View
@@ -0,0 +1,5 @@
+#!/bin/sh
+./configure --enable-debug
+make clean
+make
+
5 nodebug.sh
View
@@ -0,0 +1,5 @@
+#!/bin/sh
+./configure
+make clean
+make
+
10 src/components/modules/io.c
View
@@ -32,15 +32,7 @@
#include "general/dll.h"
#include "general/smm.h"
#include "vm/vm.h"
-
-#ifdef __DEBUG
- #define ANSI_BRIGHTRED "\33[41;33;1m"
- #define ANSI_RESET "\33[0m"
-#else
- #define ANSI_BRIGHTRED
- #define ANSI_RESET
-#endif
-
+#include "debug.h"
/**
*
4 src/components/objects/object.c
View
@@ -78,7 +78,7 @@ t_object *object_find_actual_attribute(t_object *obj, char *attr_name) {
t_object *cur_obj = obj;
while (attr == NULL) {
- DEBUG_PRINT(">>> Finding attribute '%s' on object %s\n", attr_name, cur_obj->name);
+ // DEBUG_PRINT(">>> Finding attribute '%s' on object %s\n", attr_name, cur_obj->name);
// Find the attribute in the current object
attr = ht_find(cur_obj->attributes, attr_name);
@@ -94,7 +94,7 @@ t_object *object_find_actual_attribute(t_object *obj, char *attr_name) {
cur_obj = cur_obj->parent;
}
- DEBUG_PRINT(">>> Found attribute '%s' in object %s (actually found in object %s)\n", attr_name, obj->name, cur_obj->name);
+ // DEBUG_PRINT(">>> Found attribute '%s' in object %s (actually found in object %s)\n", attr_name, obj->name, cur_obj->name);
return attr;
}
4 src/components/vm/frame.c
View
@@ -74,7 +74,7 @@ unsigned int vm_frame_get_operand(t_vm_frame *frame) {
* Pops an object from the stack. Errors when the stack is empty
*/
t_object *vm_frame_stack_pop(t_vm_frame *frame) {
- DEBUG_PRINT("STACK POP(%d): %08lX %s\n", frame->sp, (unsigned long)frame->stack[frame->sp], object_debug(frame->stack[frame->sp]));
+ DEBUG_PRINT(ANSI_BRIGHTYELLOW "STACK POP (%d): %08lX %s\n" ANSI_RESET, frame->sp, (unsigned long)frame->stack[frame->sp], object_debug(frame->stack[frame->sp]));
if (frame->sp >= frame->bytecode->stack_size) {
error_and_die(1, "Trying to pop from an empty stack");
@@ -91,7 +91,7 @@ t_object *vm_frame_stack_pop(t_vm_frame *frame) {
* Pushes an object onto the stack. Errors when the stack is full
*/
void vm_frame_stack_push(t_vm_frame *frame, t_object *obj) {
- DEBUG_PRINT("DBG PUSH: %s %08lX \n", object_debug(obj), (unsigned long)obj);
+ DEBUG_PRINT(ANSI_BRIGHTYELLOW "STACK PUSH(%d): %s %08lX \n" ANSI_RESET, frame->sp-1, object_debug(obj), (unsigned long)obj);
if (frame->sp < 0) {
error_and_die(1, "Trying to push to a full stack");
149 src/components/vm/vm.c
View
@@ -58,6 +58,7 @@ typedef struct _method_arg {
#define REASON_BREAKELSE 4
#define REASON_EXCEPTION 5
#define REASON_RERAISE 6
+#define REASON_FINALLY 7
/**
@@ -216,7 +217,7 @@ t_object *_vm_execute(t_vm_frame *frame) {
register t_object *left_obj, *right_obj;
register char *name;
register unsigned int opcode, oparg1, oparg2;
- int reason = REASON_NONE;
+ long reason = REASON_NONE;
register t_object *dst;
t_vm_frameblock *block;
@@ -248,11 +249,11 @@ t_object *_vm_execute(t_vm_frame *frame) {
#ifdef __DEBUG
if ((opcode & 0xC0) == 0xC0) {
- DEBUG_PRINT("%08lX : %s (0x%02X, 0x%02X)\n", cip, vm_code_names[vm_codes_offset[opcode]], oparg1, oparg2);
+ DEBUG_PRINT(ANSI_BRIGHTBLUE "%08lX : " ANSI_BRIGHTGREEN " %s (0x%02X, 0x%02X)\n" ANSI_RESET, cip, vm_code_names[vm_codes_offset[opcode]], oparg1, oparg2);
} else if ((opcode & 0x80) == 0x80) {
- DEBUG_PRINT("%08lX : %s (0x%02X)\n", cip, vm_code_names[vm_codes_offset[opcode]], oparg1);
+ DEBUG_PRINT(ANSI_BRIGHTBLUE "%08lX : " ANSI_BRIGHTGREEN " %s (0x%02X)\n" ANSI_RESET, cip, vm_code_names[vm_codes_offset[opcode]], oparg1);
} else {
- DEBUG_PRINT("%08lX : %s\n", cip, vm_code_names[vm_codes_offset[opcode]]);
+ DEBUG_PRINT(ANSI_BRIGHTBLUE "%08lX : " ANSI_BRIGHTGREEN " %s\n", cip, vm_code_names[vm_codes_offset[opcode]]);
}
#endif
@@ -327,8 +328,8 @@ t_object *_vm_execute(t_vm_frame *frame) {
// Hardcoded to bind the attribute to the search object!!
bound_obj = search_obj;
- DEBUG_PRINT("Fetching %s from %s\n", OBJ2STR(name), search_obj->name);
- DEBUG_PRINT("Binding to: %s\n", bound_obj ? object_debug(bound_obj) : "no binding!");
+ // DEBUG_PRINT("Fetching %s from %s\n", OBJ2STR(name), search_obj->name);
+ // DEBUG_PRINT("Binding to: %s\n", bound_obj ? object_debug(bound_obj) : "no binding!");
register t_object *attrib_obj = object_find_actual_attribute(search_obj, OBJ2STR(name));
if (attrib_obj == NULL) {
@@ -338,10 +339,10 @@ t_object *_vm_execute(t_vm_frame *frame) {
break;
}
- DEBUG_PRINT(" NAME: %s\n", OBJ2STR(name));
- DEBUG_PRINT(" SEARCH: %s\n", object_debug(search_obj));
- DEBUG_PRINT(" BOUND: %s\n", object_debug(bound_obj));
- DEBUG_PRINT(" ATTR: %s\n", object_debug(attrib_obj));
+ // DEBUG_PRINT(" NAME: %s\n", OBJ2STR(name));
+ // DEBUG_PRINT(" SEARCH: %s\n", object_debug(search_obj));
+ // DEBUG_PRINT(" BOUND: %s\n", object_debug(bound_obj));
+ // DEBUG_PRINT(" ATTR: %s\n", object_debug(attrib_obj));
if (! vm_check_visibility(bound_obj, search_obj, attrib_obj)) {
error_and_die(1, "Visibility does not allow to fetch attribute '%s'\n", OBJ2STR(name));
@@ -712,10 +713,9 @@ t_object *_vm_execute(t_vm_frame *frame) {
case VM_SETUP_EXCEPT :
/* Fillers for end_finally. Either these are used, or other values get pushed (in case of a return in the try-block)
* In the latter case, the objects below gets cleaned by the stack unwinding. */
- vm_frame_stack_push(frame, object_new(Object_Numerical, 1, REASON_NONE));
- vm_frame_stack_push(frame, object_new(Object_Numerical, 1, ret));
+ vm_frame_stack_push(frame, object_new(Object_Numerical, 1, REASON_FINALLY));
- vm_push_block_exception(frame, BLOCK_TYPE_EXCEPTION, frame->sp, frame->ip + oparg1, frame->ip + oparg1);
+ vm_push_block_exception(frame, BLOCK_TYPE_EXCEPTION, frame->sp, frame->ip + oparg1, frame->ip + oparg2);
goto dispatch;
break;
@@ -846,8 +846,6 @@ t_object *_vm_execute(t_vm_frame *frame) {
break;
case VM_END_FINALLY :
- vm_frame_stack_debug(frame);
-
// Pop value (exception, or numerical) from the stack
ret = vm_frame_stack_pop(frame);
object_dec_ref(ret);
@@ -880,15 +878,6 @@ t_object *_vm_execute(t_vm_frame *frame) {
// This loop will unwind the blockstack and act accordingly on each block (if needed)
block = unwind_blocks(frame, &reason, ret);
- // blocks unwind, ready for exception?
- if (reason == REASON_EXCEPTION && block && block->type == BLOCK_TYPE_EXCEPTION) {
-
- DEBUG_PRINT(" ----- EXCEPTION FOUND! -----\n");
- // Continue with handling the exception in the current vm frame.
- reason = REASON_NONE;
- frame->ip = block->handlers.exception.ip_catch;
- }
-
// Still not handled, break from this frame
if (reason != REASON_NONE) {
break;
@@ -905,76 +894,120 @@ t_object *_vm_execute(t_vm_frame *frame) {
}
-
+/**
+ * Unwind blocks. There are two types of blocks: LOOP and EXCEPTION. When this function is called
+ * we will by default unwind everything down EXCEPT when certain conditions are met.
+ *
+ * This function can change the reason and the blocks in the frame. It will not change any return values, but it can
+ * change the IP pointer as well (continue, break, finally, catch etc)
+ */
t_vm_frameblock *unwind_blocks(t_vm_frame *frame, long *reason, t_object *ret) {
- t_vm_frameblock *block;
+ t_vm_frameblock *block = vm_fetch_block(frame);
- DEBUG_PRINT("init unwind_blocks: %d\n", *reason);
+ DEBUG_PRINT("init unwind_blocks: [curblocks %d]\n", frame->block_cnt);
+ DEBUG_PRINT(" Current reason: %d\n", *reason);
+ DEBUG_PRINT(" Block Type: %d\n", block->type);
+ // Unwind block as long as there is a reason to unwind
while (*reason != REASON_NONE && frame->block_cnt > 0) {
block = vm_fetch_block(frame);
DEBUG_PRINT("Unwinding frameblock %d. CBT: %d\n", frame->block_cnt, block->type);
+ // Case 1: Continue inside a loop block
if (*reason == REASON_CONTINUE && block->type == BLOCK_TYPE_LOOP) {
+ DEBUG_PRINT("CASE 1\n");
DEBUG_PRINT("\n*** Continuing loop at %08lX\n\n", OBJ2NUM(ret));
- // Continue block
+
+ // Continue to at the start of the block
frame->ip = OBJ2NUM(ret);
+ *reason = REASON_NONE;
break;
}
- // Pop block. Not needed anymore @TODO: can we do this here, since we will be using "block" later on and
- // we just removed the same "block" from the stack?
- vm_pop_block(frame);
-
- // If we have thrown an exception, we must unwind all blocks until we hit an exception
- // block which has been created by SETUP_EXCEPT. After this, we will store our exception onto the stack
- // since the catch-blocks expect this.
- if (block->type == BLOCK_TYPE_EXCEPTION) {
- while (frame->sp < block->sp) {
- DEBUG_PRINT("UNWIND Exception Current SP: %d -> Needed SP: %d\n", frame->sp, block->sp);
- t_object *dst = vm_frame_stack_pop(frame);
- object_dec_ref(dst);
- }
-
- vm_frame_stack_push(frame, thread_get_exception());
- // Continue loop, pointing to the correct block type
- break;
+// Case 2: Return inside try block
+ if (*reason == REASON_RETURN && block->type == BLOCK_TYPE_EXCEPTION) {
+ DEBUG_PRINT("CASE 2\n");
+ /* We push the return value and the reason (REASON_RETURN) onto the stack, since END_FINALLY will expect
+ * this. We have no real way to know if a try block has a return statement inside, so SETUP_EXCEPT will
+ * push a REASON_NONE and a dummy retval onto the stack as well. END_EXCEPTION will catch this because
+ * when the popped reason is REASON_NONE, no return has been called in the try block. Ultimately this
+ * doesn't really matter. Because END_FINALLY will unwind the block, which means the variable-stack will
+ * match prior to the exception-block, so the dummy variables will be removed as well (IF they were
+ * present */
+
+ // push ret and reason onto the stack
+ vm_frame_stack_push(frame, ret);
+ vm_frame_stack_push(frame, object_new(Object_Numerical, 1, *reason));
+
+ // Reset reason, and jump to our finally block
+ *reason = REASON_NONE;
+ frame->ip = block->handlers.exception.ip_finally;
+ break;
}
- // Unwind the stack. Make sure we are at the same level as the caller block.
+ /*
+ * All cases below here are pop the block.
+ */
+
+
+ /* Pop the block from the frame, but we still use it. As long as we don't push another block in
+ * this function, this works ok. */
+ block = vm_pop_block(frame);
+
+ // Unwind the variable stack. This will remove all variables used in the current (unwound) block.
while (frame->sp < block->sp) {
DEBUG_PRINT("Current SP: %d -> Needed SP: %d\n", frame->sp, block->sp);
t_object *dst = vm_frame_stack_pop(frame);
object_dec_ref(dst);
}
+
+ // Case 3: Exception raised, but we are not an exception block. Try again with the next block.
+ if (*reason == REASON_EXCEPTION && block->type != BLOCK_TYPE_EXCEPTION) {
+ DEBUG_PRINT("CASE 3\n");
+ continue;
+ }
+
+ // Case 4: Exception raised, and we are an exception block.
+ if (*reason == REASON_EXCEPTION && block->type == BLOCK_TYPE_EXCEPTION) {
+ DEBUG_PRINT("CASE 4\n");
+
+ DEBUG_PRINT(" ----- EXCEPTION FOUND! -----\n");
+
+ // We throw the current exception onto the stack. The catch-blocks will expect this.
+ vm_frame_stack_push(frame, thread_get_exception());
+
+ // Continue with handling the exception in the current vm frame.
+ *reason = REASON_NONE;
+ frame->ip = block->handlers.exception.ip_catch;
+ break;
+ }
+
+ // Case 5: BreakElse inside a loop-block
if (*reason == REASON_BREAKELSE && block->type == BLOCK_TYPE_LOOP) {
+ DEBUG_PRINT("CASE 5\n");
DEBUG_PRINT("\nBreaking loop to %08X\n\n", block->handlers.loop.ip_else);
frame->ip = block->handlers.loop.ip_else;
break;
}
+ // Case 6: Break inside a loop-block
if (*reason == REASON_BREAK && block->type == BLOCK_TYPE_LOOP) {
+ DEBUG_PRINT("CASE 6\n");
DEBUG_PRINT("\nBreaking loop to %08X\n\n", block->handlers.loop.ip);
frame->ip = block->handlers.loop.ip;
break;
}
- if (block->type == BLOCK_TYPE_EXCEPTION) {
- // We found a return (or continue) statement inside our try block
- if (*reason == REASON_RETURN || *reason == REASON_CONTINUE) {
- // push ret and reason onto the stack
- vm_frame_stack_push(frame, ret);
- vm_frame_stack_push(frame, (t_object *)*reason);
-
- // Reset reason, and jump to our finally block
- *reason = REASON_NONE;
- frame->ip = block->handlers.exception.ip_finally;
- }
+ if (*reason == REASON_FINALLY && block->type == BLOCK_TYPE_EXCEPTION) {
+ *reason = REASON_NONE;
}
}
- DEBUG_PRINT("fini unwind_blocks: %d\n", *reason);
+ // It might be possible that we unwind every block and still have a a reason different than REASON_NONE. This will
+ // be handled by the parent frame.
+
+ DEBUG_PRINT("fini unwind_blocks: [block_cnt: %d] %d\n", frame->block_cnt, *reason);
return block;
}
12 src/include/debug.h
View
@@ -35,5 +35,17 @@
#define DEBUG_PRINT(format, args...) ((void)0)
#endif
+
+ #ifdef __DEBUG
+ #define ANSI_BRIGHTRED "\33[41;33;1m"
+ #define ANSI_BRIGHTGREEN "\33[32;1m"
+ #define ANSI_BRIGHTYELLOW "\33[33;1m"
+ #define ANSI_BRIGHTBLUE "\33[34;1m"
+ #define ANSI_RESET "\33[0m"
+ #else
+ #define ANSI_BRIGHTRED
+ #define ANSI_RESET
+ #endif
+
#endif
69 unittests/tests/base/trycatch-001.sfu
View
@@ -90,20 +90,46 @@ class foo {
public method bar() {
try {
io.print("try\n");
- return "bar";
+ return "1";
} catch (exception e) {
io.print("exception\n");
+ return "2";
} finally {
io.print("finally\n");
}
}
}
f = foo();
-io.print(f.bar());
+io.print(f.bar(),"\n");
====
try
finally
-bar
+1
+@@@@@@
+// Return in catch
+import io;
+class foo {
+ public method bar() {
+ try {
+ io.print("try\n");
+ foo.notexisting();
+ return "1";
+ } catch (exception e) {
+ io.print("exception\n");
+ return "2";
+ } finally {
+ io.print("finally\n");
+ }
+ return "4";
+ }
+}
+f = foo();
+io.print(f.bar(),"\n");
+====
+try
+exception
+finally
+2
@@@@@@
// Return in finally
import io;
@@ -111,21 +137,22 @@ class foo {
public method bar() {
try {
io.print("try\n");
- return "bar";
+ return "1";
} catch (exception e) {
io.print("exception\n");
+ return "2";
} finally {
io.print("finally\n");
- return "baz";
+ return "3";
}
}
}
f = foo();
-io.print(f.bar());
+io.print(f.bar(),"\n");
====
try
finally
-baz
+3
@@@@@@
// Double exception
import io;
@@ -148,6 +175,32 @@ try {
io.print("finally1\n");
}
====
+try1
+exception1
+try2
+exception2
+finally2
+finally1
+@@@@@@
+// Return in try should not continue, but only do finally
+import io;
+class foo {
+ public method bar() {
+ try {
+ io.print("try\n");
+ return "1";
+ io.print("This should not be printed!\n");
+ } catch (exception e) {
+ io.print("exception\n");
+ return "2";
+ } finally {
+ io.print("finally\n");
+ }
+ }
+}
+f = foo();
+io.print(f.bar(),"\n");
+====
try
finally
-baz
+1
Please sign in to comment.
Something went wrong with that request. Please try again.