bootsnap 1.12.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 761e97c3b4772e9c0af1bd8391d70dddebd94addf88e9295e72a790a85deb5e0
4
- data.tar.gz: 17f80fcf67adc6a4e7f9fb475d3e7d5c008c89b491e23804da1f2603e859888c
3
+ metadata.gz: 40e594cf459a80b9e118cf8cdb6d9f306228fe682e3c2af3a1c2b70432f47890
4
+ data.tar.gz: c120e3c24944e9c0568615a0b6ec23d422e949755339c8ffa118f0265e85755b
5
5
  SHA512:
6
- metadata.gz: dca99dc5e57a644e627e8705958c8c4916a9c444c2a64788fd4214aa174d6c3eed7fc15ef7bdb6e06922fb8d1eef3beeff08e316aadbc3015a36e82b1a232309
7
- data.tar.gz: 1c9ffc9c5c04694f4838693edda976f58925649f7b14411d61bec64eca10bb04548c5bb4e2be6ff897c8d5a0e929aa2cd885d017ccd1770530971fedba831ec3
6
+ metadata.gz: 45c52711bd2fbf273f93d27ea4a8ee71d04c81b7444f0f49aa9b672e131544103b74d3326afe5228122dc52844b681c1e18bbb59fe429b2a2216e182e90ac5e7
7
+ data.tar.gz: ad78b4da53ffc8d6653f74ed777bae1987704de2a76062bbebc2b1d3977baeca900b36066994957adf3cefbda446b4393dd18e41637d1a39f9adac42878b6732
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.13.0
4
+
5
+ * Stop decorating `Kernel.load`. This used to be very useful in development because the Rails "classic" autoloader
6
+ was using `Kernel.load` in dev and `Kernel.require` in production. But Zeitwerk is now the default, and it doesn't
7
+ use `Kernel.load` at all.
8
+
9
+ People still using the classic autoloader might want to stick to `bootsnap 1.12`.
10
+
11
+ * Add `Bootsnap.unload_cache!`. Applications can call it at the end of their boot sequence when they know
12
+ no more code will be loaded to reclaim a bit of memory.
13
+
3
14
  # 1.12.0
4
15
 
5
16
  * `bootsnap precompile` CLI will now also precompile `Rakefile` and `.rake` files.
@@ -22,5 +22,5 @@ if RUBY_ENGINE == "ruby"
22
22
 
23
23
  create_makefile("bootsnap/bootsnap")
24
24
  else
25
- File.write("Makefile", dummy_makefile($srcdir).join(""))
25
+ File.write("Makefile", dummy_makefile($srcdir).join)
26
26
  end
data/lib/bootsnap/cli.rb CHANGED
@@ -66,7 +66,7 @@ module Bootsnap
66
66
 
67
67
  # Gems that include JSON or YAML files usually don't put them in `lib/`.
68
68
  # So we look at the gem root.
69
- gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems\/[^/]+}
69
+ gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
70
70
  gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] }.compact.uniq
71
71
  precompile_yaml_files(gem_paths, exclude: gem_exclude)
72
72
  precompile_json_files(gem_paths, exclude: gem_exclude)
@@ -138,8 +138,8 @@ module Bootsnap
138
138
 
139
139
  def precompile_yaml(*yaml_files)
140
140
  Array(yaml_files).each do |yaml_file|
141
- if CompileCache::YAML.precompile(yaml_file)
142
- STDERR.puts(yaml_file) if verbose
141
+ if CompileCache::YAML.precompile(yaml_file) && verbose
142
+ $stderr.puts(yaml_file)
143
143
  end
144
144
  end
145
145
  end
@@ -161,8 +161,8 @@ module Bootsnap
161
161
 
162
162
  def precompile_json(*json_files)
163
163
  Array(json_files).each do |json_file|
164
- if CompileCache::JSON.precompile(json_file)
165
- STDERR.puts(json_file) if verbose
164
+ if CompileCache::JSON.precompile(json_file) && verbose
165
+ $stderr.puts(json_file)
166
166
  end
