bootsnap 1.9.1 → 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 +36 -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 +42 -12
- 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 +57 -41
- data/lib/bootsnap/load_path_cache/change_observer.rb +2 -0
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +33 -40
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +1 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +31 -20
- 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 +24 -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 +39 -28
- metadata +5 -75
@@ -1,11 +1,17 @@
|
|
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 JSON
|
7
8
|
class << self
|
8
|
-
attr_accessor(:msgpack_factory, :
|
9
|
+
attr_accessor(:msgpack_factory, :supported_options)
|
10
|
+
attr_reader(:cache_dir)
|
11
|
+
|
12
|
+
def cache_dir=(cache_dir)
|
13
|
+
@cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}json" : "#{cache_dir}-json"
|
14
|
+
end
|
9
15
|
|
10
16
|
def input_to_storage(payload, _)
|
11
17
|
obj = ::JSON.parse(payload)
|
@@ -13,7 +19,7 @@ module Bootsnap
|
|
13
19
|
end
|
14
20
|
|
15
21
|
def storage_to_output(data, kwargs)
|
16
|
-
if kwargs
|
22
|
+
if kwargs&.key?(:symbolize_names)
|
17
23
|
kwargs[:symbolize_keys] = kwargs.delete(:symbolize_names)
|
18
24
|
end
|
19
25
|
msgpack_factory.load(data, kwargs)
|
@@ -23,7 +29,7 @@ module Bootsnap
|
|
23
29
|
::JSON.parse(data, **(kwargs || {}))
|
24
30
|
end
|
25
31
|
|
26
|
-
def precompile(path
|
32
|
+
def precompile(path)
|
27
33
|
Bootsnap::CompileCache::Native.precompile(
|
28
34
|
cache_dir,
|
29
35
|
path.to_s,
|
@@ -40,22 +46,23 @@ module Bootsnap
|
|
40
46
|
end
|
41
47
|
|
42
48
|
def init!
|
43
|
-
require(
|
44
|
-
require(
|
49
|
+
require("json")
|
50
|
+
require("msgpack")
|
45
51
|
|
46
52
|
self.msgpack_factory = MessagePack::Factory.new
|
47
53
|
self.supported_options = [:symbolize_names]
|
48
54
|
if ::JSON.parse('["foo"]', freeze: true).first.frozen?
|
49
55
|
self.supported_options = [:freeze]
|
50
56
|
end
|
51
|
-
|
57
|
+
supported_options.freeze
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
55
61
|
module Patch
|
56
62
|
def load_file(path, *args)
|
57
63
|
return super if args.size > 1
|
58
|
-
|
64
|
+
|
65
|
+
if (kwargs = args.first)
|
59
66
|
return super unless kwargs.is_a?(Hash)
|
60
67
|
return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
|
61
68
|
end
|
@@ -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}")
|