Skip to content

Commit

Permalink
make exception handlers aware of labels
Browse files Browse the repository at this point in the history
Frame handlers have now a register which can hold a label in case we have a
labeled loop. When an exception is thrown and the category is MVM_EX_CAT_LABELED,
we check for object identity of the thrown payload and the label in the register.
That means we will have handlers for e.g. 'redo', and this handler will care
about 'redo' exceptions without a label, and also will care if the label fits.
  • Loading branch information
FROGGS committed May 21, 2014
1 parent 7c0e41b commit ce948e1
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 14 deletions.
12 changes: 11 additions & 1 deletion lib/MAST/Nodes.nqp
Expand Up @@ -586,6 +586,7 @@ module HandlerCategory {
our $warn := 256;
our $succeed := 512;
our $proceed := 1024;
our $labeled := 4096;
}

# A region with a handler.
Expand All @@ -595,8 +596,9 @@ class MAST::HandlerScope is MAST::Node {
has int $!action;
has $!goto_label;
has $!block_local;
has $!label_local;

method new(:@instructions!, :$category_mask!, :$action!, :$goto!, :$block) {
method new(:@instructions!, :$category_mask!, :$action!, :$goto!, :$block, :$label) {
my $obj := nqp::create(self);
nqp::bindattr($obj, MAST::HandlerScope, '@!instructions', @instructions);
nqp::bindattr_i($obj, MAST::HandlerScope, '$!category_mask', $category_mask);
Expand All @@ -619,6 +621,14 @@ class MAST::HandlerScope is MAST::Node {
$action != $HandlerAction::unwind_and_goto_obj {
nqp::die("Unknown handler action");
}
if $category_mask +& $HandlerCategory::labeled {
if nqp::istype($label, MAST::Local) {
nqp::bindattr($obj, MAST::HandlerScope, '$!label_local', $label);
}
else {
nqp::die("Handler category 'labeled' needs a MAST::Local");
}
}
$obj
}
}
4 changes: 4 additions & 0 deletions src/core/bytecode.c
Expand Up @@ -607,6 +607,10 @@ static MVMStaticFrame ** deserialize_frames(MVMThreadContext *tc, MVMCompUnit *c
static_frame_body->handlers[j].block_reg = read_int16(pos, 14);
static_frame_body->handlers[j].goto_offset = read_int32(pos, 16);
pos += FRAME_HANDLER_SIZE;
if (static_frame_body->handlers[j].category_mask & MVM_EX_CAT_LABELED) {
static_frame_body->handlers[j].label_reg = read_int16(pos, 0);
pos += 2;
}
}
}

Expand Down
42 changes: 29 additions & 13 deletions src/core/exceptions.c
Expand Up @@ -31,18 +31,26 @@ static const char * cat_name(MVMThreadContext *tc, MVMint32 cat) {
return "succeed";
case MVM_EX_CAT_PROCEED:
return "proceed";
case MVM_EX_CAT_NEXT | MVM_EX_CAT_LABELED:
return "next_label";
case MVM_EX_CAT_REDO | MVM_EX_CAT_LABELED:
return "redo_label";
case MVM_EX_CAT_LAST | MVM_EX_CAT_LABELED:
return "last_label";
default:
return "unknown";
}
}

