diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2020-12-04 22:02:37 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2020-12-04 22:02:37 +0900 |
| commit | 3972df57fe70a29e6bf6db590dd22651640a1217 (patch) | |
| tree | 287be63c37f79a353d17d06a31e178d52aa3f2de /src/class.c | |
| parent | 4cd349373779bb643444f85fa1d04b0769c58c63 (diff) | |
| download | mruby-3972df57fe70a29e6bf6db590dd22651640a1217.tar.gz mruby-3972df57fe70a29e6bf6db590dd22651640a1217.zip | |
Make `Module#include` and `Module#prepend` behave like Ruby3.0.
Module#include and Module#prepend now affect classes and modules
that have already included or prepended the receiver, mirroring the
behavior if the arguments were included in the receiver before
the other modules and classes included or prepended the receiver.
```ruby
class C; end
module M1; end
module M2; end
C.include M1
M1.include M2
p C.ancestors #=> [C, M1, M2, Object, Kernel, BasicObject]
```
Diffstat (limited to 'src/class.c')
| -rw-r--r-- | src/class.c | 63 |
1 files changed, 57 insertions, 6 deletions
diff --git a/src/class.c b/src/class.c index 6ec2ab8ca..a421325a3 100644 --- a/src/class.c +++ b/src/class.c @@ -1406,7 +1406,6 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); - mrb_mc_clear_by_class(mrb, ins_pos); ins_pos = ic; skip: m = m->super; @@ -1415,6 +1414,18 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru return 0; } +static int +fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data) +{ + struct RClass **m = (struct RClass**)data; + + if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && (obj->flags & MRB_FL_CLASS_IS_ORIGIN) == 0) { + struct RClass *ic = (struct RClass*)obj; + include_module_at(mrb, ic, ic, m[1], 1); + } + return MRB_EACH_OBJ_OK; +} + MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { @@ -1422,17 +1433,52 @@ mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } + if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) { + struct RClass *data[2]; + data[0] = c; + data[1] = m; + mrb_objspace_each_objects(mrb, fix_include_module, data); + } +} + +static int +fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data) +{ + struct RClass **m = (struct RClass**)data; + struct RClass *c = (struct RClass*)obj; + + if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { + struct RClass *p = c->super; + while (p) { + if (c == m[0]) break; + if (p->tt == MRB_TT_CLASS) break; + if (p->c == m[0]) { + include_module_at(mrb, c, c, m[1], 0); + break; + } + c = p; + p = p->super; + } + } + return MRB_EACH_OBJ_OK; } MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { struct RClass *origin; - int changed = 0; mrb_check_frozen(mrb, c); if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { - origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + struct RClass *c0; + + if (c->tt == MRB_TT_ICLASS) { + c0 = c->c; + } + else { + c0 = c; + } + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c0); origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED; origin->super = c->super; c->super = origin; @@ -1441,10 +1487,16 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); c->flags |= MRB_FL_CLASS_IS_PREPENDED; } - changed = include_module_at(mrb, c, c, m, 0); - if (changed < 0) { + if (include_module_at(mrb, c, c, m, 0) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } + if (c->tt == MRB_TT_MODULE && + (c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) { + struct RClass *data[2]; + data[0] = c; + data[1] = m; + mrb_objspace_each_objects(mrb, fix_prepend_module, data); + } } static mrb_value @@ -1651,7 +1703,6 @@ mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c) if (c->flags & MRB_FL_CLASS_IS_INHERITED) { mc_clear(mrb); - c->flags &= ~MRB_FL_CLASS_IS_INHERITED; return; } for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { |
