bootsnap 1.23.0 → 1.24.3
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 +22 -0
- data/README.md +29 -0
- data/ext/bootsnap/bootsnap.c +46 -46
- data/lib/bootsnap/cli.rb +1 -0
- data/lib/bootsnap/compile_cache/iseq.rb +85 -38
- data/lib/bootsnap/compile_cache/ruby_bug_22023_canary.rb +10 -0
- data/lib/bootsnap/compile_cache/yaml.rb +8 -3
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +1 -1
- data/lib/bootsnap/version.rb +1 -1
- data/lib/bootsnap.rb +29 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 31d64e50bd32368c8dffe927b63fa6e62243b697a2606ca3e2330c4694ec816b
|
|
4
|
+
data.tar.gz: 5dfc374040409d1d20e893abc6a8c6052de02cf560c15e7678a5db2084bd722c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff9e6b5b2c47d0337fa8d46c487fd6881bc567f5ae01a17f85aa3821df9b7f8416283764f8309de6207d94eb00e6cd6ee845a1307dd601659e4d26cd6abbc7ea
|
|
7
|
+
data.tar.gz: e34adc7c5162da87d94358a99a5990d0dfbc6497fb141e7407614e181328e1091bd708fac0aa96e6bce8731283a367db6a5aa6e8a2afe3b848b2d0f6b73e3ba4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Unreleased
|
|
2
2
|
|
|
3
|
+
# 1.24.3
|
|
4
|
+
|
|
5
|
+
* Fix the `1.24.2` workaround to parse Ruby files with UTF-8 even when the `LANG` environment variable
|
|
6
|
+
is unset or set to `C`.
|
|
7
|
+
|
|
8
|
+
# 1.24.2
|
|
9
|
+
|
|
10
|
+
* Workaround two Ruby bugs in `RubyVM::InstructionSequence.compile_file`, that were causing
|
|
11
|
+
files to be loaded with the old Ruby parser instead of Prism, causing issues with some pattern matching syntax.
|
|
12
|
+
Ref: https://bugs.ruby-lang.org/issues/22023
|
|
13
|
+
|
|
14
|
+
# 1.24.1
|
|
15
|
+
|
|
16
|
+
* Fix encoding of Ruby source files loaded when `BOOTSNAP_READONLY` is set.
|
|
17
|
+
Files would incorectly be loaded in `ASCII-8BIT` causing literal strings outside
|
|
18
|
+
the pure ASCII range to have `ASCII-8BIT` encoding instead of `UTF-8`.
|
|
19
|
+
This bug was introduced in `1.24.0`.
|
|
20
|
+
|
|
21
|
+
# 1.24.0
|
|
22
|
+
|
|
23
|
+
* Added a hook API to customize Ruby compilation.
|
|
24
|
+
|
|
3
25
|
# 1.23.0
|
|
4
26
|
|
|
5
27
|
* Require Ruby 2.7.
|
data/README.md
CHANGED
|
@@ -75,6 +75,7 @@ well together.
|
|
|
75
75
|
`require 'bootsnap/setup'` behavior can be changed using environment variables:
|
|
76
76
|
|
|
77
77
|
- `BOOTSNAP_CACHE_DIR` allows to define the cache location.
|
|
78
|
+
- `BOOTSNAP_CONFIG` allows to change the default config location (`config/bootsnap.rb`).
|
|
78
79
|
- `DISABLE_BOOTSNAP` allows to entirely disable bootsnap.
|
|
79
80
|
- `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` allows to disable load path caching.
|
|
80
81
|
- `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
|
|
@@ -320,6 +321,34 @@ open /c/nope.bundle -> -1
|
|
|
320
321
|
# (nothing!)
|
|
321
322
|
```
|
|
322
323
|
|
|
324
|
+
## Custom Compilers
|
|
325
|
+
|
|
326
|
+
Bootsnap allows substituing the default Ruby compiler by another one.
|
|
327
|
+
This can be configured from the bootsnap config file (defaults to `config/bootsnap.rb`).
|
|
328
|
+
|
|
329
|
+
The main use case is to programmatically enable frozen string literals for your project without impacting dependencies:
|
|
330
|
+
|
|
331
|
+
```ruby
|
|
332
|
+
Bootsnap.enable_frozen_string_literal(app_only: true)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
But it can also be used for more fine grained logic, or to implement all sort of Ruby code preprocessing:
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
# config/bootsnap.rb
|
|
339
|
+
gems_root = File.join(Bundler.bundle_path.cleanpath, "")
|
|
340
|
+
app_root = File.join(Dir.pwd, "")
|
|
341
|
+
Bootsnap::CompileCache::ISeq.compiler_selector = ->(path) do
|
|
342
|
+
# Enable `frozen_string_literal: true` for app code, but not gems.
|
|
343
|
+
|
|
344
|
+
if path.start_with?(app_root) && !path.start_with?(gems_root)
|
|
345
|
+
Bootsnap::CompileCache::ISeq::FROZEN_STRING_LITERAL
|
|
346
|
+
else
|
|
347
|
+
Bootsnap::CompileCache::ISeq::DEFAULT
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
```
|
|
351
|
+
|
|
323
352
|
## Precompilation
|
|
324
353
|
|
|
325
354
|
In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
|
data/ext/bootsnap/bootsnap.c
CHANGED
|
@@ -109,13 +109,15 @@ static bool readonly = false;
|
|
|
109
109
|
static bool revalidation = false;
|
|
110
110
|
static bool perm_issue = false;
|
|
111
111
|
|
|
112
|
+
static ID id_storage_to_output, id_input_to_output, id_input_to_storage;
|
|
113
|
+
|
|
112
114
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
|
113
115
|
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
|
114
116
|
static VALUE bs_readonly_set(VALUE self, VALUE enabled);
|
|
115
117
|
static VALUE bs_revalidation_set(VALUE self, VALUE enabled);
|
|
116
118
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
|
117
|
-
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
|
118
|
-
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
|
119
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE namespace_v, VALUE path_v, VALUE handler, VALUE args);
|
|
120
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE namespace_v, VALUE path_v, VALUE handler);
|
|
119
121
|
|
|
120
122
|
/* Helpers */
|
|
121
123
|
enum cache_status {
|
|
@@ -123,7 +125,7 @@ enum cache_status {
|
|
|
123
125
|
hit,
|
|
124
126
|
stale,
|
|
125
127
|
};
|
|
126
|
-
static void bs_cache_path(
|
|
128
|
+
static void bs_cache_path(VALUE cachedir_v, VALUE namespace_v, VALUE path_v, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
|
127
129
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
|
128
130
|
static enum cache_status cache_key_equal_fast_path(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
|
129
131
|
static int cache_key_equal_slow_path(struct bs_cache_key * current_key, struct bs_cache_key * cached_key, const VALUE input_data);
|
|
@@ -143,7 +145,7 @@ static uint32_t get_ruby_platform(void);
|
|
|
143
145
|
*/
|
|
144
146
|
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
|
145
147
|
static VALUE prot_input_to_output(VALUE arg);
|
|
146
|
-
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
|
148
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * output_data, int * exception_tag);
|
|
147
149
|
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
|
148
150
|
struct s2o_data;
|
|
149
151
|
struct i2o_data;
|
|
@@ -275,6 +277,10 @@ Init_bootsnap(void)
|
|
|
275
277
|
{
|
|
276
278
|
rb_mBootsnap = rb_define_module("Bootsnap");
|
|
277
279
|
|
|
280
|
+
id_storage_to_output = rb_intern("storage_to_output");
|
|
281
|
+
id_input_to_output = rb_intern("input_to_output");
|
|
282
|
+
id_input_to_storage = rb_intern("input_to_storage");
|
|
283
|
+
|
|
278
284
|
rb_define_singleton_method(rb_mBootsnap, "rb_get_path", bs_rb_get_path, 1);
|
|
279
285
|
|
|
280
286
|
#ifdef HAVE_FSTATAT
|
|
@@ -302,8 +308,8 @@ Init_bootsnap(void)
|
|
|
302
308
|
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
|
303
309
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
|
|
304
310
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "revalidation=", bs_revalidation_set, 1);
|
|
305
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
|
306
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile,
|
|
311
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 5);
|
|
312
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 4);
|
|
307
313
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
|
308
314
|
|
|
309
315
|
current_umask = umask(0777);
|
|
@@ -420,13 +426,28 @@ get_ruby_platform(void)
|
|
|
420
426
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
|
421
427
|
*/
|
|
422
428
|
static void
|
|
423
|
-
bs_cache_path(
|
|
429
|
+
bs_cache_path(VALUE cachedir_v, VALUE namespace_v, VALUE path_v, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
|
424
430
|
{
|
|
425
|
-
|
|
431
|
+
FilePathValue(path_v);
|
|
432
|
+
|
|
433
|
+
Check_Type(cachedir_v, T_STRING);
|
|
434
|
+
Check_Type(path_v, T_STRING);
|
|
435
|
+
if (!NIL_P(namespace_v)) {
|
|
436
|
+
Check_Type(namespace_v, T_STRING);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
|
440
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const char * cachedir = RSTRING_PTR(cachedir_v);
|
|
444
|
+
const char * namespace = NIL_P(namespace_v) ? "" : RSTRING_PTR(namespace_v);
|
|
445
|
+
|
|
446
|
+
uint64_t hash = fnv1a_64(path_v);
|
|
426
447
|
uint8_t first_byte = (hash >> (64 - 8));
|
|
427
448
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
|
428
449
|
|
|
429
|
-
sprintf(*cache_path, "%s/%02"PRIx8"/%014"PRIx64, cachedir, first_byte, remainder);
|
|
450
|
+
sprintf(*cache_path, "%s%s/%02"PRIx8"/%014"PRIx64, cachedir, namespace, first_byte, remainder);
|
|
430
451
|
}
|
|
431
452
|
|
|
432
453
|
/*
|
|
@@ -498,25 +519,14 @@ static void bs_cache_key_digest(struct bs_cache_key *key,
|
|
|
498
519
|
* conversions on the ruby VALUE arguments before passing them along.
|
|
499
520
|
*/
|
|
500
521
|
static VALUE
|
|
501
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
|
522
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE namespace_v, VALUE path_v, VALUE handler, VALUE args)
|
|
502
523
|
{
|
|
503
|
-
FilePathValue(path_v);
|
|
504
|
-
|
|
505
|
-
Check_Type(cachedir_v, T_STRING);
|
|
506
|
-
Check_Type(path_v, T_STRING);
|
|
507
|
-
|
|
508
|
-
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
|
509
|
-
rb_raise(rb_eArgError, "cachedir too long");
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
char * cachedir = RSTRING_PTR(cachedir_v);
|
|
513
|
-
char * path = RSTRING_PTR(path_v);
|
|
514
524
|
char cache_path[MAX_CACHEPATH_SIZE];
|
|
515
525
|
|
|
516
526
|
/* generate cache path to cache_path */
|
|
517
|
-
bs_cache_path(
|
|
527
|
+
bs_cache_path(cachedir_v, namespace_v, path_v, &cache_path);
|
|
518
528
|
|
|
519
|
-
return bs_fetch(
|
|
529
|
+
return bs_fetch(RSTRING_PTR(path_v), path_v, cache_path, handler, args);
|
|
520
530
|
}
|
|
521
531
|
|
|
522
532
|
/*
|
|
@@ -525,25 +535,13 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE arg
|
|
|
525
535
|
* and doesn't return the content.
|
|
526
536
|
*/
|
|
527
537
|
static VALUE
|
|
528
|
-
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
538
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE namespace_v, VALUE path_v, VALUE handler)
|
|
529
539
|
{
|
|
530
|
-
FilePathValue(path_v);
|
|
531
|
-
|
|
532
|
-
Check_Type(cachedir_v, T_STRING);
|
|
533
|
-
Check_Type(path_v, T_STRING);
|
|
534
|
-
|
|
535
|
-
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
|
536
|
-
rb_raise(rb_eArgError, "cachedir too long");
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
char * cachedir = RSTRING_PTR(cachedir_v);
|
|
540
|
-
char * path = RSTRING_PTR(path_v);
|
|
541
540
|
char cache_path[MAX_CACHEPATH_SIZE];
|
|
542
|
-
|
|
543
541
|
/* generate cache path to cache_path */
|
|
544
|
-
bs_cache_path(
|
|
542
|
+
bs_cache_path(cachedir_v, namespace_v, path_v, &cache_path);
|
|
545
543
|
|
|
546
|
-
return bs_precompile(
|
|
544
|
+
return bs_precompile(RSTRING_PTR(path_v), path_v, cache_path, handler);
|
|
547
545
|
}
|
|
548
546
|
|
|
549
547
|
static int bs_open_noatime(const char *path, int flags) {
|
|
@@ -963,7 +961,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
|
963
961
|
exception_message = path_v;
|
|
964
962
|
goto fail_errno;
|
|
965
963
|
}
|
|
966
|
-
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
|
964
|
+
bs_input_to_output(handler, args, input_data, path_v, &output_data, &exception_tag);
|
|
967
965
|
if (exception_tag != 0) goto raise;
|
|
968
966
|
goto succeed;
|
|
969
967
|
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
|
|
@@ -989,7 +987,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
|
989
987
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
|
990
988
|
* to cache anything; just return input_to_output(input_data) */
|
|
991
989
|
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
|
992
|
-
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
|
990
|
+
bs_input_to_output(handler, args, input_data, path_v, &output_data, &exception_tag);
|
|
993
991
|
if (exception_tag != 0) goto raise;
|
|
994
992
|
goto succeed;
|
|
995
993
|
}
|
|
@@ -1009,7 +1007,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
|
1009
1007
|
|
|
1010
1008
|
if (output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
|
1011
1009
|
/* If storage_to_output returned `Uncompilable` we fallback to `input_to_output` */
|
|
1012
|
-
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
|
1010
|
+
bs_input_to_output(handler, args, input_data, path_v, &output_data, &exception_tag);
|
|
1013
1011
|
if (exception_tag != 0) goto raise;
|
|
1014
1012
|
} else if (NIL_P(output_data)) {
|
|
1015
1013
|
/* If output_data is nil, delete the cache entry and generate the output
|
|
@@ -1023,7 +1021,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args
|
|
|
1023
1021
|
goto fail_errno;
|
|
1024
1022
|
}
|
|
1025
1023
|
}
|
|
1026
|
-
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
|
1024
|
+
bs_input_to_output(handler, args, input_data, path_v, &output_data, &exception_tag);
|
|
1027
1025
|
if (exception_tag != 0) goto raise;
|
|
1028
1026
|
}
|
|
1029
1027
|
|
|
@@ -1181,6 +1179,7 @@ struct i2o_data {
|
|
|
1181
1179
|
VALUE handler;
|
|
1182
1180
|
VALUE args;
|
|
1183
1181
|
VALUE input_data;
|
|
1182
|
+
VALUE pathval;
|
|
1184
1183
|
};
|
|
1185
1184
|
|
|
1186
1185
|
struct i2s_data {
|
|
@@ -1193,7 +1192,7 @@ static VALUE
|
|
|
1193
1192
|
try_storage_to_output(VALUE arg)
|
|
1194
1193
|
{
|
|
1195
1194
|
struct s2o_data * data = (struct s2o_data *)arg;
|
|
1196
|
-
return rb_funcall(data->handler,
|
|
1195
|
+
return rb_funcall(data->handler, id_storage_to_output, 2, data->storage_data, data->args);
|
|
1197
1196
|
}
|
|
1198
1197
|
|
|
1199
1198
|
static int
|
|
@@ -1210,12 +1209,13 @@ bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * outp
|
|
|
1210
1209
|
}
|
|
1211
1210
|
|
|
1212
1211
|
static void
|
|
1213
|
-
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
|
1212
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE path_v, VALUE * output_data, int * exception_tag)
|
|
1214
1213
|
{
|
|
1215
1214
|
struct i2o_data i2o_data = {
|
|
1216
1215
|
.handler = handler,
|
|
1217
1216
|
.args = args,
|
|
1218
1217
|
.input_data = input_data,
|
|
1218
|
+
.pathval = path_v,
|
|
1219
1219
|
};
|
|
1220
1220
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
|
1221
1221
|
}
|
|
@@ -1224,14 +1224,14 @@ static VALUE
|
|
|
1224
1224
|
prot_input_to_output(VALUE arg)
|
|
1225
1225
|
{
|
|
1226
1226
|
struct i2o_data * data = (struct i2o_data *)arg;
|
|
1227
|
-
return rb_funcall(data->handler,
|
|
1227
|
+
return rb_funcall(data->handler, id_input_to_output, 3, data->input_data, data->pathval, data->args);
|
|
1228
1228
|
}
|
|
1229
1229
|
|
|
1230
1230
|
static VALUE
|
|
1231
1231
|
try_input_to_storage(VALUE arg)
|
|
1232
1232
|
{
|
|
1233
1233
|
struct i2s_data * data = (struct i2s_data *)arg;
|
|
1234
|
-
return rb_funcall(data->handler,
|
|
1234
|
+
return rb_funcall(data->handler, id_input_to_storage, 2, data->input_data, data->pathval);
|
|
1235
1235
|
}
|
|
1236
1236
|
|
|
1237
1237
|
static int
|
data/lib/bootsnap/cli.rb
CHANGED
|
@@ -7,7 +7,8 @@ module Bootsnap
|
|
|
7
7
|
module CompileCache
|
|
8
8
|
module ISeq
|
|
9
9
|
class << self
|
|
10
|
-
attr_reader
|
|
10
|
+
attr_reader :cache_dir
|
|
11
|
+
attr_accessor :compiler_selector, :default_compiler
|
|
11
12
|
|
|
12
13
|
def cache_dir=(cache_dir)
|
|
13
14
|
@cache_dir = cache_dir.end_with?("/") ? "#{cache_dir}iseq" : "#{cache_dir}-iseq"
|
|
@@ -18,71 +19,117 @@ module Bootsnap
|
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
class Compiler
|
|
23
|
+
attr_reader :namespace, :compile_options
|
|
24
|
+
|
|
25
|
+
def initialize(namespace = nil, compile_options = nil)
|
|
26
|
+
@namespace = namespace
|
|
27
|
+
@compile_options = compile_options
|
|
24
28
|
end
|
|
25
|
-
false
|
|
26
|
-
rescue TypeError
|
|
27
|
-
true
|
|
28
|
-
end
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
RubyVM::InstructionSequence.compile_file(path)
|
|
34
|
-
rescue SyntaxError
|
|
35
|
-
return UNCOMPILABLE # syntax error
|
|
30
|
+
has_ruby_bug_18250 = begin # https://bugs.ruby-lang.org/issues/18250
|
|
31
|
+
if defined? RubyVM::InstructionSequence
|
|
32
|
+
RubyVM::InstructionSequence.compile("def foo(*); ->{ super }; end; def foo(**); ->{ super }; end").to_binary
|
|
36
33
|
end
|
|
34
|
+
false
|
|
35
|
+
rescue TypeError
|
|
36
|
+
true
|
|
37
|
+
end
|
|
37
38
|
|
|
39
|
+
has_ruby_bug_22023 = if defined?(RubyVM::InstructionSequence) && RubyVM::InstructionSequence.respond_to?(:compile_file_prism)
|
|
38
40
|
begin
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
RubyVM::InstructionSequence.compile_file(File.expand_path("../ruby_bug_22023_canary.rb", __FILE__))
|
|
42
|
+
false
|
|
43
|
+
rescue SyntaxError
|
|
44
|
+
true
|
|
42
45
|
end
|
|
43
46
|
end
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
if has_ruby_bug_22023 && RUBY_DESCRIPTION.include?("+PRISM")
|
|
49
|
+
module PatchRubyBug22023
|
|
50
|
+
def compile_file(path, options = nil)
|
|
51
|
+
compile_file_prism(path, options)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
has_ruby_bug_22023_bis = !RubyVM::InstructionSequence.compile_file_prism(
|
|
55
|
+
File.expand_path("../ruby_bug_22023_canary.rb", __FILE__),
|
|
56
|
+
{frozen_string_literal: true},
|
|
57
|
+
).eval.frozen?
|
|
58
|
+
|
|
59
|
+
if has_ruby_bug_22023_bis
|
|
60
|
+
def compile_file_prism(path, options = nil)
|
|
61
|
+
compile_prism(::File.read(path, encoding: Encoding::UTF_8), path, path, nil, options)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
RubyVM::InstructionSequence.singleton_class.prepend(PatchRubyBug22023)
|
|
49
66
|
end
|
|
50
|
-
end
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
nil
|
|
68
|
+
if has_ruby_bug_18250
|
|
69
|
+
def input_to_storage(_, path)
|
|
70
|
+
iseq = RubyVM::InstructionSequence.compile_file(path, @compile_options)
|
|
71
|
+
iseq.to_binary
|
|
72
|
+
rescue TypeError, SyntaxError # Ruby [Bug #18250] & [Bug #22023]
|
|
73
|
+
UNCOMPILABLE
|
|
74
|
+
end
|
|
60
75
|
else
|
|
61
|
-
|
|
76
|
+
def input_to_storage(_, path)
|
|
77
|
+
RubyVM::InstructionSequence.compile_file(path, @compile_options).to_binary
|
|
78
|
+
rescue SyntaxError # Ruby [Bug #22023]
|
|
79
|
+
UNCOMPILABLE
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def storage_to_output(binary, _args)
|
|
84
|
+
iseq = RubyVM::InstructionSequence.load_from_binary(binary)
|
|
85
|
+
binary.clear
|
|
86
|
+
iseq
|
|
87
|
+
rescue RuntimeError => error
|
|
88
|
+
if error.message == "broken binary format"
|
|
89
|
+
$stderr.puts("[Bootsnap::CompileCache] warning: rejecting broken binary")
|
|
90
|
+
nil
|
|
91
|
+
else
|
|
92
|
+
raise
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
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
|
+
)
|
|
62
104
|
end
|
|
63
105
|
end
|
|
64
106
|
|
|
107
|
+
DEFAULT = Compiler.new
|
|
108
|
+
FROZEN_STRING_LITERAL = Compiler.new("-fstr", {frozen_string_literal: true}.freeze)
|
|
109
|
+
MUTABLE_STRING_LITERAL = Compiler.new("-no-fstr", {frozen_string_literal: false}.freeze)
|
|
110
|
+
@default_compiler = DEFAULT
|
|
111
|
+
|
|
65
112
|
def self.fetch(path, cache_dir: ISeq.cache_dir)
|
|
113
|
+
compiler = compiler_selector&.call(path) || default_compiler
|
|
66
114
|
Bootsnap::CompileCache::Native.fetch(
|
|
67
115
|
cache_dir,
|
|
116
|
+
compiler.namespace,
|
|
68
117
|
path.to_s,
|
|
69
|
-
|
|
118
|
+
compiler,
|
|
70
119
|
nil,
|
|
71
120
|
)
|
|
72
121
|
end
|
|
73
122
|
|
|
74
123
|
def self.precompile(path)
|
|
124
|
+
compiler = compiler_selector&.call(path) || default_compiler
|
|
75
125
|
Bootsnap::CompileCache::Native.precompile(
|
|
76
126
|
cache_dir,
|
|
127
|
+
compiler.namespace,
|
|
77
128
|
path.to_s,
|
|
78
|
-
|
|
129
|
+
compiler,
|
|
79
130
|
)
|
|
80
131
|
end
|
|
81
132
|
|
|
82
|
-
def self.input_to_output(_data, _kwargs)
|
|
83
|
-
nil # ruby handles this
|
|
84
|
-
end
|
|
85
|
-
|
|
86
133
|
module InstructionSequenceMixin
|
|
87
134
|
def load_iseq(path)
|
|
88
135
|
# Having coverage enabled prevents iseq dumping/loading.
|
|
@@ -28,6 +28,7 @@ module Bootsnap
|
|
|
28
28
|
|
|
29
29
|
CompileCache::Native.precompile(
|
|
30
30
|
cache_dir,
|
|
31
|
+
nil,
|
|
31
32
|
path.to_s,
|
|
32
33
|
@implementation,
|
|
33
34
|
)
|
|
@@ -175,7 +176,7 @@ module Bootsnap
|
|
|
175
176
|
result
|
|
176
177
|
end
|
|
177
178
|
|
|
178
|
-
def input_to_output(data, kwargs)
|
|
179
|
+
def input_to_output(data, _path, kwargs)
|
|
179
180
|
::YAML.unsafe_load(data, **(kwargs || {}))
|
|
180
181
|
end
|
|
181
182
|
end
|
|
@@ -215,7 +216,7 @@ module Bootsnap
|
|
|
215
216
|
end
|
|
216
217
|
end
|
|
217
218
|
|
|
218
|
-
def input_to_output(data, kwargs)
|
|
219
|
+
def input_to_output(data, _path, kwargs)
|
|
219
220
|
::YAML.load(data, **(kwargs || {}))
|
|
220
221
|
end
|
|
221
222
|
end
|
|
@@ -233,6 +234,7 @@ module Bootsnap
|
|
|
233
234
|
|
|
234
235
|
CompileCache::Native.fetch(
|
|
235
236
|
CompileCache::YAML.cache_dir,
|
|
237
|
+
nil,
|
|
236
238
|
File.realpath(path),
|
|
237
239
|
CompileCache::YAML::Psych4::SafeLoad,
|
|
238
240
|
kwargs,
|
|
@@ -253,6 +255,7 @@ module Bootsnap
|
|
|
253
255
|
|
|
254
256
|
CompileCache::Native.fetch(
|
|
255
257
|
CompileCache::YAML.cache_dir,
|
|
258
|
+
nil,
|
|
256
259
|
File.realpath(path),
|
|
257
260
|
CompileCache::YAML::Psych4::UnsafeLoad,
|
|
258
261
|
kwargs,
|
|
@@ -288,7 +291,7 @@ module Bootsnap
|
|
|
288
291
|
unpacker.unpack
|
|
289
292
|
end
|
|
290
293
|
|
|
291
|
-
def input_to_output(data, kwargs)
|
|
294
|
+
def input_to_output(data, _path, kwargs)
|
|
292
295
|
::YAML.load(data, **(kwargs || {}))
|
|
293
296
|
end
|
|
294
297
|
|
|
@@ -305,6 +308,7 @@ module Bootsnap
|
|
|
305
308
|
|
|
306
309
|
CompileCache::Native.fetch(
|
|
307
310
|
CompileCache::YAML.cache_dir,
|
|
311
|
+
nil,
|
|
308
312
|
File.realpath(path),
|
|
309
313
|
CompileCache::YAML::Psych3,
|
|
310
314
|
kwargs,
|
|
@@ -325,6 +329,7 @@ module Bootsnap
|
|
|
325
329
|
|
|
326
330
|
CompileCache::Native.fetch(
|
|
327
331
|
CompileCache::YAML.cache_dir,
|
|
332
|
+
nil,
|
|
328
333
|
File.realpath(path),
|
|
329
334
|
CompileCache::YAML::Psych3,
|
|
330
335
|
kwargs,
|
|
@@ -59,7 +59,7 @@ module Bootsnap
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def purge_multi(features)
|
|
62
|
-
rejected_hashes = features.
|
|
62
|
+
rejected_hashes = features.to_h { |f| [f.hash, true] }
|
|
63
63
|
@mutex.synchronize do
|
|
64
64
|
@lfi.reject! { |_, hash| rejected_hashes.key?(hash) }
|
|
65
65
|
end
|
data/lib/bootsnap/version.rb
CHANGED
data/lib/bootsnap.rb
CHANGED
|
@@ -43,6 +43,13 @@ module Bootsnap
|
|
|
43
43
|
@instrumentation.call(event, path)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def load_config
|
|
47
|
+
config_path = File.expand_path(ENV["BOOTSNAP_CONFIG"] || "config/bootsnap.rb")
|
|
48
|
+
if File.exist?(config_path)
|
|
49
|
+
require(config_path)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
46
53
|
def setup(
|
|
47
54
|
cache_dir:,
|
|
48
55
|
development_mode: true,
|
|
@@ -76,6 +83,28 @@ module Bootsnap
|
|
|
76
83
|
readonly: readonly,
|
|
77
84
|
revalidation: revalidation,
|
|
78
85
|
)
|
|
86
|
+
|
|
87
|
+
load_config
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def enable_frozen_string_literal(app_only: false)
|
|
91
|
+
if app_only
|
|
92
|
+
gems_root = File.join(Bundler.bundle_path.cleanpath, "")
|
|
93
|
+
app_root = File.join(Dir.pwd, "")
|
|
94
|
+
Bootsnap::CompileCache::ISeq.default_compiler = Bootsnap::CompileCache::ISeq::DEFAULT
|
|
95
|
+
Bootsnap::CompileCache::ISeq.compiler_selector = lambda { |path|
|
|
96
|
+
# Enable `frozen_string_literal: true` for app code, but not gems.
|
|
97
|
+
|
|
98
|
+
if path.start_with?(app_root) && !path.start_with?(gems_root)
|
|
99
|
+
Bootsnap::CompileCache::ISeq::FROZEN_STRING_LITERAL
|
|
100
|
+
else
|
|
101
|
+
Bootsnap::CompileCache::ISeq::DEFAULT
|
|
102
|
+
end
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
Bootsnap::CompileCache::ISeq.compiler_selector = nil
|
|
106
|
+
Bootsnap::CompileCache::ISeq.default_compiler = Bootsnap::CompileCache::ISeq::FROZEN_STRING_LITERAL
|
|
107
|
+
end
|
|
79
108
|
end
|
|
80
109
|
|
|
81
110
|
def unload_cache!
|
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.
|
|
4
|
+
version: 1.24.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Burke Libbey
|
|
@@ -44,6 +44,7 @@ files:
|
|
|
44
44
|
- lib/bootsnap/cli/worker_pool.rb
|
|
45
45
|
- lib/bootsnap/compile_cache.rb
|
|
46
46
|
- lib/bootsnap/compile_cache/iseq.rb
|
|
47
|
+
- lib/bootsnap/compile_cache/ruby_bug_22023_canary.rb
|
|
47
48
|
- lib/bootsnap/compile_cache/yaml.rb
|
|
48
49
|
- lib/bootsnap/explicit_require.rb
|
|
49
50
|
- lib/bootsnap/load_path_cache.rb
|
|
@@ -80,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
81
|
- !ruby/object:Gem::Version
|
|
81
82
|
version: '0'
|
|
82
83
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
84
|
+
rubygems_version: 4.0.6
|
|
84
85
|
specification_version: 4
|
|
85
86
|
summary: Boot large ruby/rails apps faster
|
|
86
87
|
test_files: []
|