bootsnap 0.3.0.pre → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '082f8dd8be53486b79ff5248ad426cc28263778b'
4
- data.tar.gz: 5bc6a79f2054aa8b869704cdb9aeab685eabb400
3
+ metadata.gz: 3725f53309883dce83894e47eedbb4d8322eabd5
4
+ data.tar.gz: 81e14b1d48fc4431f6ac9bb2729cfb4a69aa0f5c
5
5
  SHA512:
6
- metadata.gz: ad062964bbc28d997901a1f6016289a4c6671071f9e1c4640820fe9545427136e1268f6588b350e7d058d85641604b0004225a744792fcce9b24bfcb9e5a3172
7
- data.tar.gz: 33c77f2e085ba965e1a9fccb65b5afd42a4db0485dbfca7aa5d3e3920497ff41d0a3ca0ca4b6706df55814d29f015056e6d862568fe8b48ad5194f6eb80e26dc
6
+ metadata.gz: 5a2b45cb55ceffdb5751561b3a35c476ea29bcd3d3a2a68a46521240d35cc1e61cf9ac90265db751b09967655969c7a685725878b5f80a61fd38a0efd7024458
7
+ data.tar.gz: 7f7975023cb1856d5ccada63e8be2e3ab94a751d4b80121821f2b01df6cbb94f5172041020ebb9300b63760da3fc2776e3b9b56160b95b4b0c07f82fbce07860
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.3.0
2
+
3
+ * Migrate CompileCache from xattr as a cache backend to a cache directory
4
+ * Adds support for Linux and FreeBSD
5
+
1
6
  # 0.2.15
2
7
 
3
8
  * Support more versions of ActiveSupport (`depend_on`'s signature varies; don't reiterate it)
@@ -1,3 +1,16 @@
1
+ /*
2
+ * Suggested reading order:
3
+ * 1. Skim Init_bootsnap
4
+ * 2. Skim bs_fetch
5
+ * 3. The rest of everything
6
+ *
7
+ * Init_bootsnap sets up the ruby objects and binds bs_fetch to
8
+ * Bootsnap::CompileCache::Native.fetch.
9
+ *
10
+ * bs_fetch is the ultimate caller for for just about every other function in
11
+ * here.
12
+ */
13
+
1
14
  #include "bootsnap.h"
2
15
  #include "ruby.h"
3
16
  #include <stdint.h>
@@ -5,18 +18,29 @@
5
18
  #include <errno.h>
6
19
  #include <fcntl.h>
7
20
  #include <sys/stat.h>
21
+ #include <sys/utsname.h>
8
22
 
9
- #ifdef __linux__
10
- #include <gnu/libc-version.h>
11
- #else
12
- #include <sys/sysctl.h>
13
- #endif
14
-
23
+ /* 1000 is an arbitrary limit; FNV64 plus some slashes brings the cap down to
24
+ * 981 for the cache dir */
15
25
  #define MAX_CACHEPATH_SIZE 1000
16
26
  #define MAX_CACHEDIR_SIZE 981
17
27
 
18
28
  #define KEY_SIZE 64
19
29
 
