bootsnap 1.15.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68bf895082a1d27386514357e36c1b53972f7f2e22f50b2f9b77cde53fec9110
4
- data.tar.gz: dcc033dc6522aa59d853f7840d228430aa5591892d063d91c061fb1c7edcb820
3
+ metadata.gz: a17ea0a302554fd131e2941fa8b97b1ff9441750fb4182247bb44d91aa1174e8
4
+ data.tar.gz: 9ba8386281c2dbb6896b1032e4b6a6c949b81164d54848beac032a977ee43e44
5
5
  SHA512:
6
- metadata.gz: b640bf89d0448536c17f88352e97aa4b5a321cec6abfcd011d1f442ec9c9170b0b08e0329e398919140da1b0d1e65d343b761be392d25eb75540425c42d766cb
7
- data.tar.gz: 97080b27ccca5dd741bfccc0c3cd412c7fcea917fad2e79961ef2a7c18a1dd5478fc33f2a4d9f59a5f8765696760aef76799f401b1cf13b9a68d7ffc0d0a326b
6
+ metadata.gz: a1653ddc2779d492f3e5c50719c07a0cdc7a9fab27748dd82096ec923232f9c8b04ddbee738325d13a3aa8c87a38825581677408512437e12933fe18434bcdce
7
+ data.tar.gz: 9c0f5d4fe058c13577d97011083b7bab23b290cd16b955937b00aaafb5a62239532df4e0d090e576c85dae45dcf504ffa63f9fedd5b2c5dca67afc4457cdbb59
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.17.0
4
+
5
+ * Ensure `$LOAD_PATH.dup` is Ractor shareable to fix an conflit with `did_you_mean`.
6
+ * Allow to ignore direcotries using absolute paths.
7
+ * Support YAML and JSON CompileCache on TruffleRuby.
8
+ * Support LoadPathCache on TruffleRuby.
9
+
10
+ # 1.16.0
11
+
12
+ * Use `RbConfig::CONFIG["rubylibdir"]` instead of `RbConfig::CONFIG["libdir"]` to check for stdlib files. See #431.
13
+ * Fix the cached version of `YAML.load_file` being slightly more permissive than the default `Psych` one. See #434.
14
+ `Date` and `Time` values are now properly rejected, as well as aliases.
15
+ If this causes a regression in your application, it is recommended to load *trusted* YAML files with `YAML.unsafe_load_file`.
16
+
3
17
  # 1.15.0
4
18
 
5
19
  * Add a readonly mode, for environments in which the updated cache wouldn't be persisted. See #428 and #423.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](https://github.com/Shopify/bootsnap/actions)
2
2
 
3
- Bootsnap is a library that plugs into Ruby, with optional support for `YAML`,
3
+ Bootsnap is a library that plugs into Ruby, with optional support for `YAML` and `JSON`,
4
4
  to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
5
5
 
6
6
  #### Performance
@@ -57,6 +57,7 @@ Bootsnap.setup(
57
57
  load_path_cache: true, # Optimize the LOAD_PATH with a cache
58
58
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
59
59
  compile_cache_yaml: true, # Compile YAML into a cache
60
+ compile_cache_json: true, # Compile JSON into a cache
60
61
  readonly: true, # Use the caches but don't update them on miss or stale entries.
61
62
  )
62
63
  ```
@@ -68,7 +69,7 @@ will help optimize boot time further if you have an extremely large `$LOAD_PATH`
68
69
  Note: Bootsnap and [Spring](https://github.com/rails/spring) are orthogonal tools. While Bootsnap
69
70
  speeds up the loading of individual source files, Spring keeps a copy of a pre-booted Rails process
70
71
  on hand to completely skip parts of the boot process the next time it's needed. The two tools work
71
- well together, and are both included in a newly-generated Rails applications by default.
72
+ well together.
72
73
 
73
74
  ### Environment variables
74
75
 
@@ -82,7 +83,7 @@ well together, and are both included in a newly-generated Rails applications by
82
83
  - `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
83
84
  - `BOOTSNAP_IGNORE_DIRECTORIES` a comma separated list of directories that shouldn't be scanned.
84
85
  Useful when you have large directories of non-ruby files inside `$LOAD_PATH`.
85
- It default to ignore any directory named `node_modules`.
86
+ It defaults to ignore any directory named `node_modules`.
86
87
 
87
88
  ### Environments
88
89
 
@@ -119,6 +120,7 @@ into two broad categories:
119
120
  compilation.
120
121
  * `YAML.load_file` is modified to cache the result of loading a YAML object in MessagePack format
121
122
  (or Marshal, if the message uses types unsupported by MessagePack).
123
+ * `JSON.load_file` is modified to cache the result of loading a JSON object in MessagePack format
122
124
 
123
125
  ### Path Pre-Scanning
124
126
 
@@ -189,9 +191,9 @@ translated ruby source to an internal bytecode format, which is then executed by
189
191
  allows caching that bytecode. This allows us to bypass the relatively-expensive compilation step on
190
192
  subsequent loads of the same file.
191
193
 
192
- We also noticed that we spend a lot of time loading YAML documents during our application boot, and
193
- that MessagePack and Marshal are *much* faster at deserialization than YAML, even with a fast
194
- implementation. We use the same strategy of compilation caching for YAML documents, with the
194
+ We also noticed that we spend a lot of time loading YAML and JSON documents during our application boot, and
195
+ that MessagePack and Marshal are *much* faster at deserialization than YAML and JSON, even with a fast
196
+ implementation. We use the same strategy of compilation caching for YAML and JSON documents, with the
195
197
  equivalent of Ruby's "bytecode" format being a MessagePack document (or, in the case of YAML
196
198
  documents with types unsupported by MessagePack, a Marshal stream).
197
199
 
@@ -377,7 +377,9 @@ open_current_file(char * path, struct bs_cache_key * key, const char ** errno_pr
377
377
 
378
378
  if (fstat(fd, &statbuf) < 0) {
379
379
  *errno_provenance = "bs_fetch:open_current_file:fstat";
380
+ int previous_errno = errno;
380
381
  close(fd);
382
+ errno = previous_errno;
381
383
  return -1;
382
384
  }
383
385
 
@@ -467,7 +469,6 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
467
469
  static int
468
470
  fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
469
471
  {
470
- char * data = NULL;
471
472
  ssize_t nread;
472
473
  int ret;
473
474
 
@@ -479,8 +480,8 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
479
480
  ret = ERROR_WITH_ERRNO;
480
481
  goto done;
481
482
  }
482
- data = ALLOC_N(char, data_size);
483
- nread = read(fd, data, data_size);
483
+ storage_data = rb_str_buf_new(data_size);
484
+ nread = read(fd, RSTRING_PTR(storage_data), data_size);
484
485
  if (nread < 0) {
485
486
  *errno_provenance = "bs_fetch:fetch_cached_data:read";
486
487
  ret = ERROR_WITH_ERRNO;
@@ -491,7 +492,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
491
492
  goto done;
492
493
  }
493
494
 
494
- storage_data = rb_str_new(data, data_size);
495
+ rb_str_set_len(storage_data, nread);
495
496
 
496
497
  *exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
497
498
  if (*output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
@@ -500,7 +501,6 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE *
500
501
  }
501
502
  ret = 0;
502
503
  done:
503
- if (data != NULL) xfree(data);
504
504
  return ret;
505
505
  }
506
506
 
@@ -607,17 +607,22 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons
607
607
 
608
608
 
609
609
  /* Read contents from an fd, whose contents are asserted to be +size+ bytes
610
- * long, into a buffer */
611
- static ssize_t
612
- bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
610
+ * long, returning a Ruby string on success and Qfalse on failure */
611
+ static VALUE
612
+ bs_read_contents(int fd, size_t size, const char ** errno_provenance)
613
613
  {
614
+ VALUE contents;
614
615
  ssize_t nread;
615
- *contents = ALLOC_N(char, size);
616
- nread = read(fd, *contents, size);
616
+ contents = rb_str_buf_new(size);
617
+ nread = read(fd, RSTRING_PTR(contents), size);
618
+
617
619
  if (nread < 0) {
618
620
  *errno_provenance = "bs_fetch:bs_read_contents:read";
621
+ return Qfalse;
622
+ } else {
623
+ rb_str_set_len(contents, nread);
624
+ return contents;
619
625
  }
620
- return nread;
621
626
  }
622
627
 
623
628
  /*
@@ -668,7 +673,6 @@ static VALUE
668
673
  bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
669
674
  {
670
675
  struct bs_cache_key cached_key, current_key;
671
- char * contents = NULL;
672
676
  int cache_fd = -1, current_fd = -1;
673
677
  int res, valid_cache = 0, exception_tag = 0;
674
678
  const char * errno_provenance = NULL;
@@ -678,10 +682,14 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
678
682
  VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
679
683
 
680
684
  VALUE exception; /* ruby exception object to raise instead of returning */
