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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -3
- data/LICENSE.txt +1 -1
- data/README.md +4 -4
- data/exe/bootsnap +1 -1
- data/ext/bootsnap/bootsnap.c +47 -84
- 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 +18 -9
- data/lib/bootsnap/compile_cache/yaml.rb +264 -77
- data/lib/bootsnap/compile_cache.rb +11 -7
- data/lib/bootsnap/explicit_require.rb +4 -3
- data/lib/bootsnap/load_path_cache/cache.rb +46 -30
- data/lib/bootsnap/load_path_cache/change_observer.rb +2 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +26 -72
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +32 -34
- data/lib/bootsnap/load_path_cache/path.rb +29 -5
- data/lib/bootsnap/load_path_cache/path_scanner.rb +6 -5
- data/lib/bootsnap/load_path_cache/store.rb +36 -13
- data/lib/bootsnap/load_path_cache.rb +16 -24
- data/lib/bootsnap/setup.rb +2 -1
- data/lib/bootsnap/version.rb +2 -1
- data/lib/bootsnap.rb +111 -99
- metadata +6 -77
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +0 -32
|
@@ -1,80 +1,86 @@
|
|
|
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
|
-
|
|
8
|
+
Uncompilable = Class.new(StandardError)
|
|
9
|
+
UnsupportedTags = Class.new(Uncompilable)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
26
|
-
|
|
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
|
|
34
|
-
|
|
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
|
-
|
|
41
|
-
Bootsnap::CompileCache::Native.precompile(
|
|
29
|
+
CompileCache::Native.precompile(
|
|
42
30
|
cache_dir,
|
|
43
31
|
path.to_s,
|
|
44
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
52
|
+
def unpack(payload)
|
|
53
|
+
(+payload).force_encoding(Encoding::UTF_8).to_sym
|
|
61
54
|
end
|
|
62
|
-
|
|
63
|
-
|
|
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(
|
|
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
|
-
|
|
103
|
+
supported_options << :symbolize_names
|
|
98
104
|
end
|
|
99
105
|
if params.include?([:key, :freeze])
|
|
100
|
-
if factory.load(factory.dump(
|
|
101
|
-
|
|
106
|
+
if factory.load(factory.dump("yaml"), freeze: true).frozen?
|
|
107
|
+
supported_options << :freeze
|
|
102
108
|
end
|
|
103
109
|
end
|
|
104
|
-
|
|
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
|
|
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
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
179
|
+
module SafeLoad
|
|
180
|
+
extend self
|
|
140
181
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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 ==
|
|
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[
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
186
|
+
def search_index(feature, try_extensions: true)
|
|
177
187
|
if try_extensions
|
|
178
|
-
try_index(
|
|
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(
|
|
193
|
+
try_index(feature)
|
|
181
194
|
end
|
|
182
195
|
end
|
|
183
196
|
|
|
184
|
-
def maybe_append_extension(
|
|
185
|
-
try_ext(
|
|
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(
|
|
204
|
+
def search_index(feature, try_extensions: true)
|
|
189
205
|
if try_extensions
|
|
190
|
-
try_index(
|
|
206
|
+
try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
|
|
191
207
|
else
|
|
192
|
-
try_index(
|
|
208
|
+
try_index(feature)
|
|
193
209
|
end
|
|
194
210
|
end
|
|
195
211
|
|
|
196
|
-
def maybe_append_extension(
|
|
197
|
-
try_ext(
|
|
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 >=
|
|
204
|
-
def try_index(
|
|
205
|
-
if (
|
|
206
|
-
-
|
|
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(
|
|
211
|
-
if (
|
|
212
|
-
-File.join(
|
|
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(
|
|
218
|
-
if (
|
|
219
|
-
File.join(
|
|
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(
|
|
225
|
-
|
|
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
|