a ruby2c C generator for use in the any ruby interpreter (generates C suitable for use as a standard Ruby extension)
add a new ruby function to the current @cp
# File samples/dynamic_ruby.rb, line 1603 def self.compile(klass, *methlist) @rcp ||= new methlist.each { |meth| ast = RubyHack.read_method_ast(klass, meth) @rcp.compile(ast, klass, meth) } self end
# File samples/dynamic_ruby.rb, line 1612 def self.compile_singleton(klass, *methlist) @rcp ||= new methlist.each { |meth| ast = RubyHack.read_singleton_method_ast(klass, meth) @rcp.compile(ast, klass, meth, true) } self end
# File samples/dynamic_ruby.rb, line 1621 def self.dump "#ifdef __ELF__ asm .pt_gnu_stack rw; #endif " + @rcp.cp.dump_definition('Init_compiledruby') end
# File samples/dynamic_ruby.rb, line 1633 def initialize(cp=nil) super(cp) @cp.parse <<EOS // static VALUE method(VALUE self, VALUE arg0, VALUE arg1) { return (VALUE)0; } // static VALUE const_Lol; static void do_init_once(void) { // const_Lol = rb_const_get(*rb_cObject, rb_intern("Lol")); // rb_define_method(const_Lol, "method", method, 2); } int Init_compiledruby(void) __attribute__((export)) { // use a separate func to avoid having to append statements before the 'return' do_init_once(); return 0; } EOS end
# File samples/dynamic_ruby.rb, line 1657 def compile(ast, klass, method, singleton=false) @compiled_func_cache ||= {} mname = super(ast, klass, method, singleton) return if not mname @compiled_func_cache[[klass, method.to_s, singleton]] = @cur_cfunc cls = rb_const(nil, klass) init.statements << fcall("rb_define#{'_singleton' if singleton}_method", cls, method.to_s, @cur_cfunc, method_arity) mname end
# File samples/dynamic_ruby.rb, line 1672 def declare_newtopvar(name, initializer, type=value) v = C::Variable.new(name, type) v.storage = :static @cp.toplevel.symbol[v.name] = v pos = @cp.toplevel.statements.index @cp.toplevel.statements.find { |st| st.kind_of? C::Declaration and st.var.type.kind_of? C::Function and st.var.initializer } || -1 @cp.toplevel.statements.insert pos, C::Declaration.new(v) if initializer pos = -1 if name =~ /^intern_/ pos = 0 init.statements.each { |st| break unless st.kind_of? C::CExpression and st.op == :'=' and st.lexpr.kind_of? C::Variable and st.lexpr.name < name pos += 1 } end init.statements.insert(pos, C::CExpression[v, :'=', initializer]) end v end
# File samples/dynamic_ruby.rb, line 1629 def dump(m="Init_compiledruby") m ? @cp.dump_definition(m, 'do_init_once') : @cp.to_s end
# File samples/dynamic_ruby.rb, line 1735 def get_cfuncptr(klass, method, singleton=false) # is it a func we have in the current cparser ? if ptr = @compiled_func_cache[[klass, method.to_s, singleton]] return ptr end # check if it's a C or ruby func in the current interpreter cls = singleton ? (class << klass ; self ; end) : klass ptr = RubyHack.get_method_node_ptr(cls, method) return if ptr == 0 ftype = RubyHack::NODETYPE[(RubyHack.memory_read_int(ptr) >> 11) & 0xff] return if ftype != :cfunc # ok, so assume it will be the same next time n = escape_varname "fptr_#{klass.name}#{singleton ? '.' : '#'}#{method}".gsub('::', '_') if not v = @cp.toplevel.symbol[n] v = get_cfuncptr_dyn(klass, method, singleton, n) end v end
# File samples/dynamic_ruby.rb, line 1757 def get_cfuncptr_dyn(klass, method, singleton, n) arity = singleton ? klass.method(method).arity : klass.instance_method(method).arity fproto = C::Function.new(value, []) case arity when -1; fproto.args << C::Variable.new(nil, C::BaseType.new(:int)) << C::Variable.new(nil, C::Pointer.new(value)) << C::Variable.new(nil, value) when -2; fproto.args << C::Variable.new(nil, value) << C::Variable.new(nil, value) else (arity+1).times { fproto.args << C::Variable.new(nil, value) } end if not ptr = init.symbol['ptr'] ptr = C::Variable.new('ptr', C::Pointer.new(C::BaseType.new(:int))) init.symbol[ptr.name] = ptr init.statements << C::Declaration.new(ptr) end cls = rb_const(nil, klass) cls = fcall('rb_singleton_class', cls) if singleton init.statements << C::CExpression[ptr, :'=', fcall('rb_method_node', cls, rb_intern(method))] # dynamically recheck that klass#method is a :cfunc cnd = C::CExpression[[:'!', ptr], :'||', [[[[ptr, :'[]', [0]], :>>, [11]], :&, [0xff]], :'!=', [RubyHack::NODETYPE.index(:cfunc)]]] init.statements << C::If.new(cnd, rb_raise("CFunc expected at #{klass}#{singleton ? '.' : '#'}#{method}"), nil) vi = C::CExpression[[ptr, :'[]', [1]], C::Pointer.new(fproto)] declare_newtopvar(n, vi, C::Pointer.new(fproto)) end
returns the 'do_init_once' function body
# File samples/dynamic_ruby.rb, line 1653 def init @cp.toplevel.symbol['do_init_once'].initializer end
#rb_const 'FOO', Bar::Baz ==>
const_Bar = rb_const_get(rb_cObject, rb_intern("Bar")); const_Bar_Baz = rb_const_get(const_Bar, rb_intern("Baz")); const_Bar_Baz_FOO = rb_const_get(const_Bar_Baz, rb_intern("FOO"));
use #rb_const(nil, class) to get a pointer to a class/module
# File samples/dynamic_ruby.rb, line 1706 def rb_const(constname, owner = resolve_const_owner(constname)) raise Fail, "no dynamic constant resolution #{constname}" if not owner @const_value ||= { [::Object, 'Object'] => rb_global('rb_cObject') } k = ::Object v = nil cname = owner.name cname += '::' + constname if constname cname.split('::').each { |n| kk = k.const_get(n) if not v = @const_value[[k, n]] # class A ; end ; B = A => B.name => 'A' vn = "const_#{escape_varname((k.name + '::' + n).sub(/^Object::/, '').gsub('::', '_'))}" vi = fcall('rb_const_get', rb_const(nil, k), fcall('rb_intern', n)) v = declare_newtopvar(vn, vi) # n wont be reused, so do not alloc a global intern_#{n} for this @const_value[[k, n]] = v end k = kk } v end
dynamic trace of all #rb_funcall made from our module
# File samples/dynamic_ruby.rb, line 1786 def rb_funcall(recv, meth, *args) if not defined? @rb_fcid @cp.parse <<EOS int atexit(void(*)(void)); int printf(char*, ...); static unsigned rb_fcid_max = 1; static unsigned rb_fcntr[1]; static void rb_fcstat(void) { unsigned i; for (i=0 ; i<rb_fcid_max ; ++i) if (rb_fcntr[i]) printf("%u %u\\n", i, rb_fcntr[i]); } EOS @rb_fcid = -1 @rb_fcntr = @cp.toplevel.symbol['rb_fcntr'] @rb_fcid_max = @cp.toplevel.symbol['rb_fcid_max'] init.statements << fcall('atexit', @cp.toplevel.symbol['rb_fcstat']) end @rb_fcid += 1 @rb_fcid_max.initializer = C::CExpression[[@rb_fcid+1], @rb_fcid_max.type] @rb_fcntr.type.length = @rb_fcid+1 ctr = C::CExpression[:'++', [@rb_fcntr, :'[]', [@rb_fcid]]] C::CExpression[ctr, :',', super(recv, meth, *args)] end
# File samples/dynamic_ruby.rb, line 1696 def rb_intern(sym) n = escape_varname("intern_#{sym}") @cp.toplevel.symbol[n] || declare_newtopvar(n, fcall('rb_intern', sym.to_s), C::BaseType.new(:int, :unsigned)) end