class Metasm::RubyLiveCompiler

a ruby2c C generator for use in the current ruby interpreter generates C suitable for shellcode compilation & insertion in the current interpreter has hardcoded addresses etc

Constants

RUBY_H

Attributes

cp[RW]
optim_hint[RW]

Public Class Methods

compile(klass, *methlist) click to toggle source
# File samples/dynamic_ruby.rb, line 276
def self.compile(klass, *methlist)
        @rcp ||= new
        methlist.each { |meth|
                ast = RubyHack.read_method_ast(klass, meth)
                n = @rcp.compile(ast, klass, meth)
                next if not n
                raw = RubyHack.compile_c(@rcp.cp.dump_definition(n)).encoded
                RubyHack.set_method_binary(klass, meth, raw)
        }
        self
end
new(cp=nil) click to toggle source
# File samples/dynamic_ruby.rb, line 296
def initialize(cp=nil)
        @cp = cp || DynLdr.host_cpu.new_cparser
        @cp.parse RUBY_H
        @iter_break = nil
        @optim_hint = @@optim_hint.dup
end
optim_hint() click to toggle source
# File samples/dynamic_ruby.rb, line 293
def self.optim_hint; @@optim_hint; end

Public Instance Methods

add_optimized_statement(scope, varid, varc, optim={}) click to toggle source

check if the var falls in an ::optim_hint, if so generate only selected code optim is a hash varclass (keyof @optim_hint) => c_stmt optim key can also be a C::Stmt that is used in the If clause if optim == optim, you can omit the latter must have an 'other' key that is calls the generic ruby method

# File samples/dynamic_ruby.rb, line 1332
def add_optimized_statement(scope, varid, varc, optim={})
        cat = @optim_hint[varid]
        cat = 'ary' if cat == 'ary_bnd' and not optim['ary_bnd']
        if not st = optim[cat]
                st = optim['other']
                if not cat and optim.keys.all? { |k| k.kind_of? String }
                        # no need to cascade if we have a hash and can optim ary only
                        optim.each { |i, s|
                                case i
                                when 'ary'; st = C::If.new(rb_test_class_ary(varc), s, st)
                                when 'hash'; st = C::If.new(rb_test_class_hash(varc), s, st)
                                when 'string'; st = C::If.new(rb_test_class_string(varc), s, st)
                                when 'other'; # already done as default case
                                when 'fixnum'; # add test last
                                when C::Statement; st = C::If.new(i, s, st)
                                end
                        }
                        if fs = optim['fixnum']
                                # first test to perform (fast path)
                                st = C::If.new(C::CExpression[varc, :&, 1], fs, st)
                        end
                end
        end
        scope.statements << st
end
ast_to_c(ast, scope, want_value = true) click to toggle source

the recursive AST to C compiler may append C statements to scope returns the C::CExpr holding the VALUE of the current ruby statement want_value is an optionnal hint as to the returned VALUE is needed or not if want_value is a C::Variable, the statements should try to populate this var instead of some random tmp var eg to simplify :if encoding unless we have 'foo = if 42;..'

