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