class Metasm::C::CExpression

Constants

NegateOp
OP_PRIO

key = operator, value = hash regrouping operators of lower precedence funcall/array index/member dereference/sizeof are handled in ::parse_value

RIGHTASSOC

Attributes

lexpr[RW]

nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block

op[RW]

may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast)

rexpr[RW]

nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block

type[RW]

a Type

Public Class Methods

[](*args) click to toggle source

recursive constructor with automatic type inference e.g. CExpression[foo, :+, [:*, bar]] assumes root args are correctly typed (eg *foo => foo must be a pointer) take care to use [int] with immediates, e.g. CExpression[foo, :+, [2]] CExpr returns some_cexpr

# File metasm/parse_c.rb, line 1091
def self.[](*args)
        # sub-arrays in args are to be passed to self.[] recursively (syntaxic sugar)
        splat = lambda { |e| e.kind_of?(::Array) ? self[*e] : e }

        args.shift while args.first == nil   # CExpr[nil, :&, bla] => CExpr[:&, bla]

        case args.length
        when 4
                op = args[1]
                if op == :funcall or op == :'?:'
                        x2 = args[2].map { |a| splat[a] } if args[2]
                else
                        x2 = splat[args[2]]
                end
                new(splat[args[0]], op, x2, args[3])
        when 3
                op = args[1]
                x1 = splat[args[0]]
                if op == :funcall or op == :'?:'
                        x2 = args[2].map { |a| splat[a] } if args[2]
                else
                        x2 = splat[args[2]]
                end

                case op
                when :funcall
                       rt = x1.type.untypedef
                       rt = rt.type.untypedef if rt.pointer?
                       new(x1, op, x2, rt.type)
                when :[]; new(x1, op, x2, x1.type.untypedef.type)
                when :+; new(x1, op, x2, (x2.type.pointer? ? x2.type : x1.type))
                when :-; new(x1, op, x2, ((x1.type.pointer? and x2.type.pointer?) ? BaseType.new(:int) : x2.type.pointer? ? x2.type : x1.type))
                when :'&&', :'||', :==, :'!=', :>, :<, :<=, :>=; new(x1, op, x2, BaseType.new(:int))
                when :'.', :'->'
                        t = x1.type.untypedef
                        t = t.type.untypedef if op == :'->' and x1.type.pointer?
                        raise "parse error: #{t} has no member #{x2}" if not t.kind_of? Union or not m = t.findmember(x2)
                        new(x1, op, x2, m.type)
                when :'?:'; new(x1, op, x2, x2[0].type)
                when :','; new(x1, op, x2, x2.type)
                else new(x1, op, x2, x1.type)
                end
        when 2
                x0 = splat[args[0]]
                x1 = splat[args[1]]
                x0, x1 = x1, x0 if x0.kind_of? Type
                if x1.kind_of? Type; new(nil, nil, x0, x1)  # (cast)r
                elsif x0 == :*; new(nil, x0, x1, x1.type.untypedef.type)    # *r
                elsif x0 == :& and x1.kind_of? CExpression and x1.type.kind_of? C::Array; new(nil, nil, x1, Pointer.new(x1.type.type))
                elsif x0 == :&; new(nil, x0, x1, Pointer.new(x1.type))      # &r
                elsif x0 == :'!'; new(nil, x0, x1, BaseType.new(:int))      # &r
                elsif x1.kind_of? ::Symbol; new(x0, x1, nil, x0.type)       # l++
                else new(nil, x0, x1, x1.type)      # +r
                end
        when 1
                x = splat[args[0]]
                case x
                when CExpression; x
                when ::Integer; new(nil, nil, x, BaseType.new(:int))        # XXX range => __int64 ?
                when ::Float; new(nil, nil, x, BaseType.new(:double))
                when ::String; new(nil, nil, x, Pointer.new(BaseType.new(:char)))
                else new(nil, nil, x, x.type)
                end
        else raise "parse error CExpr[*#{args.inspect}]"
        end
end
constant?(e) click to toggle source
# File metasm/parse_c.rb, line 2267
def self.constant?(e)
        e.kind_of?(self) ? e.constant? : true
end
dump(e, scope, r=[''], dep=[], brace = false) click to toggle source
# File metasm/parse_c.rb, line 3916
def self.dump(e, scope, r=[''], dep=[], brace = false)
        if $DEBUG
                brace = false
                case e
                when CExpression, Variable
                        r, dep = e.type.dump_cast(scope, r, dep)
                end
                r.last << '('
        end
        r, dep =                          case e
        when ::Numeric; r.last << e.to_s ; [r, dep]
        when ::String; r.last << string_inspect(e) ; [r, dep]
        when CExpression; e.dump_inner(scope, r, dep, brace)
        when Variable; e.dump(scope, r, dep)
        when nil; [r, dep]
        else raise 'wtf?' + e.inspect
        end
        if $DEBUG
                r.last << ')'
        end
        [r, dep]
end
lvalue?(e) click to toggle source
# File metasm/parse_c.rb, line 2254
def self.lvalue?(e)
        e.kind_of?(self) ? e.lvalue? : (e.kind_of? Variable and e.name)
end
negate(e) click to toggle source

returns a CExpr negating this one (eg 'x' => '!x', 'a > b' => 'a <= b'…)

# File metasm/parse_c.rb, line 2422
def self.negate(e)
        e.kind_of?(self) ? e.negate : CExpression[:'!', e]
end
new(l, o, r, t) click to toggle source
# File metasm/parse_c.rb, line 1065
def initialize(l, o, r, t)
        raise "invalid CExpr #{[l, o, r, t].inspect}" if (o and not o.kind_of? ::Symbol) or not t.kind_of? Type
        @lexpr, @op, @rexpr, @type = l, o, r, t
end
parse(parser, scope, allow_coma = true) click to toggle source
# File metasm/parse_c.rb, line 2903
def parse(parser, scope, allow_coma = true)
        opstack = []
        stack = []

        return if not e = parse_value(parser, scope)

        stack << e

        while op = readop(parser)
                case op.value
                when :'?'
                        # a, b ? c, d : e, f  ==  a, (b ? (c, d) : e), f
                        until opstack.empty? or OP_PRIO[opstack.last][:'?:']
                                parse_popstack(parser, stack, opstack)
                        end
                        stack << parse(parser, scope)
                        raise op || parser, '":" expected' if not op = readop(parser) or op.value != :':'
                        op = op.dup
                        op.value = :'?:'
                when :':'
                        parser.unreadtok op
                        break
                else
                        if not allow_coma and op.value == :','
                                parser.unreadtok op
                                break
                        end
                        until opstack.empty? or OP_PRIO[op.value][opstack.last]
                                parse_popstack(parser, stack, opstack)
                        end
                end

                raise op, 'need rhs' if not e = parse_value(parser, scope)
                stack << e
                opstack << op.value
        end

        until opstack.empty?
                parse_popstack(parser, stack, opstack)
        end

        CExpression[stack.first]
end
parse_intfloat(parser, scope, tok) click to toggle source

parse sizeof offsetof float immediate etc into tok.value

