bootsnap 1.9.4 → 1.10.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,66 +1,42 @@
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)
9
-
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
8
+ UnsupportedTags = Class.new(StandardError)
17
9
 
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
24
-
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
31
- end
10
+ class << self
11
+ attr_accessor(:msgpack_factory, :supported_options)
12
+ attr_reader(:implementation, :cache_dir)
32
13
 
33
- def strict_load(payload, *args)
34
- ast = ::YAML.parse(payload)
35
- return ast unless ast
36
- strict_visitor.create(*args).visit(ast)
14
+ def cache_dir=(cache_dir)
15
+ @cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}yaml" : "#{cache_dir}-yaml"
37
16
  end
38
- ruby2_keywords :strict_load if respond_to?(:ruby2_keywords, true)
39
17
 
40
- def precompile(path, cache_dir: YAML.cache_dir)
18
+ def precompile(path)
41
19
  Bootsnap::CompileCache::Native.precompile(
42
20
  cache_dir,
43
21
  path.to_s,
44
- Bootsnap::CompileCache::YAML,
22
+ @implementation,
45
23
  )
46
24
  end
47
25
 
48
26
  def install!(cache_dir)
49
27
  self.cache_dir = cache_dir
50
28
  init!
51
- ::YAML.singleton_class.prepend(Patch)
29
+ ::YAML.singleton_class.prepend(@implementation::Patch)
52
30
  end
53
31
 
54
32
  def init!
55
- require('yaml')
56
- require('msgpack')
57
- require('date')
33
+ require("yaml")
34
+ require("msgpack")
35
+ require("date")
58
36
 
59
- if Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
60
- Patch.send(:remove_method, :unsafe_load_file)
61
- end
62
- if Patch.method_defined?(:load_file) && ::YAML::VERSION >= '4'
63
- Patch.send(:remove_method, :load_file)
37
+ @implementation = ::YAML::VERSION >= "4" ? Psych4 : Psych3
38
+ if @implementation::Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
39
+ @implementation::Patch.send(:remove_method, :unsafe_load_file)
64
40
  end
65
41
 
66
42
  # MessagePack serializes symbols as strings by default.
@@ -74,7 +50,7 @@ module Bootsnap
74
50
  MessagePack::Timestamp::TYPE, # or just -1
75
51
  Time,
76
52
  packer: MessagePack::Time::Packer,
77
- unpacker: MessagePack::Time::Unpacker
53
+ unpacker: MessagePack::Time::Unpacker,
78
54
  )
79
55
 
