diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2025-11-17 19:01:12 +0000 |
|---|---|---|
| committer | Jean Boussier <jean.boussier@gmail.com> | 2025-11-25 16:40:31 +0100 |
| commit | a36ebb18a6d4c4726915b6d7c16cfdbf4e5d417b (patch) | |
| tree | a7319c9eb41a827f83532e7fb1907af95e4ac43c | |
| parent | e920ee32894dcd2ab0f97ff6f45c29d65024da0c (diff) | |
vm_cc_new: don't assume `cme` is present.
[Bug #21694]
`vm_search_super_method` explictly calls `vm_cc_new` with `cme=NULL`
when there is no super class.
| -rw-r--r-- | test/ruby/test_super.rb | 15 | ||||
| -rw-r--r-- | vm_callinfo.h | 25 | ||||
| -rw-r--r-- | vm_insnhelper.c | 2 |
3 files changed, 35 insertions, 7 deletions
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb index 8e973b0f7f..25bad2242a 100644 --- a/test/ruby/test_super.rb +++ b/test/ruby/test_super.rb @@ -759,4 +759,19 @@ class TestSuper < Test::Unit::TestCase inherited = inherited_class.new assert_equal 2, inherited.test # it may read index=1 while it should be index=2 end + + def test_super_in_basic_object + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + class ::BasicObject + def no_super + super() + rescue ::NameError + :ok + end + end + + assert_equal :ok, "[Bug #21694]".no_super + end; + end end diff --git a/vm_callinfo.h b/vm_callinfo.h index 6701b17d76..2c51bf9092 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -302,6 +302,7 @@ struct rb_callcache { #define VM_CALLCACHE_REFINEMENT IMEMO_FL_USER3 #define VM_CALLCACHE_UNMARKABLE IMEMO_FL_USER4 #define VM_CALLCACHE_ON_STACK IMEMO_FL_USER5 +#define VM_CALLCACHE_INVALID_SUPER IMEMO_FL_USER6 enum vm_cc_type { cc_type_normal, // chained from ccs @@ -344,8 +345,6 @@ vm_cc_new(VALUE klass, *((struct rb_callable_method_entry_struct **)&cc->cme_) = (struct rb_callable_method_entry_struct *)cme; *((vm_call_handler *)&cc->call_) = call; - VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_ICLASS)); - switch (type) { case cc_type_normal: break; @@ -358,8 +357,13 @@ vm_cc_new(VALUE klass, break; } - if (cme->def->type == VM_METHOD_TYPE_ATTRSET || cme->def->type == VM_METHOD_TYPE_IVAR) { - vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + if (cme) { + if (cme->def->type == VM_METHOD_TYPE_ATTRSET || cme->def->type == VM_METHOD_TYPE_IVAR) { + vm_cc_attr_index_initialize(cc, INVALID_SHAPE_ID); + } + } + else { + *(VALUE *)&cc->flags |= VM_CALLCACHE_INVALID_SUPER; } RB_DEBUG_COUNTER_INC(cc_new); @@ -406,6 +410,14 @@ vm_cc_markable(const struct rb_callcache *cc) } static inline bool +vm_cc_invalid_super(const struct rb_callcache *cc) +{ + VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); + // Set when calling super and there is no superclass. + return FL_TEST_RAW((VALUE)cc, VM_CALLCACHE_INVALID_SUPER); +} + +static inline bool vm_cc_valid(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); @@ -418,10 +430,11 @@ static inline const struct rb_callable_method_entry_struct * vm_cc_cme(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); - VM_ASSERT(cc->klass != Qundef || !vm_cc_markable(cc)); + VM_ASSERT(cc->klass != Qundef || !vm_cc_markable(cc) || vm_cc_invalid_super(cc)); VM_ASSERT(cc_check_class(cc->klass)); VM_ASSERT(cc->call_ == NULL || // not initialized yet !vm_cc_markable(cc) || + vm_cc_invalid_super(cc) || cc->cme_ != NULL); return cc->cme_; @@ -432,7 +445,7 @@ vm_cc_call(const struct rb_callcache *cc) { VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(cc->call_ != NULL); - VM_ASSERT(cc->klass != Qundef || !vm_cc_markable(cc)); + VM_ASSERT(cc->klass != Qundef || !vm_cc_markable(cc) || vm_cc_invalid_super(cc)); VM_ASSERT(cc_check_class(cc->klass)); return cc->call_; } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 97e63387bb..c9913a92bb 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5164,7 +5164,7 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c if (!klass) { /* bound instance method of module */ - cc = vm_cc_new(klass, NULL, vm_call_method_missing, cc_type_super); + cc = vm_cc_new(Qundef, NULL, vm_call_method_missing, cc_type_super); RB_OBJ_WRITE(reg_cfp->iseq, &cd->cc, cc); } else { |