/* Checks if an exception handler is already on the active handler stack,
* so we don't re-trigger the same exception handler. */
static MVMuint8 in_handler_stack(MVMThreadContext *tc, MVMFrameHandler *fh) {
* so we don't re-trigger the same exception handler. Note: We have static
* handlers that get reused, so also check for the same handler being in
* the same frame, otherwise we consider the handler as being another one. */
static MVMuint8 in_handler_stack(MVMThreadContext *tc, MVMFrameHandler *fh, MVMFrame *f) {
if (tc->active_handlers) {
MVMActiveHandler *ah = tc->active_handlers;
while (ah) {
if (ah->handler == fh)
if (ah->handler == fh && ah->frame == f)
return 1;
ah = ah->next_handler;
}
Expand All @@ -60,18 +68,26 @@ static MVMuint8 in_caller_chain(MVMThreadContext *tc, MVMFrame *f_maybe) {
/* Looks through the handlers of a particular scope, and sees if one will
* match what we're looking for. Returns a pointer to it if so; if not,
* returns NULL. */
static MVMFrameHandler * search_frame_handlers(MVMThreadContext *tc, MVMFrame *f, MVMuint32 cat) {
static MVMFrameHandler * search_frame_handlers(MVMThreadContext *tc, MVMFrame *f, MVMuint32 cat, MVMObject *payload) {
MVMuint32 pc, i;
if (f == tc->cur_frame)
pc = (MVMuint32)(*tc->interp_cur_op - *tc->interp_bytecode_start);
else
pc = (MVMuint32)(f->return_address - f->effective_bytecode);
for (i = 0; i < f->static_info->body.num_handlers; i++) {
MVMuint32 category_mask = f->effective_handlers[i].category_mask;
if ((category_mask & cat) || ((category_mask & MVM_EX_CAT_CONTROL) && cat != MVM_EX_CAT_CATCH))
MVMFrameHandler eh = f->effective_handlers[i];
MVMuint32 category_mask = eh.category_mask;
MVMuint64 block_has_label = category_mask & MVM_EX_CAT_LABELED;
MVMuint64 block_label = block_has_label ? (MVMuint64)(f->work[eh.label_reg].o) : 0;
MVMuint64 thrown_label = payload ? (MVMuint64)payload : 0;
MVMuint64 identical_label_found = thrown_label == block_label;

if ( ((cat & category_mask) == cat && (!(cat & MVM_EX_CAT_LABELED) || identical_label_found))
|| ((category_mask & MVM_EX_CAT_CONTROL) && cat != MVM_EX_CAT_CATCH) ) {
if (pc >= f->effective_handlers[i].start_offset && pc <= f->effective_handlers[i].end_offset)
if (!in_handler_stack(tc, &f->effective_handlers[i]))
if (!in_handler_stack(tc, &f->effective_handlers[i], f))
return &f->effective_handlers[i];
}
}
return NULL;
}
Expand All @@ -85,22 +101,22 @@ typedef struct {
/* Searches for a handler of the specified category, relative to the given
* starting frame, searching according to the chosen mode. */
static LocatedHandler search_for_handler_from(MVMThreadContext *tc, MVMFrame *f,
MVMuint8 mode, MVMuint32 cat) {
MVMuint8 mode, MVMuint32 cat, MVMObject *payload) {
LocatedHandler lh;
lh.frame = NULL;
lh.handler = NULL;

if (mode == MVM_EX_THROW_LEXOTIC) {
while (f != NULL) {
lh = search_for_handler_from(tc, f, MVM_EX_THROW_LEX, cat);
lh = search_for_handler_from(tc, f, MVM_EX_THROW_LEX, cat, payload);
if (lh.frame != NULL)
return lh;
f = f->caller;
}
}
else {
while (f != NULL) {
MVMFrameHandler *h = search_frame_handlers(tc, f, cat);
MVMFrameHandler *h = search_frame_handlers(tc, f, cat, payload);
if (h != NULL) {
lh.frame = f;
lh.handler = h;
Expand Down Expand Up @@ -415,7 +431,7 @@ static void panic_unhandled_ex(MVMThreadContext *tc, MVMException *ex) {
* will next run the instruction of the handler. If there is no handler,
* it will panic and exit with a backtrace. */
void MVM_exception_throwcat(MVMThreadContext *tc, MVMuint8 mode, MVMuint32 cat, MVMRegister *resume_result) {
LocatedHandler lh = search_for_handler_from(tc, tc->cur_frame, mode, cat);
LocatedHandler lh = search_for_handler_from(tc, tc->cur_frame, mode, cat, NULL);
if (lh.frame == NULL)
panic_unhandled_cat(tc, cat);
run_handler(tc, lh, NULL);
Expand All @@ -438,7 +454,7 @@ void MVM_exception_throwobj(MVMThreadContext *tc, MVMuint8 mode, MVMObject *ex_o
ex->body.category = MVM_EX_CAT_CATCH;
if (resume_result)
ex->body.resume_addr = *tc->interp_cur_op;
lh = search_for_handler_from(tc, tc->cur_frame, mode, ex->body.category);
lh = search_for_handler_from(tc, tc->cur_frame, mode, ex->body.category, ex->body.payload);
if (lh.frame == NULL)
panic_unhandled_ex(tc, ex);

Expand Down Expand Up @@ -612,7 +628,7 @@ void MVM_exception_throw_adhoc_va(MVMThreadContext *tc, const char *messageForma

/* Try to locate a handler, so long as we're in the interpreter. */
if (tc->interp_cur_op)
lh = search_for_handler_from(tc, tc->cur_frame, MVM_EX_THROW_DYN, ex->body.category);
lh = search_for_handler_from(tc, tc->cur_frame, MVM_EX_THROW_DYN, ex->body.category, NULL);
else
lh.frame = NULL;

Expand Down
5 changes: 5 additions & 0 deletions src/core/exceptions.h
Expand Up @@ -15,6 +15,7 @@
#define MVM_EX_CAT_WARN 256
#define MVM_EX_CAT_SUCCEED 512
#define MVM_EX_CAT_PROCEED 1024
#define MVM_EX_CAT_LABELED 4096

/* Ways to throw an exception. */
#define MVM_EX_THROW_DYN 0
Expand All @@ -40,6 +41,10 @@ struct MVMFrameHandler {

/* Offset into the frame's bytecode of the handler, for goto handlers. */
MVMuint32 goto_offset;

/* Register containing a label in case we have a labeled loop. We need to
* be able to check for its identity when handling e.g. `next LABEL`. */
MVMuint16 label_reg;
};

/* An active (currently executing) exception handler. */
Expand Down
29 changes: 29 additions & 0 deletions src/mast/compiler.c
Expand Up @@ -53,6 +53,9 @@ typedef struct {

/* Label, which will need resolving. */
MASTNode *label;

/* Local holding a label in case we have a labeled loop. */
unsigned short label_reg;
} FrameHandler;

/* Handler actions. */
Expand Down Expand Up @@ -864,6 +867,28 @@ void compile_instruction(VM, WriterState *ws, MASTNode *node) {
ws->cur_frame->handlers[i].end_offset = end;
ws->cur_frame->handlers[i].category_mask = (unsigned int)hs->category_mask;
ws->cur_frame->handlers[i].action = (unsigned short)hs->action;
if (ws->cur_frame->handlers[i].category_mask & MVM_EX_CAT_LABELED) {
if (ISTYPE(vm, hs->label_local, ws->types->Local)) {
MAST_Local *l = GET_Local(hs->label_local);

/* Ensure it's within the set of known locals and an object. */
if (l->index >= ws->cur_frame->num_locals) {
cleanup_all(vm, ws);
DIE(vm, "MAST::Local index out of range in HandlerScope");
}
if (ws->cur_frame->local_types[l->index] != MVM_reg_obj) {
cleanup_all(vm, ws);
DIE(vm, "MAST::Local for HandlerScope must be an object");
}

/* Stash local index. */
ws->cur_frame->handlers[i].label_reg = (unsigned short)l->index;
}
else {
cleanup_all(vm, ws);
DIE(vm, "MAST::Local required for HandlerScope with loop label");
}
}

/* Ensure we have a label. */
if (ISTYPE(vm, hs->goto_label, ws->types->Label)) {
Expand Down Expand Up @@ -1079,6 +1104,10 @@ void compile_frame(VM, WriterState *ws, MASTNode *node, unsigned short idx) {
write_int32(ws->frame_seg, ws->frame_pos, 0);
}
ws->frame_pos += 4;
if (fs->handlers[i].category_mask & MVM_EX_CAT_LABELED) {
write_int16(ws->frame_seg, ws->frame_pos, fs->handlers[i].label_reg);
ws->frame_pos += 2;
}
}

/* Any leftover labels? */
Expand Down
1 change: 1 addition & 0 deletions src/mast/nodes_moar.h
Expand Up @@ -104,6 +104,7 @@ typedef struct {
MVMint64 action;
MVMObject *goto_label;
MVMObject *block_local;
MVMObject *label_local;
} MAST_HandlerScope;

/* Node types structure. */
Expand Down

0 comments on commit ce948e1

Please sign in to comment.