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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/exe/bootsnap +1 -1
- data/ext/bootsnap/bootsnap.c +32 -34
- data/ext/bootsnap/extconf.rb +13 -11
- data/lib/bootsnap/bundler.rb +1 -0
- data/lib/bootsnap/cli/worker_pool.rb +1 -0
- data/lib/bootsnap/cli.rb +49 -49
- data/lib/bootsnap/compile_cache/iseq.rb +16 -11
- data/lib/bootsnap/compile_cache/json.rb +15 -8
- data/lib/bootsnap/compile_cache/yaml.rb +216 -78
- data/lib/bootsnap/compile_cache.rb +10 -7
- data/lib/bootsnap/explicit_require.rb +4 -3
- data/lib/bootsnap/load_path_cache/cache.rb +42 -30
- data/lib/bootsnap/load_path_cache/change_observer.rb +2 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +30 -21
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +31 -33
- data/lib/bootsnap/load_path_cache/path.rb +5 -3
- data/lib/bootsnap/load_path_cache/path_scanner.rb +6 -5
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +1 -0
- data/lib/bootsnap/load_path_cache/store.rb +12 -7
- data/lib/bootsnap/load_path_cache.rb +15 -15
- data/lib/bootsnap/setup.rb +2 -1
- data/lib/bootsnap/version.rb +2 -1
- data/lib/bootsnap.rb +32 -31
- metadata +5 -75
@@ -1,66 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require("bootsnap/bootsnap")
|
3
4
|
|
4
5
|
module Bootsnap
|
5
6
|
module CompileCache
|
6
7
|
module YAML
|
7
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
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
|
34
|
-
|
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
|
18
|
+
def precompile(path)
|
41
19
|
Bootsnap::CompileCache::Native.precompile(
|
42
20
|
cache_dir,
|
43
21
|
path.to_s,
|
44
|
-
|
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(
|
56
|
-
require(
|
57
|
-
require(
|
33
|
+
require("yaml")
|
34
|
+
require("msgpack")
|
35
|
+
require("date")
|
58
36
|
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
73
|
+
supported_options << :symbolize_names
|
98
74
|
end
|
99
75
|
if params.include?([:key, :freeze])
|
100
|
-
if factory.load(factory.dump(
|
101
|
-
|
76
|
+
if factory.load(factory.dump("yaml"), freeze: true).frozen?
|
77
|
+
supported_options << :freeze
|
102
78
|
end
|
103
79
|
end
|
104
|
-
|
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
|
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
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
)
|
134
|
-
|
135
|
-
|
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
|
-
|
147
|
+
module SafeLoad
|
148
|
+
extend self
|
140
149
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
)
|
155
|
-
|
156
|
-
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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 ==
|
48
|
-
|
49
|
-
|
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[
|
5
|
-
RUBYLIBDIR = RbConfig::CONFIG[
|
6
|
-
DLEXT = RbConfig::CONFIG[
|
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(
|
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,
|
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,
|
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
|
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,
|
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(
|
182
|
+
def search_index(feature, try_extensions: true)
|
177
183
|
if try_extensions
|
178
|
-
try_index(
|
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(
|
189
|
+
try_index(feature)
|
181
190
|
end
|
182
191
|
end
|
183
192
|
|
184
|
-
def maybe_append_extension(
|
185
|
-
try_ext(
|
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(
|
200
|
+
def search_index(feature, try_extensions: true)
|
189
201
|
if try_extensions
|
190
|
-
try_index(
|
202
|
+
try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
|
191
203
|
else
|
192
|
-
try_index(
|
204
|
+
try_index(feature)
|
193
205
|
end
|
194
206
|
end
|
195
207
|
|
196
|
-
def maybe_append_extension(
|
197
|
-
try_ext(
|
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 >=
|
204
|
-
def try_index(
|
205
|
-
if (
|
206
|
-
-
|
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(
|
211
|
-
if (
|
212
|
-
-File.join(
|
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(
|
218
|
-
if (
|
219
|
-
File.join(
|
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(
|
225
|
-
|
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
|