bootsnap 1.4.0 → 1.4.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40694ca2512ff7861584a4f9cee05e424c3d9319f3b36c7f4f45f9196d2d9f19
4
- data.tar.gz: '087c4f2dafdd51a6a3d0a69d31aadabfe6ef90bf2ab12b861f2a288d7b16c04a'
3
+ metadata.gz: be54a60d54f32f824d5c2fdccf189cdcb5409d1848c55f6e654fe44ace1d3f21
4
+ data.tar.gz: c6239c30984a936b76e218c2b1292a7e72d817d3e4847a6c58f0121c1e3068dc
5
5
  SHA512:
6
- metadata.gz: ecbabde06c5f871702a000128e4dbc15d60fe0e16ab7adb4a8bb40b1c3b5b5f54b39c6927dc5aea9e49242d7e7f5deb596c42c44f8c2b8f998f835ec5b058b47
7
- data.tar.gz: d566ffe7c3048f57c3c4fcd925d5e8ff5a722fc2622628e26571361e7a0fbdb5cbe8c724d45f2fc8ba09d0de614c5fad87f1875ecc99a08a34aab3afa7665fe1
6
+ metadata.gz: c251990457406cd51726eec8d62e0e1028bbeade6e24f266cb743d6f00f53650841beed9ee8e1795c78febd18ea234691125920c16aa5a95031718a28619bb31
7
+ data.tar.gz: 554a885bf2264f088618c41864fb84069d0664b35af0ae4b619c8fbb1cf285c80fad564d36845161f8044c35c6b53a33e66f27eb75c6715f25869af2795c97f6
@@ -0,0 +1,2 @@
1
+ # mvm:maintainer
2
+ * @burke
@@ -0,0 +1,2 @@
1
+ enabled:
2
+ - cla
data/.travis.yml CHANGED
@@ -17,8 +17,5 @@ matrix:
17
17
  - rvm: jruby
18
18
  os: linux
19
19
  env: MINIMAL_SUPPORT=1
20
- - rvm: truffleruby
21
- os: linux
22
- env: MINIMAL_SUPPORT=1
23
20
 
24
21
  script: bin/ci
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # 1.4.5
2
+
3
+ * MRI 2.7 support
4
+ * Fixed concurrency bugs
5
+
6
+ # 1.4.4
7
+
8
+ * Disable ISeq cache in `bootsnap/setup` by default in Ruby 2.5
9
+
10
+ # 1.4.3
11
+
12
+ * Fix some cache permissions and umask issues after switch to mkstemp
13
+
14
+ # 1.4.2
15
+
16
+ * Fix bug when removing features loaded by relative path from `$LOADED_FEATURES`
17
+ * Fix bug with propagation of `NameError` up from nested calls to `require`
18
+
19
+ # 1.4.1
20
+
21
+ * Don't register change observers to frozen objects.
22
+
1
23
  # 1.4.0
2
24
 
3
25
  * When running in development mode, always fall back to a full path scan on LoadError, making
data/README.md CHANGED
@@ -4,9 +4,14 @@ Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSup
4
4
  to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
5
5
 
6
6
  #### Performance