685
+ VALUE exception_message; /* ruby exception string to use instead of errno_provenance */
681
686
 
682
687
  /* Open the source file and generate a cache key for it */
683
688
  current_fd = open_current_file(path, &current_key, &errno_provenance);
684
- if (current_fd < 0) goto fail_errno;
689
+ if (current_fd < 0) {
690
+ exception_message = path_v;
691
+ goto fail_errno;
692
+ }
685
693
 
686
694
  /* Open the cache key if it exists, and read its cache key in */
687
695
  cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
@@ -691,6 +699,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
691
699
  rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
692
700
  }
693
701
  } else if (cache_fd < 0) {
702
+ exception_message = rb_str_new_cstr(cache_path);
694
703
  goto fail_errno;
695
704
  } else {
696
705
  /* True if the cache existed and no invalidating changes have occurred since
@@ -713,13 +722,18 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
713
722
  else if (res == CACHE_UNCOMPILABLE) {
714
723
  /* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
715
724
  This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
716
- if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
717
- input_data = rb_str_new(contents, current_key.size);
725
+ if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
726
+ exception_message = path_v;
727
+ goto fail_errno;
728
+ }
718
729
  bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
719
730
  if (exception_tag != 0) goto raise;
720
731
  goto succeed;
721
732
  } else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
722
- else if (res == ERROR_WITH_ERRNO) goto fail_errno;
733
+ else if (res == ERROR_WITH_ERRNO){
734
+ exception_message = rb_str_new_cstr(cache_path);
735
+ goto fail_errno;
736
+ }
723
737
  else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
724
738
  }
725
739
  close(cache_fd);
@@ -727,8 +741,10 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
727
741
  /* Cache is stale, invalid, or missing. Regenerate and write it out. */
728
742
 
729
743
  /* Read the contents of the source file into a buffer */
730
- if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
731
- input_data = rb_str_new(contents, current_key.size);
744
+ if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse){
745
+ exception_message = path_v;
746
+ goto fail_errno;
747
+ }
732
748
 
733
749
  /* Try to compile the input_data using input_to_storage(input_data) */
734
750
  exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
@@ -765,6 +781,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
765
781
  * No point raising an error */
766
782
  if (errno != ENOENT) {
767
783
  errno_provenance = "bs_fetch:unlink";
784
+ exception_message = rb_str_new_cstr(cache_path);
768
785
  goto fail_errno;
769
786
  }
770
787
  }
@@ -775,7 +792,6 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
775
792
  goto succeed; /* output_data is now the correct return. */
776
793
 
777
794
  #define CLEANUP \
778
- if (contents != NULL) xfree(contents); \
779
795
  if (current_fd >= 0) close(current_fd); \
780
796
  if (cache_fd >= 0) close(cache_fd);
781
797
 
@@ -784,7 +800,7 @@ succeed:
784
800
  return output_data;
785
801
  fail_errno:
786
802
  CLEANUP;
787
- exception = rb_syserr_new(errno, errno_provenance);
803
+ exception = rb_syserr_new_str(errno, exception_message);
788
804
  rb_exc_raise(exception);
789
805
  __builtin_unreachable();
790
806
  raise:
@@ -836,8 +852,7 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
836
852
  /* Cache is stale, invalid, or missing. Regenerate and write it out. */
837
853
 
838
854
  /* Read the contents of the source file into a buffer */
839
- if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
840
- input_data = rb_str_new(contents, current_key.size);
855
+ if ((input_data = bs_read_contents(current_fd, current_key.size, &errno_provenance)) == Qfalse) goto fail;
841
856
 
842
857
  /* Try to compile the input_data using input_to_storage(input_data) */
843
858
  exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
@@ -858,7 +873,6 @@ bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
858
873
  goto succeed;
859
874
 
860
875
  #define CLEANUP \
861
- if (contents != NULL) xfree(contents); \
862
876
  if (current_fd >= 0) close(current_fd); \
863
877
  if (cache_fd >= 0) close(cache_fd);
864
878
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  require("mkmf")
4
4
 
5
- if RUBY_ENGINE == "ruby"
5
+ if %w[ruby truffleruby].include?(RUBY_ENGINE)
6
6
  $CFLAGS << " -O3 "
7
7
  $CFLAGS << " -std=c99"
8
8
 
@@ -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
@@ -83,8 +87,6 @@ module Bootsnap
83
87
  return nil if defined?(Coverage) && Bootsnap::CompileCache::Native.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
