bootsnap 1.1.8-java → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![
|
1
|
+
# Bootsnap [![Actions Status](https://github.com/Shopify/bootsnap/workflows/ci/badge.svg)](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 = {
|