# File metasm/parse_c.rb, line 2521
def parse_intfloat(parser, scope, tok)
        if tok.type == :string and not tok.value
                case tok.raw
                when 'sizeof'
                        if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
                                # check type
                                if v = Variable.parse_type(parser, scope)
                                        v.parse_declarator(parser, scope)
                                        raise tok if v.name != false
                                        raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
                                else
                                        raise tok, 'expr expected' if not v = parse(parser, scope)
                                        raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
                                end
                        else
                                parser.unreadtok ntok
                                raise tok, 'expr expected' if not v = parse_value(parser, scope)
                        end
                        tok.value = parser.sizeof(v)
                        return
                when '__builtin_offsetof'
                        raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != '('
                        raise tok if not ntok = parser.skipspaces or ntok.type != :string or ntok.raw != 'struct'
                        raise tok if not ntok = parser.skipspaces or ntok.type != :string
                        raise tok, 'unknown structure' if not struct = scope.struct_ancestors[ntok.raw] or not struct.kind_of? Union or not struct.members
                        raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
                        raise tok if not ntok = parser.skipspaces or ntok.type != :string
                        tok.value = struct.offsetof(parser, ntok.raw)
                        raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
                        return
                end
        end

        Expression.parse_num_value(parser, tok)
end
parse_popstack(parser, stack, opstack) click to toggle source
# File metasm/parse_c.rb, line 2803
def parse_popstack(parser, stack, opstack)
        r = stack.pop
        l = stack.pop
        case op = opstack.pop
        when :'?:'
                stack << CExpression.new(stack.pop, op, [l, r], l.type)
        when :','
                stack << CExpression.new(l, op, r, r.type)
        when :'='
                unless r.kind_of?(CExpression) and not r.lexpr and r.type.kind_of?(BaseType) and
                    ((not r.op and r.rexpr.kind_of?(Integer)) or
                     (r.op == :- and r.rexpr.kind_of?(CExpression) and not r.rexpr.op and not r.rexpr.lexpr and r.rexpr.rexpr.kind_of?(Integer))) and
                     l.kind_of?(Typed) and (l.type.untypedef.kind_of?(BaseType) or (l.type.untypedef.kind_of?(Pointer) and r.rexpr == 0))
                        # avoid useless warnings on unsigned foo = -1  /  void *foo = 0
                        parser.check_compatible_type(parser, r.type, l.type)
                end
                if l.kind_of?(Typed) and (lt = l.type.untypedef).kind_of?(BaseType) and r.kind_of?(Typed) and (rt = r.type.untypedef).kind_of?(BaseType) and lt.specifier != :unsigned and rt.specifier == :unsigned and parser.typesize[lt.name] > parser.typesize[rt.name]
                        # (int32)i = (uchar)255 => 255, not -1
                        r = CExpression.new(nil, nil, r, BaseType.new(lt.name, :unsigned))
                end
                stack << CExpression.new(l, op, r, l.type)
        when :'&&', :'||'
                stack << CExpression.new(l, op, r, BaseType.new(:int))
        else
                # XXX struct == struct ?
                raise parser, "invalid type #{l.type} #{l} for #{op.inspect}" if not l.type.arithmetic? and not parser.allow_bad_c
                raise parser, "invalid type #{r.type} #{r} for #{op.inspect}" if not r.type.arithmetic? and not parser.allow_bad_c

                if l.type.pointer? and r.type.pointer?
                        type =                                          case op
                        when :'-'; BaseType.new(:long)     # addr_t or sumthin ?
                        when :'-='; l.type
                        when :'>', :'>=', :'<', :'<=', :'==', :'!='; BaseType.new(:long)
                        else raise parser, "cannot do #{op.inspect} on pointers" unless parser.allow_bad_c ; l.type
                        end
                elsif l.type.pointer? or r.type.pointer?
                        puts parser.exception("should not #{op.inspect} a pointer").message if $VERBOSE and not [:'+', :'-', :'=', :'+=', :'-=', :==, :'!='].include? op
                        type = l.type.pointer? ? l.type : r.type
                elsif RIGHTASSOC[op] and op != :'?:'        # += etc
                        type = l.type
                else
                        # yay integer promotion
                        lt = l.type.untypedef
                        rt = r.type.untypedef
                        if    (t = lt).name == :longdouble or (t = rt).name == :longdouble or
                              (t = lt).name == :double or (t = rt).name == :double or
                              (t = lt).name == :float or (t = rt).name == :float
                        # long double > double > float ...
                                type = t
                        elsif true
                                # custom integer rules based on type sizes
                                lts = parser.typesize[lt.name]
                                rts = parser.typesize[rt.name]
                                its = parser.typesize[:int]
                                if not lts or not rts
                                        type = BaseType.new(:int)
                                elsif lts >  rts and lts >= its
                                        type = lt
                                elsif rts >  lts and rts >= its
                                        type = rt
                                elsif lts == rts and lts >= its
                                        type = lt
                                        type = rt if rt.specifier == :unsigned
                                else
                                        type = BaseType.new(:int)
                                end
                                # end of custom rules
                        elsif ((t = lt).name == :long and t.specifier == :unsigned) or
                              ((t = rt).name == :long and t.specifier == :unsigned)
                        # ... ulong ...
                                type = t
                        elsif (lt.name == :long and rt.name == :int and rt.specifier == :unsigned) or
                              (rt.name == :long and lt.name == :int and lt.specifier == :unsigned)
                        # long+uint => ulong
                                type = BaseType.new(:long, :unsigned)
                        elsif (t = lt).name == :long or (t = rt).name == :long or
                              ((t = lt).name == :int and t.specifier == :unsigned) or
                              ((t = rt).name == :int and t.specifier == :unsigned)
                        # ... long > uint ...
                                type = t
                        else
                        # int
                                type = BaseType.new(:int)
                        end
                end

                case op
                when :'>', :'>=', :'<', :'<=', :'==', :'!='
                        # cast both sides
                        l = CExpression[l, type] if l.type != type
                        r = CExpression[r, type] if r.type != type
                        stack << CExpression.new(l, op, r, BaseType.new(:int))
                else
                        # promote result
                        stack << CExpression.new(l, op, r, type)
                end
        end
end
parse_value(parser, scope) click to toggle source

returns the next value from parser (parenthesised expression, immediate, variable, unary operators)

