bootsnap 1.4.0 → 1.10.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +193 -0
- data/LICENSE.txt +1 -1
- data/README.md +70 -15
- data/exe/bootsnap +5 -0
- data/ext/bootsnap/bootsnap.c +319 -119
- data/ext/bootsnap/extconf.rb +22 -14
- data/lib/bootsnap/bundler.rb +2 -0
- data/lib/bootsnap/cli/worker_pool.rb +136 -0
- data/lib/bootsnap/cli.rb +281 -0
- data/lib/bootsnap/compile_cache/iseq.rb +65 -18
- data/lib/bootsnap/compile_cache/json.rb +88 -0
- data/lib/bootsnap/compile_cache/yaml.rb +332 -39
- data/lib/bootsnap/compile_cache.rb +35 -7
- data/lib/bootsnap/explicit_require.rb +5 -3
- data/lib/bootsnap/load_path_cache/cache.rb +83 -32
- data/lib/bootsnap/load_path_cache/change_observer.rb +7 -1
- data/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb +39 -47
- data/lib/bootsnap/load_path_cache/core_ext/loaded_features.rb +12 -0
- data/lib/bootsnap/load_path_cache/loaded_features_index.rb +69 -26
- data/lib/bootsnap/load_path_cache/path.rb +8 -5
- data/lib/bootsnap/load_path_cache/path_scanner.rb +56 -29
- data/lib/bootsnap/load_path_cache/realpath_cache.rb +6 -5
- data/lib/bootsnap/load_path_cache/store.rb +49 -18
- data/lib/bootsnap/load_path_cache.rb +20 -32
- data/lib/bootsnap/setup.rb +3 -33
- data/lib/bootsnap/version.rb +3 -1
- data/lib/bootsnap.rb +126 -36
- metadata +15 -96
- data/.gitignore +0 -17
- data/.rubocop.yml +0 -20
- data/.travis.yml +0 -24
- data/CODE_OF_CONDUCT.md +0 -74
- data/CONTRIBUTING.md +0 -21
- data/Gemfile +0 -8
- data/README.jp.md +0 -231
- data/Rakefile +0 -12
- data/bin/ci +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/test-minimal-support +0 -7
- data/bin/testunit +0 -8
- data/bootsnap.gemspec +0 -45
- data/dev.yml +0 -10
- data/lib/bootsnap/load_path_cache/core_ext/active_support.rb +0 -100
- data/shipit.rubygems.yml +0 -4
data/ext/bootsnap/bootsnap.c
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
* Suggested reading order:
|
3
3
|
* 1. Skim Init_bootsnap
|
4
4
|
* 2. Skim bs_fetch
|
5
|
-
* 3. The rest of
|
5
|
+
* 3. The rest of everyrything
|
6
6
|
*
|
7
7
|
* Init_bootsnap sets up the ruby objects and binds bs_fetch to
|
8
8
|
* Bootsnap::CompileCache::Native.fetch.
|
@@ -14,6 +14,7 @@
|
|
14
14
|
#include "bootsnap.h"
|
15
15
|
#include "ruby.h"
|
16
16
|
#include <stdint.h>
|
17
|
+
#include <stdbool.h>
|
17
18
|
#include <sys/types.h>
|
18
19
|
#include <errno.h>
|
19
20
|
#include <fcntl.h>
|
@@ -21,6 +22,9 @@
|
|
21
22
|
#ifndef _WIN32
|
22
23
|
#include <sys/utsname.h>
|
23
24
|
#endif
|
25
|
+
#ifdef __GLIBC__
|
26
|
+
#include <gnu/libc-version.h>
|
27
|
+
#endif
|
24
28
|
|
25
29
|
/* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
|
26
30
|
* 981 for the cache dir */
|
@@ -29,6 +33,12 @@
|
|
29
33
|
|
30
34
|
#define KEY_SIZE 64
|
31
35
|
|
36
|
+
#define MAX_CREATE_TEMPFILE_ATTEMPT 3
|
37
|
+
|
38
|
+
#ifndef RB_UNLIKELY
|
39
|
+
#define RB_UNLIKELY(x) (x)
|
40
|
+
#endif
|
41
|
+
|
32
42
|
/*
|
33
43
|
* An instance of this key is written as the first 64 bytes of each cache file.
|
34
44
|
* The mtime and size members track whether the file contents have changed, and
|
@@ -65,7 +75,7 @@ struct bs_cache_key {
|
|
65
75
|
STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
|
66
76
|
|
67
77
|
/* Effectively a schema version. Bumping invalidates all previous caches */
|
68
|
-
static const uint32_t current_version =
|
78
|
+
static const uint32_t current_version = 4;
|
69
79
|
|
70
80
|
/* hash of e.g. "x86_64-darwin17", invalidating when ruby is recompiled on a
|
71
81
|
* new OS ABI, etc. */
|
@@ -74,38 +84,44 @@ static uint32_t current_ruby_platform;
|
|
74
84
|
static uint32_t current_ruby_revision;
|
75
85
|
/* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
|
76
86
|
static uint32_t current_compile_option_crc32 = 0;
|
87
|
+
/* Current umask */
|
88
|
+
static mode_t current_umask;
|
77
89
|
|
78
90
|
/* Bootsnap::CompileCache::{Native, Uncompilable} */
|
79
91
|
static VALUE rb_mBootsnap;
|
80
92
|
static VALUE rb_mBootsnap_CompileCache;
|
81
93
|
static VALUE rb_mBootsnap_CompileCache_Native;
|
82
|
-
static VALUE
|
83
|
-
static ID
|
94
|
+
static VALUE rb_cBootsnap_CompileCache_UNCOMPILABLE;
|
95
|
+
static ID instrumentation_method;
|
96
|
+
static VALUE sym_miss;
|
97
|
+
static VALUE sym_stale;
|
98
|
+
static bool instrumentation_enabled = false;
|
84
99
|
|
85
100
|
/* Functions exposed as module functions on Bootsnap::CompileCache::Native */
|
101
|
+
static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
|
86
102
|
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);
|
103
|
+
static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
|
104
|
+
static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
|
88
105
|
|
89
106
|
/* Helpers */
|
90
|
-
static
|
91
|
-
static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
|
107
|
+
static void bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE]);
|
92
108
|
static int bs_read_key(int fd, struct bs_cache_key * key);
|
93
109
|
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
|
110
|
+
static VALUE bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args);
|
111
|
+
static VALUE bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler);
|
112
|
+
static int open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance);
|
113
|
+
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);
|
114
|
+
static uint32_t get_ruby_revision(void);
|
97
115
|
static uint32_t get_ruby_platform(void);
|
98
116
|
|
99
117
|
/*
|
100
118
|
* Helper functions to call ruby methods on handler object without crashing on
|
101
119
|
* exception.
|
102
120
|
*/
|
103
|
-
static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
|
104
|
-
static VALUE prot_storage_to_output(VALUE arg);
|
121
|
+
static int bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data);
|
105
122
|
static VALUE prot_input_to_output(VALUE arg);
|
106
|
-
static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
|
107
|
-
static
|
108
|
-
static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
123
|
+
static void bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag);
|
124
|
+
static int bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data);
|
109
125
|
struct s2o_data;
|
110
126
|
struct i2o_data;
|
111
127
|
struct i2s_data;
|
@@ -132,16 +148,35 @@ Init_bootsnap(void)
|
|
132
148
|
rb_mBootsnap = rb_define_module("Bootsnap");
|
133
149
|
rb_mBootsnap_CompileCache = rb_define_module_under(rb_mBootsnap, "CompileCache");
|
134
150
|
rb_mBootsnap_CompileCache_Native = rb_define_module_under(rb_mBootsnap_CompileCache, "Native");
|
135
|
-
|
151
|
+
rb_cBootsnap_CompileCache_UNCOMPILABLE = rb_const_get(rb_mBootsnap_CompileCache, rb_intern("UNCOMPILABLE"));
|
152
|
+
rb_global_variable(&rb_cBootsnap_CompileCache_UNCOMPILABLE);
|
136
153
|
|
137
|
-
current_ruby_revision =
|
154
|
+
current_ruby_revision = get_ruby_revision();
|
138
155
|
current_ruby_platform = get_ruby_platform();
|
139
156
|
|
140
|
-
|
157
|
+
instrumentation_method = rb_intern("_instrument");
|
158
|
+
|
159
|
+
sym_miss = ID2SYM(rb_intern("miss"));
|
160
|
+
rb_global_variable(&sym_miss);
|
141
161
|
|
162
|
+
sym_stale = ID2SYM(rb_intern("stale"));
|
163
|
+
rb_global_variable(&sym_stale);
|
164
|
+
|
165
|
+
rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
|
142
166
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
|
143
|
-
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch,
|
167
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
|
168
|
+
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
|
144
169
|
rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
|
170
|
+
|
171
|
+
current_umask = umask(0777);
|
172
|
+
umask(current_umask);
|
173
|
+
}
|
174
|
+
|
175
|
+
static VALUE
|
176
|
+
bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
|
177
|
+
{
|
178
|
+
instrumentation_enabled = RTEST(enabled);
|
179
|
+
return enabled;
|
145
180
|
}
|
146
181
|
|
147
182
|
/*
|
@@ -172,7 +207,7 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
|
|
172
207
|
* - 32 bits doesn't feel collision-resistant enough; 64 is nice.
|
173
208
|
*/
|
174
209
|
static uint64_t
|
175
|
-
|
210
|
+
fnv1a_64_iter_cstr(uint64_t h, const char *str)
|
176
211
|
{
|
177
212
|
unsigned char *s = (unsigned char *)str;
|
178
213
|
|
@@ -185,12 +220,46 @@ fnv1a_64_iter(uint64_t h, const char *str)
|
|
185
220
|
}
|
186
221
|
|
187
222
|
static uint64_t
|
188
|
-
|
223
|
+
fnv1a_64_iter(uint64_t h, const VALUE str)
|
224
|
+
{
|
225
|
+
unsigned char *s = (unsigned char *)RSTRING_PTR(str);
|
226
|
+
unsigned char *str_end = (unsigned char *)RSTRING_PTR(str) + RSTRING_LEN(str);
|
227
|
+
|
228
|
+
while (s < str_end) {
|
229
|
+
h ^= (uint64_t)*s++;
|
230
|
+
h += (h << 1) + (h << 4) + (h << 5) + (h << 7) + (h << 8) + (h << 40);
|
231
|
+
}
|
232
|
+
|
233
|
+
return h;
|
234
|
+
}
|
235
|
+
|
236
|
+
static uint64_t
|
237
|
+
fnv1a_64(const VALUE str)
|
189
238
|
{
|
190
239
|
uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
|
191
240
|
return fnv1a_64_iter(h, str);
|
192
241
|
}
|
193
242
|
|
243
|
+
/*
|
244
|
+
* Ruby's revision may be Integer or String. CRuby 2.7 or later uses
|
245
|
+
* Git commit ID as revision. It's String.
|
246
|
+
*/
|
247
|
+
static uint32_t
|
248
|
+
get_ruby_revision(void)
|
249
|
+
{
|
250
|
+
VALUE ruby_revision;
|
251
|
+
|
252
|
+
ruby_revision = rb_const_get(rb_cObject, rb_intern("RUBY_REVISION"));
|
253
|
+
if (RB_TYPE_P(ruby_revision, RUBY_T_FIXNUM)) {
|
254
|
+
return FIX2INT(ruby_revision);
|
255
|
+
} else {
|
256
|
+
uint64_t hash;
|
257
|
+
|
258
|
+
hash = fnv1a_64(ruby_revision);
|
259
|
+
return (uint32_t)(hash >> 32);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
194
263
|
/*
|
195
264
|
* When ruby's version doesn't change, but it's recompiled on a different OS
|
196
265
|
* (or OS version), we need to invalidate the cache.
|
@@ -206,16 +275,19 @@ get_ruby_platform(void)
|
|
206
275
|
VALUE ruby_platform;
|
207
276
|
|
208
277
|
ruby_platform = rb_const_get(rb_cObject, rb_intern("RUBY_PLATFORM"));
|
209
|
-
hash = fnv1a_64(
|
278
|
+
hash = fnv1a_64(ruby_platform);
|
210
279
|
|
211
280
|
#ifdef _WIN32
|
212
281
|
return (uint32_t)(hash >> 32) ^ (uint32_t)GetVersion();
|
282
|
+
#elif defined(__GLIBC__)
|
283
|
+
hash = fnv1a_64_iter_cstr(hash, gnu_get_libc_version());
|
284
|
+
return (uint32_t)(hash >> 32);
|
213
285
|
#else
|
214
286
|
struct utsname utsname;
|
215
287
|
|
216
288
|
/* Not worth crashing if this fails; lose extra cache invalidation potential */
|
217
289
|
if (uname(&utsname) >= 0) {
|
218
|
-
hash =
|
290
|
+
hash = fnv1a_64_iter_cstr(hash, utsname.version);
|
219
291
|
}
|
220
292
|
|
221
293
|
return (uint32_t)(hash >> 32);
|
@@ -230,14 +302,13 @@ get_ruby_platform(void)
|
|
230
302
|
* The path will look something like: <cachedir>/12/34567890abcdef
|
231
303
|
*/
|
232
304
|
static void
|
233
|
-
bs_cache_path(const char * cachedir, const
|
305
|
+
bs_cache_path(const char * cachedir, const VALUE path, char (* cache_path)[MAX_CACHEPATH_SIZE])
|
234
306
|
{
|
235
307
|
uint64_t hash = fnv1a_64(path);
|
236
|
-
|
237
308
|
uint8_t first_byte = (hash >> (64 - 8));
|
238
309
|
uint64_t remainder = hash & 0x00ffffffffffffff;
|
239
310
|
|
240
|
-
sprintf(*cache_path, "%s/%
|
311
|
+
sprintf(*cache_path, "%s/%02"PRIx8"/%014"PRIx64, cachedir, first_byte, remainder);
|
241
312
|
}
|
242
313
|
|
243
314
|
/*
|
@@ -267,7 +338,7 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
|
|
267
338
|
* conversions on the ruby VALUE arguments before passing them along.
|
268
339
|
*/
|
269
340
|
static VALUE
|
270
|
-
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
341
|
+
bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args)
|
271
342
|
{
|
272
343
|
FilePathValue(path_v);
|
273
344
|
|
@@ -282,27 +353,51 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
|
282
353
|
char * path = RSTRING_PTR(path_v);
|
283
354
|
char cache_path[MAX_CACHEPATH_SIZE];
|
284
355
|
|
285
|
-
|
286
|
-
|
287
|
-
bs_cache_path(cachedir, path, &tmp);
|
288
|
-
}
|
356
|
+
/* generate cache path to cache_path */
|
357
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
289
358
|
|
290
|
-
return bs_fetch(path, path_v, cache_path, handler);
|
359
|
+
return bs_fetch(path, path_v, cache_path, handler, args);
|
291
360
|
}
|
292
361
|
|
362
|
+
/*
|
363
|
+
* Entrypoint for Bootsnap::CompileCache::Native.precompile.
|
364
|
+
* Similar to fetch, but it only generate the cache if missing
|
365
|
+
* and doesn't return the content.
|
366
|
+
*/
|
367
|
+
static VALUE
|
368
|
+
bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
|
369
|
+
{
|
370
|
+
FilePathValue(path_v);
|
371
|
+
|
372
|
+
Check_Type(cachedir_v, T_STRING);
|
373
|
+
Check_Type(path_v, T_STRING);
|
374
|
+
|
375
|
+
if (RSTRING_LEN(cachedir_v) > MAX_CACHEDIR_SIZE) {
|
376
|
+
rb_raise(rb_eArgError, "cachedir too long");
|
377
|
+
}
|
378
|
+
|
379
|
+
char * cachedir = RSTRING_PTR(cachedir_v);
|
380
|
+
char * path = RSTRING_PTR(path_v);
|
381
|
+
char cache_path[MAX_CACHEPATH_SIZE];
|
382
|
+
|
383
|
+
/* generate cache path to cache_path */
|
384
|
+
bs_cache_path(cachedir, path_v, &cache_path);
|
385
|
+
|
386
|
+
return bs_precompile(path, path_v, cache_path, handler);
|
387
|
+
}
|
293
388
|
/*
|
294
389
|
* Open the file we want to load/cache and generate a cache key for it if it
|
295
390
|
* was loaded.
|
296
391
|
*/
|
297
392
|
static int
|
298
|
-
open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenance)
|
393
|
+
open_current_file(char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
299
394
|
{
|
300
395
|
struct stat statbuf;
|
301
396
|
int fd;
|
302
397
|
|
303
398
|
fd = open(path, O_RDONLY);
|
304
399
|
if (fd < 0) {
|
305
|
-
*errno_provenance =
|
400
|
+
*errno_provenance = "bs_fetch:open_current_file:open";
|
306
401
|
return fd;
|
307
402
|
}
|
308
403
|
#ifdef _WIN32
|
@@ -310,7 +405,7 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
310
405
|
#endif
|
311
406
|
|
312
407
|
if (fstat(fd, &statbuf) < 0) {
|
313
|
-
*errno_provenance =
|
408
|
+
*errno_provenance = "bs_fetch:open_current_file:fstat";
|
314
409
|
close(fd);
|
315
410
|
return -1;
|
316
411
|
}
|
@@ -326,7 +421,9 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
326
421
|
}
|
327
422
|
|
328
423
|
#define ERROR_WITH_ERRNO -1
|
329
|
-
#define
|
424
|
+
#define CACHE_MISS -2
|
425
|
+
#define CACHE_STALE -3
|
426
|
+
#define CACHE_UNCOMPILABLE -4
|
330
427
|
|
331
428
|
/*
|
332
429
|
* Read the cache key from the given fd, which must have position 0 (e.g.
|
@@ -334,15 +431,16 @@ open_current_file(char * path, struct bs_cache_key * key, char ** errno_provenan
|
|
334
431
|
*
|
335
432
|
* Possible return values:
|
336
433
|
* - 0 (OK, key was loaded)
|
337
|
-
* - CACHE_MISSING_OR_INVALID (-2)
|
338
434
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
435
|
+
* - CACHE_MISS (-2)
|
436
|
+
* - CACHE_STALE (-3)
|
339
437
|
*/
|
340
438
|
static int
|
341
439
|
bs_read_key(int fd, struct bs_cache_key * key)
|
342
440
|
{
|
343
441
|
ssize_t nread = read(fd, key, KEY_SIZE);
|
344
442
|
if (nread < 0) return ERROR_WITH_ERRNO;
|
345
|
-
if (nread < KEY_SIZE) return
|
443
|
+
if (nread < KEY_SIZE) return CACHE_STALE;
|
346
444
|
return 0;
|
347
445
|
}
|
348
446
|
|
@@ -352,19 +450,19 @@ bs_read_key(int fd, struct bs_cache_key * key)
|
|
352
450
|
*
|
353
451
|
* Possible return values:
|
354
452
|
* - 0 (OK, key was loaded)
|
355
|
-
* -
|
453
|
+
* - CACHE_MISS (-2)
|
454
|
+
* - CACHE_STALE (-3)
|
356
455
|
* - ERROR_WITH_ERRNO (-1, errno is set)
|
357
456
|
*/
|
358
457
|
static int
|
359
|
-
open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_provenance)
|
458
|
+
open_cache_file(const char * path, struct bs_cache_key * key, const char ** errno_provenance)
|
360
459
|
{
|
361
460
|
int fd, res;
|
362
461
|
|
363
462
|
fd = open(path, O_RDONLY);
|
364
463
|
if (fd < 0) {
|
365
|
-
*errno_provenance =
|
366
|
-
|
367
|
-
return ERROR_WITH_ERRNO;
|
464
|
+
*errno_provenance = "bs_fetch:open_cache_file:open";
|
465
|
+
return CACHE_MISS;
|
368
466
|
}
|
369
467
|
#ifdef _WIN32
|
370
468
|
setmode(fd, O_BINARY);
|
@@ -372,7 +470,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
372
470
|
|
373
471
|
res = bs_read_key(fd, key);
|
374
472
|
if (res < 0) {
|
375
|
-
*errno_provenance =
|
473
|
+
*errno_provenance = "bs_fetch:open_cache_file:read";
|
376
474
|
close(fd);
|
377
475
|
return res;
|
378
476
|
}
|
@@ -396,7 +494,7 @@ open_cache_file(const char * path, struct bs_cache_key * key, char ** errno_prov
|
|
396
494
|
* or exception, will be the final data returnable to the user.
|
397
495
|
*/
|
398
496
|
static int
|
399
|
-
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag, char ** errno_provenance)
|
497
|
+
fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE args, VALUE * output_data, int * exception_tag, const char ** errno_provenance)
|
400
498
|
{
|
401
499
|
char * data = NULL;
|
402
500
|
ssize_t nread;
|
@@ -405,26 +503,30 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
|
|
405
503
|
VALUE storage_data;
|
406
504
|
|
407
505
|
if (data_size > 100000000000) {
|
408
|
-
*errno_provenance =
|
506
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:datasize";
|
409
507
|
errno = EINVAL; /* because wtf? */
|
410
|
-
ret =
|
508
|
+
ret = ERROR_WITH_ERRNO;
|
411
509
|
goto done;
|
412
510
|
}
|
413
511
|
data = ALLOC_N(char, data_size);
|
414
512
|
nread = read(fd, data, data_size);
|
415
513
|
if (nread < 0) {
|
416
|
-
*errno_provenance =
|
417
|
-
ret =
|
514
|
+
*errno_provenance = "bs_fetch:fetch_cached_data:read";
|
515
|
+
ret = ERROR_WITH_ERRNO;
|
418
516
|
goto done;
|
419
517
|
}
|
420
518
|
if (nread != data_size) {
|
421
|
-
ret =
|
519
|
+
ret = CACHE_STALE;
|
422
520
|
goto done;
|
423
521
|
}
|
424
522
|
|
425
|
-
storage_data =
|
523
|
+
storage_data = rb_str_new(data, data_size);
|
426
524
|
|
427
|
-
*exception_tag = bs_storage_to_output(handler, storage_data, output_data);
|
525
|
+
*exception_tag = bs_storage_to_output(handler, args, storage_data, output_data);
|
526
|
+
if (*output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
527
|
+
ret = CACHE_UNCOMPILABLE;
|
528
|
+
goto done;
|
529
|
+
}
|
428
530
|
ret = 0;
|
429
531
|
done:
|
430
532
|
if (data != NULL) xfree(data);
|
@@ -465,30 +567,36 @@ mkpath(char * file_path, mode_t mode)
|
|
465
567
|
* path.
|
466
568
|
*/
|
467
569
|
static int
|
468
|
-
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char ** errno_provenance)
|
570
|
+
atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, const char ** errno_provenance)
|
469
571
|
{
|
470
572
|
char template[MAX_CACHEPATH_SIZE + 20];
|
471
|
-
char * dest;
|
472
573
|
char * tmp_path;
|
473
|
-
int fd, ret;
|
574
|
+
int fd, ret, attempt;
|
474
575
|
ssize_t nwrite;
|
475
576
|
|
476
|
-
|
477
|
-
|
577
|
+
for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) {
|
578
|
+
tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE);
|
579
|
+
strcat(tmp_path, ".tmp.XXXXXX");
|
478
580
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
}
|
486
|
-
fd = open(tmp_path, O_WRONLY | O_CREAT, 0664);
|
487
|
-
if (fd < 0) {
|
488
|
-
*errno_provenance = (char *)"bs_fetch:atomic_write_cache_file:open";
|
581
|
+
// mkstemp modifies the template to be the actual created path
|
582
|
+
fd = mkstemp(tmp_path);
|
583
|
+
if (fd > 0) break;
|
584
|
+
|
585
|
+
if (attempt == 0 && mkpath(tmp_path, 0775) < 0) {
|
586
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath";
|
489
587
|
return -1;
|
490
588
|
}
|
491
589
|
}
|
590
|
+
if (fd < 0) {
|
591
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp";
|
592
|
+
return -1;
|
593
|
+
}
|
594
|
+
|
595
|
+
if (chmod(tmp_path, 0644) < 0) {
|
596
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
597
|
+
return -1;
|
598
|
+
}
|
599
|
+
|
492
600
|
#ifdef _WIN32
|
493
601
|
setmode(fd, O_BINARY);
|
494
602
|
#endif
|
@@ -496,11 +604,11 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
496
604
|
key->data_size = RSTRING_LEN(data);
|
497
605
|
nwrite = write(fd, key, KEY_SIZE);
|
498
606
|
if (nwrite < 0) {
|
499
|
-
*errno_provenance =
|
607
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:write";
|
500
608
|
return -1;
|
501
609
|
}
|
502
610
|
if (nwrite != KEY_SIZE) {
|
503
|
-
*errno_provenance =
|
611
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:keysize";
|
504
612
|
errno = EIO; /* Lies but whatever */
|
505
613
|
return -1;
|
506
614
|
}
|
@@ -508,7 +616,7 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
508
616
|
nwrite = write(fd, RSTRING_PTR(data), RSTRING_LEN(data));
|
509
617
|
if (nwrite < 0) return -1;
|
510
618
|
if (nwrite != RSTRING_LEN(data)) {
|
511
|
-
*errno_provenance =
|
619
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:writelength";
|
512
620
|
errno = EIO; /* Lies but whatever */
|
513
621
|
return -1;
|
514
622
|
}
|
@@ -516,7 +624,12 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
516
624
|
close(fd);
|
517
625
|
ret = rename(tmp_path, path);
|
518
626
|
if (ret < 0) {
|
519
|
-
*errno_provenance =
|
627
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:rename";
|
628
|
+
return -1;
|
629
|
+
}
|
630
|
+
ret = chmod(path, 0664 & ~current_umask);
|
631
|
+
if (ret < 0) {
|
632
|
+
*errno_provenance = "bs_fetch:atomic_write_cache_file:chmod";
|
520
633
|
}
|
521
634
|
return ret;
|
522
635
|
}
|
@@ -525,13 +638,13 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, char
|
|
525
638
|
/* Read contents from an fd, whose contents are asserted to be +size+ bytes
|
526
639
|
* long, into a buffer */
|
527
640
|
static ssize_t
|
528
|
-
bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance)
|
641
|
+
bs_read_contents(int fd, size_t size, char ** contents, const char ** errno_provenance)
|
529
642
|
{
|
530
643
|
ssize_t nread;
|
531
644
|
*contents = ALLOC_N(char, size);
|
532
645
|
nread = read(fd, *contents, size);
|
533
646
|
if (nread < 0) {
|
534
|
-
*errno_provenance =
|
647
|
+
*errno_provenance = "bs_fetch:bs_read_contents:read";
|
535
648
|
}
|
536
649
|
return nread;
|
537
650
|
}
|
@@ -581,13 +694,13 @@ bs_read_contents(int fd, size_t size, char ** contents, char ** errno_provenance
|
|
581
694
|
* - Return storage_to_output(storage_data)
|
582
695
|
*/
|
583
696
|
static VALUE
|
584
|
-
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
697
|
+
bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler, VALUE args)
|
585
698
|
{
|
586
699
|
struct bs_cache_key cached_key, current_key;
|
587
700
|
char * contents = NULL;
|
588
701
|
int cache_fd = -1, current_fd = -1;
|
589
702
|
int res, valid_cache = 0, exception_tag = 0;
|
590
|
-
char * errno_provenance = NULL;
|
703
|
+
const char * errno_provenance = NULL;
|
591
704
|
|
592
705
|
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
593
706
|
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
@@ -601,26 +714,42 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
601
714
|
|
602
715
|
/* Open the cache key if it exists, and read its cache key in */
|
603
716
|
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
604
|
-
if (cache_fd ==
|
717
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
605
718
|
/* This is ok: valid_cache remains false, we re-populate it. */
|
719
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
720
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, cache_fd == CACHE_MISS ? sym_miss : sym_stale, path_v);
|
721
|
+
}
|
606
722
|
} else if (cache_fd < 0) {
|
607
723
|
goto fail_errno;
|
608
724
|
} else {
|
609
725
|
/* True if the cache existed and no invalidating changes have occurred since
|
610
726
|
* it was generated. */
|
611
727
|
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
728
|
+
if (RB_UNLIKELY(instrumentation_enabled)) {
|
729
|
+
if (!valid_cache) {
|
730
|
+
rb_funcall(rb_mBootsnap, instrumentation_method, 2, sym_stale, path_v);
|
731
|
+
}
|
732
|
+
}
|
612
733
|
}
|
613
734
|
|
614
735
|
if (valid_cache) {
|
615
736
|
/* Fetch the cache data and return it if we're able to load it successfully */
|
616
737
|
res = fetch_cached_data(
|
617
|
-
cache_fd, (ssize_t)cached_key.data_size, handler,
|
738
|
+
cache_fd, (ssize_t)cached_key.data_size, handler, args,
|
618
739
|
&output_data, &exception_tag, &errno_provenance
|
619
740
|
);
|
620
|
-
if (exception_tag != 0)
|
621
|
-
else if (res ==
|
622
|
-
|
623
|
-
|
741
|
+
if (exception_tag != 0) goto raise;
|
742
|
+
else if (res == CACHE_UNCOMPILABLE) {
|
743
|
+
/* If fetch_cached_data returned `Uncompilable` we fallback to `input_to_output`
|
744
|
+
This happens if we have say, an unsafe YAML cache, but try to load it in safe mode */
|
745
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
746
|
+
input_data = rb_str_new(contents, current_key.size);
|
747
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
748
|
+
if (exception_tag != 0) goto raise;
|
749
|
+
goto succeed;
|
750
|
+
} else if (res == CACHE_MISS || res == CACHE_STALE) valid_cache = 0;
|
751
|
+
else if (res == ERROR_WITH_ERRNO) goto fail_errno;
|
752
|
+
else if (!NIL_P(output_data)) goto succeed; /* fast-path, goal */
|
624
753
|
}
|
625
754
|
close(cache_fd);
|
626
755
|
cache_fd = -1;
|
@@ -628,37 +757,47 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
|
628
757
|
|
629
758
|
/* Read the contents of the source file into a buffer */
|
630
759
|
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail_errno;
|
631
|
-
input_data =
|
760
|
+
input_data = rb_str_new(contents, current_key.size);
|
632
761
|
|
633
762
|
/* Try to compile the input_data using input_to_storage(input_data) */
|
634
|
-
exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
|
763
|
+
exception_tag = bs_input_to_storage(handler, args, input_data, path_v, &storage_data);
|
635
764
|
if (exception_tag != 0) goto raise;
|
636
765
|
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
637
766
|
* to cache anything; just return input_to_output(input_data) */
|
638
|
-
if (storage_data ==
|
639
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
767
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
768
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
640
769
|
if (exception_tag != 0) goto raise;
|
641
770
|
goto succeed;
|
642
771
|
}
|
643
772
|
/* If storage_data isn't a string, we can't cache it */
|
644
773
|
if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
|
645
774
|
|
646
|
-
/*
|
647
|
-
|
648
|
-
|
775
|
+
/* Attempt to write the cache key and storage_data to the cache directory.
|
776
|
+
* We do however ignore any failures to persist the cache, as it's better
|
777
|
+
* to move along, than to interrupt the process.
|
778
|
+
*/
|
779
|
+
atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
649
780
|
|
650
781
|
/* Having written the cache, now convert storage_data to output_data */
|
651
|
-
exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
|
782
|
+
exception_tag = bs_storage_to_output(handler, args, storage_data, &output_data);
|
652
783
|
if (exception_tag != 0) goto raise;
|
653
784
|
|
654
|
-
|
655
|
-
|
656
|
-
|
785
|
+
if (output_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
786
|
+
/* If storage_to_output returned `Uncompilable` we fallback to `input_to_output` */
|
787
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
788
|
+
if (exception_tag != 0) goto raise;
|
789
|
+
} else if (NIL_P(output_data)) {
|
790
|
+
/* If output_data is nil, delete the cache entry and generate the output
|
791
|
+
* using input_to_output */
|
657
792
|
if (unlink(cache_path) < 0) {
|
658
|
-
|
659
|
-
|
793
|
+
/* If the cache was already deleted, it might be that another process did it before us.
|
794
|
+
* No point raising an error */
|
795
|
+
if (errno != ENOENT) {
|
796
|
+
errno_provenance = "bs_fetch:unlink";
|
797
|
+
goto fail_errno;
|
798
|
+
}
|
660
799
|
}
|
661
|
-
bs_input_to_output(handler, input_data, &output_data, &exception_tag);
|
800
|
+
bs_input_to_output(handler, args, input_data, &output_data, &exception_tag);
|
662
801
|
if (exception_tag != 0) goto raise;
|
663
802
|
}
|
664
803
|
|
@@ -689,6 +828,79 @@ invalid_type_storage_data:
|
|
689
828
|
#undef CLEANUP
|
690
829
|
}
|
691
830
|
|
831
|
+
static VALUE
|
832
|
+
bs_precompile(char * path, VALUE path_v, char * cache_path, VALUE handler)
|
833
|
+
{
|
834
|
+
struct bs_cache_key cached_key, current_key;
|
835
|
+
char * contents = NULL;
|
836
|
+
int cache_fd = -1, current_fd = -1;
|
837
|
+
int res, valid_cache = 0, exception_tag = 0;
|
838
|
+
const char * errno_provenance = NULL;
|
839
|
+
|
840
|
+
VALUE input_data; /* data read from source file, e.g. YAML or ruby source */
|
841
|
+
VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
|
842
|
+
|
843
|
+
/* Open the source file and generate a cache key for it */
|
844
|
+
current_fd = open_current_file(path, ¤t_key, &errno_provenance);
|
845
|
+
if (current_fd < 0) goto fail;
|
846
|
+
|
847
|
+
/* Open the cache key if it exists, and read its cache key in */
|
848
|
+
cache_fd = open_cache_file(cache_path, &cached_key, &errno_provenance);
|
849
|
+
if (cache_fd == CACHE_MISS || cache_fd == CACHE_STALE) {
|
850
|
+
/* This is ok: valid_cache remains false, we re-populate it. */
|
851
|
+
} else if (cache_fd < 0) {
|
852
|
+
goto fail;
|
853
|
+
} else {
|
854
|
+
/* True if the cache existed and no invalidating changes have occurred since
|
855
|
+
* it was generated. */
|
856
|
+
valid_cache = cache_key_equal(¤t_key, &cached_key);
|
857
|
+
}
|
858
|
+
|
859
|
+
if (valid_cache) {
|
860
|
+
goto succeed;
|
861
|
+
}
|
862
|
+
|
863
|
+
close(cache_fd);
|
864
|
+
cache_fd = -1;
|
865
|
+
/* Cache is stale, invalid, or missing. Regenerate and write it out. */
|
866
|
+
|
867
|
+
/* Read the contents of the source file into a buffer */
|
868
|
+
if (bs_read_contents(current_fd, current_key.size, &contents, &errno_provenance) < 0) goto fail;
|
869
|
+
input_data = rb_str_new(contents, current_key.size);
|
870
|
+
|
871
|
+
/* Try to compile the input_data using input_to_storage(input_data) */
|
872
|
+
exception_tag = bs_input_to_storage(handler, Qnil, input_data, path_v, &storage_data);
|
873
|
+
if (exception_tag != 0) goto fail;
|
874
|
+
|
875
|
+
/* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
|
876
|
+
* to cache anything; just return false */
|
877
|
+
if (storage_data == rb_cBootsnap_CompileCache_UNCOMPILABLE) {
|
878
|
+
goto fail;
|
879
|
+
}
|
880
|
+
/* If storage_data isn't a string, we can't cache it */
|
881
|
+
if (!RB_TYPE_P(storage_data, T_STRING)) goto fail;
|
882
|
+
|
883
|
+
/* Write the cache key and storage_data to the cache directory */
|
884
|
+
res = atomic_write_cache_file(cache_path, ¤t_key, storage_data, &errno_provenance);
|
885
|
+
if (res < 0) goto fail;
|
886
|
+
|
887
|
+
goto succeed;
|
888
|
+
|
889
|
+
#define CLEANUP \
|
890
|
+
if (contents != NULL) xfree(contents); \
|
891
|
+
if (current_fd >= 0) close(current_fd); \
|
892
|
+
if (cache_fd >= 0) close(cache_fd);
|
893
|
+
|
894
|
+
succeed:
|
895
|
+
CLEANUP;
|
896
|
+
return Qtrue;
|
897
|
+
fail:
|
898
|
+
CLEANUP;
|
899
|
+
return Qfalse;
|
900
|
+
#undef CLEANUP
|
901
|
+
}
|
902
|
+
|
903
|
+
|
692
904
|
/*****************************************************************************/
|
693
905
|
/********************* Handler Wrappers **************************************/
|
694
906
|
/*****************************************************************************
|
@@ -708,11 +920,13 @@ invalid_type_storage_data:
|
|
708
920
|
|
709
921
|
struct s2o_data {
|
710
922
|
VALUE handler;
|
923
|
+
VALUE args;
|
711
924
|
VALUE storage_data;
|
712
925
|
};
|
713
926
|
|
714
927
|
struct i2o_data {
|
715
928
|
VALUE handler;
|
929
|
+
VALUE args;
|
716
930
|
VALUE input_data;
|
717
931
|
};
|
718
932
|
|
@@ -723,29 +937,31 @@ struct i2s_data {
|
|
723
937
|
};
|
724
938
|
|
725
939
|
static VALUE
|
726
|
-
|
940
|
+
try_storage_to_output(VALUE arg)
|
727
941
|
{
|
728
942
|
struct s2o_data * data = (struct s2o_data *)arg;
|
729
|
-
return rb_funcall(data->handler, rb_intern("storage_to_output"),
|
943
|
+
return rb_funcall(data->handler, rb_intern("storage_to_output"), 2, data->storage_data, data->args);
|
730
944
|
}
|
731
945
|
|
732
946
|
static int
|
733
|
-
bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data)
|
947
|
+
bs_storage_to_output(VALUE handler, VALUE args, VALUE storage_data, VALUE * output_data)
|
734
948
|
{
|
735
949
|
int state;
|
736
950
|
struct s2o_data s2o_data = {
|
737
951
|
.handler = handler,
|
952
|
+
.args = args,
|
738
953
|
.storage_data = storage_data,
|
739
954
|
};
|
740
|
-
*output_data = rb_protect(
|
955
|
+
*output_data = rb_protect(try_storage_to_output, (VALUE)&s2o_data, &state);
|
741
956
|
return state;
|
742
957
|
}
|
743
958
|
|
744
959
|
static void
|
745
|
-
bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag)
|
960
|
+
bs_input_to_output(VALUE handler, VALUE args, VALUE input_data, VALUE * output_data, int * exception_tag)
|
746
961
|
{
|
747
962
|
struct i2o_data i2o_data = {
|
748
963
|
.handler = handler,
|
964
|
+
.args = args,
|
749
965
|
.input_data = input_data,
|
750
966
|
};
|
751
967
|
*output_data = rb_protect(prot_input_to_output, (VALUE)&i2o_data, exception_tag);
|
@@ -755,7 +971,7 @@ static VALUE
|
|
755
971
|
prot_input_to_output(VALUE arg)
|
756
972
|
{
|
757
973
|
struct i2o_data * data = (struct i2o_data *)arg;
|
758
|
-
return rb_funcall(data->handler, rb_intern("input_to_output"),
|
974
|
+
return rb_funcall(data->handler, rb_intern("input_to_output"), 2, data->input_data, data->args);
|
759
975
|
}
|
760
976
|
|
761
977
|
static VALUE
|
@@ -765,24 +981,8 @@ try_input_to_storage(VALUE arg)
|
|
765
981
|
return rb_funcall(data->handler, rb_intern("input_to_storage"), 2, data->input_data, data->pathval);
|
766
982
|
}
|
767
983
|
|
768
|
-
static VALUE
|
769
|
-
rescue_input_to_storage(VALUE arg)
|
770
|
-
{
|
771
|
-
return uncompilable;
|
772
|
-
}
|
773
|
-
|
774
|
-
static VALUE
|
775
|
-
prot_input_to_storage(VALUE arg)
|
776
|
-
{
|
777
|
-
struct i2s_data * data = (struct i2s_data *)arg;
|
778
|
-
return rb_rescue2(
|
779
|
-
try_input_to_storage, (VALUE)data,
|
780
|
-
rescue_input_to_storage, Qnil,
|
781
|
-
rb_eBootsnap_CompileCache_Uncompilable, 0);
|
782
|
-
}
|
783
|
-
|
784
984
|
static int
|
785
|
-
bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
985
|
+
bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
|
786
986
|
{
|
787
987
|
int state;
|
788
988
|
struct i2s_data i2s_data = {
|
@@ -790,6 +990,6 @@ bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * stor
|
|
790
990
|
.input_data = input_data,
|
791
991
|
.pathval = pathval,
|
792
992
|
};
|
793
|
-
*storage_data = rb_protect(
|
993
|
+
*storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
|
794
994
|
return state;
|
795
995
|
}
|