@@ -74,16 +74,12 @@ module Bootsnap
74
74
  return super unless (kwargs.keys - ::Bootsnap::CompileCache::JSON.supported_options).empty?
75
75
  end
76
76
 
77
- begin
78
- ::Bootsnap::CompileCache::Native.fetch(
79
- Bootsnap::CompileCache::JSON.cache_dir,
80
- File.realpath(path),
81
- ::Bootsnap::CompileCache::JSON,
82
- kwargs,
83
- )
84
- rescue Errno::EACCES
85
- ::Bootsnap::CompileCache.permission_error(path)
86
- end
77
+ ::Bootsnap::CompileCache::Native.fetch(
78
+ Bootsnap::CompileCache::JSON.cache_dir,
79
+ File.realpath(path),
80
+ ::Bootsnap::CompileCache::JSON,
81
+ kwargs,
82
+ )
87
83
  end
88
84
 
89
85
  ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
@@ -65,7 +65,7 @@ module Bootsnap
65
65
  end
66
66
 
67
67
  unless const_defined?(:NoTagsVisitor)
68
- visitor = Class.new(Psych::Visitors::ToRuby) do
68
+ visitor = Class.new(Psych::Visitors::NoAliasRuby) do
69
69
  def visit(target)
70
70
  if target.tag
71
71
  raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
@@ -129,7 +129,10 @@ module Bootsnap
129
129
  ast = ::YAML.parse(payload)
130
130
  return ast unless ast
131
131
 
132
- NoTagsVisitor.create.visit(ast)
132
+ loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], [])
133
+ scanner = ::Psych::ScalarScanner.new(loader)
134
+
135
+ NoTagsVisitor.new(scanner, loader).visit(ast)
133
136
  end
134
137
  end
135
138
 
@@ -226,16 +229,12 @@ module Bootsnap
226
229
  return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
227
230
  end
228
231
 
229
- begin
230
- CompileCache::Native.fetch(
231
- CompileCache::YAML.cache_dir,
232
- File.realpath(path),
233
- CompileCache::YAML::Psych4::SafeLoad,
234
- kwargs,
235
- )
236
- rescue Errno::EACCES
237
- CompileCache.permission_error(path)
238
- end
232
+ CompileCache::Native.fetch(
233
+ CompileCache::YAML.cache_dir,
234
+ File.realpath(path),
235
+ CompileCache::YAML::Psych4::SafeLoad,
236
+ kwargs,
237
+ )
239
238
  end
240
239
 
241
240
  ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
@@ -250,16 +249,12 @@ module Bootsnap
250
249
  return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
251
250
  end
252
251
 
253
- begin
254
- CompileCache::Native.fetch(
255
- CompileCache::YAML.cache_dir,
256
- File.realpath(path),
257
- CompileCache::YAML::Psych4::UnsafeLoad,
258
- kwargs,
259
- )
260
- rescue Errno::EACCES
261
- CompileCache.permission_error(path)
262
- end
252
+ CompileCache::Native.fetch(
253
+ CompileCache::YAML.cache_dir,
254
+ File.realpath(path),
255
+ CompileCache::YAML::Psych4::UnsafeLoad,
256
+ kwargs,
257
+ )
263
258
  end
264
259
 
265
260
  ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
@@ -306,16 +301,12 @@ module Bootsnap
306
301
  return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
307
302
  end
308
303
 
309
- begin
310
- CompileCache::Native.fetch(
311
- CompileCache::YAML.cache_dir,
312
- File.realpath(path),
313
- CompileCache::YAML::Psych3,
314
- kwargs,
315
- )
316
- rescue Errno::EACCES
317
- CompileCache.permission_error(path)
318
- end
304
+ CompileCache::Native.fetch(
305
+ CompileCache::YAML.cache_dir,
306
+ File.realpath(path),
307
+ CompileCache::YAML::Psych3,
308
+ kwargs,
309
+ )
319
310
  end
320
311
 
321
312
  ruby2_keywords :load_file if respond_to?(:ruby2_keywords, true)
@@ -330,16 +321,12 @@ module Bootsnap
330
321
  return super unless (kwargs.keys - CompileCache::YAML.supported_options).empty?
331
322
  end
332
323
 
333
- begin
334
- CompileCache::Native.fetch(
335
- CompileCache::YAML.cache_dir,
336
- File.realpath(path),
337
- CompileCache::YAML::Psych3,
338
- kwargs,
339
- )
340
- rescue Errno::EACCES
341
- CompileCache.permission_error(path)
342
- end
324
+ CompileCache::Native.fetch(
325
+ CompileCache::YAML.cache_dir,
326
+ File.realpath(path),
327
+ CompileCache::YAML::Psych3,
328
+ kwargs,
329
+ )
343
330
  end
