Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider modified modules initialized [Bug #18185] #4883

Merged
merged 2 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,22 @@ copy_tables(VALUE clone, VALUE orig)

static bool ensure_origin(VALUE klass);

/**
* If this flag is set, that module is allocated but not initialized yet.
*/
enum {RMODULE_ALLOCATED_BUT_NOT_INITIALIZED = RUBY_FL_USER5};

static inline bool
RMODULE_UNINITIALIZED(VALUE module)
{
return RCLASS_SUPER(module) == rb_cBasicObject;
return FL_TEST_RAW(module, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
}

void
rb_module_set_initialized(VALUE mod)
{
FL_UNSET_RAW(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
/* no more re-initialization */
}

void
Expand Down Expand Up @@ -808,7 +820,7 @@ rb_module_s_alloc(VALUE klass)
{
VALUE mod = class_alloc(T_MODULE, klass);
RCLASS_M_TBL_INIT(mod);
RB_OBJ_WRITE(mod, &RCLASS(mod)->super, rb_cBasicObject);
FL_SET(mod, RMODULE_ALLOCATED_BUT_NOT_INITIALIZED);
return mod;
}

Expand Down Expand Up @@ -916,10 +928,7 @@ ensure_includable(VALUE klass, VALUE module)
{
rb_class_modify_check(klass);
Check_Type(module, T_MODULE);
if (RMODULE_UNINITIALIZED(module)) {
RB_OBJ_WRITE(module, &RCLASS(module)->super, 0);
/* no more re-initialization */
}
rb_module_set_initialized(module);
if (!NIL_P(rb_refinement_module_get_refined_class(module))) {
rb_raise(rb_eArgError, "refinement module is not allowed");
}
Expand Down
3 changes: 3 additions & 0 deletions eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ rb_class_modify_check(VALUE klass)
if (SPECIAL_CONST_P(klass)) {
Check_Type(klass, T_CLASS);
}
if (RB_TYPE_P(klass, T_MODULE)) {
rb_module_set_initialized(klass);
}
if (OBJ_FROZEN(klass)) {
const char *desc;

Expand Down
1 change: 1 addition & 0 deletions internal/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ int rb_singleton_class_internal_p(VALUE sklass);
VALUE rb_class_boot(VALUE);
VALUE rb_class_s_alloc(VALUE klass);
VALUE rb_module_s_alloc(VALUE klass);
void rb_module_set_initialized(VALUE module);
void rb_module_check_initializable(VALUE module);
VALUE rb_make_metaclass(VALUE, VALUE);
VALUE rb_include_class_new(VALUE, VALUE);
Expand Down
29 changes: 29 additions & 0 deletions test/ruby/test_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,35 @@ def initialize_copy(other)
end
end

class Bug18185 < Module
module InstanceMethods
end
attr_reader :ancestor_list
def initialize
@ancestor_list = ancestors
include InstanceMethods
end
class Foo
attr_reader :key
def initialize(key:)
@key = key
end
end
end

def test_module_subclass_initialize
mod = Bug18185.new
c = Class.new(Bug18185::Foo) do
include mod
end
anc = c.ancestors
assert_include(anc, mod)
assert_equal(1, anc.count(BasicObject), ->{anc.inspect})
b = c.new(key: 1)
assert_equal(1, b.key)
assert_not_include(mod.ancestor_list, BasicObject)
end

def test_dup
OtherSetup.call

Expand Down