public
Description: a 1.8.6 compatible version of why's mixico lib + thread safety
Homepage:
Clone URL: git://github.com/coderrr/mixico-inline.git
mixico-inline / lib / mixico.rb
100644 146 lines (126 sloc) 3.55 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require 'rubygems'
require 'inline'
 
class Module
  inline do |builder|
    builder.c %{
static VALUE
disable_mixin(VALUE super)
{
VALUE p, kid;
VALUE module = self;
 
Check_Type(super, T_MODULE);
for (kid = module, p = RCLASS(module)->super; p; kid = p, p = RCLASS(p)->super) {
if (BUILTIN_TYPE(p) == T_ICLASS) {
if (RBASIC(p)->klass == super) {
RCLASS(kid)->super = RCLASS(p)->super;
rb_clear_cache_by_class(module);
return p;
}
}
}
 
return Qnil;
}
}
 
    builder.c %{
static VALUE
enable_mixin(VALUE mixin)
{
VALUE p;
VALUE module = self;
 
Check_Type(mixin, T_ICLASS);
Check_Type(RBASIC(mixin)->klass, T_MODULE);
for (p = module; p; p = RCLASS(p)->super) {
if (RCLASS(p)->super == RCLASS(mixin)->super) {
RCLASS(p)->super = mixin;
rb_clear_cache_by_class(module);
return RBASIC(mixin)->klass;
}
}
 
return Qnil;
}
}
  end
end
 
class Object
  inline do |builder|
    builder.prefix %{
#define KLASS_OF(o) RCLASS(RBASIC(o)->klass)
}
 
    builder.c %{
void redirect_tbls(VALUE obj) {
unsigned long orig_iv_tbl, orig_m_tbl;
orig_iv_tbl = (unsigned long)ROBJECT(self)->iv_tbl;
orig_m_tbl = (unsigned long)KLASS_OF(self)->m_tbl;
ROBJECT(self)->iv_tbl = ROBJECT(obj)->iv_tbl;
KLASS_OF(self)->m_tbl = KLASS_OF(obj)->m_tbl;
rb_iv_set(self, "__orig_m_tbl__", orig_m_tbl);
rb_iv_set(self, "__orig_iv_tbl__", orig_iv_tbl);
}
}
 
    # restore needed, or else GC will crash
    builder.c %{
void restore_tbls() {
KLASS_OF(self)->m_tbl = (struct st_table *)rb_iv_get(self, "__orig_m_tbl__");
ROBJECT(self)->iv_tbl = (struct st_table *)rb_iv_get(self, "__orig_iv_tbl__");
}
}
  end
end
 
class Class
  inline do |builder|
    builder.c %{
void redirect_tbls(VALUE class) {
unsigned long orig_iv_tbl, orig_m_tbl;
orig_iv_tbl = (unsigned long)RCLASS(self)->iv_tbl;
orig_m_tbl = (unsigned long)RCLASS(self)->m_tbl;
RCLASS(self)->iv_tbl = RCLASS(class)->iv_tbl;
RCLASS(self)->m_tbl = RCLASS(class)->m_tbl;
rb_iv_set(self, "__orig_iv_tbl__", orig_iv_tbl);
rb_iv_set(self, "__orig_m_tbl__", orig_m_tbl);
}
}
 
    # restore needed, or else GC will crash
    builder.c %{
void restore_tbls() {
RCLASS(self)->m_tbl = rb_iv_get(self, "__orig_m_tbl__");
RCLASS(self)->iv_tbl = rb_iv_get(self, "__orig_iv_tbl__");
}
}
  end
end
 
class Proc
  def mixin mod
    context.extend mod
  end
 
  def mixout mod
    (class << context; self end).disable_mixin mod
  end
  
  def context
    eval('self', self)
  end
end
 
class Module
  def mix_eval mod, *args, &blk
    blk.mixin mod
    begin
      yield *args
    ensure
      blk.mixout mod
    end
  end
  
  def safe_mix_eval mod, &blk
    duped_context = blk.context.dup
    # make sure the singleton class is in existence
    class << duped_context; self; end
 
    duped_context.redirect_tbls(blk.context)
 
    duped_context.extend mod
    begin
      m = duped_context.is_a?(Module) ? :class_eval : :instance_eval
      duped_context.send(m, &blk)
    ensure
      duped_context.restore_tbls
      (class << duped_context; self; end).disable_mixin mod
    end
  end
 
  alias_method :mix_exec, :mix_eval
end