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.
This commit is contained in:
Jean Boussier 2025-11-17 19:01:12 +00:00
parent e920ee3289
commit a36ebb18a6
Notes: git 2025-11-25 15:41:00 +00:00
3 changed files with 35 additions and 7 deletions

View File

@ -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

View File

@ -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);
@ -405,6 +409,14 @@ vm_cc_markable(const struct rb_callcache *cc)
return FL_TEST_RAW((VALUE)cc, VM_CALLCACHE_UNMARKABLE) == 0;
}
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)
{
@ -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_;
}

View File

@ -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 {