diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index b8ae5a989c..0b469ff5fa 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -130,6 +130,52 @@ assert_normal_exit %q{ foo() } +# Method aliasing +assert_equal '42', %q{ + class Foo + def method_a + 42 + end + + alias method_b method_a + + def method_a + :somethingelse + end + end + + @obj = Foo.new + + def test + @obj.method_b + end + + test + test +} + +# Method aliasing with method from parent class +assert_equal '777', %q{ + class A + def method_a + 777 + end + end + + class B < A + alias method_b method_a + end + + @obj = B.new + + def test + @obj.method_b + end + + test + test +} + # The hash method is a C function and uses the self argument assert_equal 'true', %q{ def lehashself diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 124de73afb..772fa76ffd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3126,6 +3126,12 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me) return cme; } +const rb_callable_method_entry_t * +rb_aliased_callable_method_entry(const rb_callable_method_entry_t *me) +{ + return aliased_callable_method_entry(me); +} + static VALUE vm_call_alias(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { diff --git a/yjit_codegen.c b/yjit_codegen.c index dfcf198978..ba332687b4 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -2093,6 +2093,9 @@ gen_send_iseq(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const r return true; } +const rb_callable_method_entry_t * +rb_aliased_callable_method_entry(const rb_callable_method_entry_t *me); + static codegen_status_t gen_send_general(jitstate_t *jit, ctx_t *ctx, struct rb_call_data *cd, rb_iseq_t *block) { @@ -2177,54 +2180,61 @@ gen_send_general(jitstate_t *jit, ctx_t *ctx, struct rb_call_data *cd, rb_iseq_t // Method calls may corrupt types ctx_clear_local_types(ctx); - switch (cme->def->type) { - case VM_METHOD_TYPE_ISEQ: - return gen_send_iseq(jit, ctx, ci, cme, block, argc); - case VM_METHOD_TYPE_CFUNC: - return gen_send_cfunc(jit, ctx, ci, cme, block, argc); - case VM_METHOD_TYPE_IVAR: - if (argc != 0) { - // Argument count mismatch. Getters take no arguments. - GEN_COUNTER_INC(cb, send_getter_arity); + // To handle the aliased method case (VM_METHOD_TYPE_ALIAS) + while (true) { + // switch on the method type + switch (cme->def->type) { + case VM_METHOD_TYPE_ISEQ: + return gen_send_iseq(jit, ctx, ci, cme, block, argc); + case VM_METHOD_TYPE_CFUNC: + return gen_send_cfunc(jit, ctx, ci, cme, block, argc); + case VM_METHOD_TYPE_IVAR: + if (argc != 0) { + // Argument count mismatch. Getters take no arguments. + GEN_COUNTER_INC(cb, send_getter_arity); + return YJIT_CANT_COMPILE; + } + else { + mov(cb, REG0, recv); + + ID ivar_name = cme->def->body.attr.id; + return gen_get_ivar(jit, ctx, SEND_MAX_DEPTH, comptime_recv, ivar_name, recv_opnd, side_exit); + } + case VM_METHOD_TYPE_ATTRSET: + GEN_COUNTER_INC(cb, send_ivar_set_method); return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_BMETHOD: + GEN_COUNTER_INC(cb, send_bmethod); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_ZSUPER: + GEN_COUNTER_INC(cb, send_zsuper_method); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_ALIAS: { + // Retrieve the alised method and re-enter the switch + cme = rb_aliased_callable_method_entry(cme); + continue; + } + case VM_METHOD_TYPE_UNDEF: + GEN_COUNTER_INC(cb, send_undef_method); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_NOTIMPLEMENTED: + GEN_COUNTER_INC(cb, send_not_implemented_method); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_OPTIMIZED: + GEN_COUNTER_INC(cb, send_optimized_method); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_MISSING: + GEN_COUNTER_INC(cb, send_missing_method); + return YJIT_CANT_COMPILE; + case VM_METHOD_TYPE_REFINED: + GEN_COUNTER_INC(cb, send_refined_method); + return YJIT_CANT_COMPILE; + // no default case so compiler issues a warning if this is not exhaustive } - else { - mov(cb, REG0, recv); - ID ivar_name = cme->def->body.attr.id; - return gen_get_ivar(jit, ctx, SEND_MAX_DEPTH, comptime_recv, ivar_name, recv_opnd, side_exit); - } - case VM_METHOD_TYPE_ATTRSET: - GEN_COUNTER_INC(cb, send_ivar_set_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_BMETHOD: - GEN_COUNTER_INC(cb, send_bmethod); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_ZSUPER: - GEN_COUNTER_INC(cb, send_zsuper_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_ALIAS: - GEN_COUNTER_INC(cb, send_alias_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_UNDEF: - GEN_COUNTER_INC(cb, send_undef_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_NOTIMPLEMENTED: - GEN_COUNTER_INC(cb, send_not_implemented_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_OPTIMIZED: - GEN_COUNTER_INC(cb, send_optimized_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_MISSING: - GEN_COUNTER_INC(cb, send_missing_method); - return YJIT_CANT_COMPILE; - case VM_METHOD_TYPE_REFINED: - GEN_COUNTER_INC(cb, send_refined_method); - return YJIT_CANT_COMPILE; - // no default case so compiler issues a warning if this is not exhaustive + // Unreachable + RUBY_ASSERT(false); } - - return YJIT_CANT_COMPILE; } static codegen_status_t