Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only disable the compile cache for source files impacted by Ruby 3.0.3 [Bug #18250] #380

Merged
merged 1 commit into from Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,8 @@
# Unreleased

* Only disable the compile cache for source files impacted by [Ruby 3.0.3 [Bug 18250]](https://bugs.ruby-lang.org/issues/18250).
This should keep the performance loss to a minimum.

# 1.9.2

* Disable compile cache if Ruby 3.0.3's ISeq cache bug is detected.
Expand Down
35 changes: 6 additions & 29 deletions lib/bootsnap.rb
Expand Up @@ -57,15 +57,9 @@ def self.setup(
"If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
end

if compile_cache_iseq
if iseq_cache_tracing_bug?
warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommended " \
"to turn `compile_cache_iseq` off on Ruby 2.5"
end

if iseq_cache_anonymous_params_bug?
warn "Your version of Ruby 3.1.0-dev has a bug that break bytecode caching (https://github.com/ruby/ruby/pull/4961)."
end
if compile_cache_iseq && !iseq_cache_supported?
warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommened " \
"to turn `compile_cache_iseq` off on Ruby 2.5"
end

Bootsnap::LoadPathCache.setup(
Expand All @@ -81,28 +75,11 @@ def self.setup(
)
end

def self.iseq_cache_tracing_bug?
return @iseq_cache_tracing_bug if defined? @iseq_cache_tracing_bug
def self.iseq_cache_supported?
return @iseq_cache_supported if defined? @iseq_cache_supported

ruby_version = Gem::Version.new(RUBY_VERSION)
@iseq_cache_tracing_bug = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0')
end

def self.iseq_cache_anonymous_params_bug?
return @iseq_cache_anonymous_params_bug if defined? @iseq_cache_anonymous_params_bug
begin
if defined? RubyVM::InstructionSequence
RubyVM::InstructionSequence.compile("def foo(*); ->{ super }; end").to_binary
RubyVM::InstructionSequence.compile("def foo(**); ->{ super }; end").to_binary
end
false
rescue TypeError
true
end
end

def self.iseq_cache_supported?
!(iseq_cache_tracing_bug? || iseq_cache_anonymous_params_bug?)
@iseq_cache_supported = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0')
end

def self.default_setup
Expand Down
33 changes: 29 additions & 4 deletions lib/bootsnap/compile_cache/iseq.rb
Expand Up @@ -9,10 +9,35 @@ class << self
attr_accessor(:cache_dir)
end

def self.input_to_storage(_, path)
RubyVM::InstructionSequence.compile_file(path).to_binary
rescue SyntaxError
raise(Uncompilable, 'syntax error')
has_ruby_bug_18250 = begin # https://bugs.ruby-lang.org/issues/18250
if defined? RubyVM::InstructionSequence
RubyVM::InstructionSequence.compile("def foo(*); ->{ super }; end; def foo(**); ->{ super }; end").to_binary
end
false
rescue TypeError
true
end

if has_ruby_bug_18250
def self.input_to_storage(_, path)
iseq = begin
RubyVM::InstructionSequence.compile_file(path)
rescue SyntaxError
raise(Uncompilable, 'syntax error')
end

begin
iseq.to_binary
rescue TypeError
raise(Uncompilable, 'ruby bug #18250')
end
end
else
def self.input_to_storage(_, path)
RubyVM::InstructionSequence.compile_file(path).to_binary
rescue SyntaxError
raise(Uncompilable, 'syntax error')
end
end

def self.storage_to_output(binary, _args)
Expand Down
11 changes: 11 additions & 0 deletions test/compile_cache/iseq_cache_test.rb
@@ -0,0 +1,11 @@
# frozen_string_literal: true
require('test_helper')

class CompileCacheISeqTest < Minitest::Test
include(TmpdirHelper)

def test_ruby_bug_18250
Help.set_file('a.rb', 'def foo(*); ->{ super }; end; def foo(**); ->{ super }; end', 100)
Bootsnap::CompileCache::ISeq.fetch('a.rb')
end
end