# File samples/dynamic_ruby.rb, line 812
def ast_to_c(ast, scope, want_value = true)
        ret = 
        case ast.to_a[0]
        when :block
                if ast[1]
                        ast[1..-2].each { |a| ast_to_c(a, scope, false) }
                        ast_to_c(ast.last, scope, want_value)
                end

        when :lvar
                local(ast[1])
        when :lasgn
                if scope == @scope
                        l = local(ast[1], :none)
                else
                        # w = 4 if false ; p w  => should be nil
                        l = local(ast[1])
                end
                st = ast_to_c(ast[2], scope, l)
                scope.statements << C::CExpression[l, :'=', st] if st != l
                l
        when :dvar
                dvar(ast[1])
        when :dasgn_curr
                l = dvar(ast[1])
                st = ast_to_c(ast[2], scope, l)
                scope.statements << C::CExpression[l, :'=', st] if st != l
                l
        when :ivar
                fcall('rb_ivar_get', rb_self, rb_intern(ast[1]))
        when :iasgn
                if want_value
                        tmp = get_new_tmp_var("ivar_#{ast[1]}", want_value)
                        scope.statements << C::CExpression[tmp, :'=', ast_to_c(ast[2], scope)]
                        scope.statements << fcall('rb_ivar_set', rb_self, rb_intern(ast[1]), tmp)
                        tmp
                else
                        scope.statements << fcall('rb_ivar_set', rb_self, rb_intern(ast[1]), ast_to_c(ast[2], scope))
                end
        when :cvar
                fcall('rb_cvar_get', rb_selfclass, rb_intern(ast[1]))
        when :cvasgn
                if want_value
                        tmp = get_new_tmp_var("cvar_#{ast[1]}", want_value)
                        scope.statements << C::CExpression[tmp, :'=', ast_to_c(ast[2], scope)]
                        scope.statements << fcall('rb_cvar_set', rb_selfclass, rb_intern(ast[1]), tmp, rb_false)
                        tmp
                else
                        scope.statements << fcall('rb_cvar_set', rb_selfclass, rb_intern(ast[1]), ast_to_c(ast[2], scope), rb_false)
                end
        when :gvar
                fcall('rb_gv_get', ast[1])
        when :gasgn
                if want_value
                        tmp = get_new_tmp_var("gvar_#{ast[1]}", want_value)
                        scope.statements << C::CExpression[tmp, :'=', ast_to_c(ast[2], scope)]
                        scope.statements << fcall('rb_gv_set', ast[1], tmp)
                        tmp
                else
                        scope.statements << fcall('rb_gv_set', ast[1], ast_to_c(ast[2], scope))
                end
        when :attrasgn        # foo.bar= 42 (same as :call, except for return value)
                recv = ast_to_c(ast[1], scope)
                raise Fail, "unsupported #{ast.inspect}" if not ast[3] or ast[3][0] != :array
                if ast[3].length != 2
                        if ast[2] != '[]=' or ast[3].length != 3
                                raise Fail, "unsupported #{ast.inspect}"
                        end
                        # foo[4] = 2
                        idx = ast_to_c(ast[3][1], scope)
                end
                arg = ast_to_c(ast[3].last, scope)
                if want_value
                        tmp = get_new_tmp_var('call', want_value)
                        scope.statements << C::CExpression[tmp, :'=', arg]
                end
                if idx
                        scope.statements << rb_funcall(recv, ast[2], idx, arg)
                else
                        scope.statements << rb_funcall(recv, ast[2], arg)
                end
                tmp

        when :rb2cvar # hax, used in vararg parsing
                get_var(ast[1])
        when :rb2cstmt
                ast[1]

        when :block_arg
                local(ast[3], fcall('rb_block_proc'))

        when :lit
                case ast[1]
                when Symbol
                        # XXX ID2SYM
                        C::CExpression[[rb_intern(ast[1].to_s), :<<, 8], :|, 0xe]
                when Range
                        fcall('rb_range_new', ast[1].begin.object_id, ast[1].end.object_id, ast[1].exclude_end? ? 0 : 1)
                else # true/false/nil/fixnum
                        ast[1].object_id
                end
        when :self
                rb_self
        when :str
                fcall('rb_str_new2', ast[1])
        when :array
                tmp = get_new_tmp_var('ary', want_value)
                scope.statements << C::CExpression[tmp, :'=', fcall('rb_ary_new')]
                ast[1..-1].each { |e|
                        scope.statements << fcall('rb_ary_push', tmp, ast_to_c(e, scope))
                }
                tmp
        when :hash
                raise Fail, "bad #{ast.inspect}" if ast[1][0] != :array
                tmp = get_new_tmp_var('hash', want_value)
                scope.statements << C::CExpression[tmp, :'=', fcall('rb_hash_new')]
                ki = nil
                ast[1][1..-1].each { |k|
                        if not ki
                                ki = k
                        else
                                scope.statements << fcall('rb_hash_aset', tmp, ast_to_c(ki, scope), ast_to_c(k, scope))
                                ki = nil
                        end
                }
                tmp

        when :iter
                if v = optimize_iter(ast, scope, want_value)
                        return v
                end
                # for full support of :iter, we need access to the interpreter's ruby_block private global variable in eval.c
                # we can find it by analysing rb_block_given_p, but this won't work with a static precompiled rubyhack...
                # even with access to ruby_block, there we would need to redo PUSH_BLOCK, create a temporary dvar list,
                # handle [:break, lol], and do all the stack magic reused in rb_yield (probably incl setjmp etc)
                raise Fail, "unsupported iter #{ast[3].inspect}  {  | #{ast[1].inspect} |    #{ast[2].inspect}  }"

        when :call, :vcall, :fcall
                if v = optimize_call(ast, scope, want_value)
                        return v
                end
                recv = ((ast[0] == :call) ? ast_to_c(ast[1], scope) : rb_self)
                if not ast[3]
                        f = rb_funcall(recv, ast[2])
                elsif ast[3][0] == :array
                        args = ast[3][1..-1].map { |a| ast_to_c(a, scope) }
                        f = rb_funcall(recv, ast[2], *args)
                elsif ast[3][0] == :splat
                        args = ast_to_c(ast[3], scope)
                        if not args.kind_of? C::Variable
                                tmp = get_new_tmp_var('args', want_value)
                                scope.statements << C::CExpression[tmp, :'=', args]
                                args = tmp
                        end
                        f = fcall('rb_funcall3', recv, rb_intern(ast[2]), rb_ary_len(args), rb_ary_ptr(args))
                # elsif ast[3][0] == :argscat
                else
                        raise Fail, "unsupported #{ast.inspect}"
                end
                if want_value
                        tmp ||= get_new_tmp_var('call', want_value)
                        scope.statements << C::CExpression[tmp, :'=', f]
                        tmp
                else
                        scope.statements << f
                        f
                end

        when :if, :when
                if ast[0] == :when and ast[1][0] == :array
                        cnd = nil
                        ast[1][1..-1].map { |cd| rb_test(ast_to_c(cd, scope), scope) }.each { |cd|
                                cnd = (cnd ? C::CExpression[cnd, :'||', cd] : cd)
                        }
                else
                        cnd = rb_test(ast_to_c(ast[1], scope), scope)
                end

                tbdy = C::Block.new(scope)
                ebdy = C::Block.new(scope) if ast[3] or want_value

                if want_value
                        tmp = get_new_tmp_var('if', want_value)
                        thn = ast_to_c(ast[2], tbdy, tmp)
                        tbdy.statements << C::CExpression[tmp, :'=', thn] if tmp != thn
                        if ast[3]
                                els = ast_to_c(ast[3], ebdy, tmp)
                        else
                                # foo = if bar ; baz ; end  => nil if !bar
                                els = rb_nil
                        end
                        ebdy.statements << C::CExpression[tmp, :'=', els] if tmp != els
                else
                        ast_to_c(ast[2], tbdy, false)
                        ast_to_c(ast[3], ebdy, false)
                end

                scope.statements << C::If.new(cnd, tbdy, ebdy)

                tmp

        when :while, :until
                pib = @iter_break
                @iter_break = nil    # XXX foo = while ()...

                body = C::Block.new(scope)
                if ast[3] == 0       # do .. while();
                        ast_to_c(ast[2], body, false)
                end
                t = nil
                e = C::Break.new
                t, e = e, t if ast[0] == :until
                body.statements << C::If.new(rb_test(ast_to_c(ast[1], body), body), t, e)
                if ast[3] != 0       # do .. while();
                        ast_to_c(ast[2], body, false)
                end
                scope.statements << C::For.new(nil, nil, nil, body)

                @iter_break = pib
                nil.object_id

        when :and, :or, :not
                # beware lazy evaluation !
                tmp = get_new_tmp_var('and', want_value)
                v1 = ast_to_c(ast[1], scope, tmp)
                # and/or need that tmp has the actual v1 value (returned when shortcircuit)
                scope.statements << C::CExpression[tmp, :'=', v1] if v1 != tmp
                v1 = tmp
                case ast[0]
                when :and
                        t = C::Block.new(scope)
                        v2 = ast_to_c(ast[2], t, tmp)
                        t.statements << C::CExpression[tmp, :'=', v2] if v2 != tmp
                when :or
                        e = C::Block.new(scope)
                        v2 = ast_to_c(ast[2], e, tmp)
                        e.statements << C::CExpression[tmp, :'=', v2] if v2 != tmp
                when :not
                        t = C::CExpression[tmp, :'=', rb_false]
                        e = C::CExpression[tmp, :'=', rb_true]
                end
                scope.statements << C::If.new(rb_test(v1, scope), t, e)
                tmp
        when :return
                scope.statements << C::Return.new(ast_to_c(ast[1], scope))
                nil.object_id
        when :break
                if @iter_break
                        v = (ast[1] ? ast_to_c(ast[1], scope, @iter_break) : nil.object_id)
                        scope.statements << C::CExpression[@iter_break, :'=', [[v], value]] if @iter_break != v
                end
                scope.statements << C::Break.new
                nil.object_id

        when nil, :args
                nil.object_id
        when :nil
                rb_nil
        when :false
                rb_false
        when :true
                rb_true
        when :const
                rb_const(ast[1])
        when :colon2
                if cst = check_const(ast[1])
                        rb_const(ast[2], cst)
                else
                        fcall('rb_const_get', ast_to_c(ast[1], scope), rb_intern(ast[2]))
                end
        when :colon3
                rb_const(ast[1], ::Object)
        when :defined
                case ast[1][0]
                when :ivar
                        fcall('rb_ivar_defined', rb_self, rb_intern(ast[1][1]))
                else 
                        raise Fail, "unsupported #{ast.inspect}"
                end
        when :masgn
                # parallel assignment: put everything in an Array, then pop everything back?
                rb_masgn(ast, scope, want_value)
                
        when :evstr
                fcall('rb_obj_as_string', ast_to_c(ast[1], scope))
        when :dot2, :dot3
                fcall('rb_range_new', ast_to_c(ast[1], scope), ast_to_c(ast[2], scope), ast[0] == :dot2 ? 0 : 1)
        when :splat
                fcall('rb_Array', ast_to_c(ast[1], scope))
        when :to_ary
                fcall('rb_ary_to_ary', ast_to_c(ast[1], scope))
        when :dstr
                # dynamic string: "foo#{bar}baz"
                tmp = get_new_tmp_var('dstr')
                scope.statements << C::CExpression[tmp, :'=', fcall('rb_str_new2', ast[1][1])]
                ast[2..-1].compact.each { |s|
                        if s[0] == :str # directly append the char*
                                scope.statements << fcall('rb_str_cat2', tmp, s[1])
                        else
                                scope.statements << fcall('rb_str_append', tmp, ast_to_c(s, scope))
                        end
                }
                tmp
        when :case
                compile_case(ast, scope, want_value)
        when :ensure
                # TODO
                ret = ast_to_c(ast[1], scope, want_value)
                ast_to_c(ast[3], scope, false)
                ret
        else
                raise Fail, "unsupported #{ast.inspect}"
        end

        if want_value
                ret = C::CExpression[[ret], value] if ret.kind_of? Integer or ret.kind_of? String
                ret
        end