167
167
  end
168
168
  end
@@ -183,8 +183,8 @@ module Bootsnap
183
183
 
184
184
  def precompile_ruby(*ruby_files)
185
185
  Array(ruby_files).each do |ruby_file|
186
- if CompileCache::ISeq.precompile(ruby_file)
187
- STDERR.puts(ruby_file) if verbose
186
+ if CompileCache::ISeq.precompile(ruby_file) && verbose
187
+ $stderr.puts(ruby_file)
188
188
  end
189
189
  end
190
190
  end
@@ -203,9 +203,9 @@ module Bootsnap
203
203
  end
204
204
 
205
205
  def invalid_usage!(message)
206
- STDERR.puts message
207
- STDERR.puts
208
- STDERR.puts parser
206
+ $stderr.puts message
207
+ $stderr.puts
208
+ $stderr.puts parser
209
209
  1
210
210
  end
211
211
 
@@ -34,14 +34,14 @@ module Bootsnap
34
34
  begin
35
35
  iseq.to_binary
36
36
  rescue TypeError
37
- return UNCOMPILABLE # ruby bug #18250
37
+ UNCOMPILABLE # ruby bug #18250
38
38
  end
39
39
  end
40
40
  else
41
41
  def self.input_to_storage(_, path)
42
42
  RubyVM::InstructionSequence.compile_file(path).to_binary
43
43
  rescue SyntaxError
44
- return UNCOMPILABLE # syntax error
44
+ UNCOMPILABLE # syntax error
45
45
  end
46
46
  end
47
47
 
@@ -49,7 +49,7 @@ module Bootsnap
49
49
  RubyVM::InstructionSequence.load_from_binary(binary)
50
50
  rescue RuntimeError => error
51
51
  if error.message == "broken binary format"
52
- STDERR.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
52
+ $stderr.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
53
53
  nil
54
54
  else
55
55
  raise
@@ -51,13 +51,18 @@ module Bootsnap
51
51
 
52
52
  self.msgpack_factory = MessagePack::Factory.new
53
53
  self.supported_options = [:symbolize_names]
54
- if ::JSON.parse('["foo"]', freeze: true).first.frozen?
55
- if MessagePack.load(MessagePack.dump("foo"), freeze: true).frozen?
56
- self.supported_options = [:freeze]
57
- end
54
+ if supports_freeze?
55
+ self.supported_options = [:freeze]
58
56
  end
59
57
  supported_options.freeze
60
58
  end
59
+
60
+ private
61
+
62
+ def supports_freeze?
63
+ ::JSON.parse('["foo"]', freeze: true).first.frozen? &&
64
+ MessagePack.load(MessagePack.dump("foo"), freeze: true).frozen?
65
+ end
61
66
  end
62
67
 
63
68
  module Patch
@@ -64,6 +64,19 @@ module Bootsnap
64
64
  @implementation::Patch.send(:remove_method, :unsafe_load_file)
65
65
  end
66
66
 
67
+ unless const_defined?(:NoTagsVisitor)
68
+ visitor = Class.new(Psych::Visitors::ToRuby) do
69
+ def visit(target)
70
+ if target.tag
71
+ raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
72
+ end
73
+
74
+ super
75
+ end
76
+ end
77
+ const_set(:NoTagsVisitor, visitor)
78
+ end
79
+
67
80
  # MessagePack serializes symbols as strings by default.
68
81
  # We want them to roundtrip cleanly, so we use a custom factory.
69
82
  # see: https://github.com/msgpack/msgpack-ruby/pull/122
@@ -102,10 +115,8 @@ module Bootsnap
102
115
  if params.include?([:key, :symbolize_names])
103
116
  supported_options << :symbolize_names
104
117
  end
105
- if params.include?([:key, :freeze])
106
- if factory.load(factory.dump("yaml"), freeze: true).frozen?
107
- supported_options << :freeze
108
- end
118
+ if params.include?([:key, :freeze]) && factory.load(factory.dump("yaml"), freeze: true).frozen?
119
+ supported_options << :freeze
109
120
  end
110
121
  supported_options.freeze
111
122
  end
@@ -118,19 +129,7 @@ module Bootsnap
118
129
  ast = ::YAML.parse(payload)
119
130
  return ast unless ast
120
131
 
121
- strict_visitor.create.visit(ast)
122
- end
123
-
124
- def strict_visitor
125
- self::NoTagsVisitor ||= Class.new(Psych::Visitors::ToRuby) do
126
- def visit(target)
127
- if target.tag
128
- raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
129
- end
130
-
131
- super
132
- end
133
- end
132
+ NoTagsVisitor.create.visit(ast)
134
133
  end
135
134
  end
136
135
 
@@ -45,20 +45,19 @@ module Bootsnap
45
45
 
46
46
  # Try to resolve this feature to an absolute path without traversing the
47
47
  # loadpath.
48
- def find(feature, try_extensions: true)
48
+ def find(feature)
49
49
  reinitialize if (@has_relative_paths && dir_changed?) || stale?
50
50
  feature = feature.to_s.freeze
51
51
 
52
52
  return feature if Bootsnap.absolute_path?(feature)
53
53
 
54
54
  if feature.start_with?("./", "../")
55
- return try_extensions ? expand_path(feature) : File.expand_path(feature).freeze
55
+ return expand_path(feature)
56
56
  end
57
57
 
58
58
  @mutex.synchronize do
59
- x = search_index(feature, try_extensions: try_extensions)
59
+ x = search_index(feature)
60
60
  return x if x
61
- return unless try_extensions
62
61
 
63
62
  # Ruby has some built-in features that require lies about.
64
63
  # For example, 'enumerator' is built in. If you require it, ruby
@@ -115,7 +114,7 @@ module Bootsnap
115
114
  def reinitialize(path_obj = @path_obj)
116
115
  @mutex.synchronize do
117
116
  @path_obj = path_obj
118
- ChangeObserver.register(self, @path_obj)
117
+ ChangeObserver.register(@path_obj, self)
119
118
  @index = {}
120
119
  @dirs = {}
121
120
  @generated_at = now
@@ -183,15 +182,11 @@ module Bootsnap
183
182
  end
184
183
 
185
184
  if DLEXT2
186
- def search_index(feature, try_extensions: true)
187
- if try_extensions
188
- try_index(feature + DOT_RB) ||
189
- try_index(feature + DLEXT) ||
190
- try_index(feature + DLEXT2) ||
191
- try_index(feature)
192
- else
185
+ def search_index(feature)
186
+ try_index(feature + DOT_RB) ||
187
+ try_index(feature + DLEXT) ||
188
+ try_index(feature + DLEXT2) ||
193
189
  try_index(feature)
194
- end
195
190
  end
196
191
 
197
192
  def maybe_append_extension(feature)
@@ -201,12 +196,8 @@ module Bootsnap
201
196
  feature
202
197
  end
203
198
  else
204
- def search_index(feature, try_extensions: true)
205
- if try_extensions
206
- try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
207
- else
208
- try_index(feature)
209
- end
199
+ def search_index(feature)
200
+ try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
210
201
  end
211
202
 
212
203
  def maybe_append_extension(feature)
@@ -216,7 +207,7 @@ module Bootsnap
216
207
 
217
208
  s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze
218
209
  if s.respond_to?(:-@)
219
- if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= "2.7"
210
+ if ((-s).equal?(s) && (-s.dup).equal?(s)) || RUBY_VERSION >= "2.7"
220
211
  def try_index(feature)
221
212
  if (path = @index[feature])
222
213
  -File.join(path, feature).freeze
@@ -56,11 +56,22 @@ module Bootsnap
56
56
  end
57
57
  end
58
58
 
59
- def self.register(observer, arr)
59
+ def self.register(arr, observer)
60
60
  return if arr.frozen? # can't register observer, but no need to.
61
61
 
62
62
  arr.instance_variable_set(:@lpc_observer, observer)
63
- arr.extend(ArrayMixin)
63
+ ArrayMixin.instance_methods.each do |method_name|
64
+ arr.singleton_class.send(:define_method, method_name, ArrayMixin.instance_method(method_name))
65
+ end
66
+ end
67
+
68
+ def self.unregister(arr)
69
+ return unless arr.instance_variable_get(:@lpc_observer)
70
+
71
+ ArrayMixin.instance_methods.each do |method_name|
72
+ arr.singleton_class.send(:remove_method, method_name)
73
+ end
74
+ arr.instance_variable_set(:@lpc_observer, nil)
64
75
  end
65
76
  end
66
77
  end
@@ -6,6 +6,8 @@ module Kernel
6
6
  alias_method(:require_without_bootsnap, :require)
7
7
 
8
8
  def require(path)
9
+ return require_without_bootsnap(path) unless Bootsnap::LoadPathCache.enabled?
10
+
9
11
  string_path = Bootsnap.rb_get_path(path)
10
12
  return false if Bootsnap::LoadPathCache.loaded_features_index.key?(string_path)
11
13
 
@@ -32,13 +34,4 @@ module Kernel
32
34
  return ret
33
35
  end
34
36
  end
35
-
36
- alias_method(:load_without_bootsnap, :load)
37
- def load(path, wrap = false)
38
- if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path), try_extensions: false))
39
- load_without_bootsnap(resolved, wrap)
40
- else
41
- load_without_bootsnap(path, wrap)
42
- end
43
- end
44
37
  end
@@ -35,18 +35,18 @@ module Bootsnap
35
35
  return self
36
36
  end
37
37
 
38
- if realpath != path
39
- Path.new(realpath, real: true)
40
- else
38
+ if realpath == path
41
39
  @real = true
42
40
  self
41
+ else
42
+ Path.new(realpath, real: true)
43
43
  end
44
44
  end
45
45
 
46
46
  # True if the path exists, but represents a non-directory object
47
47
  def non_directory?
48
48
  !File.stat(path).directory?
49
- rescue Errno::ENOENT, Errno::ENOTDIR
49
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
50
50
  false
51
51
  end
52
52
 
@@ -101,7 +101,7 @@ module Bootsnap
101
101
  ["", *dirs].each do |dir|
102
102
  curr = begin
103
103
  File.mtime("#{path}/#{dir}").to_i
104
- rescue Errno::ENOENT, Errno::ENOTDIR
104
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EINVAL
105
105
  -1
106
106
  end
107
107
  max = curr if curr > max
@@ -121,16 +121,14 @@ module Bootsnap
121
121
  RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
122
122
 
123
123
  def stability
124
- @stability ||= begin
125
- if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
126
- STABLE
127
- elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
128
- STABLE
129
- elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
130
- STABLE
131
- else
132
- VOLATILE
133
- end
124
+ @stability ||= if Gem.path.detect { |p| expanded_path.start_with?(p.to_s) }
125
+ STABLE
126
+ elsif Bootsnap.bundler? && expanded_path.start_with?(Bundler.bundle_path.to_s)
127
+ STABLE
128
+ elsif expanded_path.start_with?(RUBY_LIBDIR) && !expanded_path.start_with?(RUBY_SITEDIR)
129
+ STABLE
130
+ else
131
+ VOLATILE
134
132
  end
135
133
  end
136
134
  end
@@ -49,11 +49,9 @@ module Bootsnap
49
49
  raise(NestedTransactionError) if @txn_mutex.owned?
50
50
 
51
51
  @txn_mutex.synchronize do
52
- begin
53
- yield
54
- ensure
55
- commit_transaction
56
- end
52
+ yield
53
+ ensure
54
+ commit_transaction
57
55
  end
58
56
  end
59
57
 
@@ -121,11 +119,9 @@ module Bootsnap
121
119
  path = File.dirname(path)
122
120
  end
123
121
  stack.reverse_each do |dir|
