From e5fb21b6d281e76ed1aadf488706600739b7f787 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 17 Jan 2018 09:38:59 +0900 Subject: Detect cyclic link of class path references; fix #3926 --- src/variable.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'src/variable.c') diff --git a/src/variable.c b/src/variable.c index 968fc2fc1..de36efac6 100644 --- a/src/variable.c +++ b/src/variable.c @@ -955,27 +955,61 @@ find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c) return arg.sym; } +static struct RClass* +outer_class(mrb_state *mrb, struct RClass *c) +{ + mrb_value ov; + + ov = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); + if (mrb_nil_p(ov)) return NULL; + switch (mrb_type(ov)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + return mrb_class_ptr(ov); + default: + break; + } + return NULL; +} + +static mrb_bool +detect_outer_loop(mrb_state *mrb, struct RClass *c) +{ + struct RClass *t = c; /* tortoise */ + struct RClass *h = c; /* hare */ + + for (;;) { + if (h == NULL) return FALSE; + h = outer_class(mrb, h); + if (h == NULL) return FALSE; + h = outer_class(mrb, h); + t = outer_class(mrb, t); + if (t == h) return TRUE; + } +} + mrb_value mrb_class_find_path(mrb_state *mrb, struct RClass *c) { - mrb_value outer, path; + struct RClass *outer; + mrb_value path; mrb_sym name; const char *str; mrb_int len; - mrb_sym osym = mrb_intern_lit(mrb, "__outer__"); - outer = mrb_obj_iv_get(mrb, (struct RObject*)c, osym); - if (mrb_nil_p(outer)) return outer; - name = find_class_sym(mrb, mrb_class_ptr(outer), c); + if (detect_outer_loop(mrb, c)) return mrb_nil_value(); + outer = outer_class(mrb, c); + if (outer == NULL) return mrb_nil_value(); + name = find_class_sym(mrb, outer, c); if (name == 0) return mrb_nil_value(); - str = mrb_class_name(mrb, mrb_class_ptr(outer)); + str = mrb_class_name(mrb, outer); path = mrb_str_new_capa(mrb, 40); mrb_str_cat_cstr(mrb, path, str); mrb_str_cat_cstr(mrb, path, "::"); str = mrb_sym2name_len(mrb, name, &len); mrb_str_cat(mrb, path, str, len); - iv_del(mrb, c->iv, osym, NULL); + iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL); iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path); mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path); return path; -- cgit v1.2.3