end
check_const(ast) click to toggle source

checks if ast maps to a constant, returns it if it does

# File samples/dynamic_ruby.rb, line 360
def check_const(ast)
        case ast[0]
        when :const
                resolve_const_owner(ast[1])
        when :colon2
                if cst = check_const(ast[1])
                        cst.const_get(ast[2])
                end
        when :colon3
                ::Object.const_get(ast[2])
        end
end
compile(ast, klass, meth, singleton=false) click to toggle source

convert a ruby AST to a new C function returns the new function name

# File samples/dynamic_ruby.rb, line 305
def compile(ast, klass, meth, singleton=false)
        return if not ast

        # TODO handle arbitrary block/yield constructs
        # TODO analyse to find/optimize numeric locals that never need a ruby VALUE (ie native int vs INT2FIX)
        # TODO detect block/closure exported out of the func & abort compilation

        @klass = klass
        @meth = meth
        @meth_singleton = singleton

        mname = escape_varname("m_#{@klass}#{singleton ? '.' : '#'}#{@meth}".gsub('::', '_'))
        @cp.parse "static void #{mname}(VALUE self) { }"
        @cur_cfunc = @cp.toplevel.symbol[mname]
        @cur_cfunc.type.type = value  # return type = VALUE, w/o 'missing return statement' warning

        @scope = @cur_cfunc.initializer

        case ast[0]
        when :ivar    # attr_reader
                ret = fcall('rb_ivar_get', rb_self, rb_intern(ast[1]))
        when :attrset # attr_writer
                compile_args(@cur_cfunc, [nil, 1])
                ret = fcall('rb_ivar_set', rb_self, rb_intern(ast[1]), local(2))
        when :scope   # standard ruby function
                @cref = ast[1][:cref]
                if ast[2] and ast[2][0] == :block and ast[2][1] and ast[2][1][0] == :args
                        compile_args(@cur_cfunc, ast[2][1])
                end
                want_value = true
                if meth.to_s == 'initialize' and not singleton
                        want_value = false
                end
                ret = ast_to_c(ast[2], @scope, want_value)
                ret = rb_nil if not want_value
        #when :cfunc  # native ruby extension
        else raise "unhandled function ast #{ast.inspect}"
        end

        @scope.statements << C::Return.new(ret)

        mname
end
compile_args(func, args) click to toggle source
# File samples/dynamic_ruby.rb, line 373
def compile_args(func, args)
        case method_arity
        when -1       # args[1] == 0 and (args[2] or args[3])
                compile_args_m1(func, args)
        when -2       # args[1] > 0 and (args[2] or args[3])
                compile_args_m2(func, args)
        else
                # fixed arity = args[1]: VALUE func(VALUE self, VALUE local_2, VALUE local_3)
                args[1].times { |i|
                        v = C::Variable.new("local_#{i+2}", value)
                        @scope.symbol[v.name] = v
                        func.type.args << v
                }
        end
end
compile_args_m1(func, args) click to toggle source

update func prototype to reflect arity -1 VALUE func(int argc, VALUE *argv, VALUE self)

# File samples/dynamic_ruby.rb, line 391
def compile_args_m1(func, args)
        c = C::Variable.new("arg_c", C::BaseType.new(:int, :unsigned))
        v = C::Variable.new("arg_v", C::Pointer.new(value))
        @scope.symbol[c.name] = c
        @scope.symbol[v.name] = v
        func.type.args.unshift v
        func.type.args.unshift c

        args[1].times { |i|
                local(i+2, C::CExpression[v, :'[]', [i]])
        }

        if args[2]
                # [:block, [:lasgn, 2, [:lit, 4]]]
                raise Fail, "unhandled vararglist #{args.inspect}" if args[2][0] != :block
                args[2][1..-1].each_with_index { |a, i|
                        raise Fail, "unhandled arg #{a.inspect}" if a[0] != :lasgn
                        cnd = C::CExpression[c, :>, i]
                        thn = C::CExpression[local(a[1], :none), :'=', [v, :'[]', [i]]]
                        els = C::Block.new(@scope)
                        ast_to_c(a, els, false)
                        @scope.statements << C::If.new(cnd, thn, els)
                }
        end

        if args[3]
                raise Fail, "unhandled vararglist3 #{args.inspect}" if args[3][0] != :lasgn
                skiplen = args[1] + args[2].length - 1
                alloc = fcall('rb_ary_new4', [c, :-, [skiplen]], [v, :+, [skiplen]])
                local(args[3][1], C::CExpression[[c, :>, skiplen], :'?:', [alloc, fcall('rb_ary_new')]])
        end
end
compile_args_m2(func, args) click to toggle source

update func prototype to reflect arity -2 VALUE func(VALUE self, VALUE arg_array)

# File samples/dynamic_ruby.rb, line 426
def compile_args_m2(func, args)
        v = C::Variable.new("arglist", value)
        @scope.symbol[v.name] = v
        func.type.args << v

        args[1].times { |i|
                local(i+2, fcall('rb_ary_shift', v))
        }

        # populate arguments with default values
        if args[2]
                # [:block, [:lasgn, 2, [:lit, 4]]]
                raise Fail, "unhandled vararglist #{args.inspect}" if args[2][0] != :block
                args[2][1..-1].each { |a|
                        raise Fail, "unhandled arg #{a.inspect}" if a[0] != :lasgn
                        t = C::CExpression[local(a[1], :none), :'=', fcall('rb_ary_shift', v)]
                        e = C::Block.new(@scope)
                        ast_to_c([:lasgn, a[1], a[2]], e, false)
                        @scope.statements << C::If.new(rb_ary_len(v), t, e)
                }
        end

        if args[3]
                raise Fail, "unhandled vararglist3 #{args.inspect}" if args[3][0] != :lasgn
                local(args[3][1], C::CExpression[v])
        end
