summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2025-11-17 19:01:12 +0000
committerJean Boussier <jean.boussier@gmail.com>2025-11-25 16:40:31 +0100
commita36ebb18a6d4c4726915b6d7c16cfdbf4e5d417b (patch)
treea7319c9eb41a827f83532e7fb1907af95e4ac43c
parente920ee32894dcd2ab0f97ff6f45c29d65024da0c (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.rb15
-rw-r--r--vm_callinfo.h25
-rw-r--r--vm_insnhelper.c2
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 {