Skip to content

Commit

Permalink
Merge pull request #380 from Shopify/finely-grained-3.0.3-fix
Browse files Browse the repository at this point in the history
Only disable the compile cache for source files impacted by Ruby 3.0.3 [Bug #18250]
  • Loading branch information
casperisfine committed Nov 24, 2021
2 parents d653813 + ee5982e commit 6d609ae
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 33 deletions.
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

0 comments on commit 6d609ae

Please sign in to comment.