bootsnap 1.12.0 → 1.18.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -1
- data/README.md +17 -8
- data/ext/bootsnap/bootsnap.c +256 -77
- data/ext/bootsnap/extconf.rb +20 -13
- data/lib/bootsnap/bundler.rb +1 -1
- data/lib/bootsnap/cli.rb +28 -23
- data/lib/bootsnap/compile_cache/iseq.rb +15 -9
- data/lib/bootsnap/compile_cache/json.rb +18 -17
- data/lib/bootsnap/compile_cache/yaml.rb +46 -60
- data/lib/bootsnap/compile_cache.rb +11 -15
- data/lib/bootsnap/explicit_require.rb +5 -0
- data/lib/bootsnap/load_path_cache/cache.rb +20 -21
- data/lib/bootsnap/load_path_cache/change_observer.rb +19 -2
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +7 -14
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +2 -2
- data/lib/bootsnap/load_path_cache/path.rb +16 -18
- data/lib/bootsnap/load_path_cache/path_scanner.rb +7 -1
- data/lib/bootsnap/load_path_cache/store.rb +13 -14
- data/lib/bootsnap/load_path_cache.rb +36 -13
- data/lib/bootsnap/setup.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +52 -36
- metadata +7 -7
data/lib/bootsnap/cli.rb
CHANGED
@@ -36,16 +36,16 @@ module Bootsnap
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def precompile_command(*sources)
|
39
|
-
require "bootsnap/compile_cache
|
40
|
-
require "bootsnap/compile_cache/yaml"
|
41
|
-
require "bootsnap/compile_cache/json"
|
39
|
+
require "bootsnap/compile_cache"
|
42
40
|
|
43
41
|
fix_default_encoding do
|
44
|
-
Bootsnap::CompileCache
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
Bootsnap::CompileCache.setup(
|
43
|
+
cache_dir: cache_dir,
|
44
|
+
iseq: iseq,
|
45
|
+
yaml: yaml,
|
46
|
+
json: json,
|
47
|
+
revalidation: true,
|
48
|
+
)
|
49
49
|
|
50
50
|
@work_pool = WorkerPool.create(size: jobs, jobs: {
|
51
51
|
ruby: method(:precompile_ruby),
|
@@ -60,14 +60,16 @@ module Bootsnap
|
|
60
60
|
precompile_json_files(main_sources)
|
61
61
|
|
62
62
|
if compile_gemfile
|
63
|
-
# Some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
64
|
-
gem_exclude = Regexp.union([exclude, "/spec/", "/test/"].compact)
|
65
|
-
precompile_ruby_files($LOAD_PATH.map { |d| File.expand_path(d) }, exclude: gem_exclude)
|
66
|
-
|
67
63
|
# Gems that include JSON or YAML files usually don't put them in `lib/`.
|
68
64
|
# So we look at the gem root.
|
69
|
-
|
70
|
-
|
65
|
+
# Similarly, gems that include Rails engines generally file Ruby files in `app/`.
|
66
|
+
# However some gems embed their tests, they're very unlikely to be loaded, so not worth precompiling.
|
67
|
+
gem_exclude = Regexp.union([exclude, "/spec/", "/test/", "/features/"].compact)
|
68
|
+
|
69
|
+
gem_pattern = %r{^#{Regexp.escape(Bundler.bundle_path.to_s)}/?(?:bundler/)?gems/[^/]+}
|
70
|
+
gem_paths = $LOAD_PATH.map { |p| p[gem_pattern] || p }.uniq
|
71
|
+
|
72
|
+
precompile_ruby_files(gem_paths, exclude: gem_exclude)
|
71
73
|
precompile_yaml_files(gem_paths, exclude: gem_exclude)
|
72
74
|
precompile_json_files(gem_paths, exclude: gem_exclude)
|
73
75
|
end
|
@@ -138,8 +140,8 @@ module Bootsnap
|
|
138
140
|
|
139
141
|
def precompile_yaml(*yaml_files)
|
140
142
|
Array(yaml_files).each do |yaml_file|
|
141
|
-
if CompileCache::YAML.precompile(yaml_file)
|
142
|
-
|
143
|
+
if CompileCache::YAML.precompile(yaml_file) && verbose
|
144
|
+
$stderr.puts(yaml_file)
|
143
145
|
end
|
144
146
|
end
|
145
147
|
end
|
@@ -161,8 +163,8 @@ module Bootsnap
|
|
161
163
|
|
162
164
|
def precompile_json(*json_files)
|
163
165
|
Array(json_files).each do |json_file|
|
164
|
-
if CompileCache::JSON.precompile(json_file)
|
165
|
-
|
166
|
+
if CompileCache::JSON.precompile(json_file) && verbose
|
167
|
+
$stderr.puts(json_file)
|
166
168
|
end
|
167
169
|
end
|
168
170
|
end
|
@@ -183,8 +185,8 @@ module Bootsnap
|
|
183
185
|
|
184
186
|
def precompile_ruby(*ruby_files)
|
185
187
|
Array(ruby_files).each do |ruby_file|
|
186
|
-
if CompileCache::ISeq.precompile(ruby_file)
|
187
|
-
|
188
|
+
if CompileCache::ISeq.precompile(ruby_file) && verbose
|
189
|
+
$stderr.puts(ruby_file)
|
188
190
|
end
|
189
191
|
end
|
190
192
|
end
|
@@ -203,9 +205,9 @@ module Bootsnap
|
|
203
205
|
end
|
204
206
|
|
205
207
|
def invalid_usage!(message)
|
206
|
-
|
207
|
-
|
208
|
-
|
208
|
+
$stderr.puts message
|
209
|
+
$stderr.puts
|
210
|
+
$stderr.puts parser
|
209
211
|
1
|
210
212
|
end
|
211
213
|
|
@@ -220,6 +222,9 @@ module Bootsnap
|
|
220
222
|
|
221
223
|
def parser
|
222
224
|
@parser ||= OptionParser.new do |opts|
|
225
|
+
opts.version = Bootsnap::VERSION
|
226
|
+
opts.program_name = "bootsnap"
|
227
|
+
|
223
228
|
opts.banner = "Usage: bootsnap COMMAND [ARGS]"
|
224
229
|
opts.separator ""
|
225
230
|
opts.separator "GLOBAL OPTIONS"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
|
+
require "zlib"
|
5
5
|
|
6
6
|
module Bootsnap
|
7
7
|
module CompileCache
|
@@ -12,6 +12,10 @@ module Bootsnap
|
|
12
12
|
def cache_dir=(cache_dir)
|
13
13
|
@cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}iseq" : "#{cache_dir}-iseq"
|
14
14
|
end
|
15
|
+
|
16
|
+
def supported?
|
17
|
+
CompileCache.supported? && defined?(RubyVM)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
has_ruby_bug_18250 = begin # https://bugs.ruby-lang.org/issues/18250
|
@@ -34,14 +38,14 @@ module Bootsnap
|
|
34
38
|
begin
|
35
39
|
iseq.to_binary
|
36
40
|
rescue TypeError
|
37
|
-
|
41
|
+
UNCOMPILABLE # ruby bug #18250
|
38
42
|
end
|
39
43
|
end
|
40
44
|
else
|
41
45
|
def self.input_to_storage(_, path)
|
42
46
|
RubyVM::InstructionSequence.compile_file(path).to_binary
|
43
47
|
rescue SyntaxError
|
44
|
-
|
48
|
+
UNCOMPILABLE # syntax error
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
@@ -49,7 +53,7 @@ module Bootsnap
|
|
49
53
|
RubyVM::InstructionSequence.load_from_binary(binary)
|
50
54
|
rescue RuntimeError => error
|
51
55
|
if error.message == "broken binary format"
|
52
|
-
|
56
|
+
$stderr.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
53
57
|
nil
|
54
58
|
else
|
55
59
|
raise
|
@@ -80,11 +84,9 @@ module Bootsnap
|
|
80
84
|
module InstructionSequenceMixin
|
81
85
|
def load_iseq(path)
|
82
86
|
# Having coverage enabled prevents iseq dumping/loading.
|
83
|
-
return nil if defined?(Coverage) &&
|
87
|
+
return nil if defined?(Coverage) && Coverage.running?
|
84
88
|
|
85
89
|
Bootsnap::CompileCache::ISeq.fetch(path.to_s)
|
86
|
-
rescue Errno::EACCES
|
87
|
-
Bootsnap::CompileCache.permission_error(path)
|
88
90
|
rescue RuntimeError => error
|
89
91
|
if error.message =~ /unmatched platform/
|
90
92
|
puts("unmatched platform for file #{path}")
|
@@ -103,11 +105,15 @@ module Bootsnap
|
|
103
105
|
crc = Zlib.crc32(option.inspect)
|
104
106
|
Bootsnap::CompileCache::Native.compile_option_crc32 = crc
|
105
107
|
end
|
106
|
-
compile_option_updated
|
108
|
+
compile_option_updated if supported?
|
107
109
|
|
108
110
|
def self.install!(cache_dir)
|
109
111
|
Bootsnap::CompileCache::ISeq.cache_dir = cache_dir
|
112
|
+
|
113
|
+
return unless supported?
|
114
|
+
|
110
115
|
Bootsnap::CompileCache::ISeq.compile_option_updated
|
116
|
+
|
111
117
|
class << RubyVM::InstructionSequence
|
112
118
|
prepend(InstructionSequenceMixin)
|
113
119
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module CompileCache
|
@@ -46,18 +46,23 @@ module Bootsnap
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def init!
|
49
|
-
require
|
50
|
-
require
|
49
|
+
require "json"
|
50
|
+
require "msgpack"
|
51
51
|
|
52
52
|
self.msgpack_factory = MessagePack::Factory.new
|
53
53
|
self.supported_options = [:symbolize_names]
|
54
|
-
if
|
55
|
-
|
56
|
-
self.supported_options = [:freeze]
|
57
|
-
end
|
54
|
+
if supports_freeze?
|
55
|
+
self.supported_options = [:freeze]
|
58
56
|
end
|
59
57
|
supported_options.freeze
|
60
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def supports_freeze?
|
63
|
+
::JSON.parse('["foo"]', freeze: true).first.frozen? &&
|
64
|
+
MessagePack.load(MessagePack.dump("foo"), freeze: true).frozen?
|
65
|
+
end
|
61
66
|
end
|
62
67
|
|
63
68
|
module Patch
|
@@ -69,16 +74,12 @@ module Bootsnap
|
|
69
74
|
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
|
70
75
|
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
)
|
79
|
-
rescue Errno::EACCES
|
80
|
-
::Bootsnap::CompileCache.permission_error(path)
|
81
|
-
end
|
77
|
+
::Bootsnap::CompileCache::Native.fetch(
|
78
|
+
Bootsnap::CompileCache::JSON.cache_dir,
|
79
|
+
File.realpath(path),
|
80
|
+
::Bootsnap::CompileCache::JSON,
|
81
|
+
kwargs,
|
82
|
+
)
|
82
83
|
end
|
83
84
|
|
84
85
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "bootsnap/bootsnap"
|
4
4
|
|
5
5
|
module Bootsnap
|
6
6
|
module CompileCache
|
@@ -55,15 +55,28 @@ module Bootsnap
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def init!
|
58
|
-
require
|
59
|
-
require
|
60
|
-
require
|
58
|
+
require "yaml"
|
59
|
+
require "msgpack"
|
60
|
+
require "date"
|
61
61
|
|
62
62
|
@implementation = ::YAML::VERSION >= "4" ? Psych4 : Psych3
|
63
63
|
if @implementation::Patch.method_defined?(:unsafe_load_file) && !::YAML.respond_to?(:unsafe_load_file)
|
64
64
|
@implementation::Patch.send(:remove_method, :unsafe_load_file)
|
65
65
|
end
|
66
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
|
+
|
67
80
|
# MessagePack serializes symbols as strings by default.
|
68
81
|
# We want them to roundtrip cleanly, so we use a custom factory.
|
69
82
|
# see: https://github.com/msgpack/msgpack-ruby/pull/122
|
@@ -102,10 +115,8 @@ module Bootsnap
|
|
102
115
|
if params.include?([:key, :symbolize_names])
|
103
116
|
supported_options << :symbolize_names
|
104
117
|
end
|
105
|
-
if params.include?([:key, :freeze])
|
106
|
-
|
107
|
-
supported_options << :freeze
|
108
|
-
end
|
118
|
+
if params.include?([:key, :freeze]) && factory.load(factory.dump("yaml"), freeze: true).frozen?
|
119
|
+
supported_options << :freeze
|
109
120
|
end
|
110
121
|
supported_options.freeze
|
111
122
|
end
|
@@ -118,19 +129,10 @@ module Bootsnap
|
|
118
129
|
ast = ::YAML.parse(payload)
|
119
130
|
return ast unless ast
|
120
131
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
def strict_visitor
|
125
|
-
self::NoTagsVisitor ||= Class.new(Psych::Visitors::ToRuby) do
|
126
|
-
def visit(target)
|
127
|
-
if target.tag
|
128
|
-
raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
|
129
|
-
end
|
132
|
+
loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], [])
|
133
|
+
scanner = ::Psych::ScalarScanner.new(loader)
|
130
134
|
|
131
|
-
|
132
|
-
end
|
133
|
-
end
|
135
|
+
NoTagsVisitor.new(scanner, loader).visit(ast)
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
@@ -227,16 +229,12 @@ module Bootsnap
|
|
227
229
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
228
230
|
end
|
229
231
|
|
230
|
-
|
231
|
-
CompileCache::
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
)
|
237
|
-
rescue Errno::EACCES
|
238
|
-
CompileCache.permission_error(path)
|
239
|
-
end
|
232
|
+
CompileCache::Native.fetch(
|
233
|
+
CompileCache::YAML.cache_dir,
|
234
|
+
File.realpath(path),
|
235
|
+
CompileCache::YAML::Psych4::SafeLoad,
|
236
|
+
kwargs,
|
237
|
+
)
|
240
238
|
end
|
241
239
|
|
242
240
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -251,16 +249,12 @@ module Bootsnap
|
|
251
249
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
252
250
|
end
|
253
251
|
|
254
|
-
|
255
|
-
CompileCache::
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
)
|
261
|
-
rescue Errno::EACCES
|
262
|
-
CompileCache.permission_error(path)
|
263
|
-
end
|
252
|
+
CompileCache::Native.fetch(
|
253
|
+
CompileCache::YAML.cache_dir,
|
254
|
+
File.realpath(path),
|
255
|
+
CompileCache::YAML::Psych4::UnsafeLoad,
|
256
|
+
kwargs,
|
257
|
+
)
|
264
258
|
end
|
265
259
|
|
266
260
|
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
|
@@ -307,16 +301,12 @@ module Bootsnap
|
|
307
301
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
308
302
|
end
|
309
303
|
|
310
|
-
|
311
|
-
CompileCache::
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
)
|
317
|
-
rescue Errno::EACCES
|
318
|
-
CompileCache.permission_error(path)
|
319
|
-
end
|
304
|
+
CompileCache::Native.fetch(
|
305
|
+
CompileCache::YAML.cache_dir,
|
306
|
+
File.realpath(path),
|
307
|
+
CompileCache::YAML::Psych3,
|
308
|
+
kwargs,
|
309
|
+
)
|
320
310
|
end
|
321
311
|
|
322
312
|
ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
|
@@ -331,16 +321,12 @@ module Bootsnap
|
|
331
321
|
return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
|
332
322
|
end
|
333
323
|
|
334
|
-
|
335
|
-
CompileCache::
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
)
|
341
|
-
rescue Errno::EACCES
|
342
|
-
CompileCache.permission_error(path)
|
343
|
-
end
|
324
|
+
CompileCache::Native.fetch(
|
325
|
+
CompileCache::YAML.cache_dir,
|
326
|
+
File.realpath(path),
|
327
|
+
CompileCache::YAML::Psych3,
|
328
|
+
kwargs,
|
329
|
+
)
|
344
330
|
end
|
345
331
|
|
346
332
|
ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
|
@@ -8,12 +8,11 @@ module Bootsnap
|
|
8
8
|
end
|
9
9
|
|
10
10
|
Error = Class.new(StandardError)
|
11
|
-
PermissionError = Class.new(Error)
|
12
11
|
|
13
|
-
def self.setup(cache_dir:, iseq:, yaml:, json:)
|
12
|
+
def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false, revalidation: false)
|
14
13
|
if iseq
|
15
14
|
if supported?
|
16
|
-
require_relative
|
15
|
+
require_relative "compile_cache/iseq"
|
17
16
|
Bootsnap::CompileCache::ISeq.install!(cache_dir)
|
18
17
|
elsif $VERBOSE
|
19
18
|
warn("[bootsnap/setup] bytecode caching is not supported on this implementation of Ruby")
|
@@ -22,7 +21,7 @@ module Bootsnap
|
|
22
21
|
|
23
22
|
if yaml
|
24
23
|
if supported?
|
25
|
-
require_relative
|
24
|
+
require_relative "compile_cache/yaml"
|
26
25
|
Bootsnap::CompileCache::YAML.install!(cache_dir)
|
27
26
|
elsif $VERBOSE
|
28
27
|
warn("[bootsnap/setup] YAML parsing caching is not supported on this implementation of Ruby")
|
@@ -31,26 +30,23 @@ module Bootsnap
|
|
31
30
|
|
32
31
|
if json
|
33
32
|
if supported?
|
34
|
-
require_relative
|
33
|
+
require_relative "compile_cache/json"
|
35
34
|
Bootsnap::CompileCache::JSON.install!(cache_dir)
|
36
35
|
elsif $VERBOSE
|
37
36
|
warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
|
38
37
|
end
|
39
38
|
end
|
40
|
-
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
"bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
|
47
|
-
"(or, less likely, doesn't have permission to read '#{path}')",
|
48
|
-
)
|
40
|
+
if supported? && defined?(Bootsnap::CompileCache::Native)
|
41
|
+
Bootsnap::CompileCache::Native.readonly = readonly
|
42
|
+
Bootsnap::CompileCache::Native.revalidation = revalidation
|
43
|
+
end
|
49
44
|
end
|
50
45
|
|
51
46
|
def self.supported?
|
52
|
-
# only enable on 'ruby' (MRI)
|
53
|
-
|
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/)
|
54
50
|
end
|
55
51
|
end
|
56
52
|
end
|
@@ -25,6 +25,11 @@ module Bootsnap
|
|
25
25
|
# This is useful before bootsnap is fully-initialized to load gems that it
|
26
26
|
# depends on, without forcing full LOAD_PATH traversals.
|
27
27
|
def self.with_gems(*gems)
|
28
|
+
# Ensure the gems are activated (their paths are in $LOAD_PATH)
|
29
|
+
gems.each do |gem_name|
|
30
|
+
gem gem_name
|
31
|
+
end
|
32
|
+
|
28
33
|
orig = $LOAD_PATH.dup
|
29
34
|
$LOAD_PATH.clear
|
30
35
|
gems.each do |gem|
|
@@ -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
|
@@ -24,8 +24,16 @@ 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
39
|
next unless feat.size < 20 && !feat.include?("/")
|
@@ -45,20 +53,19 @@ module Bootsnap
|
|
45
53
|
|
46
54
|
# Try to resolve this feature to an absolute path without traversing the
|
47
55
|
# loadpath.
|
48
|
-
def find(feature
|
56
|
+
def find(feature)
|
49
57
|
reinitialize if (@has_relative_paths && dir_changed?) || stale?
|
50
58
|
feature = feature.to_s.freeze
|
51
59
|
|
52
60
|
return feature if Bootsnap.absolute_path?(feature)
|
53
61
|
|
54
62
|
if feature.start_with?("./", "../")
|
55
|
-
return
|
63
|
+
return expand_path(feature)
|
56
64
|
end
|
57
65
|
|
58
66
|
@mutex.synchronize do
|
59
|
-
x = search_index(feature
|
67
|
+
x = search_index(feature)
|
60
68
|
return x if x
|
61
|
-
return unless try_extensions
|
62
69
|
|
63
70
|
# Ruby has some built-in features that require lies about.
|
64
71
|
# For example, 'enumerator' is built in. If you require it, ruby
|
@@ -115,7 +122,7 @@ module Bootsnap
|
|
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
|
@@ -183,15 +190,11 @@ module Bootsnap
|
|
183
190
|
end
|
184
191
|
|
185
192
|
if DLEXT2
|
186
|
-
def search_index(feature
|
187
|
-
|
188
|
-
try_index(feature +
|
189
|
-
|
190
|
-
try_index(feature + DLEXT2) ||
|
191
|
-
try_index(feature)
|
192
|
-
else
|
193
|
+
def search_index(feature)
|
194
|
+
try_index(feature + DOT_RB) ||
|
195
|
+
try_index(feature + DLEXT) ||
|
196
|
+
try_index(feature + DLEXT2) ||
|
193
197
|
try_index(feature)
|
194
|
-
end
|
195
198
|
end
|
196
199
|
|
197
200
|
def maybe_append_extension(feature)
|
@@ -201,12 +204,8 @@ module Bootsnap
|
|
201
204
|
feature
|
202
205
|
end
|
203
206
|
else
|
204
|
-
def search_index(feature
|
205
|
-
|
206
|
-
try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
|
207
|
-
else
|
208
|
-
try_index(feature)
|
209
|
-
end
|
207
|
+
def search_index(feature)
|
208
|
+
try_index(feature + DOT_RB) || try_index(feature + DLEXT) || try_index(feature)
|
210
209
|
end
|
211
210
|
|
212
211
|
def maybe_append_extension(feature)
|
@@ -216,7 +215,7 @@ module Bootsnap
|
|
216
215
|
|
217
216
|
s = rand.to_s.force_encoding(Encoding::US_ASCII).freeze
|
218
217
|
if s.respond_to?(:-@)
|
219
|
-
if (-s).equal?(s) && (-s.dup).equal?(s) || RUBY_VERSION >= "2.7"
|
218
|
+
if ((-s).equal?(s) && (-s.dup).equal?(s)) || RUBY_VERSION >= "2.7"
|
220
219
|
def try_index(feature)
|
221
220
|
if (path = @index[feature])
|
222
221
|
-File.join(path, feature).freeze
|
@@ -54,13 +54,30 @@ module Bootsnap
|
|
54
54
|
ret
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
def dup
|
59
|
+
[] + self
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :clone, :dup
|
57
63
|
end
|
58
64
|
|
59
|
-
def self.register(
|
65
|
+
def self.register(arr, observer)
|
60
66
|
return if arr.frozen? # can't register observer, but no need to.
|
61
67
|
|
62
68
|
arr.instance_variable_set(:@lpc_observer, observer)
|
63
|
-
|
69
|
+
ArrayMixin.instance_methods.each do |method_name|
|
70
|
+
arr.singleton_class.send(:define_method, method_name, ArrayMixin.instance_method(method_name))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.unregister(arr)
|
75
|
+
return unless arr.instance_variable_defined?(:@lpc_observer) && arr.instance_variable_get(:@lpc_observer)
|
76
|
+
|
77
|
+
ArrayMixin.instance_methods.each do |method_name|
|
78
|
+
arr.singleton_class.send(:remove_method, method_name)
|
79
|
+
end
|
80
|
+
arr.instance_variable_set(:@lpc_observer, nil)
|
64
81
|
end
|
65
82
|
end
|
66
83
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Kernel
|
4
|
-
|
4
|
+
alias_method :require_without_bootsnap, :require
|
5
5
|
|
6
|
-
alias_method
|
6
|
+
alias_method :require, :require # Avoid method redefinition warnings
|
7
|
+
|
8
|
+
def require(path) # rubocop:disable Lint/DuplicateMethods
|
9
|
+
return require_without_bootsnap(path) unless Bootsnap::LoadPathCache.enabled?
|
7
10
|
|
8
|
-
def require(path)
|
9
11
|
string_path = Bootsnap.rb_get_path(path)
|
10
12
|
return false if Bootsnap::LoadPathCache.loaded_features_index.key?(string_path)
|
11
13
|
|
@@ -22,9 +24,7 @@ module Kernel
|
|
22
24
|
elsif false == resolved
|
23
25
|
return false
|
24
26
|
elsif resolved.nil?
|
25
|
-
|
26
|
-
error.instance_variable_set(:@path, path)
|
27
|
-
raise error
|
27
|
+
return require_without_bootsnap(path)
|
28
28
|
else
|
29
29
|
# Note that require registers to $LOADED_FEATURES while load does not.
|
30
30
|
ret = require_without_bootsnap(resolved)
|
@@ -33,12 +33,5 @@ module Kernel
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
def load(path, wrap = false)
|
38
|
-
if (resolved = Bootsnap::LoadPathCache.load_path_cache.find(Bootsnap.rb_get_path(path), try_extensions: false))
|
39
|
-
load_without_bootsnap(resolved, wrap)
|
40
|
-
else
|
41
|
-
load_without_bootsnap(path, wrap)
|
42
|
-
end
|
43
|
-
end
|
36
|
+
private :require
|
44
37
|
end
|
@@ -40,7 +40,7 @@ module Bootsnap
|
|
40
40
|
|
41
41
|
# /a/b/lib/my/foo.rb
|
42
42
|
# ^^^^^^^^^
|
43
|
-
short = feat[(lpe.length + 1)
|
43
|
+
short = feat[(lpe.length + 1)..]
|
44
44
|
stripped = strip_extension_if_elidable(short)
|
45
45
|
@lfi[short] = hash
|
46
46
|
@lfi[stripped] = hash
|
@@ -76,7 +76,7 @@ module Bootsnap
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def identify(short, cursor)
|
79
|
-
$LOADED_FEATURES[cursor
|
79
|
+
$LOADED_FEATURES[cursor..].detect do |feat|
|
80
80
|
offset = 0
|
81
81
|
while (offset = feat.index(short, offset))
|
82
82
|
if feat.index(".", offset + 1) && !feat.index("/", offset + 2)
|