# File metasm/parse_c.rb, line 2558
def parse_value(parser, scope)
        return if not tok = parser.skipspaces
        case tok.type
        when :string
                parse_intfloat(parser, scope, tok)
                val = tok.value || tok.raw
                if val.kind_of? ::String
                        raise tok, 'undefined variable' if not val = scope.symbol_ancestors[val]
                end
                case val
                when Type
                        raise tok, 'invalid variable'
                when Variable
                        val = parse_value_postfix(parser, scope, val)
                when ::Float
                        # parse suffix
                        type = :double
                        if (0..9).include?(tok.raw[0])
                                case tok.raw.downcase[-1]
                                when l; type = :longdouble
                                when f; type = :float
                                end
                        end
                        val = CExpression[val, BaseType.new(type)]

                when ::Integer
                        # parse suffix
                        # XXX 010h ?
                        type = :int
                        specifier = []
                        if (0..9).include?(tok.raw[0])
                                suffix = tok.raw.downcase[-3, 3] || tok.raw.downcase[-2, 2] || tok.raw.downcase[-1, 1]    # short string
                                specifier << :unsigned if suffix.include?('u') # XXX or tok.raw.downcase[1] == ?x
                                type = :longlong if suffix.count('l') == 2
                                type = :long if suffix.count('l') == 1
                        end
                        val = CExpression[val, BaseType.new(type, *specifier)]
                        val = parse_value_postfix(parser, scope, val)
                else raise parser, "internal error #{val.inspect}"
                end

        when :quoted
                if tok.raw[0] == '
                        raise tok, 'invalid character constant' if not [1, 2, 4, 8].include? tok.value.length      # TODO 0fill
                        val = CExpression[Expression.decode_imm(tok.value, tok.value.length, :big), BaseType.new(:int)]
                        val = parse_value_postfix(parser, scope, val)
                else
                        val = CExpression[tok.value, Pointer.new(BaseType.new(tok.raw[0, 2] == 'L"' ? :short : :char))]
                        val = parse_value_postfix(parser, scope, val)
                end

        when :punct
                case tok.raw
                when '('
                        ntok = nil
                        # check type casting
                        if v = Variable.parse_type(parser, scope)
                                v.parse_declarator(parser, scope)
                                (v.type.attributes ||= []).concat v.attributes if v.attributes
                                raise tok, 'bad cast' if v.name != false
                                raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
                                raise ntok, 'expr expected' if not val = parse_value(parser, scope)       # parses postfix too
                                #raise ntok, 'unable to cast a struct' if val.type.untypedef.kind_of? Union
                                val = CExpression[[val], v.type]
                        # check compound statement expression
                        elsif ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '{'
                                parser.unreadtok ntok
                                blk = parser.parse_statement(scope, [:expression]) # XXX nesting ?
                                raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
                                type = blk.statements.last.kind_of?(CExpression) ? blk.statements.last.type : BaseType.new(:void)
                                val = CExpression[blk, type]
                        else
                                parser.unreadtok ntok
                                if not val = parse(parser, scope)
                                        parser.unreadtok tok
                                        return
                                end
                                raise ntok || tok, 'no ")" found' if not ntok = parser.readtok or ntok.type != :punct or ntok.raw != ')'
                                val = parse_value_postfix(parser, scope, val)
                        end
                when '.'    # float
                        parse_intfloat(parser, scope, tok)
                        if not tok.value
                                parser.unreadtok tok
                                return
                        end
                        val = tok.value || tok.raw
                        type = :double
                        case tok.raw.downcase[-1]
                        when l; type = :longdouble
                        when f; type = :float
                        end
                        val = CExpression.new[val, BaseType.new(type)]

                when '+', '-', '&', '!', '~', '*', '--', '++', '&&'
                        # unary prefix
                        # may have been read ahead

                        raise parser if not ntok = parser.readtok
                        # check for -- ++ &&
                        if ntok.type == :punct and ntok.raw == tok.raw and %w[+ - &].include?(tok.raw)
                                tok.raw << ntok.raw
                        else
                                parser.unreadtok ntok
                        end

                        case tok.raw
                        when '&'
                                val = parse_value(parser, scope)
                                if val.kind_of? CExpression and val.op == :& and not val.lexpr and
                                        (val.rexpr.kind_of? Variable or val.rexpr.kind_of? CExpression) and val.rexpr.type.kind_of? Function
                                        # &&function == &function
                                elsif (val.kind_of? CExpression or val.kind_of? Variable) and val.type.kind_of? Array
                                        # &ary = ary
                                else
                                        raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
                                        raise val.backtrace, 'cannot take addr of register' if val.kind_of? Variable and val.storage == :register and not parser.allow_bad_c
                                        val = CExpression.new(nil, tok.raw.to_sym, val, Pointer.new(val.type))
                                end
                        when '++', '--'
                                val = parse_value(parser, scope)
                                raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
                                val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
                        when '&&'
                                raise tok, 'label name expected' if not val = parser.skipspaces or val.type != :string
                                val = CExpression.new(nil, nil, Label.new(val.raw, nil), Pointer.new(BaseType.new(:void)))
                        when '*'
                                raise tok, 'expr expected' if not val = parse_value(parser, scope)
                                raise tok, 'not a pointer' if not val.type.pointer? and not parser.allow_bad_c
                                newtype = val.type.pointer? ? val.type.pointed : BaseType.new(:int)
                                if not newtype.untypedef.kind_of? Function        # *fptr == fptr
                                        val = CExpression.new(nil, tok.raw.to_sym, val, newtype)
                                end
                        when '~', '!', '+', '-'
                                raise tok, 'expr expected' if not val = parse_value(parser, scope)
                                raise tok, 'type not arithmetic' if not val.type.arithmetic? and not parser.allow_bad_c
                                val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
                                val.type = BaseType.new(:int) if tok.raw == '!'
                        else raise tok, 'internal error'
                        end
                else
                        parser.unreadtok tok
                        return
                end
        else
                parser.unreadtok tok
                return
        end

        if val.kind_of? Variable and val.type.kind_of? Function
                # void (*bla)() = printf;  =>  ...= &printf;
                val = CExpression[:&, val]
        end

        val
end
parse_value_postfix(parser, scope, val) click to toggle source

parse postfix forms (postincrement, array index, struct member dereference)

# File metasm/parse_c.rb, line 2716
def parse_value_postfix(parser, scope, val)
        tok = parser.skipspaces
        nval =                          if tok and tok.type == :punct
                case tok.raw
                when '+', '++', '-', '--', '->'
                        ntok = parser.readtok
                        if (tok.raw == '+' or tok.raw == '-') and ntok and ntok.type == :punct and
                                        (ntok.raw == tok.raw or (tok.raw == '-' and ntok.raw == '>'))
                                tok.raw << ntok.raw
                        else
                                parser.unreadtok ntok
                        end
                        case tok.raw
                        when '+', '-'
                                nil
                        when '++', '--'
                                raise parser, "#{val}: invalid lvalue" if not CExpression.lvalue?(val)
                                CExpression.new(val, tok.raw.to_sym, nil, val.type)
                        when '->'
                                # XXX allow_bad_c..
                                raise tok, "#{val}: not a pointer" if not val.type.pointer?
                                type = val.type.pointed.untypedef
                                raise tok, "#{val}: bad pointer" if not type.kind_of? Union
                                raise tok, "#{val}: incomplete type" if not type.members
                                raise tok, "#{val}: invalid member" if not tok = parser.skipspaces or tok.type != :string or not m = type.findmember(tok.raw)
                                CExpression.new(val, :'->', tok.raw, m.type)
                        end
                when '.'
                        type = val.type.untypedef
                        if not ntok = parser.skipspaces or ntok.type != :string or not type.kind_of? Union
                                parser.unreadtok ntok
                                nil
                        else
                                raise ntok, "#{val}: incomplete type" if not type.members
                                raise ntok, "#{val}: invalid member" if not m = type.findmember(ntok.raw)
                                CExpression.new(val, :'.', ntok.raw, m.type)
                        end
                when '['
                        raise tok, "#{val}: index expected" if not idx = parse(parser, scope)
                        val, idx = idx, val        if not val.type.pointer?                # fake support of "4[tab]"
                        raise tok, "#{val}: not a pointer" if not val.type.pointer?
                        raise tok, "#{val}: invalid index"  if not idx.type.integral?
                        raise tok, "#{val}: get perpendicular ! (elsewhere)" if idx.kind_of?(CExpression) and idx.op == :','
                        raise tok || parser, "']' expected" if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
                        type = val.type.untypedef.type
                        # TODO boundscheck (and become king of the universe)
                        CExpression.new(val, :'[]', idx, type)
                when '('
                        type = val.type.untypedef
                        type = type.type.untypedef if type.kind_of? Pointer
                        raise tok, "#{val}: not a function" if not type.kind_of? Function

                        args = []
                        loop do
                                a = parse(parser, scope, false)
                                break if not a
                                args << a
                                if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
                                        parser.unreadtok ntok
                                        break
                                end
                        end
                        raise ntok || parser, "#{val}: ')' expected" if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'

                        type.args ||= []
                        raise tok, "#{val}: bad argument count: #{args.length} for #{type.args.length}" if (type.varargs ? (args.length < type.args.length) : (args.length != type.args.length))
                        type.args.zip(args) { |ta, a|
                                p, i = ta.type.pointer?, ta.type.integral?
                                r = a.reduce(parser) if p or i
                                if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
                                        tok = tok.dup ; tok.raw = a.to_s
                                        parser.check_compatible_type(tok, a.type, ta.type)
                                end
                        }
                        CExpression.new(val, :funcall, args, type.type)
                end
        end

        if nval
                parse_value_postfix(parser, scope, nval)
        else
                parser.unreadtok tok
                val
        end
