diff options
-rw-r--r-- | bootstraptest/test_yjit.rb | 31 | ||||
-rw-r--r-- | yjit_codegen.c | 4 |
2 files changed, 35 insertions, 0 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 30298a820d..47744efb73 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1,3 +1,34 @@ +assert_equal '2022', %q{ + def contrivance(hash, key) + # Expect this to compile to an `opt_aref`. + hash[key] + + # The [] call above tracks that the `hash` local has a VALUE that + # is a heap pointer and the guard for the Kernel#itself call below + # doesn't check that it's a heap pointer VALUE. + # + # As you can see from the crash, the call to rb_hash_aref() can set the + # `hash` local, making eliding the heap object guard unsound. + hash.itself + end + + # This is similar to ->(recv, mid) { send(recv, mid).local_variable_set(...) }. + # By composing we avoid creating new Ruby frames and so sending :binding + # captures the environment of the frame that does the missing key lookup. + # We use it to capture the environment inside of `contrivance`. + cap_then_set = + Kernel.instance_method(:send).method(:bind_call).to_proc >> + ->(binding) { binding.local_variable_set(:hash, 2022) } + special_missing = Hash.new(&cap_then_set) + + # Make YJIT speculate that it's a hash and generate code + # that calls rb_hash_aref(). + contrivance({}, :warmup) + contrivance({}, :warmup) + + contrivance(special_missing, :binding) +} + assert_equal '18374962167983112447', %q{ # regression test for incorrectly discarding 32 bits of a pointer when it # comes to default values. diff --git a/yjit_codegen.c b/yjit_codegen.c index aa352dac70..25fcfca083 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -184,6 +184,10 @@ jit_prepare_routine_call(jitstate_t *jit, ctx_t *ctx, x86opnd_t scratch_reg) jit->record_boundary_patch_point = true; jit_save_pc(jit, scratch_reg); jit_save_sp(jit, ctx); + + // In case the routine calls Ruby methods, it can set local variables + // through Kernel#binding and other means. + ctx_clear_local_types(ctx); } // Record the current codeblock write position for rewriting into a jump into |