bootsnap 1.9.4 → 1.12.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.
@@ -1,80 +1,86 @@
1
1
  # frozen_string_literal: true
2
- require('bootsnap/bootsnap')
2
+
3
+ require("bootsnap/bootsnap")
3
4
 
4
5
  module Bootsnap
5
6
  module CompileCache
6
7
  module YAML
7
- class << self
8
- attr_accessor(:msgpack_factory, :cache_dir, :supported_options)
8
+ Uncompilable = Class.new(StandardError)
9
+ UnsupportedTags = Class.new(Uncompilable)
9
10
 
10
- def input_to_storage(contents, _)
11
- obj = strict_load(contents)
12
- msgpack_factory.dump(obj)
13
- rescue NoMethodError, RangeError
14
- # The object included things that we can't serialize
15
- raise(Uncompilable)
16
- end
11
+ SUPPORTED_INTERNAL_ENCODINGS = [
12
+ nil, # UTF-8
13
+ Encoding::UTF_8,
14
+ Encoding::ASCII,
15
+ Encoding::BINARY,
16
+ ].freeze
17
17
 
18
- def storage_to_output(data, kwargs)
19
- if kwargs && kwargs.key?(:symbolize_names)
20
- kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
21
- end
22
- msgpack_factory.load(data, kwargs)
23
- end
18
+ class << self
19
+ attr_accessor(:msgpack_factory, :supported_options)
20
+ attr_reader(:implementation, :cache_dir)
24
21
 
25
- def input_to_output(data, kwargs)
26
- if ::YAML.respond_to?(:unsafe_load)
27
- ::YAML.unsafe_load(data, **(kwargs || {}))
28
- else
29
- ::YAML.load(data, **(kwargs || {}))
30
- end
22
+ def cache_dir=(cache_dir)
23
+ @cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}yaml" : "#{cache_dir}-yaml"
31
24
  end
32
25
 
33
- def strict_load(payload, *args)
34
- ast = ::YAML.parse(payload)
35
- return ast unless ast
36
- strict_visitor.create(*args).visit(ast)
37
- end
38
- ruby2_keywords :strict_load if respond_to?(:ruby2_keywords, true)
26
+ def precompile(path)
27
+ return false unless CompileCache::YAML.supported_internal_encoding?
39
28
 