end
precompile_inner(compiler, scope, expr, nested = true) click to toggle source
# File metasm/compile_c.rb, line 1008
def self.precompile_inner(compiler, scope, expr, nested = true)
        case expr
        when CExpression; expr.precompile_inner(compiler, scope, nested)
        else expr
        end
end
precompile_type(compiler, scope, obj, declaration = false) click to toggle source

changes obj.type to a precompiled type keeps struct/union, change everything else to __intd except Arrays if declaration is true (need to know variable allocation sizes etc) returns the type

# File metasm/compile_c.rb, line 969
def self.precompile_type(compiler, scope, obj, declaration = false)
        case t = obj.type.untypedef
        when BaseType
                case t.name
                when :void
                when :float, :double, :longdouble
                else t = BaseType.new("__int#{compiler.typesize[t.name]*8}".to_sym, t.specifier)
                end
        when Array
                if declaration; precompile_type(compiler, scope, t, declaration)
                else   t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned)
                end
        when Pointer
                if t.type.untypedef.kind_of? Function
                        precompile_type(compiler, scope, t, declaration)
                else
                        t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned)
                end
        when Enum;     t = BaseType.new("__int#{compiler.typesize[:int]*8}".to_sym)
        when Function
                precompile_type(compiler, scope, t)
                t.args ||= []
                t.args.each { |a| precompile_type(compiler, scope, a) }
        when Union
                if declaration and t.members and not t.name # anonymous struct
                        t.members.each { |a| precompile_type(compiler, scope, a, true) }
                end
        else raise 'bad type ' + t.inspect
        end
        (t.qualifier  ||= []).concat obj.type.qualifier  if obj.type.qualifier  and t != obj.type
        (t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type
        while obj.type.kind_of? TypeDef
                obj.type = obj.type.type
                (t.qualifier  ||= []).concat obj.type.qualifier  if obj.type.qualifier  and t != obj.type
                (t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type
        end
        obj.type = t
end
readop(parser) click to toggle source

reads a binary operator from the parser, returns the corresponding symbol or nil

# File metasm/parse_c.rb, line 2480
def readop(parser)
        if not op = parser.skipspaces or op.type != :punct
                parser.unreadtok op
                return
        end

        case op.raw
        when '>', '<', '|', '&' # << >> || &&
                if ntok = parser.readtok and ntok.type == :punct and ntok.raw == op.raw
                        op.raw << ntok.raw
                else
                        parser.unreadtok ntok
                end
        when '!' # != (mandatory)
                if not ntok = parser.readtok or ntok.type != :punct and ntok.raw != '='
                        parser.unreadtok op
                        return
                end
                op.raw << ntok.raw
        when '+', '-', '*', '/', '%', '^', '=', ',', '?', ':', '>>', '<<', '||', '&&',
             '+=','-=','*=','/=','%=','^=','==','&=','|=','!=' # ok
        else # bad
                parser.unreadtok op
                return
        end

        # may be followed by '='
        case op.raw
        when '+', '-', '*', '/', '%', '^', '&', '|', '>>', '<<', '<', '>', '='
                if ntok = parser.readtok and ntok.type == :punct and ntok.raw == '='
                        op.raw << ntok.raw
                else
                        parser.unreadtok ntok
                end
        end

        op.value = op.raw.to_sym
        op
end
reduce(parser, e) click to toggle source
# File metasm/parse_c.rb, line 2283
def self.reduce(parser, e)
        e.kind_of?(self) ? e.reduce(parser) : e
end
string_inspect(s) click to toggle source
# File metasm/parse_c.rb, line 3911
def self.string_inspect(s)
        # keep all ascii printable except \ and "
        '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o| '\x' + o.unpack('H*').first } + '"'
end

Public Instance Methods

==(o) click to toggle source
# File metasm/parse_c.rb, line 2410
def ==(o)
        o.object_id == self.object_id or
        (self.class == o.class and op == o.op and lexpr == o.lexpr and rexpr == o.rexpr)
end
===(o) click to toggle source
# File metasm/parse_c.rb, line 2415
def ===(o)
        (self.class == o.class and op == o.op and lexpr === o.lexpr and rexpr === o.rexpr) or
        (o.class == Variable and not @op and @rexpr == o)
end
complexity() click to toggle source
# File metasm/parse_c.rb, line 2456
def complexity
        cx = 1
        walk { |e| cx += e.complexity if e.kind_of?(CExpression) }
        cx
end
constant?() click to toggle source
# File metasm/parse_c.rb, line 2270
def constant?
        # gcc considers '1, 2' not constant
        if [:',', :funcall, :'=', :'--', :'++', :'+=', :'-=', :'*=', :'/=', :'>>=', :'<<=', :'&=', :'|=', :'^=', :'%=', :'->', :'[]'].include?(@op)
                false
        elsif @op == :'*' and not @lexpr; false
        elsif not @lexpr and not @op and @rexpr.kind_of? Block; false
        else
                out = true
                walk { |e| break out = false if not CExpression.constant?(e) }
                out
        end
end
deep_dup() click to toggle source

deep copy of the object recurses only within CExpressions, anything else is copied by reference

# File metasm/parse_c.rb, line 1078
def deep_dup
        n = dup
        n.lexpr = n.lexpr.deep_dup if n.lexpr.kind_of? CExpression
        n.rexpr = n.rexpr.deep_dup if n.rexpr.kind_of? CExpression
        n.rexpr = n.rexpr.map { |e| e.kind_of?(CExpression) ? e.deep_dup : e } if n.rexpr.kind_of? ::Array
        n
end
dump(scope, r=[''], dep=[]) click to toggle source
# File metasm/parse_c.rb, line 3940
def dump(scope, r=[''], dep=[])
        r, dep = dump_inner(scope, r, dep)
        r.last << ';'
        [r, dep]
end
dump_inner(scope, r=[''], dep=[], brace = false) click to toggle source
# File metasm/parse_c.rb, line 3946
def dump_inner(scope, r=[''], dep=[], brace = false)
        r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
        if not @lexpr
                if not @op
                        case @rexpr
                        when ::Numeric
                                if @rexpr < 0
                                        r.last << -
                                        re = -@rexpr
                                else
                                        re = @rexpr
                                end
                                if re >= 0x1000
                                        r.last << ("0x%X" % re)
                                else
                                        r.last << re.to_s
                                end
                                if @type.kind_of? BaseType
                                        r.last << 'U' if @type.specifier == :unsigned
                                        case @type.name
                                        when :longlong, :__int64; r.last << 'LL'
                                        when :long, :longdouble; r.last << 'L'
                                        when :float; r.last << 'F'
                                        end
                                end
                        when ::String
                                r.last << 'L' if @type.kind_of? Pointer and @type.type.kind_of? BaseType and @type.type.name == :short
                                r.last << CExpression.string_inspect(@rexpr)
                        when CExpression # cast
                                r, dep = @type.dump_cast(scope, r, dep)
                                r, dep = CExpression.dump(@rexpr, scope, r, dep, true)
                        when Variable
                                r, dep = @rexpr.dump(scope, r, dep)
                        when Block
                                r.last << '('
                                r, dep = Statement.dump(@rexpr, scope, r, dep)
                                r.last << ' )'
                        when Label
                                r.last << '&&' << @rexpr.name
                        else raise "wtf? #{inspect}"
                        end
                else
                        r.last << @op.to_s
                        r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? C::CExpression and @rexpr.lexpr))
                end
        elsif not @rexpr
                r, dep = CExpression.dump(@lexpr, scope, r, dep)
                r.last << @op.to_s
        else
                case @op
                when :'->', :'.'
                        r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
                        r.last << @op.to_s << @rexpr
                when :'[]'
                        r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
                        r.last << '['
                        l = lexpr if lexpr.kind_of? Variable
                        l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'.'
                        l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'->'
                        # honor __attribute__((indexenum(enumname)))
                        if l and l.attributes and rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr.kind_of? ::Integer and
                                        n = l.has_attribute_var('indexenum') and enum = scope.struct_ancestors[n] and i = enum.members.index(rexpr.rexpr)
                                r.last << i
                                dep |= [enum]
                        else
                                r, dep = CExpression.dump(@rexpr, scope, r, dep)
                        end
                        r.last << ']'
                when :funcall
                        r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
                        r.last << '('
                        @rexpr.each { |arg|
                                r.last << ', ' if r.last[-1] != (
                                r, dep = CExpression.dump(arg, scope, r, dep)
                        }
                        r.last << ')'
                when :'?:'
                        r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
                        r.last << ' ? '
                        r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true)
                        r.last << ' : '
                        r, dep = CExpression.dump(@rexpr[1], scope, r, dep, true)
                else
                        r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op))
                        r.last << ' ' << @op.to_s << ' '
                        r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall))
                end
        end
        r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
        [r, dep]
