summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-08-14 23:55:34 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-08-14 23:55:34 +0000
commit582b7057dc92fcf23b01a4fb699ebe008fd92240 (patch)
treee4a47fc630528fe8b4456af7e288be22c10f7885 /compile.c
parent12693e245ff8ec149dfc73eb25458035e9211637 (diff)
compile.c: compile_break
* compile.c (compile_break): extract from iseq_compile_each. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59593 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c132
1 files changed, 70 insertions, 62 deletions
diff --git a/compile.c b/compile.c
index 6b1aedced0..b8f04584f4 100644
--- a/compile.c
+++ b/compile.c
@@ -4362,6 +4362,71 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped, co
return COMPILE_OK;
}
+static int
+compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped)
+{
+ const int line = nd_line(node);
+ unsigned long level = 0;
+
+ if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
+ /* while/until */
+ LABEL *splabel = NEW_LABEL(0);
+ ADD_LABEL(ret, splabel);
+ ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
+ CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
+ ISEQ_COMPILE_DATA(iseq)->loopval_popped));
+ add_ensure_iseq(ret, iseq, 0);
+ ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
+ ADD_ADJUST_RESTORE(ret, splabel);
+
+ if (!popped) {
+ ADD_INSN(ret, line, putnil);
+ }
+ }
+ else if (iseq->body->type == ISEQ_TYPE_BLOCK) {
+ break_by_insn:
+ /* escape from block */
+ CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
+ ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_BREAK));
+ if (popped) {
+ ADD_INSN(ret, line, pop);
+ }
+ }
+ else if (iseq->body->type == ISEQ_TYPE_EVAL) {
+ break_in_eval:
+ COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
+ return COMPILE_NG;
+ }
+ else {
+ const rb_iseq_t *ip = iseq->body->parent_iseq;
+
+ while (ip) {
+ if (!ISEQ_COMPILE_DATA(ip)) {
+ ip = 0;
+ break;
+ }
+
+ level++;
+ if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
+ level = VM_THROW_NO_ESCAPE_FLAG;
+ goto break_by_insn;
+ }
+ else if (ip->body->type == ISEQ_TYPE_BLOCK) {
+ level <<= VM_THROW_LEVEL_SHIFT;
+ goto break_by_insn;
+ }
+ else if (ip->body->type == ISEQ_TYPE_EVAL) {
+ goto break_in_eval;
+ }
+
+ ip = ip->body->parent_iseq;
+ }
+ COMPILE_ERROR(ERROR_ARGS "Invalid break");
+ return COMPILE_NG;
+ }
+ return COMPILE_OK;
+}
+
static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popped);
/**
compile each node
@@ -4527,69 +4592,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
break;
}
- case NODE_BREAK:{
- unsigned long level = 0;
-
- if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
- /* while/until */
- LABEL *splabel = NEW_LABEL(0);
- ADD_LABEL(ret, splabel);
- ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
- CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
- ISEQ_COMPILE_DATA(iseq)->loopval_popped));
- add_ensure_iseq(ret, iseq, 0);
- ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
- ADD_ADJUST_RESTORE(ret, splabel);
-
- if (!popped) {
- ADD_INSN(ret, line, putnil);
- }
- }
- else if (iseq->body->type == ISEQ_TYPE_BLOCK) {
- break_by_insn:
- /* escape from block */
- CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
- ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_BREAK));
- if (popped) {
- ADD_INSN(ret, line, pop);
- }
- }
- else if (iseq->body->type == ISEQ_TYPE_EVAL) {
- break_in_eval:
- COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
- ng:
- debug_node_end();
- return COMPILE_NG;
- }
- else {
- const rb_iseq_t *ip = iseq->body->parent_iseq;
-
- while (ip) {
- if (!ISEQ_COMPILE_DATA(ip)) {
- ip = 0;
- break;
- }
-
- level++;
- if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
- level = VM_THROW_NO_ESCAPE_FLAG;
- goto break_by_insn;
- }
- else if (ip->body->type == ISEQ_TYPE_BLOCK) {
- level <<= VM_THROW_LEVEL_SHIFT;
- goto break_by_insn;
- }
- else if (ip->body->type == ISEQ_TYPE_EVAL) {
- goto break_in_eval;
- }
-
- ip = ip->body->parent_iseq;
- }
- COMPILE_ERROR(ERROR_ARGS "Invalid break");
- goto ng;
- }
+ case NODE_BREAK:
+ CHECK(compile_break(iseq, ret, node, popped));
break;
- }
case NODE_NEXT:{
unsigned long level = 0;
@@ -6501,6 +6506,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
}
default:
UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
+ ng:
+ debug_node_end();
+ return COMPILE_NG;
}
/* check & remove redundant trace(line) */