summaryrefslogtreecommitdiffhomepage
path: root/src/kernel.c
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2020-07-17 16:15:47 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2020-07-17 16:15:47 +0900
commit1952004d5a4a3c0758036baca6158aa51e93d4d3 (patch)
tree8125d8fd1a19bbec8f067303261deca3f28e2758 /src/kernel.c
parente41f1f5139ef03dac2bd1c0b9a7a4143ddf07f59 (diff)
downloadmruby-1952004d5a4a3c0758036baca6158aa51e93d4d3.tar.gz
mruby-1952004d5a4a3c0758036baca6158aa51e93d4d3.zip
Use `proc->env` to check `block_given?` if possible; fix #5039
This bug has been there since mruby 1.4.0 (2018-04).
Diffstat (limited to 'src/kernel.c')
-rw-r--r--src/kernel.c42
1 files changed, 32 insertions, 10 deletions
diff --git a/src/kernel.c b/src/kernel.c
index 8f0c9c7b5..a74f8a8ed 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -99,6 +99,18 @@ mrb_obj_id_m(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(mrb_obj_id(self));
}
+static int
+env_bidx(struct REnv *e)
+{
+ int bidx;
+
+ /* use saved block arg position */
+ bidx = MRB_ENV_BIDX(e);
+ /* bidx may be useless (e.g. define_method) */
+ if (bidx >= MRB_ENV_LEN(e)) return -1;
+ return bidx;
+}
+
/* 15.3.1.2.2 */
/* 15.3.1.2.5 */
/* 15.3.1.3.6 */
@@ -129,6 +141,8 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
mrb_callinfo *ci = &mrb->c->ci[-1];
mrb_callinfo *cibase = mrb->c->cibase;
mrb_value *bp;
+ int bidx;
+ struct REnv *e = NULL;
struct RProc *p;
if (ci <= cibase) {
@@ -139,29 +153,36 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
/* search method/class/module proc */
while (p) {
if (MRB_PROC_SCOPE_P(p)) break;
+ e = MRB_PROC_ENV(p);
p = p->upper;
}
if (p == NULL) return mrb_false_value();
+ if (e) {
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
+ goto block_given;
+ }
/* search ci corresponding to proc */
while (cibase < ci) {
if (ci->proc == p) break;
ci--;
}
if (ci == cibase) {
- return mrb_false_value();
+ /* proc is closure */
+ if (!MRB_PROC_ENV_P(p)) return mrb_false_value();
+ e = MRB_PROC_ENV(p);
+ bidx = env_bidx(e);
+ if (bidx < 0) return mrb_false_value();
+ bp = &e->stack[bidx];
}
else if (ci->env) {
- struct REnv *e = ci->env;
- int bidx;
-
+ e = ci->env;
/* top-level does not have block slot (always false) */
- if (e->stack == mrb->c->stbase)
- return mrb_false_value();
- /* use saved block arg position */
- bidx = MRB_ENV_BIDX(e);
+ if (e->stack == mrb->c->stbase) return mrb_false_value();
+ bidx = env_bidx(e);
/* bidx may be useless (e.g. define_method) */
- if (bidx >= MRB_ENV_LEN(e))
- return mrb_false_value();
+ if (bidx < 0) return mrb_false_value();
bp = &e->stack[bidx];
}
else {
@@ -173,6 +194,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
bp++;
}
}
+ block_given:
if (mrb_nil_p(*bp))
return mrb_false_value();
return mrb_true_value();