From f962890a928b566c0f5ca7fdff5ef4ce19207e65 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Thu, 9 Jul 2015 23:46:54 +0200 Subject: Implement Module#prepend. --- include/mruby/class.h | 1 + src/class.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- test/t/module.rb | 33 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/include/mruby/class.h b/include/mruby/class.h index 9d5260a24..60310ae9d 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -16,6 +16,7 @@ struct RClass { struct iv_tbl *iv; struct kh_mt *mt; struct RClass *super; + struct RClass *origin; }; #define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v))) diff --git a/src/class.c b/src/class.c index 8a9fdaca6..3b1ea2321 100644 --- a/src/class.c +++ b/src/class.c @@ -194,6 +194,7 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass * if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { c = class_from_sym(mrb, outer, name); + c = c->origin; if (super && mrb_class_real(c->super) != super) { mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", mrb_sym2str(mrb, name), @@ -763,12 +764,13 @@ boot_defclass(mrb_state *mrb, struct RClass *super) else { c->super = mrb->object_class; } + c->origin = c; c->mt = kh_init(mt, mrb); return c; } -MRB_API void -mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +MRB_API inline void +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search_super) { struct RClass *ins_pos; @@ -782,6 +784,7 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) } while (p) { if (c != p && p->tt == MRB_TT_CLASS) { + if (!search_super) break; superclass_seen = 1; } else if (p->mt == m->mt) { @@ -810,6 +813,63 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) } } +MRB_API void +mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + include_module_at(mrb, c, m, FALSE); +} + +MRB_API void +mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + struct RClass *origin; + int changed = 0; + + origin = c->origin; + if (origin == c) { + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ + origin->super = c->super; + c->super = origin; + c->origin = origin; + origin->mt = c->mt; + c->mt = kh_init(mt, mrb); + } + include_module_at(mrb, c, m, FALSE); // changed = + if (changed) { + //rb_vm_check_redefinition_by_prepend(klass); + } +} + +static mrb_value +mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_prepend(mrb_state *mrb, mrb_value klass) +{ + mrb_value *argv; + mrb_int argc, i; + + mrb_get_args(mrb, "*", &argv, &argc); + for (i=0; i Date: Fri, 10 Jul 2015 20:17:16 +0200 Subject: Rename classes because of conflicts --- test/t/module.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 9faaf6e2c..3c87c122b 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -491,18 +491,18 @@ assert('Module#prepend') do def m1; [:M4, super, :M4] end end - class C0 + class P0 include M0 prepend M1 def m1; [:C0, super, :C0] end end - class C1 < C0 + class P1 < P0 prepend M2, M3 include M4 def m1; [:C1, super, :C1] end end - obj = C1.new + obj = P1.new expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] assert_equal(expected, obj.m1) end -- cgit v1.2.3 From ad9f32c5a876d5f230584b3c9abe77833943e6e6 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 10 Jul 2015 21:00:14 +0200 Subject: Expose insert position, which should be at origin for include and klass for prepend. --- src/class.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/class.c b/src/class.c index 3b1ea2321..c1d377e86 100644 --- a/src/class.c +++ b/src/class.c @@ -770,11 +770,8 @@ boot_defclass(mrb_state *mrb, struct RClass *super) } MRB_API inline void -include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search_super) +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { - struct RClass *ins_pos; - - ins_pos = c; while (m) { struct RClass *p = c, *ic; int superclass_seen = 0; @@ -816,7 +813,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *m, int search MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, m, FALSE); + include_module_at(mrb, c, c->origin, m, FALSE); } MRB_API void @@ -835,7 +832,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin->mt = c->mt; c->mt = kh_init(mt, mrb); } - include_module_at(mrb, c, m, FALSE); // changed = + include_module_at(mrb, c, c, m, FALSE); // changed = if (changed) { //rb_vm_check_redefinition_by_prepend(klass); } -- cgit v1.2.3 From 2e617e37e134b428ce7a979446cc2d768d1cf2fe Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 10 Jul 2015 21:14:28 +0200 Subject: origin must be initialized --- src/class.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/class.c b/src/class.c index c1d377e86..9f21ae395 100644 --- a/src/class.c +++ b/src/class.c @@ -76,6 +76,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); + sc->origin = sc; sc->mt = 0; sc->iv = 0; if (o->tt == MRB_TT_CLASS) { @@ -799,6 +800,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru else { ic->c = m; } + ic->origin = ic; ic->mt = m->mt; ic->iv = m->iv; ic->super = ins_pos->super; @@ -825,6 +827,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin = c->origin; if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->origin = origin; //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ origin->super = c->super; c->super = origin; @@ -1547,6 +1550,7 @@ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); + m->origin = m; m->mt = kh_init(mt, mrb); return m; -- cgit v1.2.3 From 97529c2a9a7b75a838234a420bbe2c6dc59c56ba Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 10 Jul 2015 22:01:07 +0200 Subject: Comment in a refactor to match MRI, but that fails 320 tests. --- src/class.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index 9f21ae395..ca4582041 100644 --- a/src/class.c +++ b/src/class.c @@ -776,11 +776,25 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru while (m) { struct RClass *p = c, *ic; int superclass_seen = 0; - + + //if (m->origin != m) + // goto skip; if (c->mt && c->mt == m->mt) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } while (p) { + /*if (p->tt == MRB_TT_ICLASS) { + if (!superclass_seen) { + ins_pos = p; // move insert point + } + goto skip; + } else if (p->tt == MRB_TT_CLASS) { + if (p->mt == m->mt) { + if (!search_super) break; + superclass_seen = 1; + } + }*/ + // if (c != p && p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; -- cgit v1.2.3 From 005cacf18b8ce2cf854872aa8cb803819478a47d Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:15:18 +0200 Subject: Additional patches to make this work --- src/class.c | 129 ++++++++++++++++++++++++++++++++--------------------------- src/kernel.c | 12 ++++++ 2 files changed, 83 insertions(+), 58 deletions(-) diff --git a/src/class.c b/src/class.c index ca4582041..73e6687c0 100644 --- a/src/class.c +++ b/src/class.c @@ -325,9 +325,16 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { - khash_t(mt) *h = c->mt; + khash_t(mt) *h; khiter_t k; + if (!c->origin) { + printf("Warning, class %s does not have valid origin\n", mrb_class_name(mrb, c)); + mrb_raisef(mrb, E_RUNTIME_ERROR, "Invalid origin"); + c->origin = c; + } + h = c->origin->mt; + if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); kh_value(h, k) = p; @@ -758,6 +765,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super) struct RClass *c; c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); + c->origin = c; if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); @@ -765,71 +773,74 @@ boot_defclass(mrb_state *mrb, struct RClass *super) else { c->super = mrb->object_class; } - c->origin = c; c->mt = kh_init(mt, mrb); return c; } -MRB_API inline void -include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + if (m->tt == MRB_TT_ICLASS) { + m = m->c; + } + ic->origin = ic; + ic->iv = m->iv; + ic->mt = m->origin->mt; + ic->super = super; + if (m->tt == MRB_TT_ICLASS) { + ic->c = m->c; + } else { + ic->c = m; + } + return ic; +} + +MRB_API int +include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct RClass *module, int search_super) { - while (m) { - struct RClass *p = c, *ic; + struct RClass *p, *iclass; + void *klass_mt = klass->origin->mt; + + while (module) { int superclass_seen = 0; - - //if (m->origin != m) - // goto skip; - if (c->mt && c->mt == m->mt) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); - } - while (p) { - /*if (p->tt == MRB_TT_ICLASS) { - if (!superclass_seen) { - ins_pos = p; // move insert point + + if (module->origin != module) + goto skip; + + if (klass_mt && klass_mt == module->mt) + return -1; + + p = klass->super; + while(p) { + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == module->mt) { + if (!superclass_seen) { + c = p; // move insert point + } + goto skip; } - goto skip; } else if (p->tt == MRB_TT_CLASS) { - if (p->mt == m->mt) { - if (!search_super) break; - superclass_seen = 1; - } - }*/ - // - if (c != p && p->tt == MRB_TT_CLASS) { if (!search_super) break; superclass_seen = 1; } - else if (p->mt == m->mt) { - if (p->tt == MRB_TT_ICLASS && !superclass_seen) { - ins_pos = p; - } - goto skip; - } p = p->super; } - ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); - if (m->tt == MRB_TT_ICLASS) { - ic->c = m->c; - } - else { - ic->c = m; - } - ic->origin = ic; - ic->mt = m->mt; - ic->iv = m->iv; - ic->super = ins_pos->super; - ins_pos->super = ic; - mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); - ins_pos = ic; + + iclass = include_class_new(mrb, module, c->super); + c->super = iclass; + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->super); + c = iclass; skip: - m = m->super; + module = module->super; } + return 0; } MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, c->origin, m, FALSE); + include_module_at(mrb, c, c->origin, m, 1); } MRB_API void @@ -842,16 +853,16 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); origin->origin = origin; - //OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ origin->super = c->super; c->super = origin; c->origin = origin; origin->mt = c->mt; c->mt = kh_init(mt, mrb); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->origin); } - include_module_at(mrb, c, c, m, FALSE); // changed = - if (changed) { - //rb_vm_check_redefinition_by_prepend(klass); + changed = include_module_at(mrb, c, c, m, 0); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } } @@ -955,15 +966,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - result = mrb_ary_new(mrb); - mrb_ary_push(mrb, result, mrb_obj_value(c)); - c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->tt != MRB_TT_SCLASS) { + else if (c->origin == c) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -1005,9 +1013,14 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; + /* hack, fix missing module->origin */ + struct RClass *m = mrb_class_ptr(mod); + if (!m->origin) + m->origin = m; + mrb_get_args(mrb, "&", &b); if (!mrb_nil_p(b)) { - mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod)); + mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } @@ -1324,9 +1337,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->super; + c = c->origin->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->super; + c = c->origin->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1925,7 +1938,7 @@ static void remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) { struct RClass *c = mrb_class_ptr(mod); - khash_t(mt) *h = c->mt; + khash_t(mt) *h = c->origin->mt; khiter_t k; if (h) { diff --git a/src/kernel.c b/src/kernel.c index b5b13f874..bafab6e76 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -248,6 +248,11 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } + if (klass->origin != klass) + clone->origin = klass->origin; + else + clone->origin = clone; + clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); @@ -269,6 +274,13 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) { struct RClass *dc = mrb_class_ptr(dst); struct RClass *sc = mrb_class_ptr(src); + /* if the origin is not the same as the class, then the origin and + the current class need to be copied */ + if (sc->origin != sc) { + dc->origin = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(sc->origin))); + } else { + dc->origin = dc; + } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; } -- cgit v1.2.3 From ce6672717706365929b1c2c3c2c278e75a3c4be4 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 08:16:04 -0500 Subject: Remove some remnant debug code --- src/class.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/class.c b/src/class.c index 73e6687c0..e9df1dae4 100644 --- a/src/class.c +++ b/src/class.c @@ -327,12 +327,6 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro { khash_t(mt) *h; khiter_t k; - - if (!c->origin) { - printf("Warning, class %s does not have valid origin\n", mrb_class_name(mrb, c)); - mrb_raisef(mrb, E_RUNTIME_ERROR, "Invalid origin"); - c->origin = c; - } h = c->origin->mt; if (!h) h = c->mt = kh_init(mt, mrb); -- cgit v1.2.3 From 40f48034d512a8e4f24f8d67373f18093e96513c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 08:25:43 -0500 Subject: Added changed check to the mrb_include_module --- src/class.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index e9df1dae4..4a64e5b23 100644 --- a/src/class.c +++ b/src/class.c @@ -834,7 +834,10 @@ include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - include_module_at(mrb, c, c->origin, m, 1); + int changed = include_module_at(mrb, c, c->origin, m, 1); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); + } } MRB_API void -- cgit v1.2.3 From d4b009b394d3933ee1b828d0e91d3c6aaa522339 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:09:13 -0500 Subject: Fixed Modules not being fully initialized before #initialize was called --- src/class.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/class.c b/src/class.c index 4a64e5b23..d83216cb4 100644 --- a/src/class.c +++ b/src/class.c @@ -77,7 +77,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); sc->origin = sc; - sc->mt = 0; + sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -771,6 +771,13 @@ boot_defclass(mrb_state *mrb, struct RClass *super) return c; } +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) +{ + mod->origin = mod; + mod->mt = kh_init(mt, mrb); +} + static struct RClass* include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { @@ -1012,10 +1019,8 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod) /* hack, fix missing module->origin */ struct RClass *m = mrb_class_ptr(mod); - if (!m->origin) - m->origin = m; - - mrb_get_args(mrb, "&", &b); + boot_initmod(mrb, m); // bootstrap a newly initialized module + mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } @@ -1574,9 +1579,7 @@ MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); - m->origin = m; - m->mt = kh_init(mt, mrb); - + boot_initmod(mrb, m); return m; } -- cgit v1.2.3 From 99aff17075905738a8e45d8b213b92a291588053 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:09:52 -0500 Subject: Ported a bit more of the MRI Module#prepend tests over Currently kind_of fails miserably, still looking for the reason --- test/t/module.rb | 94 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 3c87c122b..6fa22e8b4 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -474,38 +474,80 @@ end # Not ISO specified +# @!group prepend assert('Module#prepend') do - module M0 - def m1; [:M0] end - end - module M1 - def m1; [:M1, super, :M1] end - end - module M2 - def m1; [:M2, super, :M2] end - end - M3 = Module.new do - def m1; [:M3, super, :M3] end - end - module M4 - def m1; [:M4, super, :M4] end - end + assert('test_prepend') do + module M0 + def m1; [:M0] end + end + module M1 + def m1; [:M1, super, :M1] end + end + module M2 + def m1; [:M2, super, :M2] end + end + M3 = Module.new do + def m1; [:M3, super, :M3] end + end + module M4 + def m1; [:M4, super, :M4] end + end - class P0 - include M0 - prepend M1 - def m1; [:C0, super, :C0] end + class P0 + include M0 + prepend M1 + def m1; [:C0, super, :C0] end + end + class P1 < P0 + prepend M2, M3 + include M4 + def m1; [:C1, super, :C1] end + end + + obj = P1.new + expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] + assert_equal(expected, obj.m1) end - class P1 < P0 - prepend M2, M3 - include M4 - def m1; [:C1, super, :C1] end + + # mruby shouldn't be affected by this since there is + # no visibility control (yet) + assert('test_public_prepend') do + assert_nothing_raised('ruby/ruby #8846') do + Class.new.prepend(Module.new) + end end - obj = P1.new - expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2] - assert_equal(expected, obj.m1) + assert('test_prepend_inheritance') do + bug6654 = '[ruby-core:45914]' + a = Module.new + b = Module.new { include a } + c = Module.new { prepend b } + + assert bug6654 do + # the Module#< operator should be used here instead, but we don't have it + assert_include(c.ancestors, a) + assert_include(c.ancestors, b) + end + + bug8357 = '[ruby-core:54736] [Bug #8357]' + b = Module.new { prepend a } + c = Class.new { include b } + + assert bug8357 do + # the Module#< operator should be used here instead, but we don't have it + assert_include(c.ancestors, a) + assert_include(c.ancestors, b) + end + + bug8357 = '[ruby-core:54742] [Bug #8357]' + t_print c.new.kind_of?(b) + t_print "\n" + t_print [b, b.ancestors, c, c.ancestors].inspect + t_print "\n" + assert_kind_of(b, c.new, bug8357) + end end +# @!endgroup prepend assert('Module#to_s') do module Test4to_sModules -- cgit v1.2.3 From 319553f0ef310b4f098773db713e7fcee18f69be Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:11:12 -0500 Subject: Removed some debug prints from the test --- test/t/module.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 6fa22e8b4..5baaadd2e 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -540,10 +540,6 @@ assert('Module#prepend') do end bug8357 = '[ruby-core:54742] [Bug #8357]' - t_print c.new.kind_of?(b) - t_print "\n" - t_print [b, b.ancestors, c, c.ancestors].inspect - t_print "\n" assert_kind_of(b, c.new, bug8357) end end -- cgit v1.2.3 From 47264bf2898fbfa5cae6709074b5e5564dbbe99c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Sat, 11 Jul 2015 11:32:20 -0500 Subject: Ported all MRI prepend tests And of course, some of them fail miserably --- test/t/module.rb | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 226 insertions(+), 5 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 5baaadd2e..7314d140f 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -1,6 +1,26 @@ ## # Module ISO Test +def labeled_module(name, &block) + Module.new do + singleton_class.class_eval do + define_method(:to_s) { name } + alias_method :inspect, :to_s + end + class_eval(&block) if block + end +end + +def labeled_class(name, supklass = Object, &block) + Class.new(supklass) do + singleton_class.class_eval do + define_method(:to_s) { name } + alias_method :inspect, :to_s + end + class_eval(&block) if block + end +end + assert('Module', '15.2.2') do assert_equal Class, Module.class end @@ -519,9 +539,9 @@ assert('Module#prepend') do assert('test_prepend_inheritance') do bug6654 = '[ruby-core:45914]' - a = Module.new - b = Module.new { include a } - c = Module.new { prepend b } + a = labeled_module('a') + b = labeled_module('b') { include a } + c = labeled_module('c') { prepend b } assert bug6654 do # the Module#< operator should be used here instead, but we don't have it @@ -530,8 +550,8 @@ assert('Module#prepend') do end bug8357 = '[ruby-core:54736] [Bug #8357]' - b = Module.new { prepend a } - c = Class.new { include b } + b = labeled_module('b') { prepend a } + c = labeled_class('c') { include b } assert bug8357 do # the Module#< operator should be used here instead, but we don't have it @@ -542,6 +562,207 @@ assert('Module#prepend') do bug8357 = '[ruby-core:54742] [Bug #8357]' assert_kind_of(b, c.new, bug8357) end + + assert('test_prepend_instance_methods') do + bug6655 = '[ruby-core:45915]' + assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655) + end + + assert 'test_prepend_singleton_methods' do + o = Object.new + o.singleton_class.class_eval {prepend Module.new} + assert_equal([], o.singleton_methods) + end + + assert 'test_prepend_remove_method' do + c = Class.new do + prepend Module.new { def foo; end } + end + assert_raise(NameError) do + c.class_eval do + remove_method(:foo) + end + end + c.class_eval do + def foo; end + end + removed = nil + c.singleton_class.class_eval do + define_method(:method_removed) {|id| removed = id} + end + assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do + c.class_eval do + remove_method(:foo) + end + end + assert_equal(:foo, removed) + end + + assert 'test_prepend_class_ancestors' do + bug6658 = '[ruby-core:45919]' + m = labeled_module("m") + c = labeled_class("c") {prepend m} + assert_equal([m, c], c.ancestors[0, 2], bug6658) + + bug6662 = '[ruby-dev:45868]' + c2 = labeled_class("c2", c) + anc = c2.ancestors + assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662) + end + + # this assertion causes the mrbtest to segfault + #assert 'test_prepend_module_ancestors' do + # bug6659 = '[ruby-dev:45861]' + # m0 = labeled_module("m0") { def x; [:m0, *super] end } + # m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } + # m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } + # c0 = labeled_class("c0") { def x; [:c0] end } + # c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } + # c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } + # # + # assert_equal([m0, m1], m1.ancestors, bug6659) + # # + # bug6662 = '[ruby-dev:45868]' + # assert_equal([m0, m1, m2], m2.ancestors, bug6662) + # assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) + # assert_equal([:m0, :m1, :m2, :c1], c1.new.x) + # assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) + # assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) + # # + # m3 = labeled_module("m3") { include m1; prepend m1 } + # assert_equal([m3, m0, m1], m3.ancestors) + # m3 = labeled_module("m3") { prepend m1; include m1 } + # assert_equal([m0, m1, m3], m3.ancestors) + # m3 = labeled_module("m3") { prepend m1; prepend m1 } + # assert_equal([m0, m1, m3], m3.ancestors) + # m3 = labeled_module("m3") { include m1; include m1 } + # assert_equal([m3, m0, m1], m3.ancestors) + #end + + assert 'test_prepend_instance_methods_false' do + bug6660 = '[ruby-dev:45863]' + assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660) + assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660) + end + + assert 'test_cyclic_prepend' do + bug7841 = '[ruby-core:52205] [Bug #7841]' + m1 = Module.new + m2 = Module.new + m1.instance_eval { prepend(m2) } + assert_raise(ArgumentError, bug7841) do + m2.instance_eval { prepend(m1) } + end + end + + # these assertions will not run without a #assert_seperately method + #assert 'test_prepend_optmethod' do + # bug7983 = '[ruby-dev:47124] [Bug #7983]' + # assert_separately [], %{ + # module M + # def /(other) + # to_f / other + # end + # end + # Fixnum.send(:prepend, M) + # assert_equal(0.5, 1 / 2, "#{bug7983}") + # } + # assert_equal(0, 1 / 2) + #end + + #assert 'test_prepend_visibility' do + # bug8005 = '[ruby-core:53106] [Bug #8005]' + # c = Class.new do + # prepend Module.new {} + # def foo() end + # protected :foo + # end + # a = c.new + # assert_respond_to a, [:foo, true], bug8005 + # assert_nothing_raised(NoMethodError, bug8005) {a.send :foo} + #end + + #assert 'test_prepend_visibility_inherited' do + # bug8238 = '[ruby-core:54105] [Bug #8238]' + # assert_separately [], <<-"end;", timeout: 20 + # class A + # def foo() A; end + # private :foo + # end + # class B < A + # public :foo + # prepend Module.new + # end + # assert_equal(A, B.new.foo, "#{bug8238}") + # end; + #end + + assert 'test_prepend_included_modules' do + bug8025 = '[ruby-core:53158] [Bug #8025]' + mixin = labeled_module("mixin") + c = labeled_module("c") {prepend mixin} + im = c.included_modules + assert_not_include(im, c, bug8025) + assert_include(im, mixin, bug8025) + c1 = labeled_class("c1") {prepend mixin} + c2 = labeled_class("c2", c1) + im = c2.included_modules + assert_not_include(im, c1, bug8025) + assert_not_include(im, c2, bug8025) + assert_include(im, mixin, bug8025) + end + + assert 'test_prepend_super_in_alias' do + bug7842 = '[Bug #7842]' + + p = labeled_module("P") do + def m; "P"+super; end + end + a = labeled_class("A") do + def m; "A"; end + end + b = labeled_class("B", a) do + def m; "B"+super; end + alias m2 m + prepend p + alias m3 m + end + assert_equal("BA", b.new.m2, bug7842) + assert_equal("PBA", b.new.m3, bug7842) + end + + assert 'test_prepend_each_classes' do + m = labeled_module("M") + c1 = labeled_class("C1") {prepend m} + c2 = labeled_class("C2", c1) {prepend m} + assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each classes") + end + + assert 'test_prepend_no_duplication' do + m = labeled_module("M") + c = labeled_class("C") {prepend m; prepend m} + assert_equal([m, c], c.ancestors[0, 2], "should never duplicate") + end + + assert 'test_prepend_in_superclass' do + m = labeled_module("M") + c1 = labeled_class("C1") + c2 = labeled_class("C2", c1) {prepend m} + c1.class_eval {prepend m} + assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass") + end + + # requires #assert_seperately + #assert 'test_prepend_call_super' do + # assert_separately([], <<-'end;') #do + # bug10847 = '[ruby-core:68093] [Bug #10847]' + # module M; end + # Float.prepend M + # assert_nothing_raised(SystemStackError, bug10847) do + # 0.3.numerator + # end + # end; + #end end # @!endgroup prepend -- cgit v1.2.3 From a725cb909333335ad178997795e84c332e555c65 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 12 Jul 2015 22:07:16 +0200 Subject: Include prepended methods in the instance_methods list. --- src/kernel.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/kernel.c b/src/kernel.c index bafab6e76..36ad683ee 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -653,13 +653,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl { khint_t i; mrb_value ary; + mrb_bool prepended; struct RClass* oldklass; khash_t(st)* set = kh_init(st, mrb); + if (!recur && klass->origin != klass) { + klass = klass->origin; + prepended = 1; + } + oldklass = 0; while (klass && (klass != oldklass)) { method_entry_loop(mrb, klass, set); - if ((klass->tt == MRB_TT_ICLASS) || + if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { -- cgit v1.2.3 From 11dad715784a61174b68fba57f6dc6c5311e22e3 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 12 Jul 2015 22:57:29 +0200 Subject: Enable visibility prepend tests again --- test/t/module.rb | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 7314d140f..89cc257a6 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -670,32 +670,34 @@ assert('Module#prepend') do # assert_equal(0, 1 / 2) #end - #assert 'test_prepend_visibility' do - # bug8005 = '[ruby-core:53106] [Bug #8005]' - # c = Class.new do - # prepend Module.new {} - # def foo() end - # protected :foo - # end - # a = c.new - # assert_respond_to a, [:foo, true], bug8005 - # assert_nothing_raised(NoMethodError, bug8005) {a.send :foo} - #end - - #assert 'test_prepend_visibility_inherited' do - # bug8238 = '[ruby-core:54105] [Bug #8238]' - # assert_separately [], <<-"end;", timeout: 20 - # class A - # def foo() A; end - # private :foo - # end - # class B < A - # public :foo - # prepend Module.new - # end - # assert_equal(A, B.new.foo, "#{bug8238}") - # end; - #end + # mruby has no visibility control + assert 'test_prepend_visibility' do + bug8005 = '[ruby-core:53106] [Bug #8005]' + c = Class.new do + prepend Module.new {} + def foo() end + protected :foo + end + a = c.new + assert_true a.respond_to?(:foo), bug8005 + assert_nothing_raised(NoMethodError, bug8005) {a.send :foo} + end + + # mruby has no visibility control + assert 'test_prepend_visibility_inherited' do + bug8238 = '[ruby-core:54105] [Bug #8238]' + module Test4PrependVisibilityInherited + class A + def foo() A; end + private :foo + end + class B < A + public :foo + prepend Module.new + end + end + assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}") + end assert 'test_prepend_included_modules' do bug8025 = '[ruby-core:53158] [Bug #8025]' -- cgit v1.2.3 From 668153092f48275279f85d2618fff6eb9730332c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:38:24 -0500 Subject: Added Module#method_removed hook --- src/class.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/class.c b/src/class.c index d83216cb4..0b00a6484 100644 --- a/src/class.c +++ b/src/class.c @@ -1945,6 +1945,7 @@ remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) k = kh_get(mt, mrb, h, mid); if (k != kh_end(h)) { kh_del(mt, mrb, h, k); + mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); return; } } @@ -2197,6 +2198,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); -- cgit v1.2.3 From 1f678a4acefc14531a2ed25c33f62a0d628b1474 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:40:40 -0500 Subject: Removed comment beside method_removed Not sure if this apart of the ISO standard, so make sure its not misrepresented --- src/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index 0b00a6484..a13da63ac 100644 --- a/src/class.c +++ b/src/class.c @@ -2198,7 +2198,7 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ - mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.41 */ + mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); -- cgit v1.2.3 From 199a808e36197bd6f893112bbd43f18f2c25345f Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:49:18 -0500 Subject: Bugfix, included_modules would include classes --- src/class.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index a13da63ac..5450f3a20 100644 --- a/src/class.c +++ b/src/class.c @@ -1004,7 +1004,9 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) result = mrb_ary_new(mrb); while (c) { if (c->tt == MRB_TT_ICLASS) { - mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + if (c->c->tt == MRB_TT_MODULE) { + mrb_ary_push(mrb, result, mrb_obj_value(c->c)); + } } c = c->super; } -- cgit v1.2.3 From 81a2b3431c63c969f73e6c0eeaa968acbace0e44 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 09:49:51 -0500 Subject: included_modules, origin fix Prepended modules would include their origin ICLASS --- src/class.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index 5450f3a20..cde5736e0 100644 --- a/src/class.c +++ b/src/class.c @@ -1000,10 +1000,11 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); + struct RClass *origin = c->origin; result = mrb_ary_new(mrb); while (c) { - if (c->tt == MRB_TT_ICLASS) { + if (c != origin && c->tt == MRB_TT_ICLASS) { if (c->c->tt == MRB_TT_MODULE) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } -- cgit v1.2.3 From 11cb41770dc7b36edac0af1e6784bd40d1c8243c Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Mon, 13 Jul 2015 10:42:33 -0500 Subject: Space out test_prepend_super_in_alias assert Also tried to fix it, however the problem lies with how aliased methods are done and their internal structure. mruby simply aliases methods by grabbing the RProc and giving it a new name, super then determines the original method to call by using the name so a method called m, aliased as m2, will call the m2 super method instead of m --- test/t/module.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 89cc257a6..605ca28a7 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -720,17 +720,25 @@ assert('Module#prepend') do p = labeled_module("P") do def m; "P"+super; end end + a = labeled_class("A") do def m; "A"; end end + b = labeled_class("B", a) do def m; "B"+super; end alias m2 m prepend p alias m3 m end - assert_equal("BA", b.new.m2, bug7842) - assert_equal("PBA", b.new.m3, bug7842) + + assert_nothing_raised do + assert_equal("BA", b.new.m2, bug7842) + end + + assert_nothing_raised do + assert_equal("PBA", b.new.m3, bug7842) + end end assert 'test_prepend_each_classes' do -- cgit v1.2.3 From b0fb9ccfd8cb5986ea153142687cfbed1a876d6b Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 13 Jul 2015 23:25:01 +0200 Subject: Enable test_prepend_module_ancestors because it seems to pass. --- test/t/module.rb | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 605ca28a7..7b4a8fec7 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -611,33 +611,33 @@ assert('Module#prepend') do end # this assertion causes the mrbtest to segfault - #assert 'test_prepend_module_ancestors' do - # bug6659 = '[ruby-dev:45861]' - # m0 = labeled_module("m0") { def x; [:m0, *super] end } - # m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } - # m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } - # c0 = labeled_class("c0") { def x; [:c0] end } - # c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } - # c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } - # # - # assert_equal([m0, m1], m1.ancestors, bug6659) - # # - # bug6662 = '[ruby-dev:45868]' - # assert_equal([m0, m1, m2], m2.ancestors, bug6662) - # assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) - # assert_equal([:m0, :m1, :m2, :c1], c1.new.x) - # assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) - # assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) - # # - # m3 = labeled_module("m3") { include m1; prepend m1 } - # assert_equal([m3, m0, m1], m3.ancestors) - # m3 = labeled_module("m3") { prepend m1; include m1 } - # assert_equal([m0, m1, m3], m3.ancestors) - # m3 = labeled_module("m3") { prepend m1; prepend m1 } - # assert_equal([m0, m1, m3], m3.ancestors) - # m3 = labeled_module("m3") { include m1; include m1 } - # assert_equal([m3, m0, m1], m3.ancestors) - #end + assert 'test_prepend_module_ancestors' do + bug6659 = '[ruby-dev:45861]' + m0 = labeled_module("m0") { def x; [:m0, *super] end } + m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } + m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 } + c0 = labeled_class("c0") { def x; [:c0] end } + c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 } + c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 } + # + assert_equal([m0, m1], m1.ancestors, bug6659) + # + bug6662 = '[ruby-dev:45868]' + assert_equal([m0, m1, m2], m2.ancestors, bug6662) + assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662) + assert_equal([:m0, :m1, :m2, :c1], c1.new.x) + assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662) + assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x) + # + m3 = labeled_module("m3") { include m1; prepend m1 } + assert_equal([m3, m0, m1], m3.ancestors) + m3 = labeled_module("m3") { prepend m1; include m1 } + assert_equal([m0, m1, m3], m3.ancestors) + m3 = labeled_module("m3") { prepend m1; prepend m1 } + assert_equal([m0, m1, m3], m3.ancestors) + m3 = labeled_module("m3") { include m1; include m1 } + assert_equal([m3, m0, m1], m3.ancestors) + end assert 'test_prepend_instance_methods_false' do bug6660 = '[ruby-dev:45863]' -- cgit v1.2.3 From 8c13e2b7c6e6c1c02771e4f9e5aebda109892503 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 13 Jul 2015 23:35:30 +0200 Subject: Set origin when doing kind_of? comparisons --- src/object.c | 1 + test/t/module.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/object.c b/src/object.c index f8f41bfe8..c834ee04f 100644 --- a/src/object.c +++ b/src/object.c @@ -487,6 +487,7 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } + c = c->origin; while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; diff --git a/test/t/module.rb b/test/t/module.rb index 7b4a8fec7..a1996f5f6 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -543,21 +543,21 @@ assert('Module#prepend') do b = labeled_module('b') { include a } c = labeled_module('c') { prepend b } - assert bug6654 do + #assert bug6654 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) - end + #end bug8357 = '[ruby-core:54736] [Bug #8357]' b = labeled_module('b') { prepend a } c = labeled_class('c') { include b } - assert bug8357 do + #assert bug8357 do # the Module#< operator should be used here instead, but we don't have it assert_include(c.ancestors, a) assert_include(c.ancestors, b) - end + #end bug8357 = '[ruby-core:54742] [Bug #8357]' assert_kind_of(b, c.new, bug8357) -- cgit v1.2.3 From 8c4da7accd98fb36e3f1f5f9e4ea069244345858 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 13 Jul 2015 23:38:37 +0200 Subject: assert() cannot be nested --- test/t/module.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index a1996f5f6..5aba53c46 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -495,7 +495,6 @@ end # Not ISO specified # @!group prepend -assert('Module#prepend') do assert('test_prepend') do module M0 def m1; [:M0] end @@ -773,7 +772,6 @@ assert('Module#prepend') do # end # end; #end -end # @!endgroup prepend assert('Module#to_s') do -- cgit v1.2.3 From 78462c9181ccbc92d26a466c5a3f06aec2d2f714 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 13 Jul 2015 23:46:25 +0200 Subject: Clean up tests --- test/t/module.rb | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/t/module.rb b/test/t/module.rb index 5aba53c46..4bde20fbe 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -495,7 +495,7 @@ end # Not ISO specified # @!group prepend - assert('test_prepend') do + assert('Module#prepend') do module M0 def m1; [:M0] end end @@ -530,13 +530,13 @@ end # mruby shouldn't be affected by this since there is # no visibility control (yet) - assert('test_public_prepend') do + assert('Module#prepend public') do assert_nothing_raised('ruby/ruby #8846') do Class.new.prepend(Module.new) end end - assert('test_prepend_inheritance') do + assert('Module#prepend inheritance') do bug6654 = '[ruby-core:45914]' a = labeled_module('a') b = labeled_module('b') { include a } @@ -562,18 +562,18 @@ end assert_kind_of(b, c.new, bug8357) end - assert('test_prepend_instance_methods') do + assert('Moduler#prepend + #instance_methods') do bug6655 = '[ruby-core:45915]' assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655) end - assert 'test_prepend_singleton_methods' do + assert 'Module#prepend + #singleton_methods' do o = Object.new o.singleton_class.class_eval {prepend Module.new} assert_equal([], o.singleton_methods) end - assert 'test_prepend_remove_method' do + assert 'Module#prepend + #remove_method' do c = Class.new do prepend Module.new { def foo; end } end @@ -597,7 +597,7 @@ end assert_equal(:foo, removed) end - assert 'test_prepend_class_ancestors' do + assert 'Module#prepend + Class#ancestors' do bug6658 = '[ruby-core:45919]' m = labeled_module("m") c = labeled_class("c") {prepend m} @@ -609,8 +609,7 @@ end assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662) end - # this assertion causes the mrbtest to segfault - assert 'test_prepend_module_ancestors' do + assert 'Module#prepend + Module#ancestors' do bug6659 = '[ruby-dev:45861]' m0 = labeled_module("m0") { def x; [:m0, *super] end } m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 } @@ -638,13 +637,13 @@ end assert_equal([m3, m0, m1], m3.ancestors) end - assert 'test_prepend_instance_methods_false' do + assert 'Module#prepend #instance_methods(false)' do bug6660 = '[ruby-dev:45863]' assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660) assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660) end - assert 'test_cyclic_prepend' do + assert 'cyclic Module#prepend' do bug7841 = '[ruby-core:52205] [Bug #7841]' m1 = Module.new m2 = Module.new @@ -670,7 +669,7 @@ end #end # mruby has no visibility control - assert 'test_prepend_visibility' do + assert 'Module#prepend visibility' do bug8005 = '[ruby-core:53106] [Bug #8005]' c = Class.new do prepend Module.new {} @@ -683,7 +682,7 @@ end end # mruby has no visibility control - assert 'test_prepend_visibility_inherited' do + assert 'Module#prepend inherited visibility' do bug8238 = '[ruby-core:54105] [Bug #8238]' module Test4PrependVisibilityInherited class A @@ -698,7 +697,7 @@ end assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}") end - assert 'test_prepend_included_modules' do + assert 'Module#prepend + #included_modules' do bug8025 = '[ruby-core:53158] [Bug #8025]' mixin = labeled_module("mixin") c = labeled_module("c") {prepend mixin} @@ -713,7 +712,8 @@ end assert_include(im, mixin, bug8025) end - assert 'test_prepend_super_in_alias' do + assert 'Module#prepend super in alias' do + skip "super does not currently work in aliased methods" bug7842 = '[Bug #7842]' p = labeled_module("P") do @@ -740,20 +740,20 @@ end end end - assert 'test_prepend_each_classes' do + assert 'Module#prepend each class' do m = labeled_module("M") c1 = labeled_class("C1") {prepend m} c2 = labeled_class("C2", c1) {prepend m} - assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each classes") + assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class") end - assert 'test_prepend_no_duplication' do + assert 'Module#prepend no duplication' do m = labeled_module("M") c = labeled_class("C") {prepend m; prepend m} assert_equal([m, c], c.ancestors[0, 2], "should never duplicate") end - assert 'test_prepend_in_superclass' do + assert 'Module#prepend in superclass' do m = labeled_module("M") c1 = labeled_class("C1") c2 = labeled_class("C2", c1) {prepend m} @@ -762,7 +762,7 @@ end end # requires #assert_seperately - #assert 'test_prepend_call_super' do + #assert 'Module#prepend call super' do # assert_separately([], <<-'end;') #do # bug10847 = '[ruby-core:68093] [Bug #10847]' # module M; end -- cgit v1.2.3 From f0e920baf07e17fd1073566d7e9605f9192d766d Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 08:55:53 -0500 Subject: Renamed parameters in include_module_at --- src/class.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/class.c b/src/class.c index cde5736e0..14d81495e 100644 --- a/src/class.c +++ b/src/class.c @@ -798,26 +798,26 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) } MRB_API int -include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct RClass *module, int search_super) +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { - struct RClass *p, *iclass; - void *klass_mt = klass->origin->mt; + struct RClass *p, *ic; + void *klass_mt = c->origin->mt; - while (module) { + while (m) { int superclass_seen = 0; - if (module->origin != module) + if (m->origin != m) goto skip; - if (klass_mt && klass_mt == module->mt) + if (klass_mt && klass_mt == m->mt) return -1; - p = klass->super; + p = c->super; while(p) { if (p->tt == MRB_TT_ICLASS) { - if (p->mt == module->mt) { + if (p->mt == m->mt) { if (!superclass_seen) { - c = p; // move insert point + ins_pos = p; // move insert point } goto skip; } @@ -828,12 +828,12 @@ include_module_at(mrb_state *mrb, struct RClass *klass, struct RClass *c, struct p = p->super; } - iclass = include_class_new(mrb, module, c->super); - c->super = iclass; - mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)c->super); - c = iclass; + ic = include_class_new(mrb, m, ins_pos->super); + ins_pos->super = ic; + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super); + ins_pos = ic; skip: - module = module->super; + m = m->super; } return 0; } -- cgit v1.2.3 From eb172c28d79b4fdf978e78fc7e929caa855dd29b Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 09:44:04 -0500 Subject: Applied gc patch to fix ORIGIN ICLASS method table leak Based on the gc patch by ko1 https://github.com/ruby/ruby/commit/5922c954614e5947a548780bb3b894626affe6dd --- include/mruby/class.h | 7 +++++-- include/mruby/object.h | 2 ++ src/class.c | 1 + src/gc.c | 12 ++++++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/mruby/class.h b/include/mruby/class.h index 60310ae9d..9f2c32bb0 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -49,8 +49,11 @@ mrb_class(mrb_state *mrb, mrb_value v) } } -#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt) -#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff) +// TODO: figure out where to put user flags +#define MRB_FLAG_IS_ORIGIN (1 << 20) +#define MRB_FLAG_IS_INSTANCE (0xFF) +#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_FLAG_IS_INSTANCE) | (char)tt) +#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_FLAG_IS_INSTANCE) MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); diff --git a/include/mruby/object.h b/include/mruby/object.h index fe55620fe..6633a23e8 100644 --- a/include/mruby/object.h +++ b/include/mruby/object.h @@ -14,6 +14,8 @@ struct RClass *c;\ struct RBasic *gcnext +#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag) + /* white: 011, black: 100, gray: 000 */ #define MRB_GC_GRAY 0 #define MRB_GC_WHITE_A 1 diff --git a/src/class.c b/src/class.c index 14d81495e..58742299b 100644 --- a/src/class.c +++ b/src/class.c @@ -856,6 +856,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) origin = c->origin; if (origin == c) { origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->flags |= MRB_FLAG_IS_ORIGIN; origin->origin = origin; origin->super = c->super; c->super = origin; diff --git a/src/gc.c b/src/gc.c index 8bd8243f1..15e1bd423 100644 --- a/src/gc.c +++ b/src/gc.c @@ -498,7 +498,12 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: - mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } break; case MRB_TT_CLASS: @@ -624,7 +629,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj) mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); break; - + case MRB_TT_ICLASS: + if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN)) + mrb_gc_free_mt(mrb, (struct RClass*)obj); + break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; -- cgit v1.2.3 From dbbf2e26231c04d76badb1889627869d1f9361bb Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Tue, 14 Jul 2015 14:12:23 -0500 Subject: Remove non-applicable "hack" comment --- src/class.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/class.c b/src/class.c index 58742299b..f342ded20 100644 --- a/src/class.c +++ b/src/class.c @@ -1020,8 +1020,6 @@ static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; - - /* hack, fix missing module->origin */ struct RClass *m = mrb_class_ptr(mod); boot_initmod(mrb, m); // bootstrap a newly initialized module mrb_get_args(mrb, "|&", &b); -- cgit v1.2.3 From 667f7788db2b3b78cc4cc65c0f37ab38347116c5 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Wed, 15 Jul 2015 07:27:31 -0500 Subject: Renamed MRB_FLAG_IS_INSTANCE to MRB_INSTANCE_TT_MASK --- include/mruby/class.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mruby/class.h b/include/mruby/class.h index 9f2c32bb0..80a0cbe35 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -51,9 +51,9 @@ mrb_class(mrb_state *mrb, mrb_value v) // TODO: figure out where to put user flags #define MRB_FLAG_IS_ORIGIN (1 << 20) -#define MRB_FLAG_IS_INSTANCE (0xFF) -#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_FLAG_IS_INSTANCE) | (char)tt) -#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_FLAG_IS_INSTANCE) +#define MRB_INSTANCE_TT_MASK (0xFF) +#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt) +#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK) MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*); MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym); -- cgit v1.2.3 From ae1ece72282fdfa558810e8422bb690bbc6d2193 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Thu, 16 Jul 2015 15:25:14 -0500 Subject: Make include_module_at static Since I can't forsee any reason to use it directly inplace of using prepend/include --- src/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class.c b/src/class.c index f342ded20..90c463579 100644 --- a/src/class.c +++ b/src/class.c @@ -797,7 +797,7 @@ include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) return ic; } -MRB_API int +static int include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) { struct RClass *p, *ic; -- cgit v1.2.3 From 26bee4a16b5763b407a842bd1697389961600d68 Mon Sep 17 00:00:00 2001 From: Corey Powell Date: Thu, 16 Jul 2015 15:25:27 -0500 Subject: Added mrb_prepend_module to mruby header --- include/mruby.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mruby.h b/include/mruby.h index 1b792ce90..b4ec13fdc 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -206,6 +206,7 @@ MRB_API struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass* MRB_API struct RClass *mrb_define_module(mrb_state *, const char*); MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value); MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*); +MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*); MRB_API void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec); MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec); -- cgit v1.2.3