end
lvalue?() click to toggle source
# File metasm/parse_c.rb, line 2257
def lvalue?
        case @op
        when :*; true if not @lexpr
        when :'[]', :'.', :'->'; true
        when nil     # cast
                CExpression.lvalue?(@rexpr)
        else false
        end
end
negate() click to toggle source
# File metasm/parse_c.rb, line 2425
def negate
        if @op == :'!'
                CExpression[@rexpr]
        elsif nop = NegateOp[@op]
                if nop == :== and @rexpr.kind_of? CExpression and not @rexpr.op and @rexpr.rexpr == 0 and
                                @lexpr.kind_of? CExpression and [:==, :'!=', :>, :<, :>=, :<=, :'!'].include? @lexpr.op
                        # (a > b) != 0  =>  (a > b)
                        CExpression[@lexpr]
                else
                        CExpression.new(@lexpr, nop, @rexpr, @type)
                end
        elsif nop = { :'||' => :'&&', :'&&' => :'||' }[@op]
                CExpression.new(CExpression.negate(@lexpr), nop, CExpression.negate(@rexpr), @type)
        else
                CExpression[:'!', self]
        end
end
precompile(compiler, scope) click to toggle source
# File metasm/compile_c.rb, line 960
def precompile(compiler, scope)
        i = precompile_inner(compiler, scope, false)
        scope.statements << i if i
end
precompile_inner(compiler, scope, nested = true) click to toggle source

returns a new CExpression with simplified self.type, computes structure offsets turns char[]/float immediates to reference to anonymised const TODO 'a = b += c' => 'b += c; a = b' (use nested argument) TODO handle ::precompile_inner return nil TODO struct.bits

