bootsnap 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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