30
+ /*
31
+ * An instance of this key is written as the first 64 bytes of each cache file.
32
+ * The mtime and size members track whether the file contents have changed, and
33
+ * the version, os_version, compile_option, and ruby_revision members track
34
+ * changes to the environment that could invalidate compile results without
35
+ * file contents having changed. The data_size member is not truly part of the
36
+ * "key". Really, this could be called a "header" with the first six members
37
+ * being an embedded "key" struct and an additional data_size member.
38
+ *
39
+ * The data_size indicates the remaining number of bytes in the cache file
40
+ * after the header (the size of the cached artifact).
41
+ *
42
+ * After data_size, the struct is padded to 64 bytes.
43
+ */
20
44
  struct bs_cache_key {
21
45
  uint32_t version;
22
46
  uint32_t os_version;
@@ -28,31 +52,40 @@ struct bs_cache_key {
28
52
  uint8_t pad[24];
29
53
  } __attribute__((packed));
30
54
 
55
+ /*
56
+ * If the struct padding isn't correct to pad the key to 64 bytes, refuse to
57
+ * compile.
58
+ */
31
59
  #define STATIC_ASSERT(X) STATIC_ASSERT2(X,__LINE__)
32
60
  #define STATIC_ASSERT2(X,L) STATIC_ASSERT3(X,L)
33
61
  #define STATIC_ASSERT3(X,L) STATIC_ASSERT_MSG(X,at_line_##L)
34
62
  #define STATIC_ASSERT_MSG(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
35
-
36
63
  STATIC_ASSERT(sizeof(struct bs_cache_key) == KEY_SIZE);
37
64
 
65
+ /* Effectively a schema version. Bumping invalidates all previous caches */
38
66
  static const uint32_t current_version = 2;
39
67
 
68
+ /* Derived from kernel or libc version; intended to roughly correspond to when
69
+ * ABIs have changed, requiring recompilation of native gems. */
40
70
  static uint32_t current_os_version;
71
+ /* Invalidates cache when switching ruby versions */
41
72
  static uint32_t current_ruby_revision;
73
+ /* Invalidates cache when RubyVM::InstructionSequence.compile_option changes */
42
74
  static uint32_t current_compile_option_crc32 = 0;
43
75
 
76
+ /* Bootsnap::CompileCache::{Native, Uncompilable} */
44
77
  static VALUE rb_mBootsnap;
45
78
  static VALUE rb_mBootsnap_CompileCache;
46
79
  static VALUE rb_mBootsnap_CompileCache_Native;
47
80
  static VALUE rb_eBootsnap_CompileCache_Uncompilable;
48
81
  static ID uncompilable;
49
82
 
50
- /* Real API */
83
+ /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
51
84
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
52
85
  static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
53
86
 
54
87
  /* Helpers */
55
- static uint64_t fnv_64a(const char *str);
88
+ static uint64_t fnv1a_64(const char *str);
56
89
  static void bs_cache_path(const char * cachedir, const char * path, char ** cache_path);
57
90
  static int bs_read_key(int fd, struct bs_cache_key * key);
58
91
  static int cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2);
@@ -62,29 +95,27 @@ static int fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * o
62
95
  static VALUE prot_exception_for_errno(VALUE err);
63
96
  static uint32_t get_os_version(void);
64
97
 
98
+ /*
99
+ * Helper functions to call ruby methods on handler object without crashing on
100
+ * exception.
101
+ */
65
102
  static int bs_storage_to_output(VALUE handler, VALUE storage_data, VALUE * output_data);
66
103
  static VALUE prot_storage_to_output(VALUE arg);
67
104
  static VALUE prot_input_to_output(VALUE arg);
68
105
  static void bs_input_to_output(VALUE handler, VALUE input_data, VALUE * output_data, int * exception_tag);
69
106
  static VALUE prot_input_to_storage(VALUE arg);
70
107
  static int bs_input_to_storage(VALUE handler, VALUE input_data, VALUE pathval, VALUE * storage_data);
108
+ struct s2o_data;
109
+ struct i2o_data;
110
+ struct i2s_data;
71
111
 
72
- struct s2o_data {
73
- VALUE handler;
74
- VALUE storage_data;
75
- };
76
-
77
- struct i2o_data {
78
- VALUE handler;
79
- VALUE input_data;
80
- };
81
-
82
- struct i2s_data {
83
- VALUE handler;
84
- VALUE input_data;
85
- VALUE pathval;
86
- };
87
-
112
+ /*
113
+ * Ruby C extensions are initialized by calling Init_<extname>.
114
+ *
115
+ * This sets up the module hierarchy and attaches functions as methods.
116
+ *
117
+ * We also populate some semi-static information about the current OS and so on.
118
+ */
88
119
  void
89
120
  Init_bootsnap(void)
90
121
  {
@@ -102,6 +133,10 @@ Init_bootsnap(void)
102
133
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "compile_option_crc32=", bs_compile_option_crc32_set, 1);
103
134
  }
104
135
 
136
+ /*
137
+ * Bootsnap's ruby code registers a hook that notifies us via this function
138
+ * when compile_option changes. These changes invalidate all existing caches.
139
+ */
105
140
  static VALUE
106
141
  bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
107
142
  {
@@ -110,11 +145,21 @@ bs_compile_option_crc32_set(VALUE self, VALUE crc32_v)
110
145
  return Qnil;
111
146
  }
112
147
 
148
+ /*
149
+ * We use FNV1a-64 to derive cache paths. The choice is somewhat arbitrary but
150
+ * it has several nice properties:
151
+ *
152
+ * - Tiny implementation
153
+ * - No external dependency
154
+ * - Solid performance
155
+ * - Solid randomness
156
+ * - 32 bits doesn't feel collision-resistant enough; 64 is nice.
157
+ */
113
158
  static uint64_t
114
- fnv_64a(const char *str)
159
+ fnv1a_64(const char *str)
115
160
  {
116
161
  unsigned char *s = (unsigned char *)str;
117
- uint64_t h = ((uint64_t)0xcbf29ce484222325ULL);
162
+ uint64_t h = (uint64_t)0xcbf29ce484222325ULL;
118
163
 
119
164
  while (*s) {
120
165
  h ^= (uint64_t)*s++;
@@ -124,36 +169,35 @@ fnv_64a(const char *str)
124
169
  return h;
125
170
  }
126
171
 
127
- /* On Darwin and FreeBSD, this is the kernel version, since that tends to vary
128
- * with whole-system upgrades. What we probably care about more is the libc
129
- * version, which is what we explicitly ask for on linux.
130
- * (and KERN_OSRELEASE came back empty for me one one linux box, so...?)
172
+ /*
173
+ * The idea here is that we want a cache key member that changes when the OS
174
+ * changes in such a way as to make existing compiled ISeqs unloadable.
131
175
  */
132
176
  static uint32_t
133
177
  get_os_version(void)
134
178
  {
135
- size_t len;
136
179
  uint64_t hash;
137
- #ifdef __linux__
138
- const char * version;
139
- version = gnu_get_libc_version();
140
- hash = fnv_64a(version);
141
- #else
142
- char * version;
143
- int mib[2] = {CTL_KERN, KERN_OSRELEASE};
144
- if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) return 0;
145
- version = malloc(sizeof(char) * len);
146
- if (sysctl(mib, 2, version, &len, NULL, 0) < 0) return 0;
147
- hash = fnv_64a(version);
148
- free(version);
149
- #endif
180
+ struct utsname utsname;
181
+
182
+ /* Not worth crashing if this fails; lose cache invalidation potential */
183
+ if (uname(&utsname) < 0) return 0;
184
+
185
+ hash = fnv1a_64(utsname.version);
186
+
150
187
  return (uint32_t)(hash >> 32);
151
188
  }
152
189
 
190
+ /*
191
+ * Given a cache root directory and the full path to a file being cached,
192
+ * generate a path under the cache directory at which the cached artifact will
193
+ * be stored.
194
+ *
195
+ * The path will look something like: <cachedir>/12/34567890abcdef
196
+ */
153
197
  static void
154
198
  bs_cache_path(const char * cachedir, const char * path, char ** cache_path)
155
199
  {
156
- uint64_t hash = fnv_64a(path);
200
+ uint64_t hash = fnv1a_64(path);
157
201
 
158
202
  uint8_t first_byte = (hash >> (64 - 8));
159
203
  uint64_t remainder = hash & 0x00ffffffffffffff;
@@ -161,6 +205,14 @@ bs_cache_path(const char * cachedir, const char * path, char ** cache_path)
161
205
  sprintf(*cache_path, "%s/%02x/%014llx", cachedir, first_byte, remainder);
162
206
  }
163
207
 
208
+ /*
209
+ * Test whether a newly-generated cache key based on the file as it exists on
210
+ * disk matches the one that was generated when the file was cached (or really
211
+ * compare any two keys).
212
+ *
213
+ * The data_size member is not compared, as it serves more of a "header"
214
+ * function.
215
+ */
164
216
  static int
165
217
  cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
166
218
  {
@@ -174,6 +226,11 @@ cache_key_equal(struct bs_cache_key * k1, struct bs_cache_key * k2)
174
226
  );
175
227
  }
176
228
 
229
+ /*
230
+ * Entrypoint for Bootsnap::CompileCache::Native.fetch. The real work is done
231
+ * in bs_fetch; this function just performs some basic typechecks and
232
+ * conversions on the ruby VALUE arguments before passing them along.
233
+ */
177
234
  static VALUE
178
235
  bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
179
236
  {
@@ -196,6 +253,10 @@ bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler)
196
253
  return bs_fetch(path, path_v, cache_path, handler);
197
254
  }
198
255
 
256
+ /*
257
+ * Open the file we want to load/cache and generate a cache key for it if it
258
+ * was loaded.
259
+ */
199
260
  static int
200
261
  open_current_file(char * path, struct bs_cache_key * key)
201
262
  {
@@ -224,8 +285,13 @@ open_current_file(char * path, struct bs_cache_key * key)
224
285
  #define CACHE_MISSING_OR_INVALID -2
225
286
 
226
287
  /*
227
- * @return CACHE_MISSING_OR_INVALID
228
- * @return ERROR_WITH_ERRNO(-1) and errno
288
+ * Read the cache key from the given fd, which must have position 0 (e.g.
289
+ * freshly opened file).
290
+ *
291
+ * Possible return values:
292
+ * - 0 (OK, key was loaded)
293
+ * - CACHE_MISSING_OR_INVALID (-2)
294
+ * - ERROR_WITH_ERRNO (-1, errno is set)
229
295
  */
230
296
  static int
231
297
  bs_read_key(int fd, struct bs_cache_key * key)
@@ -237,8 +303,13 @@ bs_read_key(int fd, struct bs_cache_key * key)
237
303
  }
238
304
 
239
305
  /*
240
- * @return CACHE_MISSING_OR_INVALID
241
- * @return ERROR_WITH_ERRNO(-1) and errno
306
+ * Open the cache file at a given path, if it exists, and read its key into the
307
+ * struct.
308
+ *
309
+ * Possible return values:
310
+ * - 0 (OK, key was loaded)
311
+ * - CACHE_MISSING_OR_INVALID (-2)
312
+ * - ERROR_WITH_ERRNO (-1, errno is set)
242
313
  */
243
314
  static int
244
315
  open_cache_file(const char * path, struct bs_cache_key * key)
@@ -260,10 +331,25 @@ open_cache_file(const char * path, struct bs_cache_key * key)
260
331
  return fd;
261
332
  }
262
333
 
334
+ /*
335
+ * The cache file is laid out like:
336
+ * 0...64 : bs_cache_key
337
+ * 64..-1 : cached artifact
338
+ *
339
+ * This function takes a file descriptor whose position is pre-set to 64, and
340
+ * the data_size (corresponding to the remaining number of bytes) listed in the
341
+ * cache header.
342
+ *
343
+ * We load the text from this file into a buffer, and pass it to the ruby-land
344
+ * handler with exception handling via the exception_tag param.
345
+ *
346
+ * Data is returned via the output_data parameter, which, if there's no error
347
+ * or exception, will be the final data returnable to the user.
348
+ */
263
349
  static int
264
350
  fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data, int * exception_tag)
265
351
  {
266
- char * data;
352
+ char * data = NULL;
267
353
  ssize_t nread;
268
354
  int ret;
269
355
 
@@ -271,7 +357,8 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
271
357
 
272
358
  if (data_size > 100000000000) {
273
359
  errno = EINVAL; /* because wtf? */
274
- return -1;
360
+ ret = -1;
361
+ goto done;
275
362
  }
276
363
  data = ALLOC_N(char, data_size);
277
364
  nread = read(fd, data, data_size);
@@ -289,17 +376,14 @@ fetch_cached_data(int fd, ssize_t data_size, VALUE handler, VALUE * output_data,
289
376
  *exception_tag = bs_storage_to_output(handler, storage_data, output_data);
290
377
  ret = 0;
291
378
  done:
292
- xfree(data);
379
+ if (data != NULL) xfree(data);
293
380
  return ret;
294
381
  }
295
382
 
296
- static ssize_t
297
- bs_read_contents(int fd, size_t size, char ** contents)
298
- {
299
- *contents = ALLOC_N(char, size);
300
- return read(fd, *contents, size);
301
- }
302
-
383
+ /*
384
+ * Like mkdir -p, this recursively creates directory parents of a file. e.g.
385
+ * given /a/b/c, creates /a and /a/b.
386
+ */
303
387
  static int
304
388
  mkpath(char * file_path, mode_t mode)
305
389
  {
@@ -320,6 +404,11 @@ mkpath(char * file_path, mode_t mode)
320
404
  return 0;
321
405
  }
322
406
 
407
+ /*
408
+ * Write a cache header/key and a compiled artifact to a given cache path by
409
+ * writing to a tmpfile and then renaming the tmpfile over top of the final
410
+ * path.
411
+ */
323
412
  static int
324
413
  atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
325
414
  {
@@ -359,6 +448,76 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data)
359
448
  return rename(tmp_path, path);
360
449
  }
361
450
 
451
+ /*
452
+ * Given an errno value (converted to a ruby Fixnum), return the corresponding
453
+ * Errno::* constant. If none is found, return StandardError instead.
454
+ */
455
+ static VALUE
456
+ prot_exception_for_errno(VALUE err)
457
+ {
458
+ if (err != INT2FIX(0)) {
459
+ VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
460
+ VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
461
+ VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
462
+ return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
463
+ }
464
+ return rb_eStandardError;
465
+ }
466
+
467
+
468
+ /* Read contents from an fd, whose contents are asserted to be +size+ bytes
469
+ * long, into a buffer */
470
+ static ssize_t
471
+ bs_read_contents(int fd, size_t size, char ** contents)
472
+ {
473
+ *contents = ALLOC_N(char, size);
474
+ return read(fd, *contents, size);
475
+ }
476
+
477
+ /*
478
+ * This is the meat of the extension. bs_fetch is
479
+ * Bootsnap::CompileCache::Native.fetch.
480
+ *
481
+ * There are three "formats" in use here:
482
+ * 1. "input" fomat, which is what we load from the source file;
483
+ * 2. "storage" format, which we write to the cache;
484
+ * 3. "output" format, which is what we return.
485
+ *
486
+ * E.g., For ISeq compilation:
487
+ * input: ruby source, as text
488
+ * storage: binary string (RubyVM::InstructionSequence#to_binary)
489
+ * output: Instance of RubyVM::InstructionSequence
490
+ *
491
+ * And for YAML:
492
+ * input: yaml as text
493
+ * storage: MessagePack or Marshal text
494
+ * output: ruby object, loaded from yaml/messagepack/marshal
495
+ *
496
+ * The handler passed in must support three messages:
497
+ * * storage_to_output(s) -> o
498
+ * * input_to_output(i) -> o
499
+ * * input_to_storage(i) -> s
500
+ * (input_to_storage may raise Bootsnap::CompileCache::Uncompilable, which
501
+ * will prevent caching and cause output to be generated with
502
+ * input_to_output)
503
+ *
504
+ * The semantics of this function are basically:
505
+ *
506
+ * return storage_to_output(cache[path]) if cache[path]
507
+ * storage = input_to_storage(input)
508
+ * cache[path] = storage
509
+ * return storage_to_output(storage)
510
+ *
511
+ * Or expanded a bit:
512
+ *
513
+ * - Check if the cache file exists and is up to date.
514
+ * - If it is, load this data to storage_data.
515
+ * - return storage_to_output(storage_data)
516
+ * - Read the file to input_data
517
+ * - Generate storage_data using input_to_storage(input_data)
518
+ * - Write storage_data data, with a cache key, to the cache file.
519
+ * - Return storage_to_output(storage_data)
520
+ */
362
521
  static VALUE
363
522
  bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
364
523
  {
@@ -371,14 +530,18 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
371
530
  VALUE storage_data; /* compiled data, e.g. msgpack / binary iseq */
372
531
  VALUE output_data; /* return data, e.g. ruby hash or loaded iseq */
373
532
 
374
- VALUE exception;
533
+ VALUE exception; /* ruby exception object to raise instead of returning */
375
534
 
535
+ /* Open the source file and generate a cache key for it */
376
536
  current_fd = open_current_file(path, &current_key);
377
537
  if (current_fd < 0) goto fail_errno;
378
538
 
539
+ /* Open the cache key if it exists, and read its cache key in */
379
540
  cache_fd = open_cache_file(cache_path, &cached_key);
380
541
  if (cache_fd < 0 && cache_fd != CACHE_MISSING_OR_INVALID) goto fail_errno;
381
542
 
543
+ /* True if the cache existed and no invalidating changes have occurred since
544
+ * it was generated. */
382
545
  valid_cache = cache_key_equal(&current_key, &cached_key);
383
546
 
384
547
  if (valid_cache) {
@@ -393,30 +556,40 @@ bs_fetch(char * path, VALUE path_v, char * cache_path, VALUE handler)
393
556
  cache_fd = -1;
394
557
  /* Cache is stale, invalid, or missing. Regenerate and write it out. */
395
558
 
559
+ /* Read the contents of the source file into a buffer */
396
560
  if (bs_read_contents(current_fd, current_key.size, &contents) < 0) goto fail_errno;
397
561
  input_data = rb_str_new_static(contents, current_key.size);
398
562
 
563
+ /* Try to compile the input_data using input_to_storage(input_data) */
399
564
  exception_tag = bs_input_to_storage(handler, input_data, path_v, &storage_data);
400
565
  if (exception_tag != 0) goto raise;
566
+ /* If input_to_storage raised Bootsnap::CompileCache::Uncompilable, don't try
567
+ * to cache anything; just return input_to_output(input_data) */
401
568
  if (storage_data == uncompilable) {
402
569
  bs_input_to_output(handler, input_data, &output_data, &exception_tag);
403
570
  if (exception_tag != 0) goto raise;
404
571
  goto succeed;
405
572
  }
573
+ /* If storage_data isn't a string, we can't cache it */
406
574
  if (!RB_TYPE_P(storage_data, T_STRING)) goto invalid_type_storage_data;
407
575
 
576
+ /* Write the cache key and storage_data to the cache directory */
408
577
  res = atomic_write_cache_file(cache_path, &current_key, storage_data);
409
578
  if (res < 0) goto fail_errno;
410
579
 
580
+ /* Having written the cache, now convert storage_data to output_data */
411
581
  exception_tag = bs_storage_to_output(handler, storage_data, &output_data);
412
582
  if (exception_tag != 0) goto raise;
413
583
 
584
+ /* If output_data is nil, delete the cache entry and generate the output
585
+ * using input_to_output */
414
586
  if (NIL_P(output_data)) {
415
587
  if (unlink(cache_path) < 0) goto fail_errno;
416
588
  bs_input_to_output(handler, input_data, &output_data, &exception_tag);
417
589
  if (exception_tag != 0) goto raise;
418
590
  }
419
- // output_data is now the correct return; cascade into succeed:
591
+
592
+ goto succeed; /* output_data is now the correct return. */
420
593
 
421
594
  #define CLEANUP \
422
595
  if (contents != NULL) xfree(contents); \
@@ -444,22 +617,28 @@ invalid_type_storage_data:
444
617
  #undef CLEANUP
445
618
  }
446
619
 
447
- static VALUE
448
- prot_exception_for_errno(VALUE err)
449
- {
450
- if (err != INT2FIX(0)) {
451
- VALUE mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
452
- VALUE constants = rb_funcall(mErrno, rb_intern("constants"), 0);
453
- VALUE which = rb_funcall(constants, rb_intern("[]"), 1, err);
454
- return rb_funcall(mErrno, rb_intern("const_get"), 1, which);
455
- }
456
- return rb_eStandardError;
457
- }
458
-
459
-
460
620
  /*****************************************************************************/
461
621
  /********************* Handler Wrappers **************************************/
462
- /*****************************************************************************/
622
+ /*****************************************************************************
623
+ * Everything after this point in the file is just wrappers to deal with ruby's
624
+ * clunky method of handling exceptions from ruby methods invoked from C.
625
+ */
626
+
627
+ struct s2o_data {
628
+ VALUE handler;
629
+ VALUE storage_data;
630
+ };
631
+
632
+ struct i2o_data {
633
+ VALUE handler;
634
+ VALUE input_data;
635
+ };
636
+
637
+ struct i2s_data {
638
+ VALUE handler;
639
+ VALUE input_data;
640
+ VALUE pathval;
641
+ };
463
642
 
464
643
  static VALUE
465
644
  prot_storage_to_output(VALUE arg)
@@ -1,3 +1,3 @@
1
1
  module Bootsnap
2
- VERSION = "0.3.0.pre"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.pre
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-25 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -162,9 +162,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
162
162
  version: 2.3.0
163
163
  required_rubygems_version: !ruby/object:Gem::Requirement
164
164
  requirements:
165
- - - ">"
165
+ - - ">="
166
166
  - !ruby/object:Gem::Version
167
- version: 1.3.1
167
+ version: '0'
168
168
  requirements: []
169
169
  rubyforge_project:
170
170
  rubygems_version: 2.6.10