40
- def precompile(path, cache_dir: YAML.cache_dir)
41
- Bootsnap::CompileCache::Native.precompile(
29
+ CompileCache::Native.precompile(
42
30
  cache_dir,
43
31
  path.to_s,
44
- Bootsnap::CompileCache::YAML,
32
+ @implementation,
45
33
  )
46
34
  end
47
35
 
48
36
  def install!(cache_dir)
49
37
  self.cache_dir = cache_dir
50
38
  init!
51
- ::YAML.singleton_class.prepend(Patch)
39
+ ::YAML.singleton_class.prepend(@implementation::Patch)
52
40
  end
53
41
 
54
- def init!
55
- require('yaml')
56
- require('msgpack')
57
- require('date')
42
+ # Psych coerce strings to `Encoding.default_internal` but Message Pack only support
43
+ # UTF-8, US-ASCII and BINARY. So if Encoding.default_internal is set to anything else
44
+ # we can't safely use the cache
45
+ def supported_internal_encoding?
46
+ SUPPORTED_INTERNAL_ENCODINGS.include?(Encoding.default_internal)
47
+ end
48
+
49
+ module EncodingAwareSymbols
50
+ extend self
58
51
 
59
- if Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
60
- Patch.send(:remove_method, :unsafe_load_file)
52
+ def unpack(payload)
53
+ (+payload).force_encoding(Encoding::UTF_8).to_sym
61
54
  end
62
- if Patch.method_defined?(:load_file) && ::YAML::VERSION >= '4'
63
- Patch.send(:remove_method, :load_file)
55
+ end
56
+
57
+ def init!
58
+ require("yaml")
59
+ require("msgpack")
60
+ require("date")
61
+
62
+ @implementation = ::YAML::VERSION >= "4" ? Psych4 : Psych3
63
+ if @implementation::Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
64
+ @implementation::Patch.send(:remove_method, :unsafe_load_file)
64
65
  end
65
66
 
66
67
  # MessagePack serializes symbols as strings by default.
67
68
  # We want them to roundtrip cleanly, so we use a custom factory.
68
69
  # see: https://github.com/msgpack/msgpack-ruby/pull/122
69
70
  factory = MessagePack::Factory.new
70
- factory.register_type(0x00, Symbol)
71
+ factory.register_type(
72
+ 0x00,
73
+ Symbol,
74
+ packer: :to_msgpack_ext,
75
+ unpacker: EncodingAwareSymbols.method(:unpack).to_proc,
76
+ )
71
77
 
72
78
  if defined? MessagePack::Timestamp
73
79
  factory.register_type(
74
80
  MessagePack::Timestamp::TYPE, # or just -1
75
81
  Time,
76
82
  packer: MessagePack::Time::Packer,
77
- unpacker: MessagePack::Time::Unpacker
83
+ unpacker: MessagePack::Time::Unpacker,
78
84
  )
79
85
 
80
86
  marshal_fallback = {
@@ -94,70 +100,251 @@ module Bootsnap
94
100
  self.supported_options = []
95
101
  params = ::YAML.method(:load).parameters
96
102
  if params.include?([:key, :symbolize_names])
97
- self.supported_options << :symbolize_names
103
+ supported_options << :symbolize_names
98
104
  end
99
105
  if params.include?([:key, :freeze])
100
- if factory.load(factory.dump('yaml'), freeze: true).frozen?
101
- self.supported_options << :freeze
106
+ if factory.load(factory.dump("yaml"), freeze: true).frozen?
107
+ supported_options << :freeze
102
108
  end
103
109
  end
104
- self.supported_options.freeze
110
+ supported_options.freeze
111
+ end
112
+
113
+ def patch
114
+ @implementation::Patch
115
+ end
116
+
117
+ def strict_load(payload)
118
+ ast = ::YAML.parse(payload)
119
+ return ast unless ast
120
+
121
+ strict_visitor.create.visit(ast)
105
122
  end
106
123
 
107
124
  def strict_visitor
108
125
  self::NoTagsVisitor ||= Class.new(Psych::Visitors::ToRuby) do
109
126
  def visit(target)
110
127
  if target.tag
111
- raise Uncompilable, "YAML tags are not supported: #{target.tag}"
128
+ raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
112
129
  end
130
+
113
131
  super
114
132
  end
115
133
  end
116
134
  end
117
135
  end
118
136
 
119
- module Patch
120
- def load_file(path, *args)
121
- return super if args.size > 1
122
- if kwargs = args.first
123
- return super unless kwargs.is_a?(Hash)
124
- return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
137
+ module Psych4
138
+ extend self
139
+
140
+ def input_to_storage(contents, _)
141
+ obj = SafeLoad.input_to_storage(contents, nil)
142
+ if UNCOMPILABLE.equal?(obj)
143
+ obj = UnsafeLoad.input_to_storage(contents, nil)
125
144
  end
145
+ obj
146
+ end
126
147
 
127
- begin
128
- ::Bootsnap::CompileCache::Native.fetch(
129
- Bootsnap::CompileCache::YAML.cache_dir,
130
- File.realpath(path),
131
- ::Bootsnap::CompileCache::YAML,
132
- kwargs,
133
- )
134
- rescue Errno::EACCES
135
- ::Bootsnap::CompileCache.permission_error(path)
148
+ module UnsafeLoad
149
+ extend self
150
+
151
+ def input_to_storage(contents, _)
152
+ obj = ::YAML.unsafe_load(contents)
153
+ packer = CompileCache::YAML.msgpack_factory.packer
154
+ packer.pack(false) # not safe loaded
155
+ begin
156
+ packer.pack(obj)
157
+ rescue NoMethodError, RangeError
158
+ return UNCOMPILABLE # The object included things that we can't serialize
159
+ end
160
+ packer.to_s
161
+ end
162
+
163
+ def storage_to_output(data, kwargs)
164
+ if kwargs&.key?(:symbolize_names)
165
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
166
+ end
167
+
168
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
169
+ unpacker.feed(data)
170
+ _safe_loaded = unpacker.unpack
171
+ unpacker.unpack
172
+ end
173
+
174
+ def input_to_output(data, kwargs)
175
+ ::YAML.unsafe_load(data, **(kwargs || {}))
136
176
  end
137
177
  end
138
178
 
139
- ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
179
+ module SafeLoad
180
+ extend self
140
181
 
141
- def unsafe_load_file(path, *args)
142
- return super if args.size > 1
143
- if kwargs = args.first
144
- return super unless kwargs.is_a?(Hash)
145
- return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
182
+ def input_to_storage(contents, _)
183
+ obj = begin
184
+ CompileCache::YAML.strict_load(contents)
185
+ rescue Psych::DisallowedClass, Psych::BadAlias, Uncompilable
186
+ return UNCOMPILABLE
187
+ end
188
+
189
+ packer = CompileCache::YAML.msgpack_factory.packer
190
+ packer.pack(true) # safe loaded
191
+ begin
192
+ packer.pack(obj)
193
+ rescue NoMethodError, RangeError
194
+ return UNCOMPILABLE
195
+ end
196
+ packer.to_s
146
197
  end
147
198
 
199
+ def storage_to_output(data, kwargs)
200
+ if kwargs&.key?(:symbolize_names)
201
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
202
+ end
203
+
204
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
205
+ unpacker.feed(data)
206
+ safe_loaded = unpacker.unpack
207
+ if safe_loaded
208
+ unpacker.unpack
209
+ else
210
+ UNCOMPILABLE
211
+ end
212
+ end
213
+
214
+ def input_to_output(data, kwargs)
215
+ ::YAML.load(data, **(kwargs || {}))
216
+ end
217
+ end
218
+
219
+ module Patch
220
+ def load_file(path, *args)
221
+ return super unless CompileCache::YAML.supported_internal_encoding?
222
+
223
+ return super if args.size > 1
224
+
225
+ if (kwargs = args.first)
226
+ return super unless kwargs.is_a?(Hash)
227
+ return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
228
+ end
229
+
230
+ begin
231
+ CompileCache::Native.fetch(
232
+ CompileCache::YAML.cache_dir,
233
+ File.realpath(path),
234
+ CompileCache::YAML::Psych4::SafeLoad,
235
+ kwargs,
236
+ )
237
+ rescue Errno::EACCES
238
+ CompileCache.permission_error(path)
239
+ end
240
+ end
241
+
242
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
243
+
244
+ def unsafe_load_file(path, *args)
245
+ return super unless CompileCache::YAML.supported_internal_encoding?
246
+
247
+ return super if args.size > 1
248
+
249
+ if (kwargs = args.first)
250
+ return super unless kwargs.is_a?(Hash)
251
+ return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
252
+ end
253
+
254
+ begin
255
+ CompileCache::Native.fetch(
256
+ CompileCache::YAML.cache_dir,
257
+ File.realpath(path),
258
+ CompileCache::YAML::Psych4::UnsafeLoad,
259
+ kwargs,
260
+ )
261
+ rescue Errno::EACCES
262
+ CompileCache.permission_error(path)
263
+ end
264
+ end
265
+
266
+ ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
267
+ end
268
+ end
269
+
270
+ module Psych3
271
+ extend self
272
+
273
+ def input_to_storage(contents, _)
274
+ obj = ::YAML.load(contents)
275
+ packer = CompileCache::YAML.msgpack_factory.packer
276
+ packer.pack(false) # not safe loaded
148
277
  begin
149
- ::Bootsnap::CompileCache::Native.fetch(
150
- Bootsnap::CompileCache::YAML.cache_dir,
151
- File.realpath(path),
152
- ::Bootsnap::CompileCache::YAML,
153
- kwargs,
154
- )
155
- rescue Errno::EACCES
156
- ::Bootsnap::CompileCache.permission_error(path)
278
+ packer.pack(obj)
279
+ rescue NoMethodError, RangeError
280
+ return UNCOMPILABLE # The object included things that we can't serialize
157
281
  end
282
+ packer.to_s
158
283
  end
159
284
 
160
- ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
285
+ def storage_to_output(data, kwargs)
286
+ if kwargs&.key?(:symbolize_names)
287
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
288
+ end
289
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
290
+ unpacker.feed(data)
291
+ _safe_loaded = unpacker.unpack
292
+ unpacker.unpack
293
+ end
294
+
295
+ def input_to_output(data, kwargs)
296
+ ::YAML.load(data, **(kwargs || {}))
297
+ end
298
+
299
+ module Patch
300
+ def load_file(path, *args)
301
+ return super unless CompileCache::YAML.supported_internal_encoding?
302
+
303
+ return super if args.size > 1
304
+
305
+ if (kwargs = args.first)
306
+ return super unless kwargs.is_a?(Hash)
307
+ return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
308
+ end
309
+
310
+ begin
311
+ CompileCache::Native.fetch(
312
+ CompileCache::YAML.cache_dir,
313
+ File.realpath(path),
314
+ CompileCache::YAML::Psych3,
315
+ kwargs,
316
+ )
317
+ rescue Errno::EACCES
318
+ CompileCache.permission_error(path)
319
+ end
320
+ end
321
+
322
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
323
+
324
+ def unsafe_load_file(path, *args)
325
+ return super unless CompileCache::YAML.supported_internal_encoding?
326
+
327
+ return super if args.size > 1
328
+
329
+ if (kwargs = args.first)
330
+ return super unless kwargs.is_a?(Hash)
331
+ return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
332
+ end
333
+
334
+ begin
335
+ CompileCache::Native.fetch(
336
+ CompileCache::YAML.cache_dir,
337
+ File.realpath(path),
338
+ CompileCache::YAML::Psych3,
339
+ kwargs,
340
+ )
341
+ rescue Errno::EACCES
342
+ CompileCache.permission_error(path)
343
+ end
344
+ end
345
+
346
+ ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
347
+ end
161
348
  end
162
349
  end
163
350
  end
@@ -1,13 +1,19 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Bootsnap
3
4
  module CompileCache
4
- Error = Class.new(StandardError)
5
+ UNCOMPILABLE = BasicObject.new
6
+ def UNCOMPILABLE.inspect
7
+ "<Bootsnap::UNCOMPILABLE>"
8
+ end
9
+
10
+ Error = Class.new(StandardError)
5
11
  PermissionError = Class.new(Error)
6
12
 
7
13
  def self.setup(cache_dir:, iseq:, yaml:, json:)
8
14
  if iseq
9
15
  if supported?
10
- require_relative('compile_cache/iseq')
16
+ require_relative("compile_cache/iseq")
11
17
  Bootsnap::CompileCache::ISeq.install!(cache_dir)
12
18
  elsif $VERBOSE
13
19
  warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
@@ -16,7 +22,7 @@ module Bootsnap
16
22
 
17
23
  if yaml
18
24
  if supported?
19
- require_relative('compile_cache/yaml')
25
+ require_relative("compile_cache/yaml")
20
26
  Bootsnap::CompileCache::YAML.install!(cache_dir)
21
27
  elsif $VERBOSE
22
28
  warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
@@ -25,7 +31,7 @@ module Bootsnap
25
31
 
26
32
  if json
27
33
  if supported?
28
- require_relative('compile_cache/json')
34
+ require_relative("compile_cache/json")
29
35
  Bootsnap::CompileCache::JSON.install!(cache_dir)
30
36
  elsif $VERBOSE
31
37
  warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
@@ -44,9 +50,7 @@ module Bootsnap
44
50
 
45
51
  def self.supported?
46
52
  # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
47
- RUBY_ENGINE == 'ruby' &&
48
- RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
49
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
53
+ RUBY_ENGINE == "ruby" && RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
50
54
  end
51
55
  end
52
56
  end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Bootsnap
3
4
  module ExplicitRequire
4
- ARCHDIR = RbConfig::CONFIG['archdir']
5
- RUBYLIBDIR = RbConfig::CONFIG['rubylibdir']
6
- DLEXT = RbConfig::CONFIG['DLEXT']
5
+ ARCHDIR = RbConfig::CONFIG["archdir"]
6
+ RUBYLIBDIR = RbConfig::CONFIG["rubylibdir"]
7
+ DLEXT = RbConfig::CONFIG["DLEXT"]
7
8
 
8
9
  def self.from_self(feature)
9
10
  require_relative("../#{feature}")
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative('../explicit_require')
3
+ require_relative("../explicit_require")
4
4
 
5
5
  module Bootsnap
6
6
  module LoadPathCache
@@ -28,15 +28,16 @@ module Bootsnap
28
28
  BUILTIN_FEATURES = $LOADED_FEATURES.each_with_object({}) do |feat, features|
29
29
  # Builtin features are of the form 'enumerator.so'.
30
30
  # All others include paths.
31
- next unless feat.size < 20 && !feat.include?('/')
31
+ next unless feat.size < 20 && !feat.include?("/")
32
32
 
33
- base = File.basename(feat, '.*') # enumerator.so -> enumerator
33
+ base = File.basename(feat, ".*") # enumerator.so -> enumerator
34
34
  ext = File.extname(feat) # .so
35
35
 
36
36
  features[feat] = nil # enumerator.so
37
37
  features[base] = nil # enumerator
38
38
 
39
39
  next unless [DOT_SO, *DL_EXTENSIONS].include?(ext)
40
+
40
41
  DL_EXTENSIONS.each do |dl_ext|
41
42
  features["#{base}#{dl_ext}"] = nil # enumerator.bundle
42
43
  end
@@ -50,7 +51,7 @@ module Bootsnap
50
51
 
51
52
  return feature if Bootsnap.absolute_path?(feature)
52
53
 
53
- if feature.start_with?('./', '../')
54
+ if feature.start_with?("./", "../")
54
55
  return try_extensions ? expand_path(feature) : File.expand_path(feature).freeze
55
56
  end
56
57
 
@@ -64,7 +65,7 @@ module Bootsnap
64
65
  # returns false as if it were already loaded; however, there is no
65
66
  # file to find on disk. We've pre-built a list of these, and we
66
67
  # return false if any of them is loaded.
67
- raise(LoadPathCache::ReturnFalse, '', []) if BUILTIN_FEATURES.key?(feature)
68
+ return false if BUILTIN_FEATURES.key?(feature)
68
69
 
69
70
  # The feature wasn't found on our preliminary search through the index.
70
71
  # We resolve this differently depending on what the extension was.
@@ -73,13 +74,14 @@ module Bootsnap
73
74
  # native dynamic extension, e.g. .bundle or .so), we know it was a
74
75
  # failure and there's nothing more we can do to find the file.
75
76
  # no extension, .rb, (.bundle or .so)
76
- when '', *CACHED_EXTENSIONS
77
+ when "", *CACHED_EXTENSIONS
77
78
  nil
78
79
  # Ruby allows specifying native extensions as '.so' even when DLEXT
79
80
  # is '.bundle'. This is where we handle that case.
80
81
  when DOT_SO
81
82
  x = search_index(feature[0..-4] + DLEXT)
82
83
  return x if x
84
+
83
85
  if DLEXT2
84
86
  x = search_index(feature[0..-4] + DLEXT2)
85
87
  return x if x
@@ -87,7 +89,7 @@ module Bootsnap
87
89
  else
88
90
  # other, unknown extension. For example, `.rake`. Since we haven't
89
91
  # cached these, we legitimately need to run the load path search.
90
- raise(LoadPathCache::FallbackScan, '', [])
92
+ return FALLBACK_SCAN
91
93
  end
92
94
  end
93
95
 
@@ -95,16 +97,18 @@ module Bootsnap
95
97
  # cases where the file doesn't appear to be on the load path. We should
96
98
  # be able to detect newly-created files without rebooting the
97
99
  # application.
98
- raise(LoadPathCache::FallbackScan, '', []) if @development_mode
100
+ return FALLBACK_SCAN if @development_mode
99
101
  end
100
102
 
101
103
  def unshift_paths(sender, *paths)
102
104
  return unless sender == @path_obj
105
+
103
106
  @mutex.synchronize { unshift_paths_locked(*paths) }
104
107
  end
105
108
 
106
109
  def push_paths(sender, *paths)
107
110
  return unless sender == @path_obj
111
+
108
112
  @mutex.synchronize { push_paths_locked(*paths) }
109
113
  end
110
114
 
@@ -137,6 +141,9 @@ module Bootsnap
137
141
  p = Path.new(path)
138
142
  @has_relative_paths = true if p.relative?
139
143
  next if p.non_directory?
144
+
145
+ p = p.to_realpath
146
+
140
147
  expanded_path = p.expanded_path
141
148
  entries, dirs = p.entries_and_dirs(@store)
142
149
  # push -> low precedence -> set only if unset
@@ -151,6 +158,9 @@ module Bootsnap
151
158
  paths.map(&:to_s).reverse_each do |path|
152
159
  p = Path.new(path)
153
160
  next if p.non_directory?
161
+
162
+ p = p.to_realpath
163
+
154
164
  expanded_path = p.expanded_path
155
165
  entries, dirs = p.entries_and_dirs(@store)
156
166
  # unshift -> high precedence -> unconditional set
@@ -173,56 +183,62 @@ module Bootsnap
173
183
  end
174
184
 
175
185
  if DLEXT2
176
- def search_index(f, try_extensions: true)
186
+ def search_index(feature, try_extensions: true)
177
187
  if try_extensions
178
- try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
188
+ try_index(feature + DOT_RB) ||
189
+ try_index(feature + DLEXT) ||
190
+ try_index(feature + DLEXT2) ||
191
+ try_index(feature)
179
192
  else
180
- try_index(f)
193
+ try_index(feature)
181
194
  end
182
195
  end
183
196
 
184
- def maybe_append_extension(f)
185
- try_ext(f + DOT_RB) || try_ext(f + DLEXT) || try_ext(f + DLEXT2) || f
197
+ def maybe_append_extension(feature)
198
+ try_ext(feature + DOT_RB) ||
199
+ try_ext(feature + DLEXT) ||
200
+ try_ext(feature + DLEXT2) ||
201
+ feature
186
202
  end
187
203
  else
188
- def search_index(f, try_extensions: true)
204
+ def search_index(feature, try_extensions: true)
189
205
  if try_extensions
190
- try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
206
+ try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
191
207
  else
192
- try_index(f)
208
+ try_index(feature)
193
209
  end
194
210
  end
195
211
 
196
- def maybe_append_extension(f)
197
- try_ext(f + DOT_RB) || try_ext(f + DLEXT) || f
212
+ def maybe_append_extension(feature)
213
+ try_ext(feature + DOT_RB) || try_ext(feature + DLEXT) || feature
198
214
  end
199
215
  end
200
216
 
201
217
  s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze
202
218
  if s.respond_to?(:-@)
203
- if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= '2.7'
204
- def try_index(f)
205
- if (p = @index[f])
206
- -(File.join(p, f).freeze)
219
+ if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= "2.7"
220
+ def try_index(feature)
221
+ if (path = @index[feature])
222
+ -File.join(path, feature).freeze
207
223
  end
208
224
  end
209
225
  else
210
- def try_index(f)
211
- if (p = @index[f])
212
- -File.join(p, f).untaint
226
+ def try_index(feature)
227
+ if (path = @index[feature])
228
+ -File.join(path, feature).untaint
213
229
  end
214
230
  end
215
231
  end
216
232
  else
217
- def try_index(f)
218
- if (p = @index[f])
219
- File.join(p, f)
233
+ def try_index(feature)
234
+ if (path = @index[feature])
235
+ File.join(path, feature)
220
236
  end
221
237
  end
222
238
  end
223
239
 
224
- def try_ext(f)
225
- f if File.exist?(f)
240
+ def try_ext(feature)
241
+ feature if File.exist?(feature)
226
242
  end
227
243
  end
228
244
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Bootsnap
3
4
  module LoadPathCache
4
5
  module ChangeObserver
@@ -57,6 +58,7 @@ module Bootsnap
57
58
 
58
59
  def self.register(observer, arr)
59
60
  return if arr.frozen? # can't register observer, but no need to.
61
+
60
62
  arr.instance_variable_set(:@lpc_observer, observer)
61
63
  arr.extend(ArrayMixin)
62
64
  end