Skip to content

Commit 3972df5

Browse files
committed
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] ```
1 parent 4cd3493 commit 3972df5

1 file changed

Lines changed: 57 additions & 6 deletions

File tree

‎src/class.c‎

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,6 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
14061406
m->flags |= MRB_FL_CLASS_IS_INHERITED;
14071407
ins_pos->super = ic;
14081408
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
1409-
mrb_mc_clear_by_class(mrb, ins_pos);
14101409
ins_pos = ic;
14111410
skip:
14121411
m = m->super;
@@ -1415,24 +1414,71 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
14151414
return 0;
14161415
}
14171416

1417+
static int
1418+
fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data)
1419+
{
1420+
struct RClass **m = (struct RClass**)data;
1421+
1422+
if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && (obj->flags & MRB_FL_CLASS_IS_ORIGIN) == 0) {
1423+
struct RClass *ic = (struct RClass*)obj;
1424+
include_module_at(mrb, ic, ic, m[1], 1);
1425+
}
1426+
return MRB_EACH_OBJ_OK;
1427+
}
1428+
14181429
MRB_API void
14191430
mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
14201431
{
14211432
mrb_check_frozen(mrb, c);
14221433
if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
14231434
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
14241435
}
1436+
if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) {
1437+
struct RClass *data[2];
1438+
data[0] = c;
1439+
data[1] = m;
1440+
mrb_objspace_each_objects(mrb, fix_include_module, data);
1441+
}
1442+
}
1443+
1444+
static int
1445+
fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data)
1446+
{
1447+
struct RClass **m = (struct RClass**)data;
1448+
struct RClass *c = (struct RClass*)obj;
1449+
1450+
if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) {
1451+
struct RClass *p = c->super;
1452+
while (p) {
1453+
if (c == m[0]) break;
1454+
if (p->tt == MRB_TT_CLASS) break;
1455+
if (p->c == m[0]) {
1456+
include_module_at(mrb, c, c, m[1], 0);
1457+
break;
1458+
}
1459+
c = p;
1460+
p = p->super;
1461+
}
1462+
}
1463+
return MRB_EACH_OBJ_OK;
14251464
}
14261465

14271466
MRB_API void
14281467
mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
14291468
{
14301469
struct RClass *origin;
1431-
int changed = 0;
14321470

14331471
mrb_check_frozen(mrb, c);
14341472
if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
1435-
origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
1473+
struct RClass *c0;
1474+
1475+
if (c->tt == MRB_TT_ICLASS) {
1476+
c0 = c->c;
1477+
}
1478+
else {
1479+
c0 = c;
1480+
}
1481+
origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c0);
14361482
origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
14371483
origin->super = c->super;
14381484
c->super = origin;
@@ -1441,10 +1487,16 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
14411487
mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin);
14421488
c->flags |= MRB_FL_CLASS_IS_PREPENDED;
14431489
}
1444-
changed = include_module_at(mrb, c, c, m, 0);
1445-
if (changed < 0) {
1490+
if (include_module_at(mrb, c, c, m, 0) < 0) {
14461491
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
14471492
}
1493+
if (c->tt == MRB_TT_MODULE &&
1494+
(c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) {
1495+
struct RClass *data[2];
1496+
data[0] = c;
1497+
data[1] = m;
1498+
mrb_objspace_each_objects(mrb, fix_prepend_module, data);
1499+
}
14481500
}
14491501

14501502
static mrb_value
@@ -1651,7 +1703,6 @@ mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c)
16511703

16521704
if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
16531705
mc_clear(mrb);
1654-
c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
16551706
return;
16561707
}
16571708
for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {

0 commit comments

Comments
 (0)