80
56
  marshal_fallback = {
@@ -94,70 +70,232 @@ module Bootsnap
94
70
  self.supported_options = []
95
71
  params = ::YAML.method(:load).parameters
96
72
  if params.include?([:key, :symbolize_names])
97
- self.supported_options << :symbolize_names
73
+ supported_options << :symbolize_names
98
74
  end
99
75
  if params.include?([:key, :freeze])
100
- if factory.load(factory.dump('yaml'), freeze: true).frozen?
101
- self.supported_options << :freeze
76
+ if factory.load(factory.dump("yaml"), freeze: true).frozen?
77
+ supported_options << :freeze
102
78
  end
103
79
  end
104
- self.supported_options.freeze
80
+ supported_options.freeze
81
+ end
82
+
83
+ def patch
84
+ @implementation::Patch
85
+ end
86
+
87
+ def strict_load(payload)
88
+ ast = ::YAML.parse(payload)
89
+ return ast unless ast
90
+
91
+ strict_visitor.create.visit(ast)
105
92
  end
106
93
 
107
94
  def strict_visitor
108
95
  self::NoTagsVisitor ||= Class.new(Psych::Visitors::ToRuby) do
109
96
  def visit(target)
110
97
  if target.tag
111
- raise Uncompilable, "YAML tags are not supported: #{target.tag}"
98
+ raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
112
99
  end
100
+
113
101
  super
114
102
  end
115
103
  end
116
104
  end
117
105
  end
118
106
 
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?
107
+ module Psych4
108
+ extend self
109
+
110
+ def input_to_storage(contents, _)
111
+ obj = SafeLoad.input_to_storage(contents, nil)
112
+ if UNCOMPILABLE.equal?(obj)
113
+ obj = UnsafeLoad.input_to_storage(contents, nil)
125
114
  end
115
+ obj
116
+ end
126
117
 
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)
118
+ module UnsafeLoad
119
+ extend self
120
+
121
+ def input_to_storage(contents, _)
122
+ obj = CompileCache::YAML.strict_load(contents)
123
+ packer = CompileCache::YAML.msgpack_factory.packer
124
+ packer.pack(false) # not safe loaded
125
+ packer.pack(obj)
126
+ packer.to_s
127
+ rescue NoMethodError, RangeError, UnsupportedTags
128
+ UNCOMPILABLE # The object included things that we can't serialize
129
+ end
130
+
131
+ def storage_to_output(data, kwargs)
132
+ if kwargs&.key?(:symbolize_names)
133
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
134
+ end
135
+
136
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
137
+ unpacker.feed(data)
138
+ _safe_loaded = unpacker.unpack
139
+ unpacker.unpack
140
+ end
141
+
142
+ def input_to_output(data, kwargs)
143
+ ::YAML.unsafe_load(data, **(kwargs || {}))
136
144
  end
137
145
  end
138
146
 
139
- ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
147
+ module SafeLoad
148
+ extend self
140
149
 
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?
150
+ def input_to_storage(contents, _)
151
+ obj = ::YAML.load(contents)
152
+ packer = CompileCache::YAML.msgpack_factory.packer
153
+ packer.pack(true) # safe loaded
154
+ packer.pack(obj)
155
+ packer.to_s
156
+ rescue NoMethodError, RangeError, Psych::DisallowedClass, Psych::BadAlias
157
+ UNCOMPILABLE # The object included things that we can't serialize
146
158
  end
147
159
 
148
- 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)
160
+ def storage_to_output(data, kwargs)
161
+ if kwargs&.key?(:symbolize_names)
162
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
163
+ end
164
+
165
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
166
+ unpacker.feed(data)
167
+ safe_loaded = unpacker.unpack
168
+ if safe_loaded
169
+ unpacker.unpack
170
+ else
171
+ UNCOMPILABLE
172
+ end
173
+ end
174
+
175
+ def input_to_output(data, kwargs)
176
+ ::YAML.load(data, **(kwargs || {}))
177
+ end
178
+ end
179
+
180
+ module Patch
181
+ def load_file(path, *args)
182
+ return super if args.size > 1
183
+
184
+ if (kwargs = args.first)
185
+ return super unless kwargs.is_a?(Hash)
186
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
187
+ end
188
+
189
+ begin
190
+ ::Bootsnap::CompileCache::Native.fetch(
191
+ Bootsnap::CompileCache::YAML.cache_dir,
192
+ File.realpath(path),
193
+ ::Bootsnap::CompileCache::YAML::Psych4::SafeLoad,
194
+ kwargs,
195
+ )
196
+ rescue Errno::EACCES
197
+ ::Bootsnap::CompileCache.permission_error(path)
198
+ end
199
+ end
200
+
201
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
202
+
203
+ def unsafe_load_file(path, *args)
204
+ return super if args.size > 1
205
+
206
+ if (kwargs = args.first)
207
+ return super unless kwargs.is_a?(Hash)
208
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
209
+ end
210
+
211
+ begin
212
+ ::Bootsnap::CompileCache::Native.fetch(
213
+ Bootsnap::CompileCache::YAML.cache_dir,
214
+ File.realpath(path),
215
+ ::Bootsnap::CompileCache::YAML::Psych4::UnsafeLoad,
216
+ kwargs,
217
+ )
218
+ rescue Errno::EACCES
219
+ ::Bootsnap::CompileCache.permission_error(path)
220
+ end
221
+ end
222
+
223
+ ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
224
+ end
225
+ end
226
+
227
+ module Psych3
228
+ extend self
229
+
230
+ def input_to_storage(contents, _)
231
+ obj = CompileCache::YAML.strict_load(contents)
232
+ packer = CompileCache::YAML.msgpack_factory.packer
233
+ packer.pack(false) # not safe loaded
234
+ packer.pack(obj)
235
+ packer.to_s
236
+ rescue NoMethodError, RangeError, UnsupportedTags
237
+ UNCOMPILABLE # The object included things that we can't serialize
238
+ end
239
+
240
+ def storage_to_output(data, kwargs)
241
+ if kwargs&.key?(:symbolize_names)
242
+ kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
157
243
  end
