Need to patch `OP_GETUPVAR` and `OP_SETUPVAR`; fix #3732

parent aa3cb046
...@@ -40,6 +40,25 @@ get_closure_irep(mrb_state *mrb, int level) ...@@ -40,6 +40,25 @@ get_closure_irep(mrb_state *mrb, int level)
return proc->body.irep; return proc->body.irep;
} }
/* search for irep lev above the bottom */
static mrb_irep*
search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
{
size_t i;
for (i=0; i<top->rlen; i++) {
mrb_irep* tmp = top->reps[i];
if (tmp == bottom) return top;
tmp = search_irep(tmp, bnest-1, lev, bottom);
if (tmp) {
if (bnest == lev) return top;
return tmp;
}
}
return NULL;
}
static inline mrb_code static inline mrb_code
search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
{ {
...@@ -61,6 +80,20 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) ...@@ -61,6 +80,20 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
return 0; return 0;
} }
static int
irep_argc(mrb_irep *irep)
{
mrb_code c;
c = irep->iseq[0];
if (GET_OPCODE(c) == OP_ENTER) {
mrb_aspec ax = GETARG_Ax(c);
/* extra 1 means a slot for block */
return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
}
return 0;
}
static mrb_bool static mrb_bool
potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
{ {
...@@ -71,32 +104,24 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) ...@@ -71,32 +104,24 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
} }
static void static void
patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest) patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
{ {
size_t i; size_t i;
mrb_code c; mrb_code c;
int argc = 0; int argc = irep_argc(irep);
for (i = 0; i < irep->ilen; i++) { for (i = 0; i < irep->ilen; i++) {
c = irep->iseq[i]; c = irep->iseq[i];
switch(GET_OPCODE(c)){ switch(GET_OPCODE(c)){
case OP_ENTER:
{
mrb_aspec ax = GETARG_Ax(c);
/* extra 1 means a slot for block */
argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
}
break;
case OP_EPUSH: case OP_EPUSH:
patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1); patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
break; break;
case OP_LAMBDA: case OP_LAMBDA:
{ {
int arg_c = GETARG_c(c); int arg_c = GETARG_c(c);
if (arg_c & OP_L_CAPTURE) { if (arg_c & OP_L_CAPTURE) {
patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1); patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top);
} }
} }
break; break;
...@@ -133,6 +158,34 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest) ...@@ -133,6 +158,34 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
} }
break; break;
case OP_GETUPVAR:
{
int lev = GETARG_C(c)+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
if (arg != 0) {
/* must replace */
irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
}
}
}
break;
case OP_SETUPVAR:
{
int lev = GETARG_C(c)+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
if (arg != 0) {
/* must replace */
irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
}
}
}
break;
case OP_STOP: case OP_STOP:
if (mrb->c->ci->acc >= 0) { if (mrb->c->ci->acc >= 0) {
irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL); irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
...@@ -211,7 +264,8 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con ...@@ -211,7 +264,8 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con
c->ci->target_class = proc->target_class; c->ci->target_class = proc->target_class;
c->ci->env = 0; c->ci->env = 0;
proc->env = e; proc->env = e;
patch_irep(mrb, proc->body.irep, 0); patch_irep(mrb, proc->body.irep, 0, proc->body.irep);
/* mrb_codedump_all(mrb, proc); */
mrb_parser_free(p); mrb_parser_free(p);
mrbc_context_free(mrb, cxt); mrbc_context_free(mrb, cxt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment