Optimize T_OBJECT space

T_OBJECT e$B$J%$%s%9%?%s%9$G!"%$%s%9%?%s%9JQ?t$NL>A0$r%/%i%9B&e(B
e$B$K;}$?$;$F>CHq%a%b%j$r:o8:$9$k$N$O$I$&$G$7$g$&$+!#e(B

e$B$D$^$j!"%$%s%9%?%s%9$K$O%$%s%9%?%s%9JQ?t$NCM$@$1$rJ];}$9$kG[e(B
e$BNs$rMQ0U$7!"%/%i%9$K%$%s%9%?%s%9JQ?t$NL>A0$+$i$=$NG[Ns$N%$%se(B
e$B%G%C%/%9$X$N%O%C%7%eI=$r$D$/$j$^$9!#$^$?!"$=$NG[Ns$,e(B 3e$BMWAG0Je(B
e$B2<$G$"$l$Pe(B e$BEvA3e(B RObject e$B$KKd$a9~$_$^$9!#e(B

e$B$?$@!"e(BRuby e$B$K$$$$F$OF1$8%/%i%9$G$b8D!9$N%%V%8%’%/%H$4$H$Ke(B
e$B%$%s%9%?%s%9JQ?t$,B8:$7$?$j$7$J$+$C$?$j$G$-$^$9!#$=$3$G!"%/e(B
e$B%i%9$K$D$/$C$?%O%C%7%eI=$K$O0l2s$G$bB8:
$7$?%$%s%9%?%s%9JQ?te(B
e$B$O$<$s$VEPO?$9$k$3$H$K$7$F!"8D!9$N%$%s%9%?%s%9$GB8:_$7$J$$>le(B
e$B9g$O$=$NJQ?t$KAjEv$9$kG[Ns$NMWAG$re(B Qundef e$B$H$7$F$*$-$^$9!#e(B

r13509 e$B$K$=$&$$$&JQ99$r$7$FNc$N$4$H$/e(B rdoc e$B$r;n$9$H!"<!$N$he(B
e$B$&$K$J$j$^$9e(B

e$BJQ99A0e(B:
% time ./ruby -e ‘load “./bin/rdoc”
puts File.readlines("/proc/#{$$}/status").grep(/^VmSize:/)’ – --all
–ri --op “.ext/rdoc” “.”

Files: 568
Classes: 1612
Modules: 832
Methods: 8644
Elapsed: 165.757s
VmSize: 120056 kB
./ruby -e – --all --ri --op “.ext/rdoc” “.” 152.30s user 1.04s system
92% cpu 2:45.94 total

e$BJQ998ee(B:
% time ./ruby -e ‘load “./bin/rdoc”
puts File.readlines("/proc/#{$$}/status").grep(/^VmSize:/)’ – --all
–ri --op “.ext/rdoc” “.”

Files: 568
Classes: 1612
Modules: 832
Methods: 8644
Elapsed: 149.130s
VmSize: 80876 kB
./ruby -e – --all --ri --op “.ext/rdoc” “.” 137.16s user 0.90s system
92% cpu 2:29.31 total

e$B$D$^$j!"$3$NA0$N%O%C%7%eI=$Ne(B bins e$B$K5M$a$?>u67$+$i$5$i$Ke(B 1/3
e$B$/$i$$e(B (40MB e$B$/$i$$e(B) e$B%a%b%j$r:o$k$3$H$,$G$-$^$9!#e(B

e$B$^$?!"B.EY$b$$$/$i$+B.$/$J$C$F$$$k$h$&$G$9!#e(B(e$B8m:9$G$J$1$l$Pe(B)

e$B$?$@!“F1$8%/%i%9$N8D!9$N%$%s%9%?%s%9$K$h$C$F%$%s%9%?%s%9JQ?te(B
e$B$NB8:_$N$7$+$?$,0[$J$C$F$$$k>l9g$O!”;HMQ$7$J$$%$%s%9%?%s%9JQe(B
e$B?t$K$b%a%b%j$r3d$jEv$F$k$3$H$K$J$k$N$G!"%a%b%j>CHq$,A}$($k$3e(B
e$B$H$b$"$j$^$9!#e(B

e$B$?$H$($P!“0J2<$N!“e(B1000e$B8D%$%s%9%?%s%9JQ?t$,$”$k%%V%8%’%/%H$,e(B
e$B$R$H$D$H!“e(B2e$B8D%$%s%9%?%s%9JQ?t$,$”$k%
%V%8%’%/%H$,e(B
5000e$B8D!”$He(B
e$B$$$&$h$&$J>l9g$O%a%b%j>CHq$,A}$($^$9!#e(B

% ./ruby -e ’
class C
def initialize(s)
if !s
n = “a”
1000.times { self.instance_variable_set “@#{n}”, 0; n.succ! }
else
@a = @A = s
end
end
end
C.new(false)
o = nil
5000.times { o = C.new(o) }
puts File.readlines("/proc/#{$$}/status").grep(/^VmSize:/)

e$BJQ99A0$N7k2Le(B:
VmSize: 4832 kB

e$BJQ998e$N7k2Le(B:
VmSize: 24100 kB

e$B$?$@!"$=$&$$$&%1!<%9$O$"$^$j$J$$$G$"$m$&!"$H;d$OM=A[$7$F$$$^e(B
e$B$9!#e(B

e$B$"$H!"%/%i%9$,JQ2=$9$k$H:$$k$N$G$9$,!";d$NCN$k8B$je(B T_OBJECT
e$B$J%1!<%9$G$O%/%i%9$,JQ2=$9$k$3$H$O$"$j$^$;$s!#%/%i%9$,JQ2=$9e(B
e$B$k$N$Oe(B IO#reopen e$B$He(B marshal e$B$Ne(B user class
e$B$,$"$j$^$9$,!"$I$Ae(B
e$B$i$be(B T_OBJECT e$B$K$OE,MQ$G$-$^$;$s!#$b$7$J$K$+B>$K%/%i%9$,JQ2=e(B
e$B$9$k%1!<%9$,$"$C$?$i65$($F$/$@$5$$!#e(B

e$B$I$&$G$7$g$&$+!#e(B

Index: eval_method.ci

— eval_method.ci (e$B%j%S%8%g%se(B 13497)
+++ eval_method.ci (e$B:n6H%3%T!<e(B)
@@ -143,7 +143,7 @@
st_data_t data;
NODE *old_node;

  • if (st_lookup(RCLASS(klass)->m_tbl, mid, &data)) {
  • if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) {
    old_node = (NODE *)data;
    if (old_node) {
    if (nd_type(old_node->nd_body->nd_body) == NODE_CFUNC) {
    @@ -166,7 +166,7 @@
    }
    }
  • st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t) body);
  • st_insert(RCLASS_M_TBL(klass), mid, (st_data_t) body);

    if (node && mid != ID_ALLOCATOR && ruby_running) {
    if (FL_TEST(klass, FL_SINGLETON)) {
    @@ -216,8 +216,8 @@
    return 0;
    }

  • while (!st_lookup(RCLASS(klass)->m_tbl, id, &body)) {
  • klass = RCLASS(klass)->super;
  • while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) {
  • klass = RCLASS_SUPER(klass);
    if (!klass)
    return 0;
    }
    @@ -305,7 +305,7 @@
    if (mid == object_id || mid == __send || mid == __send_bang || mid
    == init) {
    rb_warn(“removing `%s’ may cause serious problem”, rb_id2name(mid));
    }
  • if (!st_delete(RCLASS(klass)->m_tbl, &mid, &data) ||
  • if (!st_delete(RCLASS_M_TBL(klass), &mid, &data) ||
    !(body = (NODE *)data) || !body->nd_body) {
    rb_name_error(mid, “method `%s’ not defined in %s”,
    rb_id2name(mid), rb_class2name(klass));
    @@ -577,7 +577,7 @@

    orig_fbody->nd_cnt++;

  • if (st_lookup(RCLASS(klass)->m_tbl, name, &data)) {
  • if (st_lookup(RCLASS_M_TBL(klass), name, &data)) {
    node = (NODE *)data;
    if (node) {
    if (RTEST(ruby_verbose) && node->nd_cnt == 0 && node->nd_body) {
    @@ -589,7 +589,7 @@
    }
    }
  • st_insert(RCLASS(klass)->m_tbl, name,
  • st_insert(RCLASS_M_TBL(klass), name,
    (st_data_t) NEW_FBODY(
    NEW_METHOD(orig_fbody->nd_body->nd_body,
    orig_fbody->nd_body->nd_clss,
    Index: include/ruby/intern.h
    ===================================================================
    — include/ruby/intern.h (e$B%j%S%8%g%se(B 13497)
    +++ include/ruby/intern.h (e$B:n6H%3%T!<e(B)
    @@ -580,6 +580,7 @@
    VALUE rb_ivar_get(VALUE, ID);
    VALUE rb_ivar_set(VALUE, ID, VALUE);
    VALUE rb_ivar_defined(VALUE, ID);
    +void rb_ivar_foreach(VALUE, int ()(ANYARGS), st_data_t);
    VALUE rb_iv_set(VALUE, const char
    , VALUE);
    VALUE rb_iv_get(VALUE, const char*);
    VALUE rb_attr_get(VALUE, ID);
    Index: include/ruby/ruby.h
    ===================================================================
    — include/ruby/ruby.h (e$B%j%S%8%g%se(B 13497)
    +++ include/ruby/ruby.h (e$B:n6H%3%T!<e(B)
    @@ -402,10 +402,26 @@
    VALUE klass;
    };

+#define ROBJECT_EMBED_LEN_MAX 3
struct RObject {
struct RBasic basic;

  • struct st_table *iv_tbl;
  • union {
  • struct {
  •  long len;
    
  •  VALUE *ptr;
    
  • } heap;
  • VALUE ary[ROBJECT_EMBED_LEN_MAX];
  • } as;
    };
    +#define ROBJECT_EMBED FL_USER1
    +#define ROBJECT_LEN(o) \
  • ((RBASIC(o)->flags & ROBJECT_EMBED) ? \
  • ROBJECT_EMBED_LEN_MAX : \
    
  • ROBJECT(o)->as.heap.len)
    

+#define ROBJECT_PTR(o) \

  • ((RBASIC(o)->flags & ROBJECT_EMBED) ? \
  • ROBJECT(o)->as.ary : \
    
  • ROBJECT(o)->as.heap.ptr)
    

struct RValues {
struct RBasic basic;
@@ -414,12 +430,24 @@
VALUE v3;
};

+typedef struct {

  • struct st_table *iv_tbl;
  • struct st_table *iv_index_tbl;
    +} rb_classext_t;

struct RClass {
struct RBasic basic;

  • struct st_table *iv_tbl;
  • rb_classext_t *ptr;
    struct st_table *m_tbl;
    VALUE super;
    };
    +#define RCLASS_IV_TBL© (RCLASS©->ptr->iv_tbl)
    +#define RCLASS_M_TBL© (RCLASS©->m_tbl)
    +#define RCLASS_SUPER© (RCLASS©->super)
    +#define RCLASS_IV_INDEX_TBL© (RCLASS©->ptr->iv_index_tbl)
    +#define RMODULE_IV_TBL(m) RCLASS_IV_TBL(m)
    +#define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
    +#define RMODULE_SUPER(m) RCLASS_SUPER(m)

struct RFloat {
struct RBasic basic;
Index: insns.def

— insns.def (e$B%j%S%8%g%se(B 13497)
+++ insns.def (e$B:n6H%3%T!<e(B)
@@ -973,7 +973,7 @@

   if (super != rb_cObject) {
 VALUE tmp;
  • tmp = rb_class_real(RCLASS(klass)->super);
  • tmp = rb_class_real(RCLASS_SUPER(klass));

    if (tmp != super) {
    rb_raise(rb_eTypeError, “superclass mismatch for class %s”,
    Index: variable.c
    ===================================================================
    — variable.c (e$B%j%S%8%g%se(B 13497)
    +++ variable.c (e$B:n6H%3%T!<e(B)
    @@ -48,8 +48,8 @@
    path = rb_str_new2(rb_id2name(name));
    while (fc) {
    if (fc->track == rb_cObject) break;

  • if (ROBJECT(fc->track)->iv_tbl &&
  •  st_lookup(ROBJECT(fc->track)->iv_tbl, classpath, &tmp)) {
    
  • if (RCLASS_IV_TBL(fc->track) &&
  •  st_lookup(RCLASS_IV_TBL(fc->track), classpath, &tmp)) {
     tmp = rb_str_dup(tmp);
     rb_str_cat2(tmp, "::");
     rb_str_append(tmp, path);
    

@@ -78,7 +78,7 @@
switch (TYPE(value)) {
case T_MODULE:
case T_CLASS:

  • if (!RCLASS(value)->iv_tbl) return ST_CONTINUE;
  • if (!RCLASS_IV_TBL(value)) return ST_CONTINUE;
    else {
    struct fc_result arg;
    struct fc_result *list;
    @@ -94,7 +94,7 @@
    arg.klass = res->klass;
    arg.track = value;
    arg.prev = res;
  •  st_foreach(RCLASS(value)->iv_tbl, fc_i, (st_data_t)&arg);
    
  •  st_foreach(RCLASS_IV_TBL(value), fc_i, (st_data_t)&arg);
     if (arg.path) {
    
    res->path = arg.path;
    return ST_STOP;
    @@ -118,18 +118,18 @@
    arg.klass = klass;
    arg.track = rb_cObject;
    arg.prev = 0;
  • if (RCLASS(rb_cObject)->iv_tbl) {
  • st_foreach_safe(RCLASS(rb_cObject)->iv_tbl, fc_i, (st_data_t)&arg);
  • if (RCLASS_IV_TBL(rb_cObject)) {
  • st_foreach_safe(RCLASS_IV_TBL(rb_cObject), fc_i, (st_data_t)&arg);
    }
    if (arg.path == 0) {
    st_foreach_safe(rb_class_tbl, fc_i, (st_data_t)&arg);
    }
    if (arg.path) {
  • if (!ROBJECT(klass)->iv_tbl) {
  •  ROBJECT(klass)->iv_tbl = st_init_numtable();
    
  • if (!RCLASS_IV_TBL(klass)) {
  •  RCLASS_IV_TBL(klass) = st_init_numtable();
    
    }
  • st_insert(ROBJECT(klass)->iv_tbl, classpath, arg.path);
  • st_delete(RCLASS(klass)->iv_tbl, &tmp_classpath, 0);
  • st_insert(RCLASS_IV_TBL(klass), classpath, arg.path);

  • st_delete(RCLASS_IV_TBL(klass), &tmp_classpath, 0);
    return arg.path;
    }
    return Qnil;
    @@ -141,17 +141,17 @@
    VALUE path = Qnil;

    if (!klass) klass = rb_cObject;

  • if (ROBJECT(klass)->iv_tbl) {
  • if (!st_lookup(ROBJECT(klass)->iv_tbl, classpath, &path)) {
  • if (RCLASS_IV_TBL(klass)) {
  • if (!st_lookup(RCLASS_IV_TBL(klass), classpath, &path)) {
    ID classid = rb_intern(“classid”);
  •  if (!st_lookup(ROBJECT(klass)->iv_tbl, classid, &path)) {
    
  •  if (!st_lookup(RCLASS_IV_TBL(klass), classid, &path)) {
    
    return find_class_path(klass);
    }
    path = rb_str_new2(rb_id2name(SYM2ID(path)));
    OBJ_FREEZE(path);
  •  st_insert(ROBJECT(klass)->iv_tbl, classpath, path);
    
  •  st_delete(RCLASS(klass)->iv_tbl, (st_data_t*)&classid, 0);
    
  •  st_insert(RCLASS_IV_TBL(klass), classpath, path);
    
  •  st_delete(RCLASS_IV_TBL(klass), (st_data_t*)&classid, 0);
    

    }
    if (TYPE(path) != T_STRING) {
    rb_bug(“class path is not set properly”);
    @@ -183,7 +183,7 @@
    VALUE path = classname(klass);

    if (!NIL_P(path)) return path;

  • if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl,
  • if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),
    tmp_classpath, &path)) {
    return path;
    }
    @@ -926,12 +926,22 @@
    ivar_get(VALUE obj, ID id, int warn)
    {
    VALUE val;

  • VALUE klass;

  • st_data_t index;

    switch (TYPE(obj)) {
    case T_OBJECT:

  •    klass = rb_obj_class(obj);
    
  •    if (!RCLASS_IV_INDEX_TBL(klass)) break;
    
  •    if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &index)) break;
    
  •    if (ROBJECT_LEN(obj) <= index) break;
    
  •    val = ROBJECT_PTR(obj)[index];
    
  •    if (val != Qundef)
    
  •        return val;
    
  • break;
    case T_CLASS:
    case T_MODULE:

  • if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id,
    &val))
  • if (RCLASS_IV_TBL(obj) && st_lookup(RCLASS_IV_TBL(obj), id, &val))
    return val;
    break;
    default:
    @@ -960,16 +970,63 @@
    VALUE
    rb_ivar_set(VALUE obj, ID id, VALUE val)
    {
  • VALUE klass;
  • st_data_t index;
  • long i, len;
  • int ivar_extended;
  • if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
    rb_raise(rb_eSecurityError, “Insecure: can’t modify instance
    variable”);
    if (OBJ_FROZEN(obj)) rb_error_frozen(“object”);
    switch (TYPE(obj)) {
    case T_OBJECT:
  •    klass = rb_obj_class(obj);
    
  •    if (!RCLASS_IV_INDEX_TBL(klass))
    
  •        RCLASS_IV_INDEX_TBL(klass) = st_init_numtable();
    
  •    ivar_extended = 0;
    
  •    if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &index)) {
    
  •        index = RCLASS_IV_INDEX_TBL(klass)->num_entries;
    
  •        st_add_direct(RCLASS_IV_INDEX_TBL(klass), id, index);
    
  •        ivar_extended = 1;
    
  •    }
    
  •    len = ROBJECT_LEN(obj);
    
  •    if (len <= index) {
    
  •        VALUE *ptr = ROBJECT_PTR(obj);
    
  •        if (index < ROBJECT_EMBED_LEN_MAX) {
    
  •            RBASIC(obj)->flags |= ROBJECT_EMBED;
    
  •            ptr = ROBJECT(obj)->as.ary;
    
  •            for (i = 0; i < ROBJECT_EMBED_LEN_MAX; i++) {
    
  •                ptr[i] = Qundef;
    
  •            }
    
  •        }
    
  •        else {
    
  •            VALUE *newptr;
    
  •            long newsize = (index+1) + (index+1)/4; /* 
    

(index+1)*1.25 */

  •            if (!ivar_extended &&
    
  •                RCLASS_IV_INDEX_TBL(klass)->num_entries < newsize) 
    

{

  •                newsize = RCLASS_IV_INDEX_TBL(klass)->num_entries;
    
  •            }
    
  •            if (RBASIC(obj)->flags & ROBJECT_EMBED) {
    
  •                newptr = ALLOC_N(VALUE, newsize);
    
  •                MEMCPY(newptr, ptr, VALUE, len);
    
  •                RBASIC(obj)->flags &= ~ROBJECT_EMBED;
    
  •                ROBJECT(obj)->as.heap.ptr = newptr;
    
  •            }
    
  •            else {
    
  •                REALLOC_N(ROBJECT(obj)->as.heap.ptr, VALUE, 
    

newsize);

  •                newptr = ROBJECT(obj)->as.heap.ptr;
    
  •            }
    
  •            for (; len < newsize; len++)
    
  •                newptr[len] = Qundef;
    
  •            ROBJECT(obj)->as.heap.len = newsize;
    
  •        }
    
  •    }
    
  •    ROBJECT_PTR(obj)[index] = val;
    
  • break;
    case T_CLASS:
    case T_MODULE:
  • if (!ROBJECT(obj)->iv_tbl) ROBJECT(obj)->iv_tbl = st_init_numtable();
  • st_insert(ROBJECT(obj)->iv_tbl, id, val);
  • break;
  • if (!RCLASS_IV_TBL(obj)) RCLASS_IV_TBL(obj) = st_init_numtable();
  • st_insert(RCLASS_IV_TBL(obj), id, val);
    default:
    generic_ivar_set(obj, id, val);
    break;
    @@ -980,11 +1037,21 @@
    VALUE
    rb_ivar_defined(VALUE obj, ID id)
    {
  • VALUE klass, val;
  • st_data_t index;
    switch (TYPE(obj)) {
    case T_OBJECT:
  •    klass = rb_obj_class(obj);
    
  •    if (!RCLASS_IV_INDEX_TBL(klass)) break;
    
  •    if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &index)) break;
    
  •    if (ROBJECT_LEN(obj) <= index) break;
    
  •    val = ROBJECT_PTR(obj)[index];
    
  •    if (val != Qundef)
    
  •        return Qtrue;
    
  • break;
    case T_CLASS:
    case T_MODULE:
  • if (ROBJECT(obj)->iv_tbl && st_lookup(ROBJECT(obj)->iv_tbl, id, 0))
  • if (RCLASS_IV_TBL(obj) && st_lookup(RCLASS_IV_TBL(obj), id, 0))
    return Qtrue;
    break;
    default:
    @@ -995,9 +1062,70 @@
    return Qfalse;
    }

+struct obj_ivar_tag {

  • VALUE obj;
  • int (*func)(ID key, VALUE val, st_data_t arg);
  • st_data_t arg;
    +};

static int
-ivar_i(ID key, struct global_entry *entry, VALUE ary)
+obj_ivar_i(ID key, VALUE index, struct obj_ivar_tag *data)
{

  • if (index < ROBJECT_LEN(data->obj)) {
  •    VALUE val = ROBJECT_PTR(data->obj)[index];
    
  •    if (val != Qundef) {
    
  •        return (data->func)(key, val, data->arg);
    
  •    }
    
  • }
  • return ST_CONTINUE;
    +}

+static void
+obj_ivar_each(VALUE obj, int (*func)(ANYARGS), st_data_t arg)
+{

  • VALUE klass = rb_obj_class(obj);
  • st_table *tbl;
  • struct obj_ivar_tag data;
  • tbl = RCLASS_IV_INDEX_TBL(klass);
  • if (!tbl)
  •    return;
    
  • data.obj = obj;
  • data.func = func;
  • data.arg = arg;
  • st_foreach_safe(tbl, obj_ivar_i, (st_data_t)&data);
    +}

+void rb_ivar_foreach(VALUE obj, int (*func)(ANYARGS), st_data_t arg)
+{

  • switch (TYPE(obj)) {
  •  case T_OBJECT:
    
  •    obj_ivar_each(obj, func, arg);
    
  • break;
  •  case T_CLASS:
    
  •  case T_MODULE:
    
  • if (RCLASS_IV_TBL(obj)) {
  •  st_foreach_safe(RCLASS_IV_TBL(obj), func, arg);
    
  • }
  • break;
  •  default:
    
  • if (!generic_iv_tbl) break;
  • if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
  •  st_data_t tbl;
    
  •  if (st_lookup(generic_iv_tbl, obj, &tbl)) {
    
  • st_foreach_safe((st_table *)tbl, func, arg);
  •  }
    
  • }
  • break;
  • }
    +}

+static int
+ivar_i(ID key, VALUE val, VALUE ary)
+{
if (rb_is_instance_id(key)) {
rb_ary_push(ary, ID2SYM(key));
}
@@ -1027,25 +1155,7 @@
VALUE ary;

 ary = rb_ary_new();
  • switch (TYPE(obj)) {
  •  case T_OBJECT:
    
  •  case T_CLASS:
    
  •  case T_MODULE:
    
  • if (ROBJECT(obj)->iv_tbl) {
  •  st_foreach_safe(ROBJECT(obj)->iv_tbl, ivar_i, ary);
    
  • }
  • break;
  •  default:
    
  • if (!generic_iv_tbl) break;
  • if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
  •  st_data_t tbl;
    
  •  if (st_lookup(generic_iv_tbl, obj, &tbl)) {
    
  • st_foreach_safe((st_table *)tbl, ivar_i, ary);
  •  }
    
  • }
  • break;
  • }
  • rb_ivar_foreach(obj, ivar_i, ary);
    return ary;
    }

@@ -1076,6 +1186,8 @@
{
VALUE val = Qnil;
ID id = rb_to_id(name);

  • VALUE klass;

  • st_data_t index;

    if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
    rb_raise(rb_eSecurityError, “Insecure: can’t modify instance
    variable”);
    @@ -1086,9 +1198,19 @@

    switch (TYPE(obj)) {
    case T_OBJECT:

  •    klass = rb_obj_class(obj);
    
  •    if (!RCLASS_IV_INDEX_TBL(klass)) break;
    
  •    if (!st_lookup(RCLASS_IV_INDEX_TBL(klass), id, &index)) break;
    
  •    if (ROBJECT_LEN(obj) <= index) break;
    
  •    val = ROBJECT_PTR(obj)[index];
    
  •    if (val != Qundef) {
    
  •        ROBJECT_PTR(obj)[index] = Qundef;
    
  •        return val;
    
  •    }
    
  • break;
    case T_CLASS:
    case T_MODULE:

  • if (ROBJECT(obj)->iv_tbl && st_delete(ROBJECT(obj)->iv_tbl,
    (st_data_t*)&id, &val)) {
  • if (RCLASS_IV_TBL(obj) && st_delete(RCLASS_IV_TBL(obj),
    (st_data_t*)&id, &val)) {
    return val;
    }
    break;
    @@ -1185,11 +1307,11 @@
    rb_raise(rb_eArgError, “empty file name”);
    }
  • if ((tbl = RCLASS(mod)->iv_tbl) && st_lookup(tbl, id, &av) && av !=
    Qundef)
  • if ((tbl = RCLASS_IV_TBL(mod)) && st_lookup(tbl, id, &av) && av !=
    Qundef)
    return;

    rb_const_set(mod, id, Qundef);

  • tbl = RCLASS(mod)->iv_tbl;
  • tbl = RCLASS_IV_TBL(mod);
    if (st_lookup(tbl, autoload, &av)) {
    tbl = check_autoload_table(av);
    }
    @@ -1210,8 +1332,8 @@
    VALUE val;
    st_data_t load = 0;
  • st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, 0);
  • if (st_lookup(RCLASS(mod)->iv_tbl, autoload, &val)) {
  • st_delete(RCLASS_IV_TBL(mod), (st_data_t*)&id, 0);
  • if (st_lookup(RCLASS_IV_TBL(mod), autoload, &val)) {
    struct st_table *tbl = check_autoload_table(val);

st_delete(tbl, (st_data_t*)&id, &load);
@@ -1220,7 +1342,7 @@
DATA_PTR(val) = 0;
st_free_table(tbl);
id = autoload;

  •  if (st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, &val)) {
    
  •  if (st_delete(RCLASS_IV_TBL(mod), (st_data_t*)&id, &val)) {
    
    rb_gc_force_recycle(val);
    }
    }
    @@ -1248,7 +1370,7 @@
    struct st_table *tbl;
    st_data_t load;
  • if (!st_lookup(RCLASS(mod)->iv_tbl, autoload, &val) ||
  • if (!st_lookup(RCLASS_IV_TBL(mod), autoload, &val) ||
    !(tbl = check_autoload_table(val)) || !st_lookup(tbl, id, &load)) {
    return Qnil;
    }
    @@ -1267,7 +1389,7 @@
    DATA_PTR(val) = 0;
    st_free_table(tbl);
    id = autoload;
  • if (st_delete(RCLASS(mod)->iv_tbl, (st_data_t*)&id, &val)) {
  • if (st_delete(RCLASS_IV_TBL(mod), (st_data_t*)&id, &val)) {
    rb_gc_force_recycle(val);
    }
    }
    @@ -1277,7 +1399,7 @@
    VALUE
    rb_autoload_p(VALUE mod, ID id)
    {
  • struct st_table *tbl = RCLASS(mod)->iv_tbl;
  • struct st_table *tbl = RCLASS_IV_TBL(mod);
    VALUE val;

    if (!tbl || !st_lookup(tbl, id, &val) || val != Qundef) {
    @@ -1295,7 +1417,7 @@
    tmp = klass;
    retry:
    while (tmp && !NIL_P(tmp)) {

  • while (RCLASS(tmp)->iv_tbl &&
    st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) {
  • while (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp),id,&value))
    {
    if (value == Qundef) {
    if (!RTEST(rb_autoload_load(tmp, id))) break;
    continue;
    @@ -1307,7 +1429,7 @@
    return value;
    }
    if (!recurse && klass != rb_cObject) break;
  • tmp = RCLASS(tmp)->super;
  • tmp = RCLASS_SUPER(tmp);
    }
    if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) {
    mod_retry = 1;
    @@ -1360,7 +1482,7 @@
    rb_raise(rb_eSecurityError, “Insecure: can’t remove constant”);
    if (OBJ_FROZEN(mod)) rb_error_frozen(“class/module”);
  • if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl,
    (st_data_t*)&id, &val)) {
  • if (RCLASS_IV_TBL(mod) && st_delete(RCLASS_IV_TBL(mod),
    (st_data_t*)&id, &val)) {
    if (val == Qundef) {
    autoload_delete(mod, id);
    val = Qnil;
    @@ -1394,8 +1516,8 @@
    if (!tbl) {
    tbl = st_init_numtable();
    }
  • if (RCLASS(mod)->iv_tbl) {
  • st_foreach_safe(RCLASS(mod)->iv_tbl, sv_i, (st_data_t)tbl);
  • if (RCLASS_IV_TBL(mod)) {
  • st_foreach_safe(RCLASS_IV_TBL(mod), sv_i, (st_data_t)tbl);
    }
    return tbl;
    }
    @@ -1406,7 +1528,7 @@
    VALUE tmp = mod;
    for (;:wink: {
    data = rb_mod_const_at(tmp, data);
  • tmp = RCLASS(tmp)->super;
  • tmp = RCLASS_SUPER(tmp);
    if (!tmp) break;
    if (tmp == rb_cObject && mod != rb_cObject) break;
    }
    @@ -1479,13 +1601,13 @@
    tmp = klass;
    retry:
    while (tmp) {
  • if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl, id,
    &value)) {
  • if (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp), id, &value))
    {
    if (value == Qundef && NIL_P(autoload_file(klass, id)))
    return Qfalse;
    return Qtrue;
    }
    if (!recurse && klass != rb_cObject) break;
  • tmp = RCLASS(tmp)->super;
  • tmp = RCLASS_SUPER(tmp);
    }
    if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) {
    mod_retry = 1;
    @@ -1528,13 +1650,13 @@
    rb_error_frozen(“class”);
    }
    }
  • if (!RCLASS(klass)->iv_tbl) {
  • RCLASS(klass)->iv_tbl = st_init_numtable();
  • if (!RCLASS_IV_TBL(klass)) {
  • RCLASS_IV_TBL(klass) = st_init_numtable();
    }
    else if (isconst) {
    VALUE value = Qfalse;
  • if (st_lookup(RCLASS(klass)->iv_tbl, id, &value)) {
  • if (st_lookup(RCLASS_IV_TBL(klass), id, &value)) {
    if (value == Qundef)
    autoload_delete(klass, id);
    else
    @@ -1545,7 +1667,7 @@
    if(isconst){
    rb_vm_change_state();
    }
  • st_insert(RCLASS(klass)->iv_tbl, id, val);
  • st_insert(RCLASS_IV_TBL(klass), id, val);
    }

void
@@ -1585,7 +1707,7 @@
}

#define CVAR_LOOKUP(v,r) do {\

  • if (RCLASS(klass)->iv_tbl &&
    st_lookup(RCLASS(klass)->iv_tbl,id,(v))) {\
  • if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),id,(v)))
    {
    return ®;
    }
    if (FL_TEST(klass, FL_SINGLETON) ) {
    @@ -1596,18 +1718,18 @@
    klass = obj;
    break;
    default:\
  •  klass = RCLASS(klass)->super;\
    
  •  klass = RCLASS_SUPER(klass);\
     break;\
    
    }
    }
    else {\
  • klass = RCLASS(klass)->super;\
  • klass = RCLASS_SUPER(klass);
    }
    while (klass) {\
  • if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl,id,(v)))
    {\
  • if (RCLASS_IV_TBL(klass) && st_lookup(RCLASS_IV_TBL(klass),id,(v)))
    {
    return ®;
    }\
  • klass = RCLASS(klass)->super;\
  • klass = RCLASS_SUPER(klass);
    }
    } while(0)

@@ -1695,8 +1817,8 @@
{
VALUE ary = rb_ary_new();

  • if (RCLASS(obj)->iv_tbl) {
  • st_foreach_safe(RCLASS(obj)->iv_tbl, cv_i, ary);
  • if (RCLASS_IV_TBL(obj)) {
  • st_foreach_safe(RCLASS_IV_TBL(obj), cv_i, ary);
    }
    return ary;
    }
    @@ -1734,7 +1856,7 @@
    rb_raise(rb_eSecurityError, “Insecure: can’t remove class variable”);
    if (OBJ_FROZEN(mod)) rb_error_frozen(“class/module”);
  • if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl,
    (st_data_t*)&id, &val)) {
  • if (RCLASS_IV_TBL(mod) && st_delete(RCLASS_IV_TBL(mod),
    (st_data_t*)&id, &val)) {
    return val;
    }
    if (rb_cvar_defined(mod, id)) {
    Index: object.c
    ===================================================================
    — object.c (e$B%j%S%8%g%se(B 13497)
    +++ object.c (e$B:n6H%3%T!<e(B)
    @@ -100,7 +100,7 @@
    rb_class_real(VALUE cl)
    {
    while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) {
  • cl = RCLASS(cl)->super;
  • cl = RCLASS_SUPER(cl);
    }
    return cl;
    }
    @@ -137,15 +137,34 @@
    rb_gc_copy_finalizer(dest, obj);
    switch (TYPE(obj)) {
    case T_OBJECT:
  •    if (!(RBASIC(dest)->flags & ROBJECT_EMBED) && 
    

ROBJECT_PTR(dest)) {

  •        xfree(ROBJECT_PTR(dest));
    
  •        ROBJECT(dest)->as.heap.ptr = 0;
    
  •        ROBJECT(dest)->as.heap.len = 0;
    
  •    }
    
  •    if (RBASIC(obj)->flags & ROBJECT_EMBED) {
    
  •        MEMCPY(ROBJECT(dest)->as.ary, ROBJECT(obj)->as.ary, VALUE, 
    

ROBJECT_EMBED_LEN_MAX);

  •        RBASIC(dest)->flags |= ROBJECT_EMBED;
    
  •    }
    
  •    else {
    
  •        long len = ROBJECT(obj)->as.heap.len;
    
  •        VALUE *ptr = ALLOC_N(VALUE, len);
    
  •        MEMCPY(ptr, ROBJECT(obj)->as.heap.ptr, VALUE, len);
    
  •        ROBJECT(dest)->as.heap.ptr = ptr;
    
  •        ROBJECT(dest)->as.heap.len = len;
    
  •        RBASIC(dest)->flags &= ~ROBJECT_EMBED;
    
  •    }
    
  •    break;
     case T_CLASS:
     case T_MODULE:
    
  • if (ROBJECT(dest)->iv_tbl) {
  •  st_free_table(ROBJECT(dest)->iv_tbl);
    
  •  ROBJECT(dest)->iv_tbl = 0;
    
  • if (RCLASS_IV_TBL(dest)) {
  •  st_free_table(RCLASS_IV_TBL(dest));
    
  •  RCLASS_IV_TBL(dest) = 0;
    
    }
  • if (ROBJECT(obj)->iv_tbl) {
  •  ROBJECT(dest)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl);
    
  • if (RCLASS_IV_TBL(obj)) {
  •  RCLASS_IV_TBL(dest) = st_copy(RCLASS_IV_TBL(obj));
    
    }
  •    break;
    
    }
    rb_funcall(dest, id_init_copy, 1, obj);
    }
    @@ -296,7 +315,7 @@
    rb_str_cat2(str, " …");
    }
    else {
  • st_foreach_safe(ROBJECT(obj)->iv_tbl, inspect_i, str);
  • rb_ivar_foreach(obj, inspect_i, str);
    }
    rb_str_cat2(str, “>”);
    RSTRING_PTR(str)[0] = ‘#’;
    @@ -321,15 +340,28 @@
    static VALUE
    rb_obj_inspect(VALUE obj)
    {
  • if (TYPE(obj) == T_OBJECT

  • && ROBJECT(obj)->iv_tbl

  • && ROBJECT(obj)->iv_tbl->num_entries > 0) {

  • VALUE str;

  • char *c;

  • c = rb_obj_classname(obj);

  • str = rb_sprintf("-<%s:%p", c, (void*)obj);

  • return rb_exec_recursive(inspect_obj, obj, str);

  • if (TYPE(obj) == T_OBJECT) {

  •    int has_ivar = 0;
    
  •    VALUE *ptr = ROBJECT_PTR(obj);
    
  •    long len = ROBJECT_LEN(obj);
    
  •    long i;
    
  •    for (i = 0; i < len; i++) {
    
  •        if (ptr[i] != Qundef) {
    
  •            has_ivar = 1;
    
  •            break;
    
  •        }
    
  •    }
    
  •    if (has_ivar) {
    
  •        VALUE str;
    
  •        char *c;
    
  •        c = rb_obj_classname(obj);
    
  •        str = rb_sprintf("-<%s:%p", c, (void*)obj);
    
  •        return rb_exec_recursive(inspect_obj, obj, str);
    
  •    }
    

    }
    return rb_funcall(obj, rb_intern(“to_s”), 0, 0);
    }
    @@ -402,9 +434,9 @@
    }

    while (cl) {

  • if (cl == c || RCLASS(cl)->m_tbl == RCLASS©->m_tbl)
  • if (cl == c || RCLASS_M_TBL(cl) == RCLASS_M_TBL©)
    return Qtrue;
  • cl = RCLASS(cl)->super;
  • cl = RCLASS_SUPER(cl);
    }
    return Qfalse;
    }
    @@ -1104,15 +1136,15 @@
    rb_raise(rb_eTypeError, “compared with non class/module”);
    }
    while (mod) {
  • if (RCLASS(mod)->m_tbl == RCLASS(arg)->m_tbl)
  • if (RCLASS_M_TBL(mod) == RCLASS_M_TBL(arg))
    return Qtrue;
  • mod = RCLASS(mod)->super;
  • mod = RCLASS_SUPER(mod);
    }
    /* not mod < arg; check if mod > arg */
    while (arg) {
  • if (RCLASS(arg)->m_tbl == RCLASS(start)->m_tbl)
  • if (RCLASS_M_TBL(arg) == RCLASS_M_TBL(start))
    return Qfalse;
  • arg = RCLASS(arg)->super;
  • arg = RCLASS_SUPER(arg);
    }
    return Qnil;
    }
    @@ -1277,7 +1309,7 @@
    {
    VALUE super;
  • if (RCLASS(klass)->super != 0) {
  • if (RCLASS_SUPER(klass) != 0) {
    rb_raise(rb_eTypeError, “already initialized class”);
    }
    if (rb_scan_args(argc, argv, “01”, &super) == 0) {
    @@ -1286,7 +1318,7 @@
    else {
    rb_check_inheritable(super);
    }
  • RCLASS(klass)->super = super;
  • RCLASS_SUPER(klass) = super;
    rb_make_metaclass(klass, RBASIC(super)->klass);
    rb_class_inherited(super, klass);
    rb_mod_initialize(klass);
    @@ -1308,7 +1340,7 @@
    {
    VALUE obj;
  • if (RCLASS(klass)->super == 0 && klass != rb_cBasicObject) {
  • if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
    rb_raise(rb_eTypeError, “can’t instantiate uninitialized class”);
    }
    if (FL_TEST(klass, FL_SINGLETON)) {
    @@ -1367,13 +1399,13 @@
    static VALUE
    rb_class_superclass(VALUE klass)
    {
  • VALUE super = RCLASS(klass)->super;
  • VALUE super = RCLASS_SUPER(klass);

    if (!super) {
    rb_raise(rb_eTypeError, “uninitialized class”);
    }
    while (TYPE(super) == T_ICLASS) {

  • super = RCLASS(super)->super;
  • super = RCLASS_SUPER(super);
    }
    if (!super) {
    return Qnil;
    Index: proc.c
    ===================================================================
    — proc.c (e$B%j%S%8%g%se(B 13497)
    +++ proc.c (e$B:n6H%3%T!<e(B)
    @@ -620,13 +620,13 @@
    body = body->nd_body;

    if (nd_type(body) == NODE_ZSUPER) {

  • klass = RCLASS(klass)->super;
  • klass = RCLASS_SUPER(klass);
    goto again;
    }

    while (rklass != klass &&
    (FL_TEST(rklass, FL_SINGLETON) || TYPE(rklass) == T_ICLASS)) {

  • rklass = RCLASS(rklass)->super;
  • rklass = RCLASS_SUPER(rklass);
    }
    if (TYPE(klass) == T_ICLASS)
    klass = RBASIC(klass)->klass;
    Index: thread.c
    ===================================================================
    — thread.c (e$B%j%S%8%g%se(B 13497)
    +++ thread.c (e$B:n6H%3%T!<e(B)
    @@ -2704,6 +2704,7 @@
    *root = hook->next;
    }
    xfree(hook);
  •        return 0;
    
    }
    prev = hook;
    hook = hook->next;
    Index: struct.c
    ===================================================================
    — struct.c (e$B%j%S%8%g%se(B 13497)
    +++ struct.c (e$B:n6H%3%T!<e(B)
    @@ -25,7 +25,7 @@
    for (;:wink: {
    if (rb_ivar_defined(c, id))
    return rb_ivar_get(c, id);
  • c = RCLASS©->super;
  • c = RCLASS_SUPER©;
    if (c == 0 || c == rb_cStruct)
    return Qnil;
    }
    Index: eval.c
    ===================================================================
    — eval.c (e$B%j%S%8%g%se(B 13497)
    +++ eval.c (e$B:n6H%3%T!<e(B)
    @@ -2295,7 +2295,7 @@
    if (nd_type(fbody->nd_body->nd_body) != NODE_ZSUPER) {
    break; /* normal case: need not to follow ‘super’ link */
    }
  •  m = RCLASS(m)->super;
    
  •  m = RCLASS_SUPER(m);
     if (!m)
    
    break;
    }
    Index: gc.c
    ===================================================================
    — gc.c (e$B%j%S%8%g%se(B 13497)
    +++ gc.c (e$B:n6H%3%T!<e(B)
    @@ -1013,9 +1013,9 @@
    case T_ICLASS:
    case T_CLASS:
    case T_MODULE:
  • mark_tbl(obj->as.klass.m_tbl, lev);
  • mark_tbl(obj->as.klass.iv_tbl, lev);
  • ptr = obj->as.klass.super;
  • mark_tbl(RCLASS_M_TBL(obj), lev);

  • mark_tbl(RCLASS_IV_TBL(obj), lev);

  • ptr = RCLASS_SUPER(obj);
    goto again;

    case T_ARRAY:
    

@@ -1050,7 +1050,13 @@
break;

   case T_OBJECT:
  • mark_tbl(obj->as.object.iv_tbl, lev);
  •    {
    
  •        long i, len = ROBJECT_LEN(obj);
    
  •  VALUE *ptr = ROBJECT_PTR(obj);
    
  •        for (i  = 0; i < len; i++) {
    
  • gc_mark(*ptr++, lev);

  •        }
    
  •    }
    

    break;

     case T_FILE:
    

@@ -1249,17 +1255,22 @@

 switch (RANY(obj)->as.basic.flags & T_MASK) {
   case T_OBJECT:
  • if (RANY(obj)->as.object.iv_tbl) {
  •  st_free_table(RANY(obj)->as.object.iv_tbl);
    
  • if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
  •        RANY(obj)->as.object.as.heap.ptr) {
    
  •  RUBY_CRITICAL(free(RANY(obj)->as.object.as.heap.ptr));
    
    }
    break;
    case T_MODULE:
    case T_CLASS:
    rb_clear_cache_by_class((VALUE)obj);
  • st_free_table(RANY(obj)->as.klass.m_tbl);
  • if (RANY(obj)->as.object.iv_tbl) {
  •  st_free_table(RANY(obj)->as.object.iv_tbl);
    
  • st_free_table(RCLASS_M_TBL(obj));
  • if (RCLASS_IV_TBL(obj)) {
  •  st_free_table(RCLASS_IV_TBL(obj));
    
    }
  • if (RCLASS_IV_INDEX_TBL(obj)) {
  •  st_free_table(RCLASS_IV_INDEX_TBL(obj));
    
  • }
  •    RUBY_CRITICAL(free(RANY(obj)->as.klass.ptr));
    
    break;
    case T_STRING:
    rb_str_free(obj);
    Index: class.c
    ===================================================================
    — class.c (e$B%j%S%8%g%se(B 13497)
    +++ class.c (e$B:n6H%3%T!<e(B)
    @@ -18,16 +18,27 @@

extern st_table *rb_class_tbl;

+static VALUE
+class_alloc(VALUE flags, VALUE klass)
+{

  • rb_classext_t *ext = ALLOC(rb_classext_t);
  • NEWOBJ(obj, struct RClass);
  • OBJSETUP(obj, klass, flags);
  • obj->ptr = ext;
  • RCLASS_IV_TBL(obj) = 0;
  • RCLASS_M_TBL(obj) = 0;
  • RCLASS_SUPER(obj) = 0;
  • RCLASS_IV_INDEX_TBL(obj) = 0;
  • return (VALUE)obj;
    +}

VALUE
rb_class_boot(VALUE super)
{

  • NEWOBJ(klass, struct RClass);
  • OBJSETUP(klass, rb_cClass, T_CLASS);
  • VALUE klass = class_alloc(T_CLASS, rb_cClass);
  • klass->super = super;
  • klass->iv_tbl = 0;
  • klass->m_tbl = 0; /* safe GC */
  • klass->m_tbl = st_init_numtable();
  • RCLASS_SUPER(klass) = super;

  • RCLASS_M_TBL(klass) = st_init_numtable();

    OBJ_INFECT(klass, super);
    return (VALUE)klass;
    @@ -87,21 +98,21 @@
    if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
    RBASIC(clone)->klass = rb_singleton_class_clone(orig);
    }

  • RCLASS(clone)->super = RCLASS(orig)->super;
  • if (RCLASS(orig)->iv_tbl) {
  • RCLASS_SUPER(clone) = RCLASS_SUPER(orig);
  • if (RCLASS_IV_TBL(orig)) {
    ID id;
  • RCLASS(clone)->iv_tbl = st_copy(RCLASS(orig)->iv_tbl);
  • RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(orig));
    id = rb_intern(“classpath”);
  • st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
  • st_delete(RCLASS_IV_TBL(clone), (st_data_t*)&id, 0);
    id = rb_intern(“classid”);
  • st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
  • st_delete(RCLASS_IV_TBL(clone), (st_data_t*)&id, 0);
    }
  • if (RCLASS(orig)->m_tbl) {
  • if (RCLASS_M_TBL(orig)) {
    struct clone_method_data data;
  • data.tbl = RCLASS(clone)->m_tbl = st_init_numtable();
  • data.tbl = RCLASS_M_TBL(clone) = st_init_numtable();
    data.klass = clone;
  • st_foreach(RCLASS(orig)->m_tbl, clone_method,
  • st_foreach(RCLASS_M_TBL(orig), clone_method,
    (st_data_t)&data);
    }

@@ -112,7 +123,7 @@
VALUE
rb_class_init_copy(VALUE clone, VALUE orig)
{

  • if (RCLASS(clone)->super != 0) {
  • if (RCLASS_SUPER(clone) != 0) {
    rb_raise(rb_eTypeError, “already initialized class”);
    }
    if (FL_TEST(orig, FL_SINGLETON)) {
    @@ -131,8 +142,7 @@
    else {
    struct clone_method_data data;
    /* copy singleton(unnamed) class */
  • NEWOBJ(clone, struct RClass);
  • OBJSETUP(clone, 0, RBASIC(klass)->flags);
  •    VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
    

    if (BUILTIN_TYPE(obj) == T_CLASS) {
    RBASIC(clone)->klass = (VALUE)clone;
    @@ -141,16 +151,14 @@
    RBASIC(clone)->klass = rb_singleton_class_clone(klass);
    }

  • clone->super = RCLASS(klass)->super;
  • clone->iv_tbl = 0;
  • clone->m_tbl = 0;
  • if (RCLASS(klass)->iv_tbl) {
  •  clone->iv_tbl = st_copy(RCLASS(klass)->iv_tbl);
    
  • RCLASS_SUPER(clone) = RCLASS_SUPER(klass);
  • if (RCLASS_IV_TBL(klass)) {
  •  RCLASS_IV_TBL(clone) = st_copy(RCLASS_IV_TBL(klass));
    
    }
  • clone->m_tbl = st_init_numtable();
  • data.tbl = clone->m_tbl;
  • RCLASS_M_TBL(clone) = st_init_numtable();
  • data.tbl = RCLASS_M_TBL(clone);
    data.klass = (VALUE)clone;
  • st_foreach(RCLASS(klass)->m_tbl, clone_method,
  • st_foreach(RCLASS_M_TBL(klass), clone_method,
    (st_data_t)&data);
    rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
    FL_SET(clone, FL_SINGLETON);
    @@ -162,10 +170,10 @@
    rb_singleton_class_attached(VALUE klass, VALUE obj)
    {
    if (FL_TEST(klass, FL_SINGLETON)) {
  • if (!RCLASS(klass)->iv_tbl) {
  •  RCLASS(klass)->iv_tbl = st_init_numtable();
    
  • if (!RCLASS_IV_TBL(klass)) {
  •  RCLASS_IV_TBL(klass) = st_init_numtable();
    
    }
  • st_insert(RCLASS(klass)->iv_tbl, rb_intern(“attached”), obj);
  • st_insert(RCLASS_IV_TBL(klass), rb_intern(“attached”), obj);
    }
    }

@@ -223,7 +231,7 @@
if (TYPE(klass) != T_CLASS) {
rb_raise(rb_eTypeError, “%s is not a class”, name);
}

  • if (rb_class_real(RCLASS(klass)->super) != super) {
  • if (rb_class_real(RCLASS_SUPER(klass)) != super) {
    rb_name_error(id, “%s is already defined”, name);
    }
    return klass;
    @@ -252,7 +260,7 @@
    if (TYPE(klass) != T_CLASS) {
    rb_raise(rb_eTypeError, “%s is not a class”, name);
    }
  • if (rb_class_real(RCLASS(klass)->super) != super) {
  • if (rb_class_real(RCLASS_SUPER(klass)) != super) {
    rb_name_error(id, “%s is already defined”, name);
    }
    return klass;
    @@ -272,13 +280,9 @@
    VALUE
    rb_module_new(void)
    {
  • NEWOBJ(mdl, struct RClass);
  • OBJSETUP(mdl, rb_cModule, T_MODULE);
  • VALUE mdl = class_alloc(T_MODULE, rb_cModule);
  • mdl->super = 0;
  • mdl->iv_tbl = 0;
  • mdl->m_tbl = 0;
  • mdl->m_tbl = st_init_numtable();
  • RCLASS_M_TBL(mdl) = st_init_numtable();

    return (VALUE)mdl;
    }
    @@ -338,18 +342,17 @@
    static VALUE
    include_class_new(VALUE module, VALUE super)
    {

  • NEWOBJ(klass, struct RClass);
  • OBJSETUP(klass, rb_cClass, T_ICLASS);
  • VALUE klass = class_alloc(T_ICLASS, rb_cClass);

    if (BUILTIN_TYPE(module) == T_ICLASS) {
    module = RBASIC(module)->klass;
    }

  • if (!RCLASS(module)->iv_tbl) {
  • RCLASS(module)->iv_tbl = st_init_numtable();
  • if (!RCLASS_IV_TBL(module)) {
  • RCLASS_IV_TBL(module) = st_init_numtable();
    }
  • klass->iv_tbl = RCLASS(module)->iv_tbl;
  • klass->m_tbl = RCLASS(module)->m_tbl;
  • klass->super = super;
  • RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module);
  • RCLASS_M_TBL(klass) = RCLASS_M_TBL(module);
  • RCLASS_SUPER(klass) = super;
    if (TYPE(module) == T_ICLASS) {
    RBASIC(klass)->klass = RBASIC(module)->klass;
    }
    @@ -382,13 +385,13 @@
    while (module) {
    int superclass_seen = Qfalse;
  • if (RCLASS(klass)->m_tbl == RCLASS(module)->m_tbl)
  • if (RCLASS_M_TBL(klass) == RCLASS_M_TBL(module))
    rb_raise(rb_eArgError, “cyclic include detected”);
    /* ignore if the module included already in superclasses */
  •   for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
    
  •   for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) {
          switch (BUILTIN_TYPE(p)) {
            case T_ICLASS:
    
  •           if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
    
  •           if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {
                  if (!superclass_seen) {
                      c = p;  /* move insertion point */
                  }
    

@@ -400,10 +403,10 @@
break;
}
}

  •   c = RCLASS(c)->super = include_class_new(module, 
    

RCLASS©->super);

  •   c = RCLASS_SUPER(c) = include_class_new(module, 
    

RCLASS_SUPER©);
changed = 1;
skip:

  • module = RCLASS(module)->super;
  • module = RCLASS_SUPER(module);
    }
    if (changed) rb_clear_cache();
    }
    @@ -431,7 +434,7 @@
    VALUE ary = rb_ary_new();
    VALUE p;
  • for (p = RCLASS(mod)->super; p; p = RCLASS§->super) {
  • for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER§) {
    if (BUILTIN_TYPE§ == T_ICLASS) {
    rb_ary_push(ary, RBASIC§->klass);
    }
    @@ -464,7 +467,7 @@
    VALUE p;

    Check_Type(mod2, T_MODULE);

  • for (p = RCLASS(mod)->super; p; p = RCLASS§->super) {
  • for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER§) {
    if (BUILTIN_TYPE§ == T_ICLASS) {
    if (RBASIC§->klass == mod2) return Qtrue;
    }
    @@ -493,7 +496,7 @@
    {
    VALUE p, ary = rb_ary_new();
  • for (p = mod; p; p = RCLASS§->super) {
  • for (p = mod; p; p = RCLASS_SUPER§) {
    if (FL_TEST(p, FL_SINGLETON))
    continue;
    if (BUILTIN_TYPE§ == T_ICLASS) {
    @@ -599,8 +602,8 @@
    }

    list = st_init_numtable();

  • for (; mod; mod = RCLASS(mod)->super) {
  • st_foreach(RCLASS(mod)->m_tbl, method_entry, (st_data_t)list);
  • for (; mod; mod = RCLASS_SUPER(mod)) {
  • st_foreach(RCLASS_M_TBL(mod), method_entry, (st_data_t)list);
    if (BUILTIN_TYPE(mod) == T_ICLASS) continue;
    if (FL_TEST(mod, FL_SINGLETON)) continue;
    if (!recur) break;
    @@ -756,13 +759,13 @@
    klass = CLASS_OF(obj);
    list = st_init_numtable();
    if (klass && FL_TEST(klass, FL_SINGLETON)) {
  • st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
  • klass = RCLASS(klass)->super;
  • st_foreach(RCLASS_M_TBL(klass), method_entry, (st_data_t)list);
  • klass = RCLASS_SUPER(klass);
    }
    if (RTEST(recur)) {
    while (klass && (FL_TEST(klass, FL_SINGLETON) || TYPE(klass) ==
    T_ICLASS)) {
  •  st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
    
  •  klass = RCLASS(klass)->super;
    
  •  st_foreach(RCLASS_M_TBL(klass), method_entry, (st_data_t)list);
    
  •  klass = RCLASS_SUPER(klass);
    
    }
    }
    ary = rb_ary_new();
    Index: ext/digest/digest.c
    ===================================================================
    — ext/digest/digest.c (e$B%j%S%8%g%se(B 13497)
    +++ ext/digest/digest.c (e$B:n6H%3%T!<e(B)
    @@ -426,7 +426,7 @@
    VALUE obj;
    rb_digest_metadata_t *algo;
  • for (p = klass; p; p = RCLASS§->super) {
  • for (p = klass; p; p = RCLASS_SUPER§) {
    if (rb_ivar_defined(p, id_metadata)) {
    obj = rb_ivar_get(p, id_metadata);
    break;
    Index: error.c
    ===================================================================
    — error.c (e$B%j%S%8%g%se(B 13497)
    +++ error.c (e$B:n6H%3%T!<e(B)
    @@ -971,7 +971,7 @@
    VALUE klass = CLASS_OF(exc);

while (TYPE(klass) == T_ICLASS || FL_TEST(klass, FL_SINGLETON)) {

  •  klass = (VALUE)RCLASS(klass)->super;
    
  •  klass = (VALUE)RCLASS_SUPER(klass);
    

    }
    num = rb_const_get(klass, rb_intern(“Errno”));
    }
    Index: vm.c
    ===================================================================
    — vm.c (e$B%j%S%8%g%se(B 13497)
    +++ vm.c (e$B:n6H%3%T!<e(B)
    @@ -502,7 +502,7 @@

    if (!cfp->iseq) {
    klass = cfp->method_klass;

  • klass = RCLASS(klass)->super;
  • klass = RCLASS_SUPER(klass);

    if (klass == 0) {
    klass = vm_search_normal_super_klass(cfp->method_klass, recv);
    @@ -1000,7 +1000,7 @@
    add_opt_method(VALUE klass, ID mid, VALUE bop)
    {
    NODE *node;

  • if (st_lookup(RCLASS(klass)->m_tbl, mid, (void *)&node) &&
  • if (st_lookup(RCLASS_M_TBL(klass), mid, (void *)&node) &&
    nd_type(node->nd_body->nd_body) == NODE_CFUNC) {
    st_insert(vm_opt_method_table, (st_data_t)node, (st_data_t)bop);
    }
    Index: marshal.c
    ===================================================================
    — marshal.c (e$B%j%S%8%g%se(B 13497)
    +++ marshal.c (e$B:n6H%3%T!<e(B)
    @@ -377,17 +377,17 @@
    char *path;

    if (check && FL_TEST(klass, FL_SINGLETON)) {

  • if (RCLASS(klass)->m_tbl->num_entries ||
  •  (RCLASS(klass)->iv_tbl && RCLASS(klass)->iv_tbl->num_entries > 
    

1)) {

  • if (RCLASS_M_TBL(klass)->num_entries ||
  •  (RCLASS_IV_TBL(klass) && RCLASS_IV_TBL(klass)->num_entries > 1)) 
    

{
rb_raise(rb_eTypeError, “singleton can’t be dumped”);
}

  • klass = RCLASS(klass)->super;
  • klass = RCLASS_SUPER(klass);
    }
    while (BUILTIN_TYPE(klass) == T_ICLASS) {
    path = rb_class2name(RBASIC(klass)->klass);
    w_byte(TYPE_EXTENDED, arg);
    w_unique(path, arg);
  • klass = RCLASS(klass)->super;
  • klass = RCLASS_SUPER(klass);
    }
    }

@@ -444,6 +444,25 @@
}

static void
+w_objivar(VALUE obj, struct dump_call_arg *arg)
+{

  • VALUE *ptr;
  • long i, len, num;
  • len = ROBJECT_LEN(obj);
  • ptr = ROBJECT_PTR(obj);
  • num = 0;
  • for (i = 0; i < len; i++)
  •    if (ptr[i] != Qundef)
    
  •        num += 1;
    
  • w_long(num, arg->arg);
  • if (num != 0) {
  •    rb_ivar_foreach(obj, w_obj_each, (st_data_t)arg);
    
  • }
    +}

+static void
w_object(VALUE obj, struct dump_arg *arg, int limit)
{
struct dump_call_arg c_arg;
@@ -654,7 +673,7 @@

 case T_OBJECT:
   w_class(TYPE_OBJECT, obj, arg, Qtrue);
  •  w_ivar(ROBJECT(obj)->iv_tbl, &c_arg);
    
  •  w_objivar(obj, &c_arg);
     break;
    

    case T_DATA:
    Index: insnhelper.ci
    ===================================================================
    — insnhelper.ci (e$B%j%S%8%g%se(B 13497)
    +++ insnhelper.ci (e$B:n6H%3%T!<e(B)
    @@ -507,7 +507,7 @@
    break;
    }
    case NODE_ZSUPER:{

  •  klass = RCLASS(mn->nd_clss)->super;
    
  •  klass = RCLASS_SUPER(mn->nd_clss);
     mn = rb_method_node(klass, id);
    
     if (mn != 0) {
    

@@ -998,8 +998,8 @@
}
}
search_continue:

  •  if (RCLASS(klass)->iv_tbl &&
    
  • st_lookup(RCLASS(klass)->iv_tbl, id, &val)) {
  •  if (RCLASS_IV_TBL(klass) &&
    
  • st_lookup(RCLASS_IV_TBL(klass), id, &val)) {
    if (val == Qundef) {
    rb_autoload_load(klass, id);
    goto search_continue;
    @@ -1122,16 +1122,16 @@
    vm_search_normal_super_klass(VALUE klass, VALUE recv)
    {
    if (BUILTIN_TYPE(klass) == T_CLASS) {
  • klass = RCLASS(klass)->super;
  • klass = RCLASS_SUPER(klass);
    }
    else if (BUILTIN_TYPE(klass) == T_MODULE) {
    VALUE k = CLASS_OF(recv);
    while (k) {
    if (BUILTIN_TYPE(k) == T_ICLASS && RBASIC(k)->klass == klass) {
  • klass = RCLASS(k)->super;
  • klass = RCLASS_SUPER(k);
    break;
    }
  •  k = RCLASS(k)->super;
    
  •  k = RCLASS_SUPER(k);
    
    }
    }
    return klass;