344
331
 
345
332
  ruby2_keywords :unsafe_load_file if respond_to?(:ruby2_keywords, true)
@@ -8,7 +8,6 @@ module Bootsnap
8
8
  end
9
9
 
10
10
  Error = Class.new(StandardError)
11
- PermissionError = Class.new(Error)
12
11
 
13
12
  def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
14
13
  if iseq
@@ -43,18 +42,10 @@ module Bootsnap
43
42
  end
44
43
  end
45
44
 
46
- def self.permission_error(path)
47
- cpath = Bootsnap::CompileCache::ISeq.cache_dir
48
- raise(
49
- PermissionError,
50
- "bootsnap doesn't have permission to write cache entries in '#{cpath}' " \
51
- "(or, less likely, doesn't have permission to read '#{path}')",
52
- )
53
- end
54
-
55
45
  def self.supported?
56
- # only enable on 'ruby' (MRI), POSIX (darwin, linux, *bsd), Windows (RubyInstaller2) and >= 2.3.0
57
- RUBY_ENGINE == "ruby" && RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
46
+ # only enable on 'ruby' (MRI) and TruffleRuby for POSIX (darwin, linux, *bsd), Windows (RubyInstaller2)
47
+ %w[ruby truffleruby].include?(RUBY_ENGINE) &&
48
+ RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
58
49
  end
59
50
  end
60
51
  end
@@ -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?("/")
@@ -54,6 +54,12 @@ 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
65
  def self.register(arr, observer)
@@ -116,8 +116,8 @@ module Bootsnap
116
116
  VOLATILE = :volatile
117
117
 
118
118
  # Built-in ruby lib stuff doesn't change, but things can occasionally be
119
- # installed into sitedir, which generally lives under libdir.
120
- RUBY_LIBDIR = RbConfig::CONFIG["libdir"]
119
+ # installed into sitedir, which generally lives under rubylibdir.
120
+ RUBY_LIBDIR = RbConfig::CONFIG["rubylibdir"]
121
121
  RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
122
122
 
123
123
  def stability
@@ -54,7 +54,7 @@ module Bootsnap
54
54
 
55
55
  absolute_path = "#{absolute_dir_path}/#{name}"
56
56
  if File.directory?(absolute_path)
57
- next if ignored_directories.include?(name)
57
+ next if ignored_directories.include?(name) || ignored_directories.include?(absolute_path)
58
58
 
59
59
  if yield relative_path, absolute_path, true
60
60
  walk(absolute_path, relative_path, &block)
@@ -38,8 +38,8 @@ module Bootsnap
38
38
 
39
39
  @loaded_features_index = LoadedFeaturesIndex.new
40
40
 
41
- @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
42
41
  PathScanner.ignored_directories = ignore_directories if ignore_directories
42
+ @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
43
43
  @enabled = true
44
44
  require_relative("load_path_cache/core_ext/kernel_require")
45
45
  require_relative("load_path_cache/core_ext/loaded_features")
@@ -50,12 +50,21 @@ module Bootsnap
50
50
  @loaded_features_index = nil
51
51
  @realpath_cache = nil
52
52
  @load_path_cache = nil
53
- ChangeObserver.unregister($LOAD_PATH)
53
+ ChangeObserver.unregister($LOAD_PATH) if supported?
54
54
  end
55
55
 
56
56
  def supported?
57
- RUBY_ENGINE == "ruby" &&
58
- RUBY_PLATFORM =~ /darwin|linux|bsd|mswin|mingw|cygwin/
57
+ if RUBY_PLATFORM.match?(/darwin|linux|bsd|mswin|mingw|cygwin/)
58
+ case RUBY_ENGINE
59
+ when "truffleruby"
60
+ # https://github.com/oracle/truffleruby/issues/3131
61
+ RUBY_ENGINE_VERSION >= "23.1.0"
62
+ when "ruby"
63
+ true
64
+ else
65
+ false
66
+ end
67
+ end
59
68
  end
60
69
  end
61
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.15.0"
4
+ VERSION = "1.17.0"
5
5
  end
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.15.0
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-25 00:00:00.000000000 Z
11
+ date: 2023-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  requirements: []
86
- rubygems_version: 3.3.3
86
+ rubygems_version: 3.4.21
87
87
  signing_key:
88
88
  specification_version: 4
89
89
  summary: Boot large ruby/rails apps faster