diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index fd1310b04e..fc0b693ec2 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1238,3 +1238,60 @@ assert_equal '[:A, [:A, :B]]', %q{ C.new.bar } + +# Same invokesuper bytecode, multiple destinations +assert_equal '[:Forward, :SecondTerminus]', %q{ + module Terminus + def foo = :Terminus + end + + module SecondTerminus + def foo = :SecondTerminus + end + + + module Forward + def foo = [:Forward, super] + end + + class B + include SecondTerminus + end + + class A < B + include Terminus + include Forward + end + + A.new.foo + A.new.foo # compile + + class B + include Forward + alias bar foo + end + + # A.ancestors.take(5) == [A, Forward, Terminus, B, Forward, SecondTerminus] + + A.new.bar +} + +# invokesuper calling into itself +assert_equal '[:B, [:B, :m]]', %q{ + module M + def foo = :m + end + + class B + include M + def foo = [:B, super] + end + + ins = B.new + ins.singleton_class # materialize the singleton class + ins.foo + ins.foo # compile + + ins.singleton_class.define_method(:bar, B.instance_method(:foo)) + ins.bar +} diff --git a/yjit_codegen.c b/yjit_codegen.c index 68fadfc820..d61206c2c1 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -2995,7 +2995,8 @@ gen_send(jitstate_t *jit, ctx_t *ctx) return gen_send_general(jit, ctx, cd, block); } -RBIMPL_ATTR_MAYBE_UNUSED() // not in use as it has problems in some situations. +// Not in use as it's incorrect in some situations. See comments. +RBIMPL_ATTR_MAYBE_UNUSED() static codegen_status_t gen_invokesuper(jitstate_t *jit, ctx_t *ctx) { @@ -3096,6 +3097,9 @@ gen_invokesuper(jitstate_t *jit, ctx_t *ctx) insn_opnd_t recv_opnd = OPND_STACK(argc); mov(cb, REG0, recv); + // FIXME: This guard and the assume_method_lookup_stable() call below isn't + // always enough to correctly replicate the interpreter's behavior of + // searching at runtime for the callee through the method entry of the stack frame. if (!jit_guard_known_klass(jit, ctx, comptime_recv_klass, recv_opnd, comptime_recv, SEND_MAX_DEPTH, side_exit)) { return YJIT_CANT_COMPILE; } diff --git a/yjit_iface.c b/yjit_iface.c index ba3e5bbb83..3d589f5e5e 100644 --- a/yjit_iface.c +++ b/yjit_iface.c @@ -218,8 +218,9 @@ add_lookup_dependency_i(st_data_t *key, st_data_t *value, st_data_t data, int ex return ST_CONTINUE; } -// Remember that a block assumes that rb_callable_method_entry(receiver_klass, mid) == cme and that -// cme is vald. +// Remember that a block assumes that +// `rb_callable_method_entry(receiver_klass, cme->called_id) == cme` and that +// `cme` is valid. // When either of these assumptions becomes invalid, rb_yjit_method_lookup_change() or // rb_yjit_cme_invalidate() invalidates the block. // @@ -230,6 +231,7 @@ assume_method_lookup_stable(VALUE receiver_klass, const rb_callable_method_entry RUBY_ASSERT(!block->receiver_klass && !block->callee_cme); RUBY_ASSERT(cme_validity_dependency); RUBY_ASSERT(method_lookup_dependency); + RUBY_ASSERT(rb_callable_method_entry(receiver_klass, cme->called_id) == cme); RUBY_ASSERT_ALWAYS(RB_TYPE_P(receiver_klass, T_CLASS)); RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(receiver_klass));