end
compile_case(ast, scope, want_value) click to toggle source

compile a case/when create a real C switch() for Fixnums, and put the others === in the default case XXX will get the wrong order for “case x; when 1; when Fixnum; when 3;” …

# File samples/dynamic_ruby.rb, line 457
def compile_case(ast, scope, want_value)
        # this generates
        # var = stuff_to_test()
        # if (var & 1)
        #   switch (var >> 1) {
        #      case 12:
        #          stuff();
        #          break;
        #      default:
        #          goto default_case;
        #   }
        # else
        # default_case:
        #   if (var == true.object_id || rb_test(rb_funcall(bla, '===', var)))
        #      foo();
        #   else {
        #      default();
        #   }
        #      
        if want_value == true
                ret = get_new_tmp_var('case', want_value)
                want_value = ret
        elsif want_value
                ret = want_value
        end

        var = ast_to_c(ast[1], scope, want_value || true)
        if not var.kind_of? C::Variable
                ret ||= get_new_tmp_var('case', want_value)
                scope.statements << C::CExpression[ret, :'=', var]
                var = ret
        end

        # the scope to put all case int in
        body_int = C::Block.new(scope)
        # the scope to put the if (cs === var) cascade
        body_other_head = body_other = nil
        default = nil

        ast[2..-1].each { |cs|
                if cs[0] == :when
                        raise Fail if cs[1][0] != :array

                        # numeric case, add a case to body_int
                        if cs[1][1..-1].all? { |cd| cd[0] == :lit and (cd[1].kind_of? Fixnum or cd[1].kind_of? Range) }
                                cs[1][1..-1].each { |cd|
                                        if cd[1].kind_of? Range
                                                b = cd[1].begin
                                                e = cd[1].end
                                                e -= 1 if cd[1].exclude_end?
                                                raise Fail unless b.kind_of? Integer and e.kind_of? Integer
                                                body_int.statements << C::Case.new(b, e, nil)
                                        else
                                                body_int.statements << C::Case.new(cd[1], nil, nil)
                                        end
                                }
                                cb = C::Block.new(scope)
                                v = ast_to_c(cs[2], cb, want_value)
                                cb.statements << C::CExpression[ret, :'=', v] if want_value and v != ret
                                cb.statements << C::Break.new
                                body_int.statements << cb

                        # non-numeric (or mixed) case, add if ( cs === var )
                        else
                                cnd = nil
                                cs[1][1..-1].each { |cd|
                                        if (cd[0] == :lit and (cd[1].kind_of?(Fixnum) or cd[1].kind_of?(Symbol))) or
                                                [:nil, :true, :false].include?(cd[0])
                                                # true C equality
                                                cd = C::CExpression[var, :==, ast_to_c(cd, scope)]
                                        else
                                                # own block for ast_to_c to honor lazy evaluation
                                                tb = C::Block.new(scope)
                                                test = rb_test(rb_funcall(ast_to_c(cd, tb), '===', var), tb)
                                                # discard own block unless needed
                                                if tb.statements.empty?
                                                        cd = test
                                                else
                                                        tb.statements << test
                                                        cd = C::CExpression[tb, value]
                                                end
                                        end
                                        cnd = (cnd ? C::CExpression[cnd, :'||', cd] : cd)
                                }
                                cb = C::Block.new(scope)
                                v = ast_to_c(cs[2], cb, want_value)
                                cb.statements << C::CExpression[ret, :'=', v] if want_value and v != ret
                                
                                fu = C::If.new(cnd, cb, nil)

                                if body_other
                                        body_other.belse = fu
                                else
                                        body_other_head = fu
                                end
                                body_other = fu
                        end

                # default case statement
                else
                        cb = C::Block.new(scope)
                        v = ast_to_c(cs, cb, want_value)
                        cb.statements << C::CExpression[ret, :'=', v] if want_value and v != ret
                        default = cb
                end
        }

        # if we use the value of the case, we must add an 'else: nil'
        if want_value and not default
                default = C::Block.new(scope)
                default.statements << C::CExpression[ret, :'=', rb_nil]
        end

        # assemble everything
        scope.statements <<
        if body_int.statements.empty?
                if body_other
                        body_other.belse = default
                        body_other_head
                else
                        raise Fail, "empty case? #{ast.inspect}" if not default
                        default
                end
        else
                if body_other_head
                        @default_label_cnt ||= 0
                        dfl = "default_label_#{@default_label_cnt += 1}"
                        body_other_head = C::Label.new(dfl, body_other_head)
                        body_int.statements << C::Case.new('default', nil, C::Goto.new(dfl))
                        body_other.belse = default if default
                end
                body_int = C::Switch.new(C::CExpression[var, :>>, 1], body_int)
                C::If.new(C::CExpression[var, :&, 1], body_int, body_other_head)
        end

        ret
end
declare_newvar(name, initializer) click to toggle source

declare a new function variable no initializer if init == :none

# File samples/dynamic_ruby.rb, line 611
def declare_newvar(name, initializer)
        v = C::Variable.new(name, value)
        v.initializer = initializer if initializer != :none
        @scope.symbol[v.name] = v
        @scope.statements << C::Declaration.new(v)
        v
end
dump(m=nil) click to toggle source
# File samples/dynamic_ruby.rb, line 288
def dump(m=nil)
        m ? @cp.dump_definition(m) : @cp.to_s
end
dvar(n, init=nil) click to toggle source

retrieve/create a new dynamic variable (block argument/variable) pass :none to avoid initializer

# File samples/dynamic_ruby.rb, line 647
def dvar(n, init=nil)
        get_var "dvar_#{n}", init
end
escape_varname(n) click to toggle source

return a string suitable for use as a variable name hexencode any char not in [A-z0-9_]

# File samples/dynamic_ruby.rb, line 621
def escape_varname(n)
        n.gsub(/[^\w]/) { |c| c.unpack('H*')[0] }
end
fcall(fname, *arglist) click to toggle source

create a C::CExpr[toplevel.symbol, :funcall, args] casts int/strings in arglist to CExpr

# File samples/dynamic_ruby.rb, line 597
def fcall(fname, *arglist)
        args = arglist.map { |a| (a.kind_of?(Integer) or a.kind_of?(String)) ? [a] : a }
        fv = @cp.toplevel.symbol[fname]
        raise "need prototype for #{fname}!" if not fv
        C::CExpression[fv, :funcall, args]
end
get_cfuncptr(klass, method, singleton=false) click to toggle source

return ptr, arity ptr is a CExpr pointing to the C func implementing klass#method

# File samples/dynamic_ruby.rb, line 1360
def get_cfuncptr(klass, method, singleton=false)
        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
        fast = RubyHack.read_node(ptr)
        arity = fast[1][:arity]
        fptr = fast[1][:fptr]

        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

        C::CExpression[[fptr], C::Pointer.new(fproto)]
end
get_new_tmp_var(base=nil, var=nil) click to toggle source

create a new temporary variable XXX put_var ?