244
+ unpacker = CompileCache::YAML.msgpack_factory.unpacker(kwargs)
245
+ unpacker.feed(data)
246
+ _safe_loaded = unpacker.unpack
247
+ unpacker.unpack
158
248
  end
159
249
 
160
- ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
250
+ def input_to_output(data, kwargs)
251
+ ::YAML.load(data, **(kwargs || {}))
252
+ end
253
+
254
+ module Patch
255
+ def load_file(path, *args)
256
+ return super if args.size > 1
257
+
258
+ if (kwargs = args.first)
259
+ return super unless kwargs.is_a?(Hash)
260
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
261
+ end
262
+
263
+ begin
264
+ ::Bootsnap::CompileCache::Native.fetch(
265
+ Bootsnap::CompileCache::YAML.cache_dir,
266
+ File.realpath(path),
267
+ ::Bootsnap::CompileCache::YAML::Psych3,
268
+ kwargs,
269
+ )
270
+ rescue Errno::EACCES
271
+ ::Bootsnap::CompileCache.permission_error(path)
272
+ end
273
+ end
274
+
275
+ ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
276
+
277
+ def unsafe_load_file(path, *args)
278
+ return super if args.size > 1
279
+
280
+ if (kwargs = args.first)
281
+ return super unless kwargs.is_a?(Hash)
282
+ return super unless (kwargs.keys - ::Bootsnap::CompileCache::YAML.supported_options).empty?
283
+ end
284
+
285
+ begin
286
+ ::Bootsnap::CompileCache::Native.fetch(
287
+ Bootsnap::CompileCache::YAML.cache_dir,
288
+ File.realpath(path),
289
+ ::Bootsnap::CompileCache::YAML::Psych3,
290
+ kwargs,
291
+ )
292
+ rescue Errno::EACCES
293
+ ::Bootsnap::CompileCache.permission_error(path)
294
+ end
295
+ end
296
+
297
+ ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
298
+ end
161
299
  end
162
300
  end
163
301
  end
@@ -1,13 +1,16 @@
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
+
7
+ Error = Class.new(StandardError)
5
8
  PermissionError = Class.new(Error)
6
9
 
7
10
  def self.setup(cache_dir:, iseq:, yaml:, json:)
8
11
  if iseq
9
12
  if supported?
10
- require_relative('compile_cache/iseq')
13
+ require_relative("compile_cache/iseq")
11
14
  Bootsnap::CompileCache::ISeq.install!(cache_dir)
12
15
  elsif $VERBOSE
13
16
  warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
@@ -16,7 +19,7 @@ module Bootsnap
16
19
 
17
20
  if yaml
18
21
  if supported?
19
- require_relative('compile_cache/yaml')
22
+ require_relative("compile_cache/yaml")
20
23
  Bootsnap::CompileCache::YAML.install!(cache_dir)
21
24
  elsif $VERBOSE
22
25
  warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
@@ -25,7 +28,7 @@ module Bootsnap
25
28
 
26
29
  if json
27
30
  if supported?