7
- - [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately 50%, from roughly 6 to 3 seconds on one machine;
7
+
8
+ - [Discourse](https://github.com/discourse/discourse) reports a boot time reduction of approximately
9
+ 50%, from roughly 6 to 3 seconds on one machine;
8
10
  - One of our smaller internal apps also sees a reduction of 50%, from 3.6 to 1.8 seconds;
9
- - The core Shopify platform -- a rather large monolithic application -- boots about 75% faster, dropping from around 25s to 6.5s.
11
+ - The core Shopify platform -- a rather large monolithic application -- boots about 75% faster,
12
+ dropping from around 25s to 6.5s.
13
+ * In Shopify core (a large app), about 25% of this gain can be attributed to `compile_cache_*`
14
+ features; 75% to path caching, and ~1% to `disable_trace`. This is fairly representative.
10
15
 
11
16
  ## Usage
12
17
 
@@ -24,6 +29,14 @@ If you are using Rails, add this to `config/boot.rb` immediately after `require
24
29
  require 'bootsnap/setup'
25
30
  ```
26
31
 
32
+ Note that bootsnap writes to `tmp/cache`, and that directory *must* be writable. Rails will fail to
33
+ boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
34
+ unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
35
+
36
+ **Note also that bootsnap will never clean up its own cache: this is left up to you. Depending on your
37
+ deployment strategy, you may need to periodically purge `tmp/cache/bootsnap*`. If you notice deploys
38
+ getting progressively slower, this is almost certainly the cause.**
39
+
27
40
  It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
28
41
  important to load Bootsnap as early as possible to get maximum performance improvement.
29
42
 
@@ -41,12 +54,14 @@ Bootsnap.setup(
41
54
  development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
42
55
  load_path_cache: true, # Optimize the LOAD_PATH with a cache
43
56
  autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
44
- disable_trace: true, # (Alpha) Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
57
+ disable_trace: true, # Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
45
58
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
46
59
  compile_cache_yaml: true # Compile YAML into a cache
47
60
  )
48
61
  ```
49
62
 
63
+ **Note that `disable_trace` will break debuggers and tracing.**
64
+
50
65
  **Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
51
66
  'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
52
67
  will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
@@ -278,3 +293,12 @@ open /c/nope.bundle -> -1
278
293
  ```
279
294
  # (nothing!)
280
295
  ```
296
+
297
+ ## When not to use Bootsnap
298
+
299
+ *Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
300
+ engines.
301
+
302
+ *Non-local filesystems*: Bootsnap depends on `tmp/cache` (or whatever you set its cache directory
303
+ to) being on a relatively fast filesystem. If you put it on a network mount, bootsnap is very likely
304
+ to slow your application down quite a lot.
@@ -74,6 +74,8 @@ static uint32_t current_ruby_platform;
74
74
  static uint32_t current_ruby_revision;
75
75
  /* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
76
76
  static uint32_t current_compile_option_crc32 = 0;
77
+ /* Current umask */
78
+ static mode_t current_umask;
77
79
 
78
80
  /* Bootsnap::CompileCache::{Native, Uncompilable} */
79
81
  static VALUE rb_mBootsnap;
@@ -94,6 +96,7 @@ static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
94
96
  static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
95
97
  static int open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance);
96
98
  static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance);
99
+ static uint32_t get_ruby_revision(void);
97
100
  static uint32_t get_ruby_platform(void);
98
101
 
99
102
  /*
@@ -134,7 +137,7 @@ Init_bootsnap(void)
134
137
  rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
135
138
  rb_eBootsnap_CompileCache_Uncompilable = rb_define_class_under(rb_mBootsnap_CompileCache, "Uncompilable", rb_eStandardError);
136
139
 
137
- current_ruby_revision = FIX2INT(rb_const_get(rb_cObject, rb_intern("RUBY_REVISION")));
140
+ current_ruby_revision = get_ruby_revision();
138
141
  current_ruby_platform = get_ruby_platform();
139
142
 
140
143
  uncompilable = rb_intern("__bootsnap_uncompilable__");
@@ -142,6 +145,9 @@ Init_bootsnap(void)
142
145
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
143
146
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 3);
144
147
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
148
+
149
+ current_umask = umask(0777);
150
+ umask(current_umask);
145
151
  }
146
152
 
147
153
  /*
@@ -191,6 +197,26 @@ fnv1a_64(const char *str)
191
197
  return fnv1a_64_iter(h, str);
192
198
  }
193
199
 
200
+ /*
201
+ * Ruby's revision may be Integer or String. CRuby 2.7 or later uses
202
+ * Git commit ID as revision. It's String.
203
+ */
204
+ static uint32_t
205
+ get_ruby_revision(void)
206
+ {
207
+ VALUE ruby_revision;
208
+
209
+ ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
210
+ if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
211
+ return FIX2INT(ruby_revision);
212
+ } else {
213
+ uint64_t hash;
214
+
215
+ hash = fnv1a_64(StringValueCStr(ruby_revision));
216
+ return (uint32_t)(hash >> 32);
217
+ }
218
+ }
219
+
194
220
  /*
195
221
  * When ruby's version doesn't change, but it's recompiled on a different OS
196
222
  * (or OS version), we need to invalidate the cache.
@@ -468,18 +494,17 @@ static int
468
494
  atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
469
495
  {
470
496
  char template[MAX_CACHEPATH_SIZE + 20];
471
- char * dest;
472
497
  char * tmp_path;
473
498
  int fd, ret;
474
499
  ssize_t nwrite;
475
500
 
476
- dest = strncpy(template, path, MAX_CACHEPATH_SIZE);
477
- strcat(dest, ".tmp.XXXXXX");
501
+ tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
502
+ strcat(tmp_path, ".tmp.XXXXXX");
478
503
 
479
- tmp_path = mktemp(template);
480
- fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
504
+ // mkstemp modifies the template to be the actual created path
505
+ fd = mkstemp(tmp_path);
481
506
  if (fd < 0) {
482
- if (mkpath(path, 0775) < 0) {
507
+ if (mkpath(tmp_path, 0775) < 0) {
483
508
  *errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:mkpath";
484
509
  return -1;
485
510
  }
@@ -517,6 +542,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
517
542
  ret = rename(tmp_path, path);
518
543
  if (ret < 0) {
519
544
  *errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:rename";
545
+ return -1;
546
+ }
547
+ ret = chmod(path, 0664 & ~current_umask);
548
+ if (ret < 0) {
549
+ *errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:chmod";
520
550
  }
521
551
  return ret;
522
552
  }
@@ -39,6 +39,8 @@ module Bootsnap
39
39
  path.to_s,
40
40
  Bootsnap::CompileCache::ISeq
41
41
  )
42
+ rescue Errno::EACCES
43
+ Bootsnap::CompileCache.permission_error(path)
42
44
  rescue RuntimeError => e
43
45
  if e.message =~ /unmatched platform/
44
46
  puts("unmatched platform for file #{path}")
@@ -46,11 +46,15 @@ module Bootsnap
46
46
 
47
47
  klass = class << ::YAML; self; end
48
48
  klass.send(:define_method, :load_file) do |path|
49
- Bootsnap::CompileCache::Native.fetch(
50
- cache_dir,
51
- path,
52
- Bootsnap::CompileCache::YAML
53
- )
49
+ begin
50
+ Bootsnap::CompileCache::Native.fetch(
51
+ cache_dir,
52
+ path,
53
+ Bootsnap::CompileCache::YAML
54
+ )
55
+ rescue Errno::EACCES
56
+ Bootsnap::CompileCache.permission_error(path)
57
+ end
54
58
  end
55
59
  end
56
60
  end
@@ -1,5 +1,8 @@
1
1
  module Bootsnap
2
2
  module CompileCache
3
+ Error = Class.new(StandardError)
4
+ PermissionError = Class.new(Error)
5
+
3
6
  def self.setup(cache_dir:, iseq:, yaml:)
4
7
  if iseq
5
8
  if supported?
@@ -20,6 +23,15 @@ module Bootsnap
20
23
  end
21
24
  end
22
25
 
26
+ def self.permission_error(path)
27
+ cpath = Bootsnap::CompileCache::ISeq.cache_dir
28
+ raise(
29
+ PermissionError,
30
+ "bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
31
+ "(or, less likely, doesn't have permission to read '#{path}')",
32
+ )
33
+ end
34
+
23
35
  def self.supported?
24
36
  # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), and >= 2.3.0
25
37
  RUBY_ENGINE == 'ruby' &&
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative('../explicit_require')
2
4
 
3
5
  module Bootsnap
@@ -46,7 +48,7 @@ module Bootsnap
46
48
  reinitialize if (@has_relative_paths && dir_changed?) || stale?
47
49
  feature = feature.to_s
48
50
  return feature if absolute_path?(feature)
49
- return File.expand_path(feature) if feature.start_with?('./')
51
+ return expand_path(feature) if feature.start_with?('./')
50
52
  @mutex.synchronize do
51
53
  x = search_index(feature)
52
54
  return x if x
@@ -162,6 +164,10 @@ module Bootsnap
162
164
  end
163
165
  end
164
166
 
167
+ def expand_path(feature)
168
+ maybe_append_extension(File.expand_path(feature))
169
+ end
170
+
165
171
  def stale?
166
172
  @development_mode && @generated_at + AGE_THRESHOLD < now
167
173
  end
@@ -174,10 +180,18 @@ module Bootsnap
174
180
  def search_index(f)
175
181
  try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f + DLEXT2) || try_index(f)
176
182
  end
183
+
184
+ def maybe_append_extension(f)
185
+ try_ext(f + DOT_RB) || try_ext(f + DLEXT) || try_ext(f + DLEXT2) || f
186
+ end
177
187
  else
178
188
  def search_index(f)
179
189
  try_index(f + DOT_RB) || try_index(f + DLEXT) || try_index(f)
180
190
  end
191
+
192
+ def maybe_append_extension(f)
193
+ try_ext(f + DOT_RB) || try_ext(f + DLEXT) || f
194
+ end
181
195
  end
182
196
 
183
197
  def try_index(f)
@@ -185,6 +199,10 @@ module Bootsnap
185
199
  p + '/' + f
186
200
  end
187
201
  end
202
+
203
+ def try_ext(f)
204
+ f if File.exist?(f)
205
+ end
188
206
  end
189
207
  end
190
208
  end
@@ -53,6 +53,7 @@ module Bootsnap
53
53
  end
54
54
 
55
55
  def self.register(observer, arr)
56
+ return if arr.frozen? # can't register observer, but no need to.
56
57
  arr.instance_variable_set(:@lpc_observer, observer)
57
58
  arr.extend(ArrayMixin)
58
59
  end
@@ -60,19 +60,22 @@ module Bootsnap
60
60
  super
61
61
  end
62
62
  rescue NameError => e
63
+ raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
64
+ e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
65
+
63
66
  # This function can end up called recursively, we only want to
64
67
  # retry at the top-level.
65
- raise if Thread.current[:without_bootsnap_retry]
68
+ raise(e) if Thread.current[:without_bootsnap_retry]
66
69
  # If we already had cache disabled, there's no use retrying
67
- raise if Thread.current[:without_bootsnap_cache]
70
+ raise(e) if Thread.current[:without_bootsnap_cache]
68
71
  # NoMethodError is a NameError, but we only want to handle actual
69
72
  # NameError instances.
70
- raise unless e.class == NameError
73
+ raise(e) unless e.class == NameError
71
74
  # We can only confidently handle cases when *this* constant fails
72
75
  # to load, not other constants referred to by it.
73
- raise unless e.name == const_name
76
+ raise(e) unless e.name == const_name
74
77
  # If the constant was actually loaded, something else went wrong?
75
- raise if from_mod.const_defined?(const_name)
78
+ raise(e) if from_mod.const_defined?(const_name)
76
79
  CoreExt::ActiveSupport.without_bootsnap_cache { super }
77
80
  end
78
81
 
@@ -80,9 +83,12 @@ module Bootsnap
80
83
  # reiterate it with version polymorphism here...
81
84
  def depend_on(*)
82
85
  super
83
- rescue LoadError
86
+ rescue LoadError => e
87
+ raise(e) if e.instance_variable_defined?(Bootsnap::LoadPathCache::ERROR_TAG_IVAR)
88
+ e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
89
+
84
90
  # If we already had cache disabled, there's no use retrying
85
- raise if Thread.current[:without_bootsnap_cache]
91
+ raise(e) if Thread.current[:without_bootsnap_cache]
86
92
  CoreExt::ActiveSupport.without_bootsnap_cache { super }
87
93
  end
88
94
  end
@@ -3,6 +3,7 @@ module Bootsnap
3
3
  module CoreExt
4
4
  def self.make_load_error(path)
5
5
  err = LoadError.new("cannot load such file -- #{path}")
6
+ err.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
6
7
  err.define_singleton_method(:path) { path }
7
8
  err
8
9
  end
@@ -30,6 +31,9 @@ module Kernel
30
31
  end
31
32
 
32
33
  raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
34
+ rescue LoadError => e
35
+ e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
36
+ raise(e)
33
37
  rescue Bootsnap::LoadPathCache::ReturnFalse
34
38
  false
35
39
  rescue Bootsnap::LoadPathCache::FallbackScan
@@ -56,6 +60,9 @@ module Kernel
56
60
  end
57
61
 
58
62
  raise(Bootsnap::LoadPathCache::CoreExt.make_load_error(path))
63
+ rescue LoadError => e
64
+ e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
65
+ raise(e)
59
66
  rescue Bootsnap::LoadPathCache::ReturnFalse
60
67
  false
61
68
  rescue Bootsnap::LoadPathCache::FallbackScan
@@ -74,6 +81,9 @@ class Module
74
81
  # added to $LOADED_FEATURES and won't be able to hook that modification
75
82
  # since it's done in C-land.
76
83
  autoload_without_bootsnap(const, Bootsnap::LoadPathCache.load_path_cache.find(path) || path)
84
+ rescue LoadError => e
85
+ e.instance_variable_set(Bootsnap::LoadPathCache::ERROR_TAG_IVAR, true)
86
+ raise(e)
77
87
  rescue Bootsnap::LoadPathCache::ReturnFalse
78
88
  false
79
89
  rescue Bootsnap::LoadPathCache::FallbackScan
@@ -4,4 +4,14 @@ class << $LOADED_FEATURES
4
4
  Bootsnap::LoadPathCache.loaded_features_index.purge(key)
5
5
  delete_without_bootsnap(key)
6
6
  end
7
+
8
+ alias_method(:reject_without_bootsnap!, :reject!)
9
+ def reject!(&block)
10
+ backup = dup
11
+
12
+ # FIXME: if no block is passed we'd need to return a decorated iterator
13
+ reject_without_bootsnap!(&block)
14
+
15
+ Bootsnap::LoadPathCache.loaded_features_index.purge_multi(backup - self)
16
+ end
7
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bootsnap
2
4
  module LoadPathCache
3
5
  # LoadedFeaturesIndex partially mirrors an internal structure in ruby that
@@ -55,6 +57,13 @@ module Bootsnap
55
57
  end
56
58
  end
57
59
 
60
+ def purge_multi(features)
61
+ rejected_hashes = features.map(&:hash).to_set
62
+ @mutex.synchronize do
63
+ @lfi.reject! { |_, hash| rejected_hashes.include?(hash) }
64
+ end
65
+ end
66
+
58
67
  def key?(feature)
59
68
  @mutex.synchronize { @lfi.key?(feature) }
60
69
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative('../explicit_require')
2
4
 
3
5
  module Bootsnap
@@ -11,7 +13,7 @@ module Bootsnap
11
13
  BUNDLE_PATH = if Bootsnap.bundler?
12
14
  (Bundler.bundle_path.cleanpath.to_s << LoadPathCache::SLASH).freeze
13
15
  else
14
- ''.freeze
16
+ ''
15
17
  end
16
18
 
17
19
  def self.call(path)
@@ -11,7 +11,8 @@ module Bootsnap
11
11
 
12
12
  def initialize(store_path)
13
13
  @store_path = store_path
14
- @in_txn = false
14
+ # TODO: Remove conditional once Ruby 2.2 support is dropped.
15
+ @txn_mutex = defined?(::Mutex) ? ::Mutex.new : ::Thread::Mutex.new
15
16
  @dirty = false
16
17
  load_data
17
18
  end
@@ -21,7 +22,7 @@ module Bootsnap
21
22
  end
22
23
 
23
24
  def fetch(key)
24
- raise(SetOutsideTransactionNotAllowed) unless @in_txn
25
+ raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
25
26
  v = get(key)
26
27
  unless v
27
28
  @dirty = true
@@ -32,7 +33,7 @@ module Bootsnap
32
33
  end
33
34
 
34
35
  def set(key, value)
35
- raise(SetOutsideTransactionNotAllowed) unless @in_txn
36
+ raise(SetOutsideTransactionNotAllowed) unless @txn_mutex.owned?
36
37
  if value != @data[key]
37
38
  @dirty = true
38
39
  @data[key] = value
@@ -40,12 +41,14 @@ module Bootsnap
40
41
  end
41
42
 
42
43
  def transaction
43
- raise(NestedTransactionError) if @in_txn
44
- @in_txn = true
45
- yield
46
- ensure
47
- commit_transaction
48
- @in_txn = false
44
+ raise(NestedTransactionError) if @txn_mutex.owned?
45
+ @txn_mutex.synchronize do
46
+ begin
47
+ yield
48
+ ensure
49
+ commit_transaction
50
+ end
51
+ end
49
52
  end
50
53
 
51
54
  private
@@ -60,9 +63,11 @@ module Bootsnap
60
63
  def load_data
61
64
  @data = begin
62
65
  MessagePack.load(File.binread(@store_path))
63
- # handle malformed data due to upgrade incompatability
64
- rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
65
- {}
66
+ # handle malformed data due to upgrade incompatability
67
+ rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
68
+ {}
69
+ rescue ArgumentError => e
70
+ e.message =~ /negative array size/ ? {} : raise
66
71
  end
67
72
  end
68
73
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bootsnap
2
4
  module LoadPathCache
3
5
  ReturnFalse = Class.new(StandardError)
@@ -7,6 +9,11 @@ module Bootsnap
7
9
  DOT_SO = '.so'
8
10
  SLASH = '/'
9
11
 
12
+ # If a NameError happens several levels deep, don't re-handle it
13
+ # all the way up the chain: mark it once and bubble it up without
14
+ # more retries.
15
+ ERROR_TAG_IVAR = :@__bootsnap_rescued
16
+
10
17
  DL_EXTENSIONS = ::RbConfig::CONFIG
11
18
  .values_at('DLEXT', 'DLEXT2')
12
19
  .reject { |ext| !ext || ext.empty? }
@@ -24,12 +24,15 @@ unless cache_dir
24
24
  cache_dir = File.join(app_root, 'tmp', 'cache')
25
25
  end
26
26
 
27
+ ruby_version = Gem::Version.new(RUBY_VERSION)
28
+ iseq_cache_enabled = ruby_version < Gem::Version.new('2.5.0') || ruby_version >= Gem::Version.new('2.6.0')
29
+
27
30
  Bootsnap.setup(
28
31
  cache_dir: cache_dir,
29
32
  development_mode: development_mode,
30
33
  load_path_cache: true,
31
34
  autoload_paths_cache: true, # assume rails. open to PRs to impl. detection
32
35
  disable_trace: false,
33
- compile_cache_iseq: true,
36
+ compile_cache_iseq: iseq_cache_enabled,
34
37
  compile_cache_yaml: true,
35
38
  )
@@ -1,3 +1,3 @@
1
1
  module Bootsnap
2
- VERSION = "1.4.0"
2
+ VERSION = "1.4.5"
3
3
  end
data/shipit.rubygems.yml CHANGED
@@ -1,4 +0,0 @@
1
- dependencies:
2
- bundler:
3
- without:
4
- - development
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-11 00:00:00.000000000 Z
11
+ date: 2019-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -102,6 +102,8 @@ extensions:
102
102
  - ext/bootsnap/extconf.rb
103
103
  extra_rdoc_files: []
104
104
  files:
105
+ - ".github/CODEOWNERS"
106
+ - ".github/probots.yml"
105
107
  - ".gitignore"
106
108
  - ".rubocop.yml"
107
109
  - ".travis.yml"