# File samples/dynamic_ruby.rb, line 634
def get_new_tmp_var(base=nil, var=nil)
        return var if var.kind_of? C::Variable
        @tmp_var_id ||= 0
        get_var("tmp_#{"#{base}_" if base}#{@tmp_var_id += 1}")
end
get_var(name, initializer=:none) click to toggle source

retrieve or create a local var pass :none to avoid initializer

# File samples/dynamic_ruby.rb, line 627
def get_var(name, initializer=:none)
        name = escape_varname(name)
        @scope.symbol[name] ||= declare_newvar(name, initializer || rb_nil)
end
local(n, init=nil) click to toggle source

retrieve/create a new local variable with optionnal initializer

# File samples/dynamic_ruby.rb, line 641
def local(n, init=nil)
        get_var "local_#{n}", init
end
method_arity(name=@meth) click to toggle source

return the arity of method 'name' on self

# File samples/dynamic_ruby.rb, line 350
def method_arity(name=@meth)
        @meth_singleton ? @klass.method(name).arity : @klass.instance_method(name).arity
end
optimize_call(ast, scope, want_value) click to toggle source

optional optimization of a call (eg a == 1, c+2, …) return nil for normal #rb_funcall, or a C::CExpr to use as retval.

# File samples/dynamic_ruby.rb, line 1134
        def optimize_call(ast, scope, want_value)
                ce = C::CExpression
                op = ast[2]
                int = C::BaseType.new(:ptr)   # signed VALUE
                args = ast[3][1..-1] if ast[3] and ast[3][0] == :array
                arg0 = args[0] if args and args[0]

                if arg0 and arg0[0] == :lit and arg0[1].kind_of?(Fixnum) and %w[== > < >= <= + -].include?(op)
                        # TODO or @optim_hint[ast[1]] == 'fixnum'
                        # optimize 'x==42', 'x+42', 'x-42'
                        o2 = arg0[1]
                        if o2 < 0 and ['+', '-'].include? op
                                # need o2 >= 0 for overflow detection
                                op = {'+' => '-', '-' => '+'}[op]
                                o2 = -o2
                                return if not o2.kind_of? Fixnum    # -0x40000000
                        end

                        int_v = o2.object_id
                        recv = ast_to_c(ast[1], scope)
                        tmp = get_new_tmp_var('opt', want_value)
                        if not recv.kind_of? C::Variable
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        case op
                        when '=='
                                # XXX assume == only return true for full equality: if not Fixnum, then always false
                                # which breaks 1.0 == 1 and maybe others, but its ok
                                scope.statements << C::If.new(ce[recv, :'==', [int_v]], ce[tmp, :'=', rb_true], ce[tmp, :'=', rb_false])
                        when '>', '<', '>=', '<='
                                # do the actual comparison on signed >>1 if both Fixnum
                                t = C::If.new(
                                        ce[[[[recv], int], :>>, [1]], op.to_sym, [[[int_v], int], :>>, [1]]],
                                        ce[tmp, :'=', rb_true],
                                        ce[tmp, :'=', rb_false])
                                # fallback to actual rb_funcall
                                e = ce[tmp, :'=', rb_funcall(recv, op, o2.object_id)]
                                add_optimized_statement scope, ast[1], recv, 'fixnum' => t, 'other' => e
                        when '+'
                                e = ce[recv, :+, [int_v-1]] # overflow to Bignum ?
                                cnd = ce[[recv, :&, [1]], :'&&', [[[recv], int], :<, [[e], int]]]
                                t = ce[tmp, :'=', e]
                                e = ce[tmp, :'=', rb_funcall(recv, op, o2.object_id)]
                                if @optim_hint[ast[1]] == 'fixnum'
                                        # add_optimized_statement wont handle the overflow check correctly
                                        scope.statements << t
                                else
                                        scope.statements << C::If.new(cnd, t, e)
                                end
                        when '-'
                                e = ce[recv, :-, [int_v-1]]
                                cnd = ce[[recv, :&, [1]], :'&&', [[[recv], int], :>, [[e], int]]]
                                t = ce[tmp, :'=', e]
                                e = ce[tmp, :'=', rb_funcall(recv, op, o2.object_id)]
                                if @optim_hint[ast[1]] == 'fixnum'
                                        scope.statements << t
                                else
                                        scope.statements << C::If.new(cnd, t, e)
                                end
                        end
                        tmp
                
                # Symbol#==
                elsif arg0 and arg0[0] == :lit and arg0[1].kind_of? Symbol and op == '=='
                        s_v = ast_to_c(arg0, scope)
                        tmp = get_new_tmp_var('opt', want_value)
                        recv = ast_to_c(ast[1], scope, tmp)
                        if not recv.kind_of? C::Variable
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        scope.statements << C::If.new(ce[recv, :'==', [s_v]], ce[tmp, :'=', rb_true], ce[tmp, :'=', rb_false])
                        tmp

                elsif arg0 and op == '<<'
                        tmp = get_new_tmp_var('opt', want_value)
                        recv = ast_to_c(ast[1], scope, tmp)
                        arg = ast_to_c(arg0, scope)
                        if recv != tmp
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        ar = fcall('rb_ary_push', recv, arg)
                        st = fcall('rb_str_concat', recv, arg)
                        oth = rb_funcall(recv, op, arg)
                        oth = ce[tmp, :'=', oth] if want_value

                        add_optimized_statement scope, ast[1], recv, 'ary' => ar, 'string' => st, 'other' => oth
                        tmp

                elsif arg0 and args.length == 1 and op == '[]'
                        return if ast[1][0] == :const        # Expression[42]
                        tmp = get_new_tmp_var('opt', want_value)
                        recv = ast_to_c(ast[1], scope, tmp)
                        if not recv.kind_of? C::Variable
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        idx = get_new_tmp_var('idx')
                        arg = ast_to_c(arg0, scope, idx)
                        if not arg.kind_of? C::Variable
                                scope.statements << ce[idx, :'=', arg]
                                arg = idx
                        end
                        idx = ce[[idx], int]

                        ar = C::Block.new(scope)
                        ar.statements << ce[idx, :'=', [[[arg], int], :>>, [1]]]
                        ar.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_ary_len(recv)]], nil) 
                        ar.statements << C::If.new(ce[[idx, :<, [0]], :'||', [idx, :>=, [[rb_ary_len(recv)], int]]],
                                        ce[tmp, :'=', rb_nil],
                                        ce[tmp, :'=', rb_ary_ptr(recv, idx)])
                        st = C::Block.new(scope)
                        st.statements << ce[idx, :'=', [[[arg], int], :>>, [1]]]
                        st.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_str_len(recv)]], nil) 
                        st.statements << C::If.new(ce[[idx, :<, [0]], :'||', [idx, :>=, [[rb_str_len(recv)], int]]],
                                        ce[tmp, :'=', rb_nil],
                                        ce[tmp, :'=', [[[[rb_str_ptr(recv, idx), :&, [0xff]], :<<, [1]], :|, [1]], value]])
                        hsh = ce[tmp, :'=', fcall('rb_hash_aref', recv, arg)]
                        oth = ce[tmp, :'=', rb_funcall(recv, op, arg)]

                        # ary/string only valid with fixnum argument !
                        add_optimized_statement scope, ast[1], recv, 'hash' => hsh, 'other' => oth,
                                                'ary_bnd' => ce[tmp, :'=', rb_ary_ptr(recv, ce[[[arg], int], :>>, [1]])],
                                            ce[[arg, :&, 1], :'&&', rb_test_class_ary(recv)] => ar,
                                                     ce[[arg, :&, 1], :'&&', rb_test_class_string(recv)] => st
                        tmp

                elsif ast[1] and not arg0 and op == 'empty?'
                        tmp = get_new_tmp_var('opt', want_value)
                        recv = ast_to_c(ast[1], scope, tmp)
                        if not recv.kind_of? C::Variable
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        ar = C::If.new(rb_ary_len(recv), ce[tmp, :'=', rb_false], ce[tmp, :'=', rb_true])

                        add_optimized_statement scope, ast[1], recv, 'ary' => ar,
                                'other' => ce[tmp, :'=', rb_funcall(recv, op)]
                        tmp

                elsif ast[1] and not arg0 and op == 'pop'
                        tmp = get_new_tmp_var('opt', want_value)
                        recv = ast_to_c(ast[1], scope, tmp)
                        if not recv.kind_of? C::Variable
                                scope.statements << ce[tmp, :'=', recv]
                                recv = tmp
                        end

                        t = fcall('rb_ary_pop', recv)
                        e = rb_funcall(recv, op)
                        if want_value
                                t = ce[tmp, :'=', t]
                                e = ce[tmp, :'=', e]
                        end

                        add_optimized_statement scope, ast[1], recv, 'ary' => t, 'other' => e

                        tmp

                elsif ast[1] and op == 'kind_of?' and arg0 and (arg0[0] == :const or arg0[0] == :colon3)
                        # TODO check const maps to toplevel when :const
                        test =
                        case arg0[1]
                        when 'Symbol'
                                tmp = get_new_tmp_var('kindof', want_value)
                                ce[[ast_to_c(ast[1], scope, tmp), :'&', [0xf]], :'==', [0xe]]
                        #when 'Numeric', 'Integer'
                        when 'Fixnum'
                                tmp = get_new_tmp_var('kindof', want_value)
                                ce[ast_to_c(ast[1], scope, tmp), :'&', [0x1]]
                        when 'Array'
                                rb_test_class_ary(ast_to_c(ast[1], scope))
                        when 'String'
                                rb_test_class_string(ast_to_c(ast[1], scope))
                        else return
                        end
