summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2018-01-17 09:38:59 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2018-01-17 09:41:26 +0900
commite5fb21b6d281e76ed1aadf488706600739b7f787 (patch)
treee4f79676bfb3765d3dd6508302586dfd7ece7082 /src
parent3ae621fcb32a87e65de0ad68c291a12d315a81dd (diff)
downloadmruby-e5fb21b6d281e76ed1aadf488706600739b7f787.tar.gz
mruby-e5fb21b6d281e76ed1aadf488706600739b7f787.zip
Detect cyclic link of class path references; fix #3926
Diffstat (limited to 'src')
-rw-r--r--src/variable.c48
1 files changed, 41 insertions, 7 deletions
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;