bootsnap 1.1.8-java → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +103 -0
- data/README.md +47 -6
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +217 -88
- data/ext/bootsnap/extconf.rb +3 -1
- data/lib/bootsnap.rb +17 -8
- data/lib/bootsnap/bundler.rb +6 -3
- data/lib/bootsnap/cli.rb +246 -0
- data/lib/bootsnap/cli/worker_pool.rb +131 -0
- data/lib/bootsnap/compile_cache.rb +32 -4
- data/lib/bootsnap/compile_cache/iseq.rb +32 -15
- data/lib/bootsnap/compile_cache/yaml.rb +94 -40
- data/lib/bootsnap/explicit_require.rb +2 -1
- data/lib/bootsnap/load_path_cache.rb +35 -9
- data/lib/bootsnap/load_path_cache/cache.rb +48 -29
- data/lib/bootsnap/load_path_cache/change_observer.rb +36 -29
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +39 -7
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +70 -53
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +18 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +148 -0
- data/lib/bootsnap/load_path_cache/path.rb +8 -7
- data/lib/bootsnap/load_path_cache/path_scanner.rb +50 -39
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +32 -0
- data/lib/bootsnap/load_path_cache/store.rb +20 -14
- data/lib/bootsnap/setup.rb +11 -13
- data/lib/bootsnap/version.rb +2 -1
- metadata +44 -45
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -4
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/Rakefile +0 -11
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -39
- data/dev.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '029d63ba428f470d2cd2ef194d857e20689e7c8e377e2eaaaab83570f366034a'
|
4
|
+
data.tar.gz: 36a55f9d12dddef6ea3c4739f586c9236d7f4bc677a8b6747dfd7465d46eeca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 131ec17c4e4912f387c18778250e689467ebfcc80e91eb884237307af1a57a3c89d4fb20c772fbc0330123a796d631861a9cf145bd32c54183077bbc97d7fa6f
|
7
|
+
data.tar.gz: d0c92454c8a5d8b16a25908fd0ae134fc817c5977958bde8424baca2bb6c96e60eb648c9f770bf7c7f58a3dd98871d4e7b0e3a0950c269100fded45ca9fddfa3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,106 @@
|
|
1
|
+
# Unreleased
|
2
|
+
|
3
|
+
# 1.6.0
|
4
|
+
|
5
|
+
* Fix a Ruby 2.7/3.0 issue with `YAML.load_file` keyword arguments. (#342)
|
6
|
+
* `bootsnap precompile` CLI use multiple processes to complete faster. (#341)
|
7
|
+
* `bootsnap precompile` CLI also precompile YAML files. (#340)
|
8
|
+
* Changed the load path cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-load-path-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/load-path-cache` for ease of use. (#334)
|
9
|
+
* Changed the compile cache directory from `$BOOTSNAP_CACHE_DIR/bootsnap-compile-cache` to `$BOOTSNAP_CACHE_DIR/bootsnap/compile-cache` for ease of use. (#334)
|
10
|
+
|
11
|
+
# 1.5.1
|
12
|
+
|
13
|
+
* Workaround a Ruby bug in InstructionSequence.compile_file. (#332)
|
14
|
+
|
15
|
+
# 1.5.0
|
16
|
+
|
17
|
+
* Add a command line to statically precompile the ISeq cache. (#326)
|
18
|
+
|
19
|
+
# 1.4.9
|
20
|
+
|
21
|
+
* [Windows support](https://github.com/Shopify/bootsnap/pull/319)
|
22
|
+
* [Fix potential crash](https://github.com/Shopify/bootsnap/pull/322)
|
23
|
+
|
24
|
+
# 1.4.8
|
25
|
+
|
26
|
+
* [Prevent FallbackScan from polluting exception cause](https://github.com/Shopify/bootsnap/pull/314)
|
27
|
+
|
28
|
+
# 1.4.7
|
29
|
+
|
30
|
+
* Various performance enhancements
|
31
|
+
* Fix race condition in heavy concurrent load scenarios that would cause bootsnap to raise
|
32
|
+
|
33
|
+
# 1.4.6
|
34
|
+
|
35
|
+
* Fix bug that was erroneously considering that files containing `.` in the names were being
|
36
|
+
required if a different file with the same name was already being required
|
37
|
+
|
38
|
+
Example:
|
39
|
+
|
40
|
+
require 'foo'
|
41
|
+
require 'foo.en'
|
42
|
+
|
43
|
+
Before bootsnap was considering `foo.en` to be the same file as `foo`
|
44
|
+
|
45
|
+
* Use glibc as part of the ruby_platform cache key
|
46
|
+
|
47
|
+
# 1.4.5
|
48
|
+
|
49
|
+
* MRI 2.7 support
|
50
|
+
* Fixed concurrency bugs
|
51
|
+
|
52
|
+
# 1.4.4
|
53
|
+
|
54
|
+
* Disable ISeq cache in `bootsnap/setup` by default in Ruby 2.5
|
55
|
+
|
56
|
+
# 1.4.3
|
57
|
+
|
58
|
+
* Fix some cache permissions and umask issues after switch to mkstemp
|
59
|
+
|
60
|
+
# 1.4.2
|
61
|
+
|
62
|
+
* Fix bug when removing features loaded by relative path from `$LOADED_FEATURES`
|
63
|
+
* Fix bug with propagation of `NameError` up from nested calls to `require`
|
64
|
+
|
65
|
+
# 1.4.1
|
66
|
+
|
67
|
+
* Don't register change observers to frozen objects.
|
68
|
+
|
69
|
+
# 1.4.0
|
70
|
+
|
71
|
+
* When running in development mode, always fall back to a full path scan on LoadError, making
|
72
|
+
bootsnap more able to detect newly-created files. (#230)
|
73
|
+
* Respect `$LOADED_FEATURES.delete` in order to support code reloading, for integration with
|
74
|
+
Zeitwerk. (#230)
|
75
|
+
* Minor performance improvement: flow-control exceptions no longer generate backtraces.
|
76
|
+
* Better support for requiring from environments where some features are not supported (especially
|
77
|
+
JRuby). (#226)k
|
78
|
+
* More robust handling of OS errors when creating files. (#225)
|
79
|
+
|
80
|
+
# 1.3.2
|
81
|
+
|
82
|
+
* Fix Spring + Bootsnap incompatibility when there are files with similar names.
|
83
|
+
* Fix `YAML.load_file` monkey patch to keep accepting File objects as arguments.
|
84
|
+
* Fix the API for `ActiveSupport::Dependencies#autoloadable_module?`.
|
85
|
+
* Some performance improvements.
|
86
|
+
|
87
|
+
# 1.3.1
|
88
|
+
|
89
|
+
* Change load path scanning to more correctly follow symlinks.
|
90
|
+
|
91
|
+
# 1.3.0
|
92
|
+
|
93
|
+
* Handle cases where load path entries are symlinked (https://github.com/Shopify/bootsnap/pull/136)
|
94
|
+
|
95
|
+
# 1.2.1
|
96
|
+
|
97
|
+
* Fix method visibility of `Kernel#require`.
|
98
|
+
|
99
|
+
# 1.2.0
|
100
|
+
|
101
|
+
* Add `LoadedFeaturesIndex` to preserve fix a common bug related to `LOAD_PATH` modifications after
|
102
|
+
loading bootsnap.
|
103
|
+
|
1
104
|
# 1.1.8
|
2
105
|
|
3
106
|
* Don't cache YAML documents with `!ruby/object`
|
data/README.md
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
-
# Bootsnap [](https://github.com/Shopify/bootsnap/actions)
|
2
2
|
|
3
3
|
Bootsnap is a library that plugs into Ruby, with optional support for `ActiveSupport` and `YAML`,
|
4
4
|
to optimize and cache expensive computations. See [How Does This Work](#how-does-this-work).
|
5
5
|
|
6
6
|
#### Performance
|
7
|
-
|
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,
|
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
|
|
13
|
-
This gem works on
|
18
|
+
This gem works on macOS and Linux.
|
14
19
|
|
15
20
|
Add `bootsnap` to your `Gemfile`:
|
16
21
|
|
@@ -24,6 +29,18 @@ 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` (or the path specified by `ENV['BOOTSNAP_CACHE_DIR']`),
|
33
|
+
and that directory *must* be writable. Rails will fail to
|
34
|
+
boot if it is not. If this is unacceptable (e.g. you are running in a read-only container and
|
35
|
+
unwilling to mount in a writable tmpdir), you should remove this line or wrap it in a conditional.
|
36
|
+
|
37
|
+
**Note also that bootsnap will never clean up its own cache: this is left up to you. Depending on your
|
38
|
+
deployment strategy, you may need to periodically purge `tmp/cache/bootsnap*`. If you notice deploys
|
39
|
+
getting progressively slower, this is almost certainly the cause.**
|
40
|
+
|
41
|
+
It's technically possible to simply specify `gem 'bootsnap', require: 'bootsnap/setup'`, but it's
|
42
|
+
important to load Bootsnap as early as possible to get maximum performance improvement.
|
43
|
+
|
27
44
|
You can see how this require works [here](https://github.com/Shopify/bootsnap/blob/master/lib/bootsnap/setup.rb).
|
28
45
|
|
29
46
|
If you are not using Rails, or if you are but want more control over things, add this to your
|
@@ -38,12 +55,14 @@ Bootsnap.setup(
|
|
38
55
|
development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
|
39
56
|
load_path_cache: true, # Optimize the LOAD_PATH with a cache
|
40
57
|
autoload_paths_cache: true, # Optimize ActiveSupport autoloads with cache
|
41
|
-
disable_trace: true, #
|
58
|
+
disable_trace: true, # Set `RubyVM::InstructionSequence.compile_option = { trace_instruction: false }`
|
42
59
|
compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
|
43
60
|
compile_cache_yaml: true # Compile YAML into a cache
|
44
61
|
)
|
45
62
|
```
|
46
63
|
|
64
|
+
**Note that `disable_trace` will break debuggers and tracing.**
|
65
|
+
|
47
66
|
**Protip:** You can replace `require 'bootsnap'` with `BootLib::Require.from_gem('bootsnap',
|
48
67
|
'bootsnap')` using [this trick](https://github.com/Shopify/bootsnap/wiki/Bootlib::Require). This
|
49
68
|
will help optimize boot time further if you have an extremely large `$LOAD_PATH`.
|
@@ -196,7 +215,7 @@ Bootsnap writes a cache file containing a 64 byte header followed by the cache c
|
|
196
215
|
is a cache key including several fields:
|
197
216
|
|
198
217
|
* `version`, hardcoded in bootsnap. Essentially a schema version;
|
199
|
-
* `
|
218
|
+
* `ruby_platform`, A hash of `RUBY_PLATFORM` (e.g. x86_64-linux-gnu) variable and glibc version (on Linux) or OS version (`uname -v` on BSD, macOS)
|
200
219
|
* `compile_option`, which changes with `RubyVM::InstructionSequence.compile_option` does;
|
201
220
|
* `ruby_revision`, the version of Ruby this was compiled with;
|
202
221
|
* `size`, the size of the source file;
|
@@ -275,3 +294,25 @@ open /c/nope.bundle -> -1
|
|
275
294
|
```
|
276
295
|
# (nothing!)
|
277
296
|
```
|
297
|
+
|
298
|
+
## Precompilation
|
299
|
+
|
300
|
+
In development environments the bootsnap compilation cache is generated on the fly when source files are loaded.
|
301
|
+
But in production environments, such as docker images, you might need to precompile the cache.
|
302
|
+
|
303
|
+
To do so you can use the `bootsnap precompile` command.
|
304
|
+
|
305
|
+
Example:
|
306
|
+
|
307
|
+
```bash
|
308
|
+
$ bundle exec bootsnap precompile --gemfile app/ lib/
|
309
|
+
```
|
310
|
+
|
311
|
+
## When not to use Bootsnap
|
312
|
+
|
313
|
+
*Alternative engines*: Bootsnap is pretty reliant on MRI features, and parts are disabled entirely on alternative ruby
|
314
|
+
engines.
|
315
|
+
|
316
|
+
*Non-local filesystems*: Bootsnap depends on `tmp/cache` (or whatever you set its cache directory
|
317
|
+
to) being on a relatively fast filesystem. If you put it on a network mount, bootsnap is very likely
|
318
|
+
to slow your application down quite a lot.
|
data/exe/bootsnap
ADDED
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -21,6 +21,9 @@
|
|
21
21
|
#ifndef _WIN32
|
22
22
|
#include <sys/utsname.h>
|
23
23
|
#endif
|
24
|
+
#ifdef __GLIBC__
|
25
|
+
#include <gnu/libc-version.h>
|
26
|
+
#endif
|
24
27
|
|
25
28
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
29
|
* 981 for the cache dir */
|
@@ -29,6 +32,8 @@
|
|
29
32
|
|
30
33
|
#define KEY_SIZE 64
|
31
34
|
|
35
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
36
|
+
|
32
37
|
/*
|
33
38
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
39
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -65,7 +70,7 @@ struct bs_cache_key {
|
|
65
70
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
66
71
|
|
67
72
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
|
-
static const uint32_t current_version =
|
73
|
+
static const uint32_t current_version = 3;
|
69
74
|
|
70
75
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
76
|
* new OS ABI, etc. */
|
@@ -74,6 +79,8 @@ static uint32_t current_ruby_platform;
|
|
74
79
|
static uint32_t current_ruby_revision;
|
75
80
|
/* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
|
76
81
|
static uint32_t current_compile_option_crc32 = 0;
|
82
|
+
/* Current umask */
|
83
|
+
static mode_t current_umask;
|
77
84
|
|
78
85
|
/* Bootsnap::CompileCache::{Native, Uncompilable} */
|
79
86
|
static VALUE rb_mBootsnap;
|
@@ -84,29 +91,31 @@ static ID uncompilable;
|
|
84
91
|
|
85
92
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
86
93
|
static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
|
87
|
-
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
94
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
95
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
88
96
|
|
89
97
|
/* Helpers */
|
90
98
|
static uint64_t fnv1a_64(const char *str);
|
91
|
-
static void bs_cache_path(const char * cachedir, const char * path, char
|
99
|
+
static void bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
92
100
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
93
101
|
static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
|
94
|
-
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
95
|
-
static
|
96
|
-
static int
|
97
|
-
static
|
102
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
103
|
+
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
104
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
105
|
+
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);
|
106
|
+
static uint32_t get_ruby_revision(void);
|
98
107
|
static uint32_t get_ruby_platform(void);
|
99
108
|
|
100
109
|
/*
|
101
110
|
* Helper functions to call ruby methods on handler object without crashing on
|
102
111
|
* exception.
|
103
112
|
*/
|
104
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
113
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
105
114
|
static VALUE prot_storage_to_output(VALUE arg);
|
106
115
|
static VALUE prot_input_to_output(VALUE arg);
|
107
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
116
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
108
117
|
static VALUE prot_input_to_storage(VALUE arg);
|
109
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
118
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
110
119
|
struct s2o_data;
|
111
120
|
struct i2o_data;
|
112
121
|
struct i2s_data;
|
@@ -135,14 +144,18 @@ Init_bootsnap(void)
|
|
135
144
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
136
145
|
rb_eBootsnap_CompileCache_Uncompilable = rb_define_class_under(rb_mBootsnap_CompileCache, "Uncompilable", rb_eStandardError);
|
137
146
|
|
138
|
-
current_ruby_revision =
|
147
|
+
current_ruby_revision = get_ruby_revision();
|
139
148
|
current_ruby_platform = get_ruby_platform();
|
140
149
|
|
141
150
|
uncompilable = rb_intern("__bootsnap_uncompilable__");
|
142
151
|
|
143
152
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
144
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
153
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
154
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
145
155
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
156
|
+
|
157
|
+
current_umask = umask(0777);
|
158
|
+
umask(current_umask);
|
146
159
|
}
|
147
160
|
|
148
161
|
/*
|
@@ -192,6 +205,26 @@ fnv1a_64(const char *str)
|
|
192
205
|
return fnv1a_64_iter(h, str);
|
193
206
|
}
|
194
207
|
|
208
|
+
/*
|
209
|
+
* Ruby's revision may be Integer or String. CRuby 2.7 or later uses
|
210
|
+
* Git commit ID as revision. It's String.
|
211
|
+
*/
|
212
|
+
static uint32_t
|
213
|
+
get_ruby_revision(void)
|
214
|
+
{
|
215
|
+
VALUE ruby_revision;
|
216
|
+
|
217
|
+
ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
|
218
|
+
if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
|
219
|
+
return FIX2INT(ruby_revision);
|
220
|
+
} else {
|
221
|
+
uint64_t hash;
|
222
|
+
|
223
|
+
hash = fnv1a_64(StringValueCStr(ruby_revision));
|
224
|
+
return (uint32_t)(hash >> 32);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
195
228
|
/*
|
196
229
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
197
230
|
* (or OS version), we need to invalidate the cache.
|
@@ -211,6 +244,9 @@ get_ruby_platform(void)
|
|
211
244
|
|
212
245
|
#ifdef _WIN32
|
213
246
|
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
247
|
+
#elif defined(__GLIBC__)
|
248
|
+
hash = fnv1a_64_iter(hash, gnu_get_libc_version());
|
249
|
+
return (uint32_t)(hash >> 32);
|
214
250
|
#else
|
215
251
|
struct utsname utsname;
|
216
252
|
|
@@ -231,10 +267,9 @@ get_ruby_platform(void)
|
|
231
267
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
232
268
|
*/
|
233
269
|
static void
|
234
|
-
bs_cache_path(const char * cachedir, const char * path, char
|
270
|
+
bs_cache_path(const char * cachedir, const char * path, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
235
271
|
{
|
236
272
|
uint64_t hash = fnv1a_64(path);
|
237
|
-
|
238
273
|
uint8_t first_byte = (hash >> (64 - 8));
|
239
274
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
240
275
|
|
@@ -268,8 +303,10 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
268
303
|
* conversions on the ruby VALUE arguments before passing them along.
|
269
304
|
*/
|
270
305
|
static VALUE
|
271
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
306
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
272
307
|
{
|
308
|
+
FilePathValue(path_v);
|
309
|
+
|
273
310
|
Check_Type(cachedir_v, T_STRING);
|
274
311
|
Check_Type(path_v, T_STRING);
|
275
312
|
|
@@ -281,27 +318,51 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
281
318
|
char * path = RSTRING_PTR(path_v);
|
282
319
|
char cache_path[MAX_CACHEPATH_SIZE];
|
283
320
|
|
284
|
-
|
285
|
-
|
286
|
-
bs_cache_path(cachedir, path, &tmp);
|
287
|
-
}
|
321
|
+
/* generate cache path to cache_path */
|
322
|
+
bs_cache_path(cachedir, path, &cache_path);
|
288
323
|
|
289
|
-
return bs_fetch(path, path_v, cache_path, handler);
|
324
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
290
325
|
}
|
291
326
|
|
327
|
+
/*
|
328
|
+
* Entrypoint for Bootsnap::CompileCache::Native.precompile.
|
329
|
+
* Similar to fetch, but it only generate the cache if missing
|
330
|
+
* and doesn't return the content.
|
331
|
+
*/
|
332
|
+
static VALUE
|
333
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
334
|
+
{
|
335
|
+
FilePathValue(path_v);
|
336
|
+
|
337
|
+
Check_Type(cachedir_v, T_STRING);
|
338
|
+
Check_Type(path_v, T_STRING);
|
339
|
+
|
340
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
341
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
342
|
+
}
|
343
|
+
|
344
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
345
|
+
char * path = RSTRING_PTR(path_v);
|
346
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
347
|
+
|
348
|
+
/* generate cache path to cache_path */
|
349
|
+
bs_cache_path(cachedir, path, &cache_path);
|
350
|
+
|
351
|
+
return bs_precompile(path, path_v, cache_path, handler);
|
352
|
+
}
|
292
353
|
/*
|
293
354
|
* Open the file we want to load/cache and generate a cache key for it if it
|
294
355
|
* was loaded.
|
295
356
|
*/
|
296
357
|
static int
|
297
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
358
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
298
359
|
{
|
299
360
|
struct stat statbuf;
|
300
361
|
int fd;
|
301
362
|
|
302
363
|
fd = open(path, O_RDONLY);
|
303
364
|
if (fd < 0) {
|
304
|
-
*errno_provenance =
|
365
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
305
366
|
return fd;
|
306
367
|
}
|
307
368
|
#ifdef _WIN32
|
@@ -309,7 +370,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
309
370
|
#endif
|
310
371
|
|
311
372
|
if (fstat(fd, &statbuf) < 0) {
|
312
|
-
*errno_provenance =
|
373
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
313
374
|
close(fd);
|
314
375
|
return -1;
|
315
376
|
}
|
@@ -355,13 +416,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
355
416
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
356
417
|
*/
|
357
418
|
static int
|
358
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
419
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
359
420
|
{
|
360
421
|
int fd, res;
|
361
422
|
|
362
423
|
fd = open(path, O_RDONLY);
|
363
424
|
if (fd < 0) {
|
364
|
-
*errno_provenance =
|
425
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
365
426
|
if (errno == ENOENT) return CACHE_MISSING_OR_INVALID;
|
366
427
|
return ERROR_WITH_ERRNO;
|
367
428
|
}
|
@@ -371,7 +432,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
371
432
|
|
372
433
|
res = bs_read_key(fd, key);
|
373
434
|
if (res < 0) {
|
374
|
-
*errno_provenance =
|
435
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
375
436
|
close(fd);
|
376
437
|
return res;
|
377
438
|
}
|
@@ -395,7 +456,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
395
456
|
* or exception, will be the final data returnable to the user.
|
396
457
|
*/
|
397
458
|
static int
|
398
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
459
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
399
460
|
{
|
400
461
|
char * data = NULL;
|
401
462
|
ssize_t nread;
|
@@ -404,7 +465,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
404
465
|
VALUE storage_data;
|
405
466
|
|
406
467
|
if (data_size > 100000000000) {
|
407
|
-
*errno_provenance =
|
468
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
408
469
|
errno = EINVAL; /* because wtf? */
|
409
470
|
ret = -1;
|
410
471
|
goto done;
|
@@ -412,7 +473,7 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
412
473
|
data = ALLOC_N(char, data_size);
|
413
474
|
nread = read(fd, data, data_size);
|
414
475
|
if (nread < 0) {
|
415
|
-
*errno_provenance =
|
476
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
416
477
|
ret = -1;
|
417
478
|
goto done;
|
418
479
|
}
|
@@ -421,9 +482,9 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
421
482
|
goto done;
|
422
483
|
}
|
423
484
|
|
424
|
-
storage_data =
|
485
|
+
storage_data = rb_str_new(data, data_size);
|
425
486
|
|
426
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
487
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
427
488
|
ret = 0;
|
428
489
|
done:
|
429
490
|
if (data != NULL) xfree(data);
|
@@ -464,30 +525,36 @@ mkpath(char * file_path, mode_t mode)
|
|
464
525
|
* path.
|
465
526
|
*/
|
466
527
|
static int
|
467
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
528
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
468
529
|
{
|
469
530
|
char template[MAX_CACHEPATH_SIZE + 20];
|
470
|
-
char * dest;
|
471
531
|
char * tmp_path;
|
472
|
-
int fd, ret;
|
532
|
+
int fd, ret, attempt;
|
473
533
|
ssize_t nwrite;
|
474
534
|
|
475
|
-
|
476
|
-
|
535
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
536
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
537
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
477
538
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
}
|
485
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
486
|
-
if (fd < 0) {
|
487
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
539
|
+
// mkstemp modifies the template to be the actual created path
|
540
|
+
fd = mkstemp(tmp_path);
|
541
|
+
if (fd > 0) break;
|
542
|
+
|
543
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
544
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
488
545
|
return -1;
|
489
546
|
}
|
490
547
|
}
|
548
|
+
if (fd < 0) {
|
549
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
550
|
+
return -1;
|
551
|
+
}
|
552
|
+
|
553
|
+
if (chmod(tmp_path, 0644) < 0) {
|
554
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
555
|
+
return -1;
|
556
|
+
}
|
557
|
+
|
491
558
|
#ifdef _WIN32
|
492
559
|
setmode(fd, O_BINARY);
|
493
560
|
#endif
|
@@ -495,11 +562,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
495
562
|
key->data_size = RSTRING_LEN(data);
|
496
563
|
nwrite = write(fd, key, KEY_SIZE);
|
497
564
|
if (nwrite < 0) {
|
498
|
-
*errno_provenance =
|
565
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
499
566
|
return -1;
|
500
567
|
}
|
501
568
|
if (nwrite != KEY_SIZE) {
|
502
|
-
*errno_provenance =
|
569
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
503
570
|
errno = EIO; /* Lies but whatever */
|
504
571
|
return -1;
|
505
572
|
}
|
@@ -507,7 +574,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
507
574
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
508
575
|
if (nwrite < 0) return -1;
|
509
576
|
if (nwrite != RSTRING_LEN(data)) {
|
510
|
-
*errno_provenance =
|
577
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
511
578
|
errno = EIO; /* Lies but whatever */
|
512
579
|
return -1;
|
513
580
|
}
|
@@ -515,38 +582,27 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
515
582
|
close(fd);
|
516
583
|
ret = rename(tmp_path, path);
|
517
584
|
if (ret < 0) {
|
518
|
-
*errno_provenance =
|
585
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
586
|
+
return -1;
|
519
587
|
}
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
/*
|
524
|
-
* Given an errno value (converted to a ruby Fixnum), return the corresponding
|
525
|
-
* Errno::* constant. If none is found, return StandardError instead.
|
526
|
-
*/
|
527
|
-
static VALUE
|
528
|
-
prot_exception_for_errno(VALUE err)
|
529
|
-
{
|
530
|
-
if (err != INT2FIX(0)) {
|
531
|
-
VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
|
532
|
-
VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
|
533
|
-
VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
|
534
|
-
return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
|
588
|
+
ret = chmod(path, 0664 & ~current_umask);
|
589
|
+
if (ret < 0) {
|
590
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
535
591
|
}
|
536
|
-
return
|
592
|
+
return ret;
|
537
593
|
}
|
538
594
|
|
539
595
|
|
540
596
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
541
597
|
* long, into a buffer */
|
542
598
|
static ssize_t
|
543
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
599
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
544
600
|
{
|
545
601
|
ssize_t nread;
|
546
602
|
*contents = ALLOC_N(char, size);
|
547
603
|
nread = read(fd, *contents, size);
|
548
604
|
if (nread < 0) {
|
549
|
-
*errno_provenance =
|
605
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
550
606
|
}
|
551
607
|
return nread;
|
552
608
|
}
|
@@ -596,13 +652,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
596
652
|
* - Return storage_to_output(storage_data)
|
597
653
|
*/
|
598
654
|
static VALUE
|
599
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
655
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
600
656
|
{
|
601
657
|
struct bs_cache_key cached_key, current_key;
|
602
658
|
char * contents = NULL;
|
603
659
|
int cache_fd = -1, current_fd = -1;
|
604
660
|
int res, valid_cache = 0, exception_tag = 0;
|
605
|
-
char * errno_provenance = NULL;
|
661
|
+
const char * errno_provenance = NULL;
|
606
662
|
|
607
663
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
608
664
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -629,7 +685,7 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
629
685
|
if (valid_cache) {
|
630
686
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
631
687
|
res = fetch_cached_data(
|
632
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
688
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
633
689
|
&output_data, &exception_tag, &errno_provenance
|
634
690
|
);
|
635
691
|
if (exception_tag != 0) goto raise;
|
@@ -643,15 +699,15 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
643
699
|
|
644
700
|
/* Read the contents of the source file into a buffer */
|
645
701
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
646
|
-
input_data =
|
702
|
+
input_data = rb_str_new(contents, current_key.size);
|
647
703
|
|
648
704
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
649
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
705
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
650
706
|
if (exception_tag != 0) goto raise;
|
651
707
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
652
708
|
* to cache anything; just return input_to_output(input_data) */
|
653
709
|
if (storage_data == uncompilable) {
|
654
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
710
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
655
711
|
if (exception_tag != 0) goto raise;
|
656
712
|
goto succeed;
|
657
713
|
}
|
@@ -663,17 +719,17 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
663
719
|
if (res < 0) goto fail_errno;
|
664
720
|
|
665
721
|
/* Having written the cache, now convert storage_data to output_data */
|
666
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
722
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
667
723
|
if (exception_tag != 0) goto raise;
|
668
724
|
|
669
725
|
/* If output_data is nil, delete the cache entry and generate the output
|
670
726
|
* using input_to_output */
|
671
727
|
if (NIL_P(output_data)) {
|
672
728
|
if (unlink(cache_path) < 0) {
|
673
|
-
errno_provenance =
|
729
|
+
errno_provenance = "bs_fetch:unlink";
|
674
730
|
goto fail_errno;
|
675
731
|
}
|
676
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
732
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
677
733
|
if (exception_tag != 0) goto raise;
|
678
734
|
}
|
679
735
|
|
@@ -689,11 +745,7 @@ succeed:
|
|
689
745
|
return output_data;
|
690
746
|
fail_errno:
|
691
747
|
CLEANUP;
|
692
|
-
exception =
|
693
|
-
if (res) exception = rb_eStandardError;
|
694
|
-
if (errno_provenance != NULL) {
|
695
|
-
exception = rb_exc_new_str(exception, rb_str_new2(errno_provenance));
|
696
|
-
}
|
748
|
+
exception = rb_syserr_new(errno, errno_provenance);
|
697
749
|
rb_exc_raise(exception);
|
698
750
|
__builtin_unreachable();
|
699
751
|
raise:
|
@@ -708,6 +760,79 @@ invalid_type_storage_data:
|
|
708
760
|
#undef CLEANUP
|
709
761
|
}
|
710
762
|
|
763
|
+
static VALUE
|
764
|
+
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
765
|
+
{
|
766
|
+
struct bs_cache_key cached_key, current_key;
|
767
|
+
char * contents = NULL;
|
768
|
+
int cache_fd = -1, current_fd = -1;
|
769
|
+
int res, valid_cache = 0, exception_tag = 0;
|
770
|
+
const char * errno_provenance = NULL;
|
771
|
+
|
772
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
773
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
774
|
+
|
775
|
+
/* Open the source file and generate a cache key for it */
|
776
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
777
|
+
if (current_fd < 0) goto fail;
|
778
|
+
|
779
|
+
/* Open the cache key if it exists, and read its cache key in */
|
780
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
781
|
+
if (cache_fd == CACHE_MISSING_OR_INVALID) {
|
782
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
783
|
+
} else if (cache_fd < 0) {
|
784
|
+
goto fail;
|
785
|
+
} else {
|
786
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
787
|
+
* it was generated. */
|
788
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
789
|
+
}
|
790
|
+
|
791
|
+
if (valid_cache) {
|
792
|
+
goto succeed;
|
793
|
+
}
|
794
|
+
|
795
|
+
close(cache_fd);
|
796
|
+
cache_fd = -1;
|
797
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
798
|
+
|
799
|
+
/* Read the contents of the source file into a buffer */
|
800
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
|
801
|
+
input_data = rb_str_new(contents, current_key.size);
|
802
|
+
|
803
|
+
/* Try to compile the input_data using input_to_storage(input_data) */
|
804
|
+
exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
|
805
|
+
if (exception_tag != 0) goto fail;
|
806
|
+
|
807
|
+
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
808
|
+
* to cache anything; just return false */
|
809
|
+
if (storage_data == uncompilable) {
|
810
|
+
goto fail;
|
811
|
+
}
|
812
|
+
/* If storage_data isn't a string, we can't cache it */
|
813
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
814
|
+
|
815
|
+
/* Write the cache key and storage_data to the cache directory */
|
816
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
817
|
+
if (res < 0) goto fail;
|
818
|
+
|
819
|
+
goto succeed;
|
820
|
+
|
821
|
+
#define CLEANUP \
|
822
|
+
if (contents != NULL) xfree(contents); \
|
823
|
+
if (current_fd >= 0) close(current_fd); \
|
824
|
+
if (cache_fd >= 0) close(cache_fd);
|
825
|
+
|
826
|
+
succeed:
|
827
|
+
CLEANUP;
|
828
|
+
return Qtrue;
|
829
|
+
fail:
|
830
|
+
CLEANUP;
|
831
|
+
return Qfalse;
|
832
|
+
#undef CLEANUP
|
833
|
+
}
|
834
|
+
|
835
|
+
|
711
836
|
/*****************************************************************************/
|
712
837
|
/********************* Handler Wrappers **************************************/
|
713
838
|
/*****************************************************************************
|
@@ -727,11 +852,13 @@ invalid_type_storage_data:
|
|
727
852
|
|
728
853
|
struct s2o_data {
|
729
854
|
VALUE handler;
|
855
|
+
VALUE args;
|
730
856
|
VALUE storage_data;
|
731
857
|
};
|
732
858
|
|
733
859
|
struct i2o_data {
|
734
860
|
VALUE handler;
|
861
|
+
VALUE args;
|
735
862
|
VALUE input_data;
|
736
863
|
};
|
737
864
|
|
@@ -745,15 +872,16 @@ static VALUE
|
|
745
872
|
prot_storage_to_output(VALUE arg)
|
746
873
|
{
|
747
874
|
struct s2o_data * data = (struct s2o_data *)arg;
|
748
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
875
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
749
876
|
}
|
750
877
|
|
751
878
|
static int
|
752
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
879
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
753
880
|
{
|
754
881
|
int state;
|
755
882
|
struct s2o_data s2o_data = {
|
756
883
|
.handler = handler,
|
884
|
+
.args = args,
|
757
885
|
.storage_data = storage_data,
|
758
886
|
};
|
759
887
|
*output_data = rb_protect(prot_storage_to_output, (VALUE)&s2o_data, &state);
|
@@ -761,10 +889,11 @@ bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
|
761
889
|
}
|
762
890
|
|
763
891
|
static void
|
764
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
892
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
765
893
|
{
|
766
894
|
struct i2o_data i2o_data = {
|
767
895
|
.handler = handler,
|
896
|
+
.args = args,
|
768
897
|
.input_data = input_data,
|
769
898
|
};
|
770
899
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -774,7 +903,7 @@ static VALUE
|
|
774
903
|
prot_input_to_output(VALUE arg)
|
775
904
|
{
|
776
905
|
struct i2o_data * data = (struct i2o_data *)arg;
|
777
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
906
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
778
907
|
}
|
779
908
|
|
780
909
|
static VALUE
|
@@ -785,7 +914,7 @@ try_input_to_storage(VALUE arg)
|
|
785
914
|
}
|
786
915
|
|
787
916
|
static VALUE
|
788
|
-
rescue_input_to_storage(VALUE arg)
|
917
|
+
rescue_input_to_storage(VALUE arg, VALUE e)
|
789
918
|
{
|
790
919
|
return uncompilable;
|
791
920
|
}
|
@@ -801,7 +930,7 @@ prot_input_to_storage(VALUE arg)
|
|
801
930
|
}
|
802
931
|
|
803
932
|
static int
|
804
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
933
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
805
934
|
{
|
806
935
|
int state;
|
807
936
|
struct i2s_data i2s_data = {
|