diff options
Diffstat (limited to 'marshal.c')
| -rw-r--r-- | marshal.c | 352 |
1 files changed, 230 insertions, 122 deletions
@@ -30,6 +30,7 @@ #include "internal/hash.h" #include "internal/numeric.h" #include "internal/object.h" +#include "internal/re.h" #include "internal/struct.h" #include "internal/symbol.h" #include "internal/util.h" @@ -40,6 +41,7 @@ #include "ruby/util.h" #include "builtin.h" #include "shape.h" +#include "ruby/internal/attr/nonstring.h" #define BITSPERSHORT (2*CHAR_BIT) #define SHORTMASK ((1<<BITSPERSHORT)-1) @@ -100,6 +102,7 @@ static ID s_dump, s_load, s_mdump, s_mload; static ID s_dump_data, s_load_data, s_alloc, s_call; static ID s_getbyte, s_read, s_write, s_binmode; static ID s_encoding_short, s_ruby2_keywords_flag; +#define s_encoding_long rb_id_encoding() #define name_s_dump "_dump" #define name_s_load "_load" @@ -114,6 +117,7 @@ static ID s_encoding_short, s_ruby2_keywords_flag; #define name_s_write "write" #define name_s_binmode "binmode" #define name_s_encoding_short "E" +#define name_s_encoding_long "encoding" #define name_s_ruby2_keywords_flag "K" typedef struct { @@ -128,22 +132,6 @@ static VALUE compat_allocator_tbl_wrapper; static VALUE rb_marshal_dump_limited(VALUE obj, VALUE port, int limit); static VALUE rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze); -static int -mark_marshal_compat_i(st_data_t key, st_data_t value, st_data_t _) -{ - marshal_compat_t *p = (marshal_compat_t *)value; - rb_gc_mark(p->newclass); - rb_gc_mark(p->oldclass); - return ST_CONTINUE; -} - -static void -mark_marshal_compat_t(void *tbl) -{ - if (!tbl) return; - st_foreach(tbl, mark_marshal_compat_i, 0); -} - static st_table *compat_allocator_table(void); void @@ -156,15 +144,16 @@ rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE (*dumper)(VALUE), rb_raise(rb_eTypeError, "no allocator"); } + compat_allocator_table(); compat = ALLOC(marshal_compat_t); - compat->newclass = Qnil; - compat->oldclass = Qnil; compat->newclass = newclass; compat->oldclass = oldclass; compat->dumper = dumper; compat->loader = loader; st_insert(compat_allocator_table(), (st_data_t)allocator, (st_data_t)compat); + RB_OBJ_WRITTEN(compat_allocator_tbl_wrapper, Qundef, newclass); + RB_OBJ_WRITTEN(compat_allocator_tbl_wrapper, Qundef, oldclass); } struct dump_arg { @@ -173,6 +162,7 @@ struct dump_arg { st_table *data; st_table *compat_tbl; st_table *encodings; + st_table *userdefs; st_index_t num_entries; }; @@ -221,6 +211,7 @@ mark_dump_arg(void *ptr) rb_mark_set(p->symbols); rb_mark_set(p->data); rb_mark_hash(p->compat_tbl); + rb_mark_set(p->userdefs); rb_gc_mark(p->str); } @@ -238,6 +229,7 @@ memsize_dump_arg(const void *ptr) if (p->symbols) memsize += rb_st_memsize(p->symbols); if (p->data) memsize += rb_st_memsize(p->data); if (p->compat_tbl) memsize += rb_st_memsize(p->compat_tbl); + if (p->userdefs) memsize += rb_st_memsize(p->userdefs); if (p->encodings) memsize += rb_st_memsize(p->encodings); return memsize; } @@ -471,6 +463,31 @@ w_float(double d, struct dump_arg *arg) } } + +static VALUE +w_encivar(VALUE str, struct dump_arg *arg) +{ + VALUE encname = encoding_name(str, arg); + if (NIL_P(encname) || + is_ascii_string(str)) { + return Qnil; + } + w_byte(TYPE_IVAR, arg); + return encname; +} + +static void +w_encname(VALUE encname, struct dump_arg *arg) +{ + if (!NIL_P(encname)) { + struct dump_call_arg c_arg; + c_arg.limit = 1; + c_arg.arg = arg; + w_long(1L, arg); + w_encoding(encname, &c_arg); + } +} + static void w_symbol(VALUE sym, struct dump_arg *arg) { @@ -487,24 +504,11 @@ w_symbol(VALUE sym, struct dump_arg *arg) if (!sym) { rb_raise(rb_eTypeError, "can't dump anonymous ID %"PRIdVALUE, sym); } - encname = encoding_name(sym, arg); - if (NIL_P(encname) || - is_ascii_string(sym)) { - encname = Qnil; - } - else { - w_byte(TYPE_IVAR, arg); - } + encname = w_encivar(sym, arg); w_byte(TYPE_SYMBOL, arg); w_bytes(RSTRING_PTR(sym), RSTRING_LEN(sym), arg); st_add_direct(arg->symbols, orig_sym, arg->symbols->num_entries); - if (!NIL_P(encname)) { - struct dump_call_arg c_arg; - c_arg.limit = 1; - c_arg.arg = arg; - w_long(1L, arg); - w_encoding(encname, &c_arg); - } + w_encname(encname, arg); } } @@ -533,7 +537,7 @@ hash_each(VALUE key, VALUE value, VALUE v) static void w_extended(VALUE klass, struct dump_arg *arg, int check) { - if (check && FL_TEST(klass, FL_SINGLETON)) { + if (check && RCLASS_SINGLETON_P(klass)) { VALUE origin = RCLASS_ORIGIN(klass); if (SINGLETON_DUMP_UNABLE_P(klass) || (origin != klass && SINGLETON_DUMP_UNABLE_P(origin))) { @@ -542,7 +546,7 @@ w_extended(VALUE klass, struct dump_arg *arg, int check) klass = RCLASS_SUPER(klass); } while (BUILTIN_TYPE(klass) == T_ICLASS) { - if (!FL_TEST(klass, RICLASS_IS_ORIGIN) || + if (!RICLASS_IS_ORIGIN_P(klass) || BUILTIN_TYPE(RBASIC(klass)->klass) != T_MODULE) { VALUE path = rb_class_name(RBASIC(klass)->klass); w_byte(TYPE_EXTENDED, arg); @@ -595,13 +599,21 @@ rb_hash_ruby2_keywords(VALUE obj) RHASH(obj)->basic.flags |= RHASH_PASS_AS_KEYWORDS; } -static inline bool -to_be_skipped_id(const ID id) +/* + * if instance variable name `id` is a special name to be skipped, + * returns the name of it. otherwise it cannot be dumped (unnamed), + * returns `name` as-is. returns NULL for ID that can be dumped. + */ +static inline const char * +skipping_ivar_name(const ID id, const char *name) { - if (id == s_encoding_short) return true; - if (id == s_ruby2_keywords_flag) return true; - if (id == rb_id_encoding()) return true; - return !rb_id2str(id); +#define IS_SKIPPED_IVAR(idname) \ + ((id == idname) && (name = name_##idname, true)) + if (IS_SKIPPED_IVAR(s_encoding_short)) return name; + if (IS_SKIPPED_IVAR(s_ruby2_keywords_flag)) return name; + if (IS_SKIPPED_IVAR(s_encoding_long)) return name; + if (!rb_id2str(id)) return name; + return NULL; } struct w_ivar_arg { @@ -614,15 +626,12 @@ w_obj_each(ID id, VALUE value, st_data_t a) { struct w_ivar_arg *ivarg = (struct w_ivar_arg *)a; struct dump_call_arg *arg = ivarg->dump; + const char unnamed[] = "", *ivname = skipping_ivar_name(id, unnamed); - if (to_be_skipped_id(id)) { - if (id == s_encoding_short) { - rb_warn("instance variable `"name_s_encoding_short"' on class %"PRIsVALUE" is not dumped", - CLASS_OF(arg->obj)); - } - if (id == s_ruby2_keywords_flag) { - rb_warn("instance variable `"name_s_ruby2_keywords_flag"' on class %"PRIsVALUE" is not dumped", - CLASS_OF(arg->obj)); + if (ivname) { + if (ivname != unnamed) { + rb_warn("instance variable '%s' on class %"PRIsVALUE" is not dumped", + ivname, CLASS_OF(arg->obj)); } return ST_CONTINUE; } @@ -635,7 +644,7 @@ w_obj_each(ID id, VALUE value, st_data_t a) static int obj_count_ivars(ID id, VALUE val, st_data_t a) { - if (!to_be_skipped_id(id) && UNLIKELY(!++*(st_index_t *)a)) { + if (!skipping_ivar_name(id, "") && UNLIKELY(!++*(st_index_t *)a)) { rb_raise(rb_eRuntimeError, "too many instance variables"); } return ST_CONTINUE; @@ -719,28 +728,9 @@ has_ivars(VALUE obj, VALUE encname, VALUE *ivobj) static void w_ivar_each(VALUE obj, st_index_t num, struct dump_call_arg *arg) { - shape_id_t shape_id = rb_shape_get_shape_id(arg->obj); struct w_ivar_arg ivarg = {arg, num}; if (!num) return; - rb_ivar_foreach(obj, w_obj_each, (st_data_t)&ivarg); - - if (shape_id != rb_shape_get_shape_id(arg->obj)) { - rb_shape_t * expected_shape = rb_shape_get_shape_by_id(shape_id); - rb_shape_t * actual_shape = rb_shape_get_shape(arg->obj); - - // If the shape tree got _shorter_ then we probably removed an IV - // If the shape tree got longer, then we probably added an IV. - // The exception message might not be accurate when someone adds and - // removes the same number of IVs, but they will still get an exception - if (rb_shape_depth(expected_shape) > rb_shape_depth(actual_shape)) { - rb_raise(rb_eRuntimeError, "instance variable removed from %"PRIsVALUE" instance", - CLASS_OF(arg->obj)); - } - else { - rb_raise(rb_eRuntimeError, "instance variable added to %"PRIsVALUE" instance", - CLASS_OF(arg->obj)); - } - } + rb_ivar_foreach_buffered(obj, w_obj_each, (st_data_t)&ivarg); } static void @@ -904,6 +894,9 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) st_index_t hasiv2; VALUE encname2; + if (arg->userdefs && st_is_member(arg->userdefs, (st_data_t)obj)) { + rb_raise(rb_eRuntimeError, "can't dump recursive object using _dump()"); + } v = INT2NUM(limit); v = dump_funcall(arg, obj, s_dump, 1, &v); if (!RB_TYPE_P(v, T_STRING)) { @@ -920,7 +913,13 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) w_class(TYPE_USERDEF, obj, arg, FALSE); w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg); if (hasiv) { + st_data_t userdefs = (st_data_t)obj; + if (!arg->userdefs) { + arg->userdefs = rb_init_identtable(); + } + st_add_direct(arg->userdefs, userdefs, 0); w_ivar(hasiv, ivobj, encname, &c_arg); + st_delete(arg->userdefs, &userdefs, NULL); } w_remember(obj, arg); return; @@ -931,8 +930,9 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) hasiv = has_ivars(obj, (encname = encoding_name(obj, arg)), &ivobj); { st_data_t compat_data; - rb_alloc_func_t allocator = rb_get_alloc_func(RBASIC(obj)->klass); - if (st_lookup(compat_allocator_tbl, + VALUE klass = CLASS_OF(obj); + rb_alloc_func_t allocator = RCLASS_SINGLETON_P(klass) ? 0 : rb_get_alloc_func(klass); + if (allocator && st_lookup(compat_allocator_tbl, (st_data_t)allocator, &compat_data)) { marshal_compat_t *compat = (marshal_compat_t*)compat_data; @@ -952,19 +952,23 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) if (FL_TEST(obj, FL_SINGLETON)) { rb_raise(rb_eTypeError, "singleton class can't be dumped"); } - w_byte(TYPE_CLASS, arg); { VALUE path = class2path(obj); + VALUE encname = w_encivar(path, arg); + w_byte(TYPE_CLASS, arg); w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg); + w_encname(encname, arg); RB_GC_GUARD(path); } break; case T_MODULE: - w_byte(TYPE_MODULE, arg); { VALUE path = class2path(obj); + VALUE encname = w_encivar(path, arg); + w_byte(TYPE_MODULE, arg); w_bytes(RSTRING_PTR(path), RSTRING_LEN(path), arg); + w_encname(encname, arg); RB_GC_GUARD(path); } break; @@ -1065,7 +1069,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) case T_STRUCT: w_class(TYPE_STRUCT, obj, arg, TRUE); { - long len = RSTRUCT_LEN(obj); + long len = RSTRUCT_LEN_RAW(obj); VALUE mem; long i; @@ -1073,7 +1077,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) mem = rb_struct_members(obj); for (i=0; i<len; i++) { w_symbol(RARRAY_AREF(mem, i), arg); - w_object(RSTRUCT_GET(obj, i), arg, limit); + w_object(RSTRUCT_GET_RAW(obj, i), arg, limit); } } break; @@ -1127,6 +1131,10 @@ clear_dump_arg(struct dump_arg *arg) st_free_table(arg->encodings); arg->encodings = 0; } + if (arg->userdefs) { + st_free_table(arg->userdefs); + arg->userdefs = 0; + } } NORETURN(static inline void io_needed(void)); @@ -1166,7 +1174,7 @@ io_needed(void) * * anonymous Class/Module. * * objects which are related to system (ex: Dir, File::Stat, IO, File, Socket * and so on) - * * an instance of MatchData, Data, Method, UnboundMethod, Proc, Thread, + * * an instance of MatchData, Method, UnboundMethod, Proc, Thread, * ThreadGroup, Continuation * * objects which define singleton methods */ @@ -1204,6 +1212,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit) arg->num_entries = 0; arg->compat_tbl = 0; arg->encodings = 0; + arg->userdefs = 0; arg->str = rb_str_buf_new(0); if (!NIL_P(port)) { if (!rb_respond_to(port, s_write)) { @@ -1233,6 +1242,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit) struct load_arg { VALUE src; char *buf; + long bufsize; long buflen; long readable; long offset; @@ -1318,15 +1328,23 @@ static unsigned char r_byte1_buffered(struct load_arg *arg) { if (arg->buflen == 0) { - long readable = arg->readable < BUFSIZ ? arg->readable : BUFSIZ; + long readable = arg->readable < arg->bufsize ? arg->readable : arg->bufsize; + long read_len; VALUE str, n = LONG2NUM(readable); str = load_funcall(arg, arg->src, s_read, 1, &n); if (NIL_P(str)) too_short(); StringValue(str); - memcpy(arg->buf, RSTRING_PTR(str), RSTRING_LEN(str)); + read_len = RSTRING_LEN(str); + if (UNLIKELY(read_len < readable)) too_short(); + if (UNLIKELY(read_len > arg->bufsize)) { + arg->buf = ruby_sized_realloc_n(arg->buf, read_len, 1, arg->bufsize); + arg->bufsize = read_len; + } + memcpy(arg->buf, RSTRING_PTR(str), read_len); arg->offset = 0; - arg->buflen = RSTRING_LEN(str); + arg->buflen = read_len; + RB_GC_GUARD(str); } arg->buflen--; return arg->buf[arg->offset++]; @@ -1404,7 +1422,7 @@ long ruby_marshal_read_long(const char **buf, long len) { long x; - struct RString src; + struct RString src = {RBASIC_INIT}; struct load_arg arg; memset(&arg, 0, sizeof(arg)); arg.src = rb_setup_fake_str(&src, *buf, len, 0); @@ -1413,6 +1431,18 @@ ruby_marshal_read_long(const char **buf, long len) return x; } +static long +r_keep_readable(struct load_arg *arg, long len, size_t size) +{ + if (UNLIKELY(len < 0)) { + rb_raise(rb_eArgError, "negative length"); + } + if (UNLIKELY((unsigned long)len > SIZE_MAX / size || arg->readable >= LONG_MAX - len)) { + rb_raise(rb_eArgError, "marshaled data too big"); + } + return len; +} + static VALUE r_bytes1(long len, struct load_arg *arg) { @@ -1442,7 +1472,7 @@ r_bytes1_buffered(long len, struct load_arg *arg) long tmp_len, read_len, need_len = len - buflen; VALUE tmp, n; - readable = readable < BUFSIZ ? readable : BUFSIZ; + readable = readable < arg->bufsize ? readable : arg->bufsize; read_len = need_len > readable ? need_len : readable; n = LONG2NUM(read_len); tmp = load_funcall(arg, arg->src, s_read, 1, &n); @@ -1508,7 +1538,7 @@ name_equal(const char *name, size_t nlen, const char *p, long l) static int sym2encidx(VALUE sym, VALUE val) { - static const char name_encoding[8] = "encoding"; + RBIMPL_ATTR_NONSTRING() static const char name_encoding[8] = "encoding"; const char *p; long l; if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return -1; @@ -1696,6 +1726,42 @@ r_copy_ivar(VALUE v, VALUE data) return v; } +#define override_ivar_error(type, str) \ + rb_raise(rb_eTypeError, \ + "can't override instance variable of "type" '%"PRIsVALUE"'", \ + (str)) + +static int +r_ivar_encoding(VALUE obj, struct load_arg *arg, VALUE sym, VALUE val) +{ + int idx = sym2encidx(sym, val); + if (idx >= 0) { + if (rb_enc_capable(obj)) { + // Check if needed to avoid rb_check_frozen() check for Regexps + if (rb_enc_get_index(obj) != idx) { + rb_enc_associate_index(obj, idx); + } + } + else { + rb_raise(rb_eArgError, "%"PRIsVALUE" is not enc_capable", obj); + } + return TRUE; + } + return FALSE; +} + +static long +r_encname(VALUE obj, struct load_arg *arg) +{ + long len = r_long(arg); + if (len > 0) { + VALUE sym = r_symbol(arg); + VALUE val = r_object(arg); + len -= r_ivar_encoding(obj, arg, sym, val); + } + return len; +} + static void r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg) { @@ -1703,17 +1769,16 @@ r_ivar(VALUE obj, int *has_encoding, struct load_arg *arg) len = r_long(arg); if (len > 0) { + if (RB_TYPE_P(obj, T_MODULE)) { + override_ivar_error("module", rb_mod_name(obj)); + } + else if (RB_TYPE_P(obj, T_CLASS)) { + override_ivar_error("class", rb_class_name(obj)); + } do { VALUE sym = r_symbol(arg); VALUE val = r_object(arg); - int idx = sym2encidx(sym, val); - if (idx >= 0) { - if (rb_enc_capable(obj)) { - rb_enc_associate_index(obj, idx); - } - else { - rb_raise(rb_eArgError, "%"PRIsVALUE" is not enc_capable", obj); - } + if (r_ivar_encoding(obj, arg, sym, val)) { if (has_encoding) *has_encoding = TRUE; } else if (symname_equal_lit(sym, name_s_ruby2_keywords_flag)) { @@ -1795,22 +1860,20 @@ append_extmod(VALUE obj, VALUE extmod) #define prohibit_ivar(type, str) do { \ if (!ivp || !*ivp) break; \ - rb_raise(rb_eTypeError, \ - "can't override instance variable of "type" `%"PRIsVALUE"'", \ - (str)); \ + override_ivar_error(type, str); \ } while (0) -static VALUE r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type); +static VALUE r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE extmod, int type); static VALUE r_object0(struct load_arg *arg, bool partial, int *ivp, VALUE extmod) { int type = r_byte(arg); - return r_object_for(arg, partial, ivp, extmod, type); + return r_object_for(arg, partial, ivp, 0, extmod, type); } static VALUE -r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int type) +r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE klass, VALUE extmod, int type) { VALUE (*hash_new_with_size)(st_index_t) = rb_hash_new_with_size; VALUE v = Qnil; @@ -1825,6 +1888,9 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ } v = (VALUE)link; if (!st_lookup(arg->partial_objects, (st_data_t)v, &link)) { + if (arg->freeze && RB_TYPE_P(v, T_STRING)) { + v = rb_str_to_interned_str(v); + } v = r_post_proc(v, arg); } break; @@ -1883,13 +1949,13 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ } type = r_byte(arg); if ((c == rb_cHash) && - /* Hack for compare_by_identify */ + /* Hack for compare_by_identity */ (type == TYPE_HASH || type == TYPE_HASH_DEF)) { hash_new_with_size = rb_ident_hash_new_with_size; goto type_hash; } - v = r_object_for(arg, partial, 0, extmod, type); - if (rb_special_const_p(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) { + v = r_object_for(arg, partial, 0, c, extmod, type); + if (RB_SPECIAL_CONST_P(v) || RB_TYPE_P(v, T_OBJECT) || RB_TYPE_P(v, T_CLASS)) { goto format_error; } if (RB_TYPE_P(v, T_MODULE) || !RTEST(rb_class_inherited_p(c, RBASIC(v)->klass))) { @@ -1960,7 +2026,10 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ int sign; sign = r_byte(arg); - len = r_long(arg); + if (sign != '+' && sign != '-') { + rb_raise(rb_eArgError, "invalid Bignum sign"); + } + len = r_keep_readable(arg, r_long(arg), 2); if (SIZEOF_VALUE >= 8 && len <= 4) { // Representable within uintptr, likely FIXNUM @@ -2025,7 +2094,10 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ } rb_str_set_len(str, dst - ptr); } - VALUE regexp = rb_reg_new_str(str, options); + if (!klass) { + klass = rb_cRegexp; + } + VALUE regexp = rb_reg_init_str(rb_reg_s_alloc(klass), str, options); r_copy_ivar(regexp, str); v = r_entry0(regexp, idx, arg); @@ -2035,7 +2107,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ case TYPE_ARRAY: { - long len = r_long(arg); + long len = r_keep_readable(arg, r_long(arg), 1); v = rb_ary_new2(len); v = r_entry(v, arg); @@ -2053,7 +2125,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ case TYPE_HASH_DEF: type_hash: { - long len = r_long(arg); + long len = r_keep_readable(arg, r_long(arg), 2); v = hash_new_with_size(len); v = r_entry(v, arg); @@ -2079,7 +2151,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ VALUE slot; st_index_t idx = r_prepare(arg); VALUE klass = path2class(r_unique(arg)); - long len = r_long(arg); + long len = r_keep_readable(arg, r_long(arg), 2); v = rb_obj_alloc(klass); if (!RB_TYPE_P(v, T_STRUCT)) { @@ -2133,7 +2205,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ st_data_t d; if (!rb_obj_respond_to(klass, s_load, TRUE)) { - rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method `_load'", + rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method '_load'", name); } data = r_string(arg); @@ -2169,7 +2241,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ append_extmod(v, extmod); } if (!rb_obj_respond_to(v, s_mload, TRUE)) { - rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method `marshal_load'", + rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method 'marshal_load'", name); } v = r_entry(v, arg); @@ -2215,7 +2287,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ v = r_entry(v, arg); if (!rb_obj_respond_to(v, s_load_data, TRUE)) { rb_raise(rb_eTypeError, - "class %"PRIsVALUE" needs to have instance method `_load_data'", + "class %"PRIsVALUE" needs to have instance method '_load_data'", name); } r = r_object0(arg, partial, 0, extmod); @@ -2239,6 +2311,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ { VALUE str = r_bytes(arg); + if (ivp && *ivp > 0) *ivp = r_encname(str, arg) > 0; v = path2class(str); prohibit_ivar("class", str); v = r_entry(v, arg); @@ -2250,6 +2323,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ { VALUE str = r_bytes(arg); + if (ivp && *ivp > 0) *ivp = r_encname(str, arg) > 0; v = path2module(str); prohibit_ivar("module", str); v = r_entry(v, arg); @@ -2294,10 +2368,9 @@ r_object(struct load_arg *arg) static void clear_load_arg(struct load_arg *arg) { - if (arg->buf) { - xfree(arg->buf); - arg->buf = 0; - } + ruby_xfree_sized(arg->buf, arg->bufsize); + arg->buf = NULL; + arg->bufsize = 0; arg->buflen = 0; arg->offset = 0; arg->readable = 0; @@ -2343,10 +2416,14 @@ rb_marshal_load_with_proc(VALUE port, VALUE proc, bool freeze) arg->readable = 0; arg->freeze = freeze; - if (NIL_P(v)) + if (NIL_P(v)) { + arg->bufsize = BUFSIZ; arg->buf = xmalloc(BUFSIZ); - else + } + else { + arg->bufsize = 0; arg->buf = 0; + } major = r_byte(arg); minor = r_byte(arg); @@ -2518,29 +2595,60 @@ Init_marshal(void) } static int -free_compat_i(st_data_t key, st_data_t value, st_data_t _) +marshal_compat_table_mark_and_move_i(st_data_t key, st_data_t value, st_data_t _) { - xfree((marshal_compat_t *)value); + marshal_compat_t *p = (marshal_compat_t *)value; + rb_gc_mark_and_move(&p->newclass); + rb_gc_mark_and_move(&p->oldclass); return ST_CONTINUE; } static void -free_compat_allocator_table(void *data) +marshal_compat_table_mark_and_move(void *tbl) +{ + if (!tbl) return; + st_foreach(tbl, marshal_compat_table_mark_and_move_i, 0); +} + +static int +marshal_compat_table_free_i(st_data_t key, st_data_t value, st_data_t _) { - st_foreach(data, free_compat_i, 0); + SIZED_FREE((marshal_compat_t *)value); + return ST_CONTINUE; +} + +static void +marshal_compat_table_free(void *data) +{ + st_foreach(data, marshal_compat_table_free_i, 0); st_free_table(data); } +static size_t +marshal_compat_table_memsize(const void *data) +{ + return st_memsize(data) + sizeof(marshal_compat_t) * st_table_size(data); +} + +static const rb_data_type_t marshal_compat_type = { + .wrap_struct_name = "marshal_compat_table", + .function = { + .dmark = marshal_compat_table_mark_and_move, + .dfree = marshal_compat_table_free, + .dsize = marshal_compat_table_memsize, + .dcompact = marshal_compat_table_mark_and_move, + }, + .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY, +}; + static st_table * compat_allocator_table(void) { if (compat_allocator_tbl) return compat_allocator_tbl; compat_allocator_tbl = st_init_numtable(); -#undef RUBY_UNTYPED_DATA_WARNING -#define RUBY_UNTYPED_DATA_WARNING 0 compat_allocator_tbl_wrapper = - Data_Wrap_Struct(0, mark_marshal_compat_t, free_compat_allocator_table, compat_allocator_tbl); - rb_gc_register_mark_object(compat_allocator_tbl_wrapper); + TypedData_Wrap_Struct(0, &marshal_compat_type, compat_allocator_tbl); + rb_vm_register_global_object(compat_allocator_tbl_wrapper); return compat_allocator_tbl; } |