puts "shortcut may be incorrect for #{ast.inspect}" if arg0[0] == :const
                        tmp ||= get_new_tmp_var('kindof', want_value)
                        scope.statements << C::If.new(test, ce[tmp, :'=', rb_true], ce[tmp, :'=', rb_false])
                        tmp

                elsif not ast[1] or ast[1] == [:self]
                        optimize_call_static(ast, scope, want_value)
                end
        end
optimize_call_static(ast, scope, want_value) click to toggle source

call C funcs directly assume private function calls are not virtual and hardlink them here

# File samples/dynamic_ruby.rb, line 1382
def optimize_call_static(ast, scope, want_value)
        arity = method_arity(ast[2]) rescue return
        if ast[2].to_s == @meth.to_s
                # self is recursive
                fptr = @cur_cfunc
        else
                fptr = get_cfuncptr(@klass, ast[2], @meth_singleton)
                return if not fptr
        end

        c_arglist = []

        if not ast[3]
                args = []
        elsif ast[3][0] == :array
                args = ast[3][1..-1]
        elsif ast[3][0] == :splat
                args = ast_to_c(ast[3], scope)
                if arity != -2 and !args.kind_of?(C::Variable)
                        tmp = get_new_tmp_var('arg')
                        scope.statements << C::CExpression[tmp, :'=', args]
                        args = tmp
                end
                case arity
                when -2
                        c_arglist << rb_self << args
                when -1
                        c_arglist << [rb_ary_len(args)] << rb_ary_ptr(args) << rb_self
                else
                        cnd = C::CExpression[rb_ary_len(args), :'!=', [arity]]
                        scope.statements << C::If.new(cnd, rb_raise("#{arity} args expected", 'rb_eArgumentError'), nil)

                        c_arglist << rb_self
                        arity.times { |i| c_arglist << rb_ary_ptr(args, i) }
                end
                arity = :canttouchthis
        else return   # TODO
        end

        case arity
        when :canttouchthis
        when -2
                arg = get_new_tmp_var('arg')
                scope.statements << C::CExpression[arg, :'=', fcall('rb_ary_new')]
                args.each { |a|
                        scope.statements << fcall('rb_ary_push', arg, ast_to_c(a, scope))
                }
                c_arglist << rb_self << arg

        when -1
                case args.length
                when 0
                        argv = C::CExpression[[0], C::Pointer.new(value)]
                when 1
                        val = ast_to_c(args[0], scope)
                        if not val.kind_of? C::Variable
                                argv = get_new_tmp_var('argv')
                                scope.statements << C::CExpression[argv, :'=', val]
                                val = argv
                        end
                        argv = C::CExpression[:'&', val]
                else
                        argv = get_new_tmp_var('argv')
                        argv.type = C::Array.new(value, args.length)
                        args.each_with_index { |a, i|
                                val = ast_to_c(a, scope)
                                scope.statements << C::CExpression[[argv, :'[]', [i]], :'=', val]
                        }
                end
                c_arglist << [args.length] << argv << rb_self

        else
                c_arglist << rb_self
                args.each { |a|
                        va = get_new_tmp_var('arg')
                        val = ast_to_c(a, scope, va)
                        scope.statements << C::CExpression[va, :'=', val] if val != va
                        c_arglist << va
                }
        end

        f = C::CExpression[fptr, :funcall, c_arglist]
        if want_value
                ret = get_new_tmp_var('ccall', want_value)
                scope.statements << C::CExpression[ret, :'=', f]
                ret
        else
                scope.statements << f
        end