# File metasm/compile_c.rb, line 1020
def precompile_inner(compiler, scope, nested = true)
        case @op
        when :'.'
                # a.b => (&a)->b
                lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                ll = lexpr
                ll = lexpr.rexpr while ll.kind_of? CExpression and not ll.op
                if ll.kind_of? CExpression and ll.op == :'*' and not ll.lexpr
                        # do not change lexpr.rexpr.type directly to a pointer, might retrigger (ptr+imm) => (ptr + imm*sizeof(*ptr))
                        @lexpr = CExpression.new(nil, nil, ll.rexpr, Pointer.new(lexpr.type))
                else
                        @lexpr = CExpression.new(nil, :'&', lexpr, Pointer.new(lexpr.type))
                end
                @op = :'->'
                precompile_inner(compiler, scope)
        when :'->'
                # a->b => *(a + off(b))
                struct = @lexpr.type.untypedef.type.untypedef
                lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                @lexpr = nil
                @op = nil
                if struct.kind_of? Union and (off = struct.offsetof(compiler, @rexpr)) != 0
                        off = CExpression.new(nil, nil, off, BaseType.new(:int, :unsigned))
                        @rexpr = CExpression.new(lexpr, :'+', off, lexpr.type)
                        # ensure the (ptr + value) is not expanded to (ptr + value * sizeof(*ptr))
                        CExpression.precompile_type(compiler, scope, @rexpr)
                else
                        # union or 1st struct member
                        @rexpr = lexpr
                end
                if @type.kind_of? Array # Array member type is already an address
                else
                        @rexpr = CExpression.new(nil, :*, @rexpr, @rexpr.type)
                end
                precompile_inner(compiler, scope)
        when :'[]'
                rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                if rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr == 0
                        @rexpr = @lexpr
                else
                        @rexpr = CExpression.new(@lexpr, :'+', rexpr, @lexpr.type)
                end
                @op = :'*'
                @lexpr = nil
                precompile_inner(compiler, scope)
        when :'?:'
                # cannot precompile in place, a conditionnal expression may have a coma: must turn into If
                if @lexpr.kind_of? CExpression
                        @lexpr = @lexpr.precompile_inner(compiler, scope)
                        if not @lexpr.lexpr and not @lexpr.op and @lexpr.rexpr.kind_of? ::Numeric
                                if @lexpr.rexpr == 0
                                        e = @rexpr[1]
                                else
                                        e = @rexpr[0]
                                end
                                e = CExpression.new(nil, nil, e, e.type) if not e.kind_of? CExpression
                                return e.precompile_inner(compiler, scope)
                        end
                end
                raise 'conditional in toplevel' if scope == compiler.toplevel       # just in case
                var = Variable.new
                var.storage = :register
                var.name = compiler.new_label('ternary')
                var.type = @rexpr[0].type
                CExpression.precompile_type(compiler, scope, var)
                Declaration.new(var).precompile(compiler, scope)
                If.new(@lexpr, CExpression.new(var, :'=', @rexpr[0], var.type), CExpression.new(var, :'=', @rexpr[1], var.type)).precompile(compiler, scope)
                @lexpr = nil
                @op = nil
                @rexpr = var
                precompile_inner(compiler, scope)
        when :'&&'
                if scope == compiler.toplevel
                        @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        CExpression.precompile_type(compiler, scope, self)
                        self
                else
                        var = Variable.new
                        var.storage = :register
                        var.name = compiler.new_label('and')
                        var.type = @type
                        CExpression.precompile_type(compiler, scope, var)
                        var.initializer = CExpression.new(nil, nil, 0, var.type)
                        Declaration.new(var).precompile(compiler, scope)
                        l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
                        r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
                        If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 1, var.type), var.type))).precompile(compiler, scope)
                        @lexpr = nil
                        @op = nil
                        @rexpr = var
                        precompile_inner(compiler, scope)
                end
        when :'||'
                if scope == compiler.toplevel
                        @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        CExpression.precompile_type(compiler, scope, self)
                        self
                else
                        var = Variable.new
                        var.storage = :register
                        var.name = compiler.new_label('or')
                        var.type = @type
                        CExpression.precompile_type(compiler, scope, var)
                        var.initializer = CExpression.new(nil, nil, 1, var.type)
                        Declaration.new(var).precompile(compiler, scope)
                        l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
                        l = CExpression.new(nil, :'!', l, var.type)
                        r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
                        r = CExpression.new(nil, :'!', r, var.type)
                        If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 0, var.type), var.type))).precompile(compiler, scope)
                        @lexpr = nil
                        @op = nil
                        @rexpr = var
                        precompile_inner(compiler, scope)
                end
        when :funcall
                if @lexpr.kind_of? Variable and @lexpr.type.kind_of? Function and @lexpr.attributes and @lexpr.attributes.include? 'inline' and @lexpr.initializer
                        # TODO check recursive call (direct or indirect)
                        raise 'inline varargs unsupported' if @lexpr.type.varargs
                        rtype = @lexpr.type.type.untypedef
                        if not rtype.kind_of? BaseType or rtype.name != :void
                                rval = Variable.new
                                rval.name = compiler.new_label('inline_return')
                                rval.type = @lexpr.type.type
                                Declaration.new(rval).precompile(compiler, scope)
                        end
                        inline_label = {}
                        locals = @lexpr.type.args.zip(@rexpr).inject({}) { |h, (fa, a)|
                                h.update fa => CExpression.new(nil, nil, a, fa.type).precompile_inner(compiler, scope)
                        }
                        copy_inline_ce = lambda { |ce|
                                case ce
                                when CExpression; CExpression.new(copy_inline_ce[ce.lexpr], ce.op, copy_inline_ce[ce.rexpr], ce.type)
                                when Variable; locals[ce] || ce
                                when ::Array; ce.map { |e_| copy_inline_ce[e_] }
                                else ce
                                end
                        }
                        copy_inline = lambda { |stmt, scp|
                                case stmt
                                when Block
                                        b = Block.new(scp)
                                        stmt.statements.each { |s|
                                                s = copy_inline[s, b]
                                                b.statements << s if s
                                        }
                                        b
                                when If;     If.new(copy_inline_ce[stmt.test], copy_inline[stmt.bthen, scp])              # re-precompile ?
                                when Label;  Label.new(inline_label[stmt.name]  ||= compiler.new_label('inline_'+stmt.name))
                                when Goto;   Goto.new(inline_label[stmt.target] ||= compiler.new_label('inline_'+stmt.target))
                                when Return; CExpression.new(rval, :'=', copy_inline_ce[stmt.value], rval.type).precompile_inner(compiler, scp) if stmt.value
                                when CExpression; copy_inline_ce[stmt]
                                when Declaration
                                        nv = stmt.var.dup
                                        if nv.type.kind_of? Array and nv.type.length.kind_of? CExpression
                                                nv.type = Array.new(nv.type.type, copy_inline_ce[nv.type.length])       # XXX nested dynamic?
                                        end
                                        locals[stmt.var] = nv
                                        scp.symbol[nv.name] = nv
                                        Declaration.new(nv)
                                else raise 'unexpected inline statement ' + stmt.inspect
                                end
                        }
                        scope.statements << copy_inline[@lexpr.initializer, scope]         # body already precompiled
                        CExpression.new(nil, nil, rval, rval.type).precompile_inner(compiler, scope)
                elsif @type.kind_of? Union
                        var = Variable.new
                        var.name = compiler.new_label('return_struct')
                        var.type = @type
                        Declaration.new(var).precompile(compiler, scope)
                        @rexpr.unshift CExpression.new(nil, :&, var, Pointer.new(var.type))

                        var2 = Variable.new
                        var2.name = compiler.new_label('return_struct_ptr')
                        var2.type = Pointer.new(@type)
                        var2.storage = :register
                        CExpression.precompile_type(compiler, scope, var2)
                        Declaration.new(var2).precompile(compiler, scope)
                        @type = var2.type
                        CExpression.new(var2, :'=', self, var2.type).precompile(compiler, scope)

                        CExpression.new(nil, :'*', var2, var.type).precompile_inner(compiler, scope)
                else
                        t = @lexpr.type.untypedef
                        t = t.type.untypedef if t.pointer?
                        @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                        types = t.args.map { |a| a.type }
                        # cast args to func prototype
                        @rexpr.map! { |e_| (types.empty? ? e_ : CExpression.new(nil, nil, e_, types.shift)).precompile_inner(compiler, scope) }
                        CExpression.precompile_type(compiler, scope, self)
                        self
                end
        when :','
                lexpr = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
                rexpr = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
                lexpr.precompile(compiler, scope)
                rexpr.precompile_inner(compiler, scope)
        when :'!'
                CExpression.precompile_type(compiler, scope, self)
                if @rexpr.kind_of?(CExpression)
                        case @rexpr.op
                        when :'<', :'>', :'<=', :'>=', :'==', :'!='
                                @op = { :'<' => :'>=', :'>' => :'<=', :'<=' => :'>', :'>=' => :'<',
                                        :'==' => :'!=', :'!=' => :'==' }[@rexpr.op]
                                @lexpr = @rexpr.lexpr
                                @rexpr = @rexpr.rexpr
                                precompile_inner(compiler, scope)
                        when :'&&', :'||'
                                @op = { :'&&' => :'||', :'||' => :'&&' }[@rexpr.op]
                                @lexpr = CExpression.new(nil, :'!', @rexpr.lexpr, @type)
                                @rexpr = CExpression.new(nil, :'!', @rexpr.rexpr, @type)
                                precompile_inner(compiler, scope)
                        when :'!'
                                if @rexpr.rexpr.kind_of? CExpression
                                        @op = nil
                                        @rexpr = @rexpr.rexpr
                                else
                                        @op = :'!='
                                        @lexpr = @rexpr.rexpr
                                        @rexpr = CExpression.new(nil, nil, 0, @lexpr.type)
                                end
                                precompile_inner(compiler, scope)
                        else
                                @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                                self
                        end
                else
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        self
                end
        when :'++', :'--'
                if not @rexpr
                        var = Variable.new
                        var.storage = :register
                        var.name = compiler.new_label('postincrement')
                        var.type = @type
                        Declaration.new(var).precompile(compiler, scope)
                        CExpression.new(var, :'=', @lexpr, @type).precompile(compiler, scope)
                        CExpression.new(nil, @op, @lexpr, @type).precompile(compiler, scope)
                        @lexpr = nil
                        @op = nil
                        @rexpr = var
                        precompile_inner(compiler, scope)
                elsif @type.pointer? and compiler.sizeof(nil, @type.untypedef.type.untypedef) != 1
                        # ++ptr => ptr += sizeof(*ptr) (done in += precompiler)
                        @op = { :'++' => :'+=', :'--' => :'-=' }[@op]
                        @lexpr = @rexpr
                        @rexpr = CExpression.new(nil, nil, 1, BaseType.new(:ptr, :unsigned))
                        precompile_inner(compiler, scope)
                else
                        CExpression.precompile_type(compiler, scope, self)
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        self
                end
        when :'='
                # handle structure assignment/array assignment
                case @lexpr.type.untypedef
                when Union
                        # rexpr may be a :funcall
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        @lexpr.type.untypedef.members.zip(@rexpr.type.untypedef.members) { |m1, m2|
                                # assume m1 and m2 are compatible
                                v1 = CExpression.new(@lexpr, :'.', m1.name, m1.type)
                                v2 = CExpression.new(@rexpr, :'.', m2.name, m1.type)
                                CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope)
                        }
                        # (foo = bar).toto
                        @op = nil
                        @rexpr = @lexpr
                        @lexpr = nil
                        @type = @rexpr.type
                        precompile_inner(compiler, scope) if nested
                when Array
                        if not len = @lexpr.type.untypedef.length
                                @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                                # char toto[] = "bla"
                                if @rexpr.kind_of? CExpression and not @rexpr.lexpr and not @rexpr.op and
                                                @rexpr.rexpr.kind_of? Variable and @rexpr.rexpr.type.kind_of? Array
                                        len = @rexpr.rexpr.type.length
                                end
                        end
                        raise 'array initializer with no length !' if not len
                        # TODO optimize...
                        len.times { |i|
                                i = CExpression.new(nil, nil, i, BaseType.new(:long, :unsigned))
                                v1 = CExpression.new(@lexpr, :'[]', i, @lexpr.type.untypedef.type)
                                v2 = CExpression.new(@rexpr, :'[]', i, v1.type)
                                CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope)
                        }
                        @op = nil
                        @rexpr = @lexpr
                        @lexpr = nil
                        @type = @rexpr.type
                        precompile_inner(compiler, scope) if nested
                else
                        @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                        @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
                        CExpression.precompile_type(compiler, scope, self)
                        self
                end
        when nil
                case @rexpr
                when Block
                        # compound statements
                        raise 'compound statement in toplevel' if scope == compiler.toplevel       # just in case
                        var = Variable.new
                        var.storage = :register
                        var.name = compiler.new_label('compoundstatement')
                        var.type = @type
                        CExpression.precompile_type(compiler, scope, var)
                        Declaration.new(var).precompile(compiler, scope)
                        if @rexpr.statements.last.kind_of? CExpression
                                @rexpr.statements[-1] = CExpression.new(var, :'=', @rexpr.statements[-1], var.type)
                                @rexpr.precompile(compiler, scope)
                        end
                        @rexpr = var
                        precompile_inner(compiler, scope)
                when ::String
                        # char[] immediate
                        v = Variable.new
                        v.storage = :static
                        v.name = 'char_' + @rexpr.tr('^a-zA-Z', '')[0, 8]
                        v.type = Array.new(@type.type)
                        v.type.length = @rexpr.length + 1
                        v.type.type.qualifier = [:const]
                        v.initializer = CExpression.new(nil, nil, @rexpr, @type)
                        Declaration.new(v).precompile(compiler, scope)
                        @rexpr = v
                        precompile_inner(compiler, scope)
                when ::Float
                        # float immediate
                        v = Variable.new
                        v.storage = :static
                        v.name = @type.untypedef.name.to_s
                        v.type = @type
                        v.type.qualifier = [:const]
                        v.initializer = CExpression.new(nil, nil, @rexpr, @type)
                        Declaration.new(v).precompile(compiler, scope)
                        @rexpr = CExpression.new(nil, :'*', v, v.type)
                        precompile_inner(compiler, scope)
                when CExpression
                        # simplify casts
                        CExpression.precompile_type(compiler, scope, self)
                        # propagate type first so that __uint64 foo() { return -1 } => 0xffffffffffffffff
                        @rexpr.type = @type if @rexpr.kind_of? CExpression and @rexpr.op == :- and not @rexpr.lexpr and @type.kind_of? BaseType and @type.name == :__int64 # XXX kill me
                        @rexpr = @rexpr.precompile_inner(compiler, scope)
                        if @type.kind_of? BaseType and @rexpr.type.kind_of? BaseType
                                if @rexpr.type == @type
                                        # noop cast
                                        @lexpr, @op, @rexpr = @rexpr.lexpr, @rexpr.op, @rexpr.rexpr
                                elsif not @rexpr.op and @type.integral? and @rexpr.type.integral?
                                        if @rexpr.rexpr.kind_of? ::Numeric and (val = reduce(compiler)).kind_of? ::Numeric
                                                @rexpr = val
                                        elsif compiler.typesize[@type.name] < compiler.typesize[@rexpr.type.name]
                                                # (char)(short)(int)(long)foo => (char)foo
                                                @rexpr = @rexpr.rexpr
                                        end
                                end
                        end
                        self
                else
                        CExpression.precompile_type(compiler, scope, self)
                        self
                end
        else
                # int+ptr => ptr+int
                if @op == :+ and @lexpr and @lexpr.type.integral? and @rexpr.type.pointer?
                        @rexpr, @lexpr = @lexpr, @rexpr
                end

                # handle pointer + 2 == ((char *)pointer) + 2*sizeof(*pointer)
                if @rexpr and [:'+', :'+=', :'-', :'-='].include? @op and
                                @type.pointer? and @rexpr.type.integral?
                        sz = compiler.sizeof(nil, @type.untypedef.type.untypedef)
                        if sz != 1
                                sz = CExpression.new(nil, nil, sz, @rexpr.type)
                                @rexpr = CExpression.new(@rexpr, :'*', sz, @rexpr.type)
                        end
                end

                # type promotion => cast
                case @op
                when :+, :-, :*, :/, :&, :|, :^, :%
                    if @lexpr
                        if @lexpr.type != @type
                                @lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression
                                @lexpr = CExpression.new(nil, nil, @lexpr, @type)
                        end
                        if @rexpr.type != @type
                                @rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression
                                @rexpr = CExpression.new(nil, nil, @rexpr, @type)
                        end
                    end
                when :>>, :<<
                        # char => int
                        if @lexpr.type != @type
                                @lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression
                                @lexpr = CExpression.new(nil, nil, @lexpr, @type)
                        end
                when :'+=', :'-=', :'*=', :'/=', :'&=', :'|=', :'^=', :'%='
                        if @rexpr.type != @lexpr.type
                                @rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression
                                @rexpr = CExpression.new(nil, nil, @rexpr, @type)
                        end
                end

                @lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
                @rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)

                if @op == :'&' and not @lexpr
                        rr = @rexpr
                        rr = rr.rexpr while rr.kind_of? CExpression and not rr.op
                        if rr.kind_of? CExpression and rr.op == :'*' and not rr.lexpr
                                @lexpr = nil
                                @op = nil
                                @rexpr = rr.rexpr
                                return precompile_inner(compiler, scope)
                        elsif rr != @rexpr
                                @rexpr = rr
                                return precompile_inner(compiler, scope)
                        end
                end

                CExpression.precompile_type(compiler, scope, self)

                isnumeric = lambda { |e_| e_.kind_of?(::Numeric) or (e_.kind_of? CExpression and
                        not e_.lexpr and not e_.op and e_.rexpr.kind_of? ::Numeric) }

                # calc numeric
                # XXX do not simplify operations involving variables (for type overflow etc)
                if isnumeric[@rexpr] and (not @lexpr or isnumeric[@lexpr]) and (val = reduce(compiler)).kind_of? ::Numeric
                        @lexpr = nil
                        @op = nil
                        @rexpr = val
                end

                self
        end
