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