bootsnap 1.24.3 → 1.24.4

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: 31d64e50bd32368c8dffe927b63fa6e62243b697a2606ca3e2330c4694ec816b
4
- data.tar.gz: 5dfc374040409d1d20e893abc6a8c6052de02cf560c15e7678a5db2084bd722c
3
+ metadata.gz: 64eaf08501294e6e0fd98b67a54eadfff566f5480a0722e2fb852611a28bd1f8
4
+ data.tar.gz: 8e28bc6d5714952cf5abd837453be6774309dd4b45559c3cd31684ef6658490f
5
5
  SHA512:
6
- metadata.gz: ff9e6b5b2c47d0337fa8d46c487fd6881bc567f5ae01a17f85aa3821df9b7f8416283764f8309de6207d94eb00e6cd6ee845a1307dd601659e4d26cd6abbc7ea
7
- data.tar.gz: e34adc7c5162da87d94358a99a5990d0dfbc6497fb141e7407614e181328e1091bd708fac0aa96e6bce8731283a367db6a5aa6e8a2afe3b848b2d0f6b73e3ba4
6
+ metadata.gz: c92cf4fc73d5241a876647a67e9ff70ee63b2816a6f048f3b9f408de8c460e1f97f00162c883bbcc62b01a90d4f753126e3c3be885aee41d042fa166aad812af
7
+ data.tar.gz: 3db013dee453d69e531d4c6cc1a0e763aeadce36a3d5254febb449957447bf801c06e06544a4f571b5a103e75d397b60d51d364eede8de5bfce63abd79dc0ddf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.24.4
4
+
5
+ * Fix several compatibility issues with Ruby `4.0.4`, particularly the `should not compile with coverage` error. See #547.
6
+ * Fix `Bootsnap.enable_frozen_string_literal` to work even when coverage is enabled. Unfortunately only possible on Ruby `4.0.4+`.
7
+ On older rubies if coverage is enabled a warning will be issued and the feature won't work.
8
+ * Reduced cache files header size from 64 to 32 bytes, and got rid of the random padding element.
9
+ * Avoid leaking a private method in `Object` when testing for Parse.y bugs.
10
+
3
11
  # 1.24.3
4
12
 
5
13
  * Fix the `1.24.2` workaround to parse Ruby files with UTF-8 even when the `LANG` environment variable
data/README.md CHANGED
@@ -237,14 +237,15 @@ This may look worse at a glance, but underlies a large performance difference.
237
237
  useful. [This ruby patch](https://bugs.ruby-lang.org/issues/13378) optimizes them out when coupled
238
238
  with bootsnap.)*
239
239
 
240
- Bootsnap writes a cache file containing a 64 byte header followed by the cache contents. The header
240
+ Bootsnap writes a cache file containing a 32 byte header followed by the cache contents. The header
241
241
  is a cache key including several fields:
242
242
 
243
- * `version`, hardcoded in bootsnap. Essentially a schema version;
244
- * `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable.
245
- * `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
246
- * `ruby_revision`, A hash of `RUBY_REVISION`, the exact version of Ruby;
243
+ * `ruby_version_digest`, a digest of:
244
+ * The `RUBY_DESCRIPTION` constant e.g. `"ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [arm64-darwin25]"`.
245
+ * Bootsnap's cache version. Hardcoded in bootsnap. Essentially a schema version;
246
+ * The content of `RubyVM::InstructionSequence.compile_option`.
247
247
  * `size`, the size of the source file;
248
+ * `digest`, a fnv1a_64 hash of the source file;
248
249
  * `mtime`, the last-modification timestamp of the source file when it was compiled; and
249
250
  * `data_size`, the number of bytes following the header, which we need to read it into a buffer.
250
251
 
@@ -349,6 +350,10 @@ Bootsnap::CompileCache::ISeq.compiler_selector = ->(path) do
349
350
  end
350
351
  ```
351
352
 
353
+ *Important note*: This feature is only fully supported by Ruby `4.0.4` and newer.
354
+ On older rubies the feature work except if the `Coverage` module is enabled, in which case a warning will be emitted
355
+ and the default Ruby compiler will be used.
356
+
352
357
  ## Precompilation
353
358
 
354
359
  In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
@@ -40,7 +40,7 @@
40
40
  #define MAX_CACHEPATH_SIZE 1000
41
41
  #define MAX_CACHEDIR_SIZE 981
42
42
 
43
- #define KEY_SIZE 64
43
+ #define KEY_SIZE 32
44
44
 
45
45
  #define MAX_CREATE_TEMPFILE_ATTEMPT 3
46
46
 
@@ -49,34 +49,27 @@
49
49
  #endif
50
50
 
51
51
  /*
52
- * An instance of this key is written as the first 64 bytes of each cache file.
52
+ * An instance of this key is written as the first `KEY_SIZE` bytes of each cache file.
53
53
  * The mtime and size members track whether the file contents have changed, and
54
- * the version, ruby_platform, compile_option, and ruby_revision members track
55
- * changes to the environment that could invalidate compile results without
54
+ * the ruby_version_digest (bootsnap_cache_version + RUBY_DESCRIPTION) and compile_option
55
+ * members track changes to the environment that could invalidate compile results without
56
56
  * file contents having changed. The data_size member is not truly part of the
57
57
  * "key". Really, this could be called a "header" with the first six members
58
58
  * being an embedded "key" struct and an additional data_size member.
59
59
  *
60
60
  * The data_size indicates the remaining number of bytes in the cache file
61
61
  * after the header (the size of the cached artifact).
62
- *
63
- * After data_size, the struct is padded to 64 bytes.
64
62
  */
65
63
  struct bs_cache_key {
66
- uint32_t version;
67
- uint32_t ruby_platform;
68
- uint32_t compile_option;
69
- uint32_t ruby_revision;
70
- uint64_t size;
64
+ uint64_t ruby_version_digest;
71
65
  uint64_t mtime;
72
- uint64_t data_size; //
73
66
  uint64_t digest;
74
- uint8_t digest_set;
75
- uint8_t pad[15];
67
+ uint32_t size;
68
+ uint32_t data_size;
76
69
  } __attribute__((packed));
77
70
 
78
71
  /*
79
- * If the struct padding isn't correct to pad the key to 64 bytes, refuse to
72
+ * If the struct padding isn't correct to pad the key to `KEY_SIZE` bytes, refuse to
80
73
  * compile.
81
74
  */
82
75
  #define STATIC_ASSERT(X) STATIC_ASSERT2(X,__LINE__)
@@ -86,15 +79,15 @@ struct bs_cache_key {
86
79
  STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
87
80
 
88
81
  /* Effectively a schema version. Bumping invalidates all previous caches */
89
- static const uint32_t current_version = 6;
90
-
91
- /* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
92
- * new OS ABI, etc. */
93
- static uint32_t current_ruby_platform;
94
- /* Invalidates cache when switching ruby versions */
95
- static uint32_t current_ruby_revision;
96
- /* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
97
- static uint32_t current_compile_option_crc32 = 0;
82
+ static const uint32_t bootsnap_cache_version = 7;
83
+
84
+ /* Invalidates cache when switching ruby version, platform or ABI */
85
+ static uint64_t base_ruby_version_digest = 0;
86
+
87
+ // `base_ruby_version_digest` combined with RubyVM::InstructionSequence.compile_option
88
+ // Invalidates cache when RubyVM::InstructionSequence.compile_option changes
89
+ static uint64_t current_ruby_version_digest = 0;
90
+
98
91
  /* Current umask */
99
92
  static mode_t current_umask;
100
93
 
@@ -136,8 +129,7 @@ static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handle
136
129
  static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
137
130
  static int open_current_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance);
138
131
  static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance);
139
- static uint32_t get_ruby_revision(void);
140
- static uint32_t get_ruby_platform(void);
132
+ static uint64_t get_ruby_version_digest(void);
141
133
 
142
134
  /*
143
135
  * Helper functions to call ruby methods on handler object without crashing on
@@ -295,8 +287,7 @@ Init_bootsnap(void)
295
287
  rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
296
288
  rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
297
289
 
298
- current_ruby_revision = get_ruby_revision();
299
- current_ruby_platform = get_ruby_platform();
290
+ base_ruby_version_digest = get_ruby_version_digest();
300
291
 
301
292
  instrumentation_method = rb_intern("_instrument");
302
293
 
@@ -345,28 +336,10 @@ bs_revalidation_set(VALUE self, VALUE enabled)
345
336
  return enabled;
346
337
  }
347
338
 
348
- /*
349
- * Bootsnap's ruby code registers a hook that notifies us via this function
350
- * when compile_option changes. These changes invalidate all existing caches.
351
- *
352
- * Note that on 32-bit platforms, a CRC32 can't be represented in a Fixnum, but
353
- * can be represented by a uint.
354
- */
355
- static VALUE
356
- bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
357
- {
358
- if (!RB_TYPE_P(crc32_v, T_BIGNUM) && !RB_TYPE_P(crc32_v, T_FIXNUM)) {
359
- Check_Type(crc32_v, T_FIXNUM);
360
- }
361
- current_compile_option_crc32 = NUM2UINT(crc32_v);
362
- return Qnil;
363
- }
364
-
365
339
  static uint64_t
366
- fnv1a_64_iter(uint64_t h, const VALUE str)
340
+ fnv1a_64_iter(uint64_t h, const unsigned char *s, size_t len)
367
341
  {
368
- unsigned char *s = (unsigned char *)RSTRING_PTR(str);
369
- unsigned char *str_end = (unsigned char *)RSTRING_PTR(str) + RSTRING_LEN(str);
342
+ const unsigned char *str_end = s + len;
370
343
 
371
344
  while (s < str_end) {
372
345
  h ^= (uint64_t)*s++;
@@ -377,45 +350,43 @@ fnv1a_64_iter(uint64_t h, const VALUE str)
377
350
  }
378
351
 
379
352
  static uint64_t
380
- fnv1a_64(const VALUE str)
353
+ fnv1a_64_iter_str(uint64_t h, const VALUE str)
354
+ {
355
+ Check_Type(str, T_STRING);
356
+ return fnv1a_64_iter(h, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str));
357
+ }
358
+
359
+ static uint64_t
360
+ fnv1a_64_str(const VALUE str)
381
361
  {
382
362
  uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
383
- return fnv1a_64_iter(h, str);
363
+ return fnv1a_64_iter_str(h, str);
384
364
  }
385
365
 
386
366
  /*
387
- * Ruby's revision may be Integer or String. CRuby 2.7 or later uses
388
- * Git commit ID as revision. It's String.
367
+ * Bootsnap's ruby code registers a hook that notifies us via this function
368
+ * when compile_option changes. These changes invalidate all existing caches.
369
+ *
370
+ * Note that on 32-bit platforms, a CRC32 can't be represented in a Fixnum, but
371
+ * can be represented by a uint.
389
372
  */
390
- static uint32_t
391
- get_ruby_revision(void)
373
+ static VALUE
374
+ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
392
375
  {
393
- VALUE ruby_revision;
394
-
395
- ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
396
- if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
397
- return FIX2INT(ruby_revision);
398
- } else {
399
- uint64_t hash;
400
-
401
- hash = fnv1a_64(ruby_revision);
402
- return (uint32_t)(hash >> 32);
376
+ if (!RB_TYPE_P(crc32_v, T_BIGNUM) && !RB_TYPE_P(crc32_v, T_FIXNUM)) {
377
+ Check_Type(crc32_v, T_FIXNUM);
403
378
  }
379
+ uint32_t crc32 = (uint32_t)NUM2UINT(crc32_v);
380
+ current_ruby_version_digest = fnv1a_64_iter(base_ruby_version_digest, (unsigned char *)&crc32, sizeof(crc32));
381
+ return Qnil;
404
382
  }
405
383
 
406
- /*
407
- * When ruby's version doesn't change, but it's recompiled on a different OS
408
- * (or OS version), we need to invalidate the cache.
409
- */
410
- static uint32_t
411
- get_ruby_platform(void)
384
+ static uint64_t
385
+ get_ruby_version_digest(void)
412
386
  {
413
- uint64_t hash;
414
- VALUE ruby_platform;
415
-
416
- ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
417
- hash = fnv1a_64(ruby_platform);
418
- return (uint32_t)(hash >> 32);
387
+ uint64_t hash = fnv1a_64_str(rb_const_get(rb_cObject, rb_intern("RUBY_DESCRIPTION")));
388
+ hash = fnv1a_64_iter(hash, (unsigned char *)&bootsnap_cache_version, sizeof(bootsnap_cache_version));
389
+ return hash;
419
390
  }
420
391
 
421
392
  /*
@@ -443,7 +414,7 @@ bs_cache_path(VALUE cachedir_v, VALUE namespace_v, VALUE path_v, char (* cache_p
443
414
  const char * cachedir = RSTRING_PTR(cachedir_v);
444
415
  const char * namespace = NIL_P(namespace_v) ? "" : RSTRING_PTR(namespace_v);
445
416
 
446
- uint64_t hash = fnv1a_64(path_v);
417
+ uint64_t hash = fnv1a_64_str(path_v);
447
418
  uint8_t first_byte = (hash >> (64 - 8));
448
419
  uint64_t remainder = hash & 0x00ffffffffffffff;
449
420
 
@@ -460,10 +431,8 @@ bs_cache_path(VALUE cachedir_v, VALUE namespace_v, VALUE path_v, char (* cache_p
460
431
  */
461
432
  static enum cache_status cache_key_equal_fast_path(struct bs_cache_key *k1,
462
433
  struct bs_cache_key *k2) {
463
- if (k1->version == k2->version &&
464
- k1->ruby_platform == k2->ruby_platform &&
465
- k1->compile_option == k2->compile_option &&
466
- k1->ruby_revision == k2->ruby_revision && k1->size == k2->size) {
434
+ if (k1->ruby_version_digest == k2->ruby_version_digest &&
435
+ k1->size == k2->size) {
467
436
  if (k1->mtime == k2->mtime) {
468
437
  return hit;
469
438
  }
@@ -507,10 +476,9 @@ static int update_cache_key(struct bs_cache_key *current_key, struct bs_cache_ke
507
476
  */
508
477
  static void bs_cache_key_digest(struct bs_cache_key *key,
509
478
  const VALUE input_data) {
510
- if (key->digest_set)
479
+ if (key->digest)
511
480
  return;
512
- key->digest = fnv1a_64(input_data);
513
- key->digest_set = 1;
481
+ key->digest = fnv1a_64_str(input_data);
514
482
  }
515
483
 
516
484
  /*
@@ -587,13 +555,19 @@ open_current_file(const char * path, struct bs_cache_key * key, const char ** er
587
555
  return -1;
588
556
  }
589
557
 
590
- key->version = current_version;
591
- key->ruby_platform = current_ruby_platform;
592
- key->compile_option = current_compile_option_crc32;
593
- key->ruby_revision = current_ruby_revision;
594
- key->size = (uint64_t)statbuf.st_size;
595
- key->mtime = (uint64_t)statbuf.st_mtime;
596
- key->digest_set = false;
558
+ key->ruby_version_digest = current_ruby_version_digest;
559
+
560
+ // We're limited to file of 4GiB or less. Hopefully that's enough for everyone.
561
+ if (statbuf.st_size > (uint32_t)-1) {
562
+ *errno_provenance = "bs_fetch:open_current_file:file_too_big";
563
+ close(fd);
564
+ errno = EFBIG;
565
+ return -1;
566
+ }
567
+
568
+ key->size = (uint32_t)statbuf.st_size;
569
+ key->mtime = (uint64_t)statbuf.st_mtime;
570
+ key->digest = 0;
597
571
 
598
572
  return fd;
599
573
  }
@@ -663,10 +637,10 @@ open_cache_file(const char * path, struct bs_cache_key * key, const char ** errn
663
637
 
664
638
  /*
665
639
  * The cache file is laid out like:
666
- * 0...64 : bs_cache_key
667
- * 64..-1 : cached artifact
640
+ * 0...`KEY_SIZE` : bs_cache_key
641
+ * `KEY_SIZE`..-1 : cached artifact
668
642
  *
669
- * This function takes a file descriptor whose position is pre-set to 64, and
643
+ * This function takes a file descriptor whose position is pre-set to `KEY_SIZE`, and
670
644
  * the data_size (corresponding to the remaining number of bytes) listed in the
671
645
  * cache header.
672
646
  *
@@ -782,7 +756,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons
782
756
  setmode(fd, O_BINARY);
783
757
  #endif
784
758
 
785
- key->data_size = RSTRING_LEN(data);
759
+ uint64_t data_size = RSTRING_LEN(data);
760
+ if (data_size > (uint32_t)-1) {
761
+ return 0; // Don't cache.
762
+ }
763
+
764
+ key->data_size = (uint32_t)data_size;
786
765
  nwrite = write(fd, key, KEY_SIZE);
787
766
  if (nwrite < 0) {
788
767
  *errno_provenance = "bs_fetch:atomic_write_cache_file:write";
@@ -62,7 +62,7 @@ module Bootsnap
62
62
  # Ref: https://github.com/rails/bootsnap/issues/495
63
63
  # The second forked process will hang on some QEMU environments
64
64
  r, w = IO.pipe
65
- pids = 2.times.map do
65
+ pids = Array.new(2) do
66
66
  ::Process.fork do
67
67
  exit!(true)
68
68
  end
@@ -161,7 +161,7 @@ module Bootsnap
161
161
  end
162
162
 
163
163
  def spawn
164
- @workers = @size.times.map { Worker.new(@jobs) }
164
+ @workers = Array.new(@size) { Worker.new(@jobs) }
165
165
  @workers.each(&:spawn)
166
166
  @dispatcher_thread = Thread.new { dispatch_loop }
167
167
  @dispatcher_thread.abort_on_exception = true
@@ -27,7 +27,7 @@ module Bootsnap
27
27
  @compile_options = compile_options
28
28
  end
29
29
 
30
- has_ruby_bug_18250 = begin # https://bugs.ruby-lang.org/issues/18250
30
+ has_ruby_bug_18250 = RUBY_VERSION.start_with?("3.0.") && begin # https://bugs.ruby-lang.org/issues/18250
31
31
  if defined? RubyVM::InstructionSequence
32
32
  RubyVM::InstructionSequence.compile("def foo(*); ->{ super }; end; def foo(**); ->{ super }; end").to_binary
33
33
  end
@@ -94,23 +94,44 @@ module Bootsnap
94
94
  end
95
95
 
96
96
  def input_to_output(source, path, _kwargs)
97
- RubyVM::InstructionSequence.compile(
98
- source.force_encoding(Encoding.default_external),
99
- path,
100
- path,
101
- nil,
102
- @compile_options,
103
- )
97
+ if @compile_options
98
+ RubyVM::InstructionSequence.compile(
99
+ source.force_encoding(Encoding.default_external),
100
+ path,
101
+ path,
102
+ nil,
103
+ @compile_options,
104
+ )
105
+ end
104
106
  end
105
107
  end
106
108
 
107
109
  DEFAULT = Compiler.new
108
110
  FROZEN_STRING_LITERAL = Compiler.new("-fstr", {frozen_string_literal: true}.freeze)
109
- MUTABLE_STRING_LITERAL = Compiler.new("-no-fstr", {frozen_string_literal: false}.freeze)
111
+ COVERAGE_SUPPORTED = RUBY_VERSION >= "4.0.4"
110
112
  @default_compiler = DEFAULT
113
+ @coverage_support_warning_emitted = false
111
114
 
112
115
  def self.fetch(path, cache_dir: ISeq.cache_dir)
113
116
  compiler = compiler_selector&.call(path) || default_compiler
117
+
118
+ # Having coverage enabled prevents iseq dumping/loading.
119
+ if coverage_on?
120
+ return nil if compiler.equal?(DEFAULT)
121
+
122
+ if COVERAGE_SUPPORTED
123
+ return compiler.input_to_output(File.read(path.to_s), path.to_s, nil)
124
+ elsif !@coverage_support_warning_emitted
125
+ @coverage_support_warning_emitted = true
126
+ warn(<<~MSG)
127
+ Using `Bootsnap.enable_frozen_string_literal` with code coverage enabled is only supported on Ruby 4.0.4+.
128
+ Files loaded while coverage is on, will have mutable string literals.
129
+ MSG
130
+ end
131
+
132
+ return nil
133
+ end
134
+
114
135
  Bootsnap::CompileCache::Native.fetch(
115
136
  cache_dir,
116
137
  compiler.namespace,
@@ -130,14 +151,21 @@ module Bootsnap
130
151
  )
131
152
  end
132
153
 
154
+ if RUBY_VERSION < "3.1."
155
+ def self.coverage_on?
156
+ defined?(Coverage) && Coverage.running?
157
+ end
158
+ else
159
+ def self.coverage_on?
160
+ defined?(Coverage) && Coverage.state != :idle
161
+ end
162
+ end
163
+
133
164
  module InstructionSequenceMixin
134
165
  def load_iseq(path)
135
- # Having coverage enabled prevents iseq dumping/loading.
136
- return nil if defined?(Coverage) && Coverage.running?
137
-
138
166
  Bootsnap::CompileCache::ISeq.fetch(path.to_s)
139
167
  rescue RuntimeError => error
140
- if error.message =~ /unmatched platform/
168
+ if error.message.include?("unmatched platform")
141
169
  puts("unmatched platform for file #{path}")
142
170
  end
143
171
  raise
@@ -1,10 +1,7 @@
1
- # rubocop:disable Style/FrozenStringLiteralComment
2
- def foo(bars)
3
- case bars
1
+ _f = -> {
2
+ case foo
4
3
  in [one, "a" | "b" => two]
5
4
  puts "#{one} - #{two}"
6
5
  end
7
- end
8
-
6
+ }
9
7
  _ = "test"
10
- # rubocop:enable Style/FrozenStringLiteralComment
@@ -84,7 +84,7 @@ module Bootsnap
84
84
  rescue Errno::ENOENT, MessagePack::MalformedFormatError, MessagePack::UnknownExtTypeError, EOFError
85
85
  default_data
86
86
  rescue ArgumentError => error
87
- if error.message =~ /negative array size/
87
+ if error.message.include?("negative array size")
88
88
  default_data
89
89
  else
90
90
  raise
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.24.3"
4
+ VERSION = "1.24.4"
5
5
  end
data/lib/bootsnap.rb CHANGED
@@ -160,7 +160,7 @@ module Bootsnap
160
160
  end
161
161
  end
162
162
 
163
- if RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
163
+ if /mswin|mingw|cygwin/.match?(RbConfig::CONFIG["host_os"])
164
164
  def absolute_path?(path)
165
165
  path[1] == ":"
166
166
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.24.3
4
+ version: 1.24.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
@@ -81,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  requirements: []
84
- rubygems_version: 4.0.6
84
+ rubygems_version: 4.0.10
85
85
  specification_version: 4
86
86
  summary: Boot large ruby/rails apps faster
87
87
  test_files: []