end
reduce(parser) click to toggle source
# File metasm/parse_c.rb, line 2286
def reduce(parser)
        # parser used for arithmetic overflows (need basic type sizes)
        case @op
        when :'&&'
                case l = CExpression.reduce(parser, @lexpr)
                when 0; 0
                when ::Integer
                        case r = CExpression.reduce(parser, @rexpr)
                        when 0; 0
                        when ::Integer; 1
                        else CExpression.new(l, @op, r, @type)
                        end
                else CExpression.new(l, @op, @rexpr, @type)
                end
        when :'||'
                case l = CExpression.reduce(parser, @lexpr)
                when 0
                        case r = CExpression.reduce(parser, @rexpr)
                        when 0; 0
                        when ::Integer; 1
                        else CExpression.new(l, @op, r, @type)
                        end
                when ::Integer; 1
                else CExpression.new(l, @op, @rexpr, @type)
                end
        when :'!'
                case r = CExpression.reduce(parser, @rexpr)
                when 0; 1
                when ::Integer; 0
                else CExpression.new(nil, @op, r, @type)
                end
        when :'!=', :'==', :'<', :'>', :'>=', :'<='
                l = CExpression.reduce(parser, @lexpr)
                r = CExpression.reduce(parser, @rexpr)
                if l.kind_of?(::Integer) and r.kind_of?(::Integer)
                        if @op == :'!='; l != r ? 1 : 0
                        else l.send(@op, r) ? 1 : 0
                        end
                else
                        l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
                        r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
                        CExpression.new(l, @op, r, @type)
                end
        when :'.'
                le = CExpression.reduce(parser, @lexpr)
                if le.kind_of? Variable and le.initializer.kind_of? ::Array
                        t = le.type.untypedef
                        midx = t.members.index(t.findmember(@rexpr))
                        CExpression.reduce(parser, le.initializer[midx] || 0)
                else
                        CExpression.new(le, @op, @rexpr, @type)
                end
        when :'?:'
                case c = CExpression.reduce(parser, @lexpr)
                when 0;         CExpression.reduce(parser, @rexpr[0])
                when ::Integer; CExpression.reduce(parser, @rexpr[1])
                else CExpression.new(c, @op, @rexpr, @type)
                end
        when :'+', :'-', :'*', :'/', :'^', :'%', :'&', :'|', :'>>', :'<<', :'~', nil
                t = @type.untypedef
                case t
                when BaseType
                when Pointer; return self if @op
                else
                        return @rexpr if not @op and not @lexpr and @rexpr.kind_of? Variable and @rexpr.type == @type
                        return self # raise parser, 'not arithmetic type'
                end

                # compute value
                r = CExpression.reduce(parser, @rexpr)
                ret =                                  if not @lexpr
                        # unary
                        case @op
                        when :'+', nil, :'-', :'~'
                                return CExpression.new(nil, @op, r, @type) if not r.kind_of? ::Numeric
                                case @op
                                when :'-'; -r
                                when :'~'; ~r
                                else r
                                end
                        else return CExpression.new(nil, @op, r, @type)
                        end
                else
                        l = CExpression.reduce(parser, @lexpr)
                        if not l.kind_of?(::Numeric) or not r.kind_of?(::Numeric)
                                l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
                                r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
                                return CExpression.new(l, @op, r, @type)
                        end
                        l.send(@op, r)
                end

                # overflow
                tn = (t.pointer? ? :ptr : t.name)
                case tn
                when :char, :short, :int, :long, :ptr, :longlong, :__int8, :__int16, :__int32, :__int64
                        max = 1 << (8*parser.typesize[tn])
                        ret = ret.to_i & (max-1)
                        if not t.pointer? and t.specifier != :unsigned and (ret & (max >> 1)) > 0  # char == unsigned char
                                ret - max
                        else
                                ret
                        end
                when :float, :double, :longdouble
                        ret.to_f   # TODO
                end
        when :funcall
                l = CExpression.reduce(parser, @lexpr)
                r = @rexpr.map { |rr|
                        rr = CExpression.reduce(parser, rr)
                        rr = CExpression.new(nil, nil, rr, BaseType.new(:int)) if rr.kind_of? ::Integer
                        rr
                }
                CExpression.new(l, @op, r, @type)
        else
                l = CExpression.reduce(parser, @lexpr) if @lexpr
                r = CExpression.reduce(parser, @rexpr) if @rexpr
                l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
                r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
                CExpression.new(l, @op, r, @type)
        end
end
replace(o) click to toggle source

overwrites @lexpr @op @rexpr @type from the arg

# File metasm/parse_c.rb, line 1071
def replace(o)
        @lexpr, @op, @rexpr, @type = o.lexpr, o.op, o.rexpr, o.type
        self
end
to_s() click to toggle source
# File metasm/parse_c.rb, line 4038
def to_s
        dump_inner(Block.new(nil))[0].join(' ')
end
walk() { |lexpr| ... } click to toggle source
# File metasm/parse_c.rb, line 2443
def walk
        case @op
        when :funcall, :'?:'
                yield @lexpr
                @rexpr.each { |arg| yield arg }
        when :'->', :'.'
                yield @lexpr
        else
                yield @lexpr if @lexpr
                yield @rexpr if @rexpr
        end
end