28
- require_relative('compile_cache/json')
31
+ require_relative("compile_cache/json")
29
32
  Bootsnap::CompileCache::JSON.install!(cache_dir)
30
33
  elsif $VERBOSE
31
34
  warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
@@ -44,9 +47,9 @@ module Bootsnap
44
47
 
45
48
  def self.supported?
46
49
  # 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")
50
+ RUBY_ENGINE == "ruby" &&
51
+ RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/ &&
52
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.3.0")
50
53
  end
51
54
  end
52
55
  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
+ raise(LoadPathCache::ReturnFalse, "", []) 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
+ raise(LoadPathCache::FallbackScan, "", [])
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
+ raise(LoadPathCache::FallbackScan, "", []) 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,7 @@ 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
+
140
145
  expanded_path = p.expanded_path
141
146
  entries, dirs = p.entries_and_dirs(@store)
142
147
  # push -> low precedence -> set only if unset
@@ -151,6 +156,7 @@ module Bootsnap
151
156
  paths.map(&:to_s).reverse_each do |path|
152
157
  p = Path.new(path)
153
158
  next if p.non_directory?
159
+
154
160
  expanded_path = p.expanded_path
155
161
  entries, dirs = p.entries_and_dirs(@store)
156
162
  # unshift -> high precedence -> unconditional set
@@ -173,56 +179,62 @@ module Bootsnap
173
179
  end
174
180
 
175
181
  if DLEXT2
176
- def search_index(f, try_extensions: true)
182
+ def search_index(feature, try_extensions: true)
177
183
  if try_extensions
178
- try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
184
+ try_index(feature + DOT_RB) ||
185
+ try_index(feature + DLEXT) ||
186
+ try_index(feature + DLEXT2) ||
187
+ try_index(feature)
179
188
  else
180
- try_index(f)
189
+ try_index(feature)
181
190
  end
182
191
  end
183
192
 
184
- def maybe_append_extension(f)
185
- try_ext(f + DOT_RB) || try_ext(f + DLEXT) || try_ext(f + DLEXT2) || f
193
+ def maybe_append_extension(feature)
194
+ try_ext(feature + DOT_RB) ||
195
+ try_ext(feature + DLEXT) ||
196
+ try_ext(feature + DLEXT2) ||
197
+ feature
186
198
  end
187
199
  else
188
- def search_index(f, try_extensions: true)
200
+ def search_index(feature, try_extensions: true)
189
201
  if try_extensions
190
- try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
202
+ try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
191
203
  else
192
- try_index(f)
204
+ try_index(feature)
193
205
  end
194
206
  end
195
207
 
196
- def maybe_append_extension(f)
197
- try_ext(f + DOT_RB) || try_ext(f + DLEXT) || f
208
+ def maybe_append_extension(feature)
209
+ try_ext(feature + DOT_RB) || try_ext(feature + DLEXT) || feature
198
210
  end
199
211
  end
200
212
 
201
213
  s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze
202
214
  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)
215
+ if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= "2.7"
216
+ def try_index(feature)
217
+ if (path = @index[feature])
218
+ -File.join(path, feature).freeze
207
219
  end
208
220
  end
209
221
  else
210
- def try_index(f)
211
- if (p = @index[f])
212
- -File.join(p, f).untaint
222
+ def try_index(feature)
223
+ if (path = @index[feature])
224
+ -File.join(path, feature).untaint
213
225
  end
214
226
  end
215
227
  end
216
228
  else
217
- def try_index(f)
218
- if (p = @index[f])
219
- File.join(p, f)
229
+ def try_index(feature)
230
+ if (path = @index[feature])
231
+ File.join(path, feature)
220
232
  end
221
233
  end
222
234
  end
223
235
 
224
- def try_ext(f)
225
- f if File.exist?(f)
236
+ def try_ext(feature)
237
+ feature if File.exist?(feature)
226
238
  end
227
239
  end
228
240
  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