end
optimize_iter(ast, scope, want_value) click to toggle source
# File samples/dynamic_ruby.rb, line 1473
def optimize_iter(ast, scope, want_value)
        b_args, b_body, b_recv = ast[1, 3]

        old_ib = @iter_break
        if want_value
                # a new tmpvar, so we can overwrite it in 'break :foo'
                @iter_break = get_new_tmp_var('iterbreak')
        else
                @iter_break = nil
        end

        if b_recv[0] == :call and b_recv[2] == 'reverse_each'
                # convert ary.reverse_each to ary.reverse.each
                b_recv = b_recv.dup
                b_recv[1] = [:call, b_recv[1], 'reverse']
                b_recv[2] = 'each'
        elsif b_recv[0] == :call and b_recv[2] == 'each_key'
                # convert hash.each_key to hash.keys.each
                b_recv = b_recv.dup
                b_recv[1] = [:call, b_recv[1], 'keys']
                b_recv[2] = 'each'
        end

        # loop { }
        if b_recv[0] == :fcall and b_recv[2] == 'loop'
                body = C::Block.new(scope)
                ast_to_c(b_body, body, false)
                scope.statements << C::For.new(nil, nil, nil, body)

        # int.times { |i| }
        elsif b_recv[0] == :call and not b_recv[3] and b_recv[2] == 'times'
                limit = get_new_tmp_var('limit')
                recv = ast_to_c(b_recv[1], scope, limit)
                scope.statements << C::If.new(C::CExpression[:'!', [recv, :&, 1]], rb_raise('only Fixnum#times handled'), nil)
                if want_value
                        scope.statements << C::CExpression[@iter_break, :'=', recv]
                end
                scope.statements << C::CExpression[limit, :'=', [recv, :>>, 1]]
                cntr = get_new_tmp_var('cntr')
                cntr.type = C::BaseType.new(:int, :unsigned)
                body = C::Block.new(scope)
                if b_args and b_args[0] == :dasgn_curr
                        body.statements << C::CExpression[dvar(b_args[1]), :'=', [[cntr, :<<, 1], :|, 1]]
                end
                ast_to_c(b_body, body, false)
                scope.statements << C::For.new(C::CExpression[cntr, :'=', [[0], cntr.type]], C::CExpression[cntr, :<, limit], C::CExpression[:'++', cntr], body)

        # ary.each { |e| }
        elsif b_recv[0] == :call and not b_recv[3] and b_recv[2] == 'each' and b_args and
                        b_args[0] == :dasgn_curr
                ary = get_new_tmp_var('ary')
                recv = ast_to_c(b_recv[1], scope, ary)
                scope.statements << C::CExpression[ary, :'=', recv] if ary != recv
                scope.statements << C::If.new(rb_test_class_ary(ary), nil, rb_raise('only Array#each { |e| } handled'))
                if want_value
                        scope.statements << C::CExpression[@iter_break, :'=', ary]
                end
                cntr = get_new_tmp_var('cntr')
                cntr.type = C::BaseType.new(:int, :unsigned)
                body = C::Block.new(scope)
                if b_args and b_args[0] == :dasgn_curr
                        body.statements << C::CExpression[dvar(b_args[1]), :'=', [rb_ary_ptr(ary), :'[]', [cntr]]]
                end
                ast_to_c(b_body, body, false)
                scope.statements << C::For.new(C::CExpression[cntr, :'=', [[0], cntr.type]], C::CExpression[cntr, :<, rb_ary_len(ary)], C::CExpression[:'++', cntr], body)

        # ary.find { |e| }
        elsif b_recv[0] == :call and not b_recv[3] and b_recv[2] == 'find' and b_args and
                        b_args[0] == :dasgn_curr
                ary = get_new_tmp_var('ary')
                recv = ast_to_c(b_recv[1], scope, ary)
                scope.statements << C::CExpression[ary, :'=', recv] if ary != recv
                scope.statements << C::If.new(rb_test_class_ary(ary), nil, rb_raise('only Array#find { |e| } handled'))
                if want_value
                        scope.statements << C::CExpression[@iter_break, :'=', rb_nil]
                end
                cntr = get_new_tmp_var('cntr')
                cntr.type = C::BaseType.new(:int, :unsigned)
                body = C::Block.new(scope)
                if b_args and b_args[0] == :dasgn_curr
                        body.statements << C::CExpression[dvar(b_args[1]), :'=', [rb_ary_ptr(ary), :'[]', [cntr]]]
                end
                # same as #each up to this point (except default retval), now add a 'if (body_value) break ary[cntr];'
                # XXX 'find { next true }' 

                found = ast_to_c(b_body, body)
                t = C::Block.new(body)
                t.statements << C::CExpression[@iter_break, :'=', rb_ary_ptr(ary, cntr)]
                t.statements << C::Break.new
                body.statements << C::If.new(rb_test(found, body), t, nil)

                scope.statements << C::For.new(C::CExpression[cntr, :'=', [[0], cntr.type]], C::CExpression[cntr, :<, rb_ary_len(ary)], C::CExpression[:'++', cntr], body)

        # ary.map { |e| }
        elsif b_recv[0] == :call and not b_recv[3] and b_recv[2] == 'map' and b_args and
                        b_args[0] == :dasgn_curr
                ary = get_new_tmp_var('ary')
                recv = ast_to_c(b_recv[1], scope, ary)
                scope.statements << C::CExpression[ary, :'=', recv] if ary != recv
                scope.statements << C::If.new(rb_test_class_ary(ary), nil, rb_raise('only Array#map { |e| } handled'))
                if want_value
                        scope.statements << C::CExpression[@iter_break, :'=', fcall('rb_ary_new')]
                end
                cntr = get_new_tmp_var('cntr')
                cntr.type = C::BaseType.new(:int, :unsigned)
                body = C::Block.new(scope)
                if b_args and b_args[0] == :dasgn_curr
                        body.statements << C::CExpression[dvar(b_args[1]), :'=', [rb_ary_ptr(ary), :'[]', [cntr]]]
                end
                # same as #each up to this point (except default retval), now add a '@iter_break << body_value'
                # XXX 'next' unhandled 

                val = ast_to_c(b_body, body)
                body.statements << fcall('rb_ary_push', @iter_break, val)

                scope.statements << C::For.new(C::CExpression[cntr, :'=', [[0], cntr.type]], C::CExpression[cntr, :<, rb_ary_len(ary)], C::CExpression[:'++', cntr], body)

        else
                @iter_break = old_ib
                return
        end

        ret = @iter_break
        @iter_break = old_ib
        ret || nil.object_id
end
rb_ary_len(expr) click to toggle source

ARY_LEN(expr)

# File samples/dynamic_ruby.rb, line 726
def rb_ary_len(expr)
        rb_cast_pvalue(expr, 2)
end
rb_ary_ptr(expr, idx=nil) click to toggle source

ARY_PTR(expr)

# File samples/dynamic_ruby.rb, line 721
def rb_ary_ptr(expr, idx=nil)
        p = C::CExpression[[rb_cast_pvalue(expr, 4)], C::Pointer.new(value)]
        idx ? C::CExpression[p, :'[]', [idx]] : p
end
rb_cast_pvalue(expr, idx) click to toggle source

returns a CExpr casting expr to a VALUE*

# File samples/dynamic_ruby.rb, line 657
def rb_cast_pvalue(expr, idx)
        C::CExpression[[[expr], C::Pointer.new(value)], :'[]', [idx]]
end
rb_const(constname, owner = resolve_const_owner(constname)) click to toggle source

returns a static pointer to the constant

# File samples/dynamic_ruby.rb, line 749
def rb_const(constname, owner = resolve_const_owner(constname))
        raise Fail, "no dynamic constant resolution #{constname}" if not owner
        cst = owner.const_get(constname)
        C::CExpression[[RubyHack.rb_obj_to_value(cst)], value]
end
rb_false() click to toggle source
# File samples/dynamic_ruby.rb, line 673
def rb_false
        C::CExpression[[false.object_id], value]
end
rb_funcall(recv, meth, *args) click to toggle source

create a #rb_funcall construct

# File samples/dynamic_ruby.rb, line 684
def rb_funcall(recv, meth, *args)
        fcall('rb_funcall', recv, rb_intern(meth), args.length, *args)
end
rb_global(cname) click to toggle source
# File samples/dynamic_ruby.rb, line 784
def rb_global(cname)
        @cp.toplevel.symbol[cname]
end
rb_intern(n) click to toggle source

call #rb_intern on a string

# File samples/dynamic_ruby.rb, line 678
def rb_intern(n)
        # use the current interpreter's value
        C::CExpression[n.to_sym.to_i]
end
rb_masgn(ast, scope, want_value) click to toggle source

compile a :masgn

# File samples/dynamic_ruby.rb, line 756
def rb_masgn(ast, scope, want_value)
        raise Fail, "masgn with no rhs #{ast.inspect}" if not ast[2]
        raise Fail, "masgn with no lhs array #{ast.inspect}" if not ast[1] or ast[1][0] != :array
        if not want_value and ast[2][0] == :array and not ast[3] and ast[2].length == ast[1].length
                rb_masgn_optimized(ast, scope)
                return nil.object_id
        end
        full = get_new_tmp_var('masgn', want_value)
        ary = ast_to_c(ast[2], scope, full)
        scope.statements << C::CExpression[full, :'=', ary] if full != ary
        ast[1][1..-1].each_with_index { |e, i|
                raise Fail, "weird masgn lhs #{e.inspect} in #{ast.inspect}" if e[-1] != nil
                # local_42 = full[i]
                e = e.dup
                e[-1] = [:rb2cstmt, rb_ary_ptr(full, i)]
                ast_to_c(e, scope, false)
        }
        if ast[3]
                raise Fail, "weird masgn lhs #{e.inspect} in #{ast.inspect}" if ast[3][-1] != nil
                # local_28 = full[12..-1].to_a
                e = ast[3].dup
                e[-1] = [:call, [:call, [:rb2cvar, full.name], '[]', [:array, [:dot2, [:lit, ast[1].length-1], [:lit, -1]]]], 'to_a']
                ast_to_c(e, scope, false)
        end

        full
end
rb_masgn_optimized(ast, scope) click to toggle source

compile an optimized :masgn with rhs.length == lhs.length (no need of a ruby array)

# File samples/dynamic_ruby.rb, line 789
def rb_masgn_optimized(ast, scope)
        vars = []
        ast[2][1..-1].each { |rhs|
                var = get_new_tmp_var('masgn_opt')
                vars << var
                r = ast_to_c(rhs, scope, var)
                scope.statements << C::CExpression[var, :'=', r] if var != r
        }
        ast[1][1..-1].each { |lhs|
                var = vars.shift
                lhs = lhs.dup
                raise Fail, "weird masgn lhs #{lhs.inspect} in #{ast.inspect}" if lhs[-1] != nil
                lhs[-1] = [:rb2cvar, var.name]
                ast_to_c(lhs, scope, false)
        }
end
rb_nil() click to toggle source
# File samples/dynamic_ruby.rb, line 667
def rb_nil
        C::CExpression[[nil.object_id], value]
end
rb_raise(reason, cls='rb_eRuntimeError') click to toggle source

generate C code to raise a RuntimeError, reason

# File samples/dynamic_ruby.rb, line 706
def rb_raise(reason, cls='rb_eRuntimeError')
        fcall('rb_raise', rb_global(cls), reason)
end
rb_self() click to toggle source

retrieve self (1st func arg)

# File samples/dynamic_ruby.rb, line 652
def rb_self
        @scope.symbol['self']
end
rb_selfclass() click to toggle source

retrieve the current class, from self->klass XXX will segfault with self.kind_of? Fixnum/true/false/nil/sym

# File samples/dynamic_ruby.rb, line 663
def rb_selfclass
        rb_cast_pvalue(rb_self, 1)
end
rb_str_len(expr) click to toggle source

STR_LEN(expr)

# File samples/dynamic_ruby.rb, line 740
def rb_str_len(expr)
        rb_cast_pvalue(expr, 2)
end
rb_str_ptr(expr, idx=nil) click to toggle source

STR_PTR(expr)

# File samples/dynamic_ruby.rb, line 735
def rb_str_ptr(expr, idx=nil)
        p = C::CExpression[[rb_cast_pvalue(expr, 3)], C::Pointer.new(C::BaseType.new(:char))]
        idx ? C::CExpression[p, :'[]', [idx]] : p
end
rb_test(expr, scope) click to toggle source

ruby bool test of a var assigns to a temporary var, and check against false/nil

# File samples/dynamic_ruby.rb, line 690
def rb_test(expr, scope)
        if nil.object_id == 0 or false.object_id == 0 # just to be sure
                nf = nil.object_id | false.object_id
                C::CExpression[[expr, :|, nf], :'!=', nf]
        else
                if expr.kind_of? C::Variable
                        tmp = expr
                else
                        tmp = get_new_tmp_var('test')
                        scope.statements << C::CExpression[tmp, :'=', expr]
                end
                C::CExpression[[tmp, :'!=', rb_nil], :'&&', [tmp, :'!=', rb_false]]
        end
end
rb_test_class_ary(expr) click to toggle source

return a C expr equivallent to TYPE(expr) == T_ARRAY

# File samples/dynamic_ruby.rb, line 717
def rb_test_class_ary(expr)
        rb_test_class_type(expr, 9)
end
rb_test_class_hash(expr) click to toggle source
# File samples/dynamic_ruby.rb, line 744
def rb_test_class_hash(expr)
        rb_test_class_type(expr, 0xb)
end
rb_test_class_string(expr) click to toggle source

TYPE(expr) == T_STRING

# File samples/dynamic_ruby.rb, line 731
def rb_test_class_string(expr)
        rb_test_class_type(expr, 7)
end
rb_test_class_type(expr, type) click to toggle source

return a C expr equivallent to TYPE(expr) == type for non-immediate types XXX expr evaluated 3 times

# File samples/dynamic_ruby.rb, line 712
def rb_test_class_type(expr, type)
        C::CExpression[[[expr, :>, [7]], :'&&', [[expr, :&, [3]], :==, [0]]], :'&&', [[rb_cast_pvalue(expr, 0), :&, [0x3f]], :'==', [type]]]
end
rb_true() click to toggle source
# File samples/dynamic_ruby.rb, line 670
def rb_true
        C::CExpression[[true.object_id], value]
end
resolve_const_owner(constname) click to toggle source

find the scope where constname is defined from @cref

# File samples/dynamic_ruby.rb, line 355
def resolve_const_owner(constname)
        @cref.find { |cr| cr.constants.map { |c| c.to_s }.include? constname.to_s }
end
value() click to toggle source

the VALUE typedef

# File samples/dynamic_ruby.rb, line 605
def value
        @cp.toplevel.symbol['VALUE']
end