124
- begin
125
- Dir.mkdir(dir)
126
- rescue SystemCallError
127
- raise unless File.directory?(dir)
128
- end
122
+ Dir.mkdir(dir)
123
+ rescue SystemCallError
124
+ raise unless File.directory?(dir)
129
125
  end
130
126
  end
131
127
  end
@@ -21,8 +21,12 @@ module Bootsnap
21
21
 
22
22
  CACHED_EXTENSIONS = DLEXT2 ? [DOT_RB, DLEXT, DLEXT2] : [DOT_RB, DLEXT]
23
23
 
24
+ @enabled = false
25
+
24
26
  class << self
25
- attr_reader(:load_path_cache, :loaded_features_index)
27
+ attr_reader(:load_path_cache, :loaded_features_index, :enabled)
28
+ alias_method :enabled?, :enabled
29
+ remove_method(:enabled)
26
30
 
27
31
  def setup(cache_path:, development_mode:)
28
32
  unless supported?
@@ -35,10 +39,20 @@ module Bootsnap
35
39
  @loaded_features_index = LoadedFeaturesIndex.new
36
40
 
37
41
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
42
+ @enabled = true
38
43
  require_relative("load_path_cache/core_ext/kernel_require")
39
44
  require_relative("load_path_cache/core_ext/loaded_features")
40
45
  end
41
46
 
47
+ def unload!
48
+ @enabled = false
49
+ @loaded_features_index = nil
50
+ @realpath_cache = nil
51
+ @load_path_cache = nil
52
+ ChangeObserver.unregister($LOAD_PATH)
53
+ ::Kernel.alias_method(:require_relative, :require_relative_without_bootsnap)
54
+ end
55
+
42
56
  def supported?
43
57
  RUBY_ENGINE == "ruby" &&
44
58
  RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.12.0"
4
+ VERSION = "1.13.0"
5
5
  end
data/lib/bootsnap.rb CHANGED
@@ -47,35 +47,39 @@ module Bootsnap
47
47
  )
48
48
  unless autoload_paths_cache.nil?
49
49
  warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
50
- "If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
51
- "upgrading is recommended."
50
+ "If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
51
+ "upgrading is recommended."
52
52
  end
53
53
 
54
54
  unless disable_trace.nil?
55
55
  warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
56
- "If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
56
+ "If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
57
57
  end
58
58
 
59
59
  if compile_cache_iseq && !iseq_cache_supported?
60
60
  warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommened " \
61
- "to turn `compile_cache_iseq` off on Ruby 2.5"
61
+ "to turn `compile_cache_iseq` off on Ruby 2.5"
62
62
  end
63
63
 
64
64
  if load_path_cache
65
65
  Bootsnap::LoadPathCache.setup(
66
- cache_path: cache_dir + "/bootsnap/load-path-cache",
66
+ cache_path: "#{cache_dir}/bootsnap/load-path-cache",
67
67
  development_mode: development_mode,
68
68
  )
69
69
  end
70
70
 
71
71
  Bootsnap::CompileCache.setup(
72
- cache_dir: cache_dir + "/bootsnap/compile-cache",
72
+ cache_dir: "#{cache_dir}/bootsnap/compile-cache",
73
73
  iseq: compile_cache_iseq,
74
74
  yaml: compile_cache_yaml,
75
75
  json: compile_cache_json,
76
76
  )
77
77
  end
78
78
 
79
+ def self.unload_cache!
80
+ LoadPathCache.unload!
81
+ end
82
+
79
83
  def iseq_cache_supported?
80
84
  return @iseq_cache_supported if defined? @iseq_cache_supported
81
85
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-30 00:00:00.000000000 Z
11
+ date: 2022-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -76,14 +76,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: 2.4.0
79
+ version: 2.5.0
80
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  requirements: []
86
- rubygems_version: 3.2.20
86
+ rubygems_version: 3.3.3
87
87
  signing_key:
88
88
  specification_version: 4
89
89
  summary: Boot large ruby/rails apps faster