class Metasm::C::Variable

Attributes

backtrace[RW]
initializer[RW]
name[RW]
stackoff[RW]
storage[RW]
type[RW]

Public Class Methods

new(name=nil, type=nil) click to toggle source
# File metasm/parse_c.rb, line 768
def initialize(name=nil, type=nil)
        @name, @type = name, type
end
parse_type(parser, scope, allow_value = false) click to toggle source

parses a variable basetype/qualifier/(storage if allow_value), returns a new variable of this type populates scope.struct

# File metasm/parse_c.rb, line 1874
def self.parse_type(parser, scope, allow_value = false)
        var = new
        qualifier = []
        tok = nil
        loop do
                var.parse_attributes(parser, true)
                break if not tok = parser.skipspaces
                if tok.type != :string
                        parser.unreadtok tok
                        break
                end

                case tok.raw
                when 'const', 'volatile'
                        qualifier << tok.raw.to_sym
                        next
                when 'register', 'auto', 'static', 'typedef', 'extern'
                        raise tok, 'storage specifier not allowed here' if not allow_value
                        raise tok, 'multiple storage class' if var.storage
                        var.storage = tok.raw.to_sym
                        next
                when 'struct'
                        var.type = Struct.new
                        var.type.pack = parser.pragma_pack if parser.pragma_pack
                        var.parse_type_struct(parser, scope)
                when 'union'
                        var.type = Union.new
                        var.parse_type_struct(parser, scope)
                when 'enum'
                        var.type = Enum.new
                        var.parse_type_struct(parser, scope)
                when 'typeof'
                        if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
                                # check type
                                if v = 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 = CExpression.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 = CExpression.parse_value(parser, scope)
                        end
                        var.type = v.type # TypeDef.new('typeof', v.type, tok)
                when 'long', 'short', 'signed', 'unsigned', 'int', 'char', 'float', 'double',
                                'void', '__int8', '__int16', '__int32', '__int64',
                                'intptr_t', 'uintptr_t'
                        parser.unreadtok tok
                        var.parse_type_base(parser, scope)
                else
                        if type = scope.symbol_ancestors[tok.raw] and type.kind_of? TypeDef
                                var.type = type.dup
                        else
                                parser.unreadtok tok
                        end
                end

                break
        end

        if not var.type
                raise tok || parser, 'bad type name' if not qualifier.empty? or var.storage
                nil
        else
                var.type.qualifier = var.type.qualifier.to_a | qualifier if not qualifier.empty?
                var.type.parse_attributes(parser, true)
                var
        end
end

Public Instance Methods

===(o) click to toggle source
# File metasm/parse_c.rb, line 2248
def ===(o)
        self == o or (o.class == CExpression and not o.op and o.rexpr == self)
end
dump(scope, r=[''], dep=[]) click to toggle source
# File metasm/parse_c.rb, line 3464
def dump(scope, r=[''], dep=[])
        if name
                dep |= [scope.symbol_ancestors[@name]]
                r.last << @name
        end
        [r, dep]
end
dump_def(scope, r=[''], dep=[], skiptype=false) click to toggle source
# File metasm/parse_c.rb, line 3471
def dump_def(scope, r=[''], dep=[], skiptype=false)
        # int a=1, b=2;
        r.last << dump_attributes_pre
        if not skiptype
                r.last << @storage.to_s << ' ' if storage
                r, dep = @type.base.dump(scope, r, dep)
                r.last << ' ' if name
        end
        r, dep = @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)

        if initializer
                r.last << ' = ' if not @type.kind_of?(Function)
                r, dep = @type.dump_initializer(@initializer, scope, r, dep)
        end
        [r, dep]
end
parse_declarator(parser, scope, rec = false) click to toggle source

updates @type and @name, parses pointer/arrays/function declarations parses anonymous declarators (@name will be false) the caller is responsible for detecting redefinitions scope used only in Metasm::C::CExpression.parse for array sizes and function prototype argument types rec for internal use only

# File metasm/parse_c.rb, line 2085
def parse_declarator(parser, scope, rec = false)
        parse_attributes(parser, true)
        tok = parser.skipspaces
        # read upto name
        if tok and tok.type == :punct and tok.raw == '*'
                ptr = Pointer.new
                ptr.parse_attributes(parser)
                while ntok = parser.skipspaces and ntok.type == :string
                        case ntok.raw
                        when 'const', 'volatile'
                                (ptr.qualifier ||= []) << ntok.raw.to_sym
                                ptr.parse_attributes(parser)
                        else break
                        end
                end
                parser.unreadtok ntok
                parse_declarator(parser, scope, true)
                t = self
                t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
                ptr.type = t.type
                t.type = ptr
                if t.kind_of? Function and ptr.attributes
                        @attributes ||= []
                        @attributes |= ptr.attributes
                        ptr.attributes = nil
                end
                return
        elsif tok and tok.type == :punct and tok.raw == '('
                parse_declarator(parser, scope, true)
                raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
        elsif tok and tok.type == :string
                case tok.raw
                when 'const', 'volatile'
                        (@type.qualifier ||= []) << tok.raw.to_sym
                        return parse_declarator(parser, scope, rec)
                when 'register', 'auto', 'static', 'typedef', 'extern'
                        raise tok, 'multiple storage class' if storage
                        @storage = tok.raw.to_sym
                        puts tok.exception('misplaced storage specifier').message if $VERBOSE
                        return parse_declarator(parser, scope, rec)
                end
                raise tok if name or name == false
                raise tok, 'bad var name' if Keyword[tok.raw] or (0..9).include?(tok.raw[0])
                @name = tok.raw
                @backtrace = tok
                parse_attributes(parser, true)
        else
                # unnamed
                raise tok || parser if name or name == false
                @name = false
                @backtrace = tok
                parser.unreadtok tok
                parse_attributes(parser, true)
        end
        parse_declarator_postfix(parser, scope)
        if not rec
                raise @backtrace, 'void type is invalid' if name and (t = @type.untypedef).kind_of? BaseType and
                                t.name == :void and storage != :typedef
                raise @backtrace, "incomplete type #{@type.name}" if (@type.kind_of? Union or @type.kind_of? Enum) and
                                not @type.members and storage != :typedef and storage != :extern  # gcc uses an undefined extern struct just to cast it later (_IO_FILE_plus)
        end
end
parse_declarator_postfix(parser, scope) click to toggle source

parses array/function type

# File metasm/parse_c.rb, line 2149
def parse_declarator_postfix(parser, scope)
        if tok = parser.skipspaces and tok.type == :punct and tok.raw == '['
                # array indexing
                idx = CExpression.parse(parser, scope)      # may be nil
                if idx and (scope == parser.toplevel or storage == :static)
                        raise tok, 'array size is not constant' if not idx.constant?
                        idx = idx.reduce(parser)
                elsif idx and nidx = idx.reduce(parser) and nidx.kind_of? ::Integer
                        idx = nidx
                end
                t = self
                t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
                t.type = Array.new t.type
                t.type.length = idx
                raise tok || parser, '"]" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
                parse_attributes(parser)    # should be type.attrs, but this is should be more compiler-compatible
        elsif tok and tok.type == :punct and tok.raw == '('
                # function prototype
                # void __attribute__((noreturn)) func() => attribute belongs to func
                if @type and @type.attributes
                        @attributes ||= []
                        @attributes |= @type.attributes
                        @type.attributes = nil
                end
                t = self
                t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
                t.type = Function.new t.type
                if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
                        parser.unreadtok tok
                        t.type.args = []
                        oldstyle = false   # int func(a, b) int a; double b; { stuff; }
                        loop do
                                raise parser if not tok = parser.skipspaces
                                if tok.type == :punct and tok.raw == '.'  # variadic function
                                        raise parser, '".." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
                                        raise parser,  '"." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
                                        raise parser,  '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
                                        t.type.varargs = true
                                        break
                                elsif tok.type == :string and tok.raw == 'register'
                                        storage = :register
                                else
                                        parser.unreadtok tok
                                end

                                if oldstyle or not v = Variable.parse_type(parser, scope)
                                        raise tok if not @name   # no oldstyle in casts
                                        tok = parser.skipspaces
                                        oldstyle ||= [tok]       # arg to raise() later
                                        oldstyle << tok.raw
                                else
                                        v.storage = storage if storage
                                        v.parse_declarator(parser, scope)
                                        v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
                                        v.type = Pointer.new(v.type) if v.type.kind_of? Function

                                        t.type.args << v if not v.type.untypedef.kind_of? BaseType or v.type.untypedef.name != :void
                                end

                                if tok = parser.skipspaces and tok.type == :punct and tok.raw == ','
                                        raise tok, '")" expected' if v and t.type.args.last != v # last arg of type :void
                                elsif tok and tok.type == :punct and tok.raw == ')'
                                        break
                                else raise tok || parser, '"," or ")" expected'
                                end
                        end
                        if oldstyle
                                parse_attributes(parser, true)
                                ra = oldstyle.shift
                                while t.type.args.compact.length != oldstyle.length
                                        raise ra, "invalid prototype" if not vb = Variable.parse_type(parser, scope)
                                        loop do
                                                v = vb.dup
                                                v.parse_declarator(parser, scope)
                                                v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
                                                v.type = Pointer.new(v.type) if v.type.kind_of? Function
                                                raise parser, "unknown arg #{v.name.inspect}" if not i = oldstyle.index(v.name)
                                                t.type.args[i] = v
                                                raise parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != ',')
                                                break if tok.raw == ';'
                                        end
                                end
                                parse_attributes(parser, true)
                                raise parser, '"{" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '{'
                                parser.unreadtok tok
                        end
                        namedargs = t.type.args.map { |a| a.name }.compact - [false]
                        raise tok, "duplicate argument name #{namedargs.find { |a| namedargs.index(a) != namedargs.rindex(a) }.inspect}" if namedargs.length != namedargs.uniq.length
                end
                parse_attributes(parser, true)      # should be type.attrs, but this should be more existing-compiler-compatible
        else
                parser.unreadtok tok
                return
        end
        parse_declarator_postfix(parser, scope)
end
parse_type_base(parser, scope) click to toggle source

parses int/long int/long long/double etc

# File metasm/parse_c.rb, line 2012
def parse_type_base(parser, scope)
        specifier = []
        qualifier = []
        name = :int
        tok = nil
        loop do
                if not tok = parser.skipspaces
                        raise parser if specifier.empty?
                        break
                end
                if tok.type != :string
                        parser.unreadtok tok
                        break
                end
                case tok.raw
                when 'const', 'volatile'
                        qualifier << tok.raw.to_sym
                when 'long', 'short', 'signed', 'unsigned'
                        specifier << tok.raw.to_sym
                when 'int', 'char', 'void', 'float', 'double', '__int8', '__int16', '__int32', '__int64'
                        name = tok.raw.to_sym
                        break
                when 'intptr_t', 'uintptr_t'
                        name = :ptr
                        specifier << :unsigned if tok.raw == 'uintptr_t'
                        break
                else
                        parser.unreadtok tok
                        break
                end
        end

        case name
        when :double # long double
                if specifier == [:long]
                        name = :longdouble
                        specifier.clear
                elsif not specifier.empty?
                        raise tok || parser, 'invalid specifier list'
                end
        when :int    # short, long, long long X signed, unsigned
                # Array#count not available on old ruby (eg 1.8.4), so use ary.len - (ary-stuff).len
                specifier = specifier - [:long] + [:longlong] if specifier.length - (specifier-[:long]).length == 2
                if specifier.length - (specifier-[:signed, :unsigned]).length > 1 or specifier.length - (specifier-[:short, :long, :longlong]).length > 1
                        raise tok || parser, 'invalid specifier list'
                else
                        name = (specifier & [:longlong, :long, :short])[0] || :int
                        specifier -= [:longlong, :long, :short]
                end
                specifier.delete :signed    # default
        when :char   # signed, unsigned
                # signed char != char and unsigned char != char
                if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
                        raise tok || parser, 'invalid specifier list'
                end
        when :__int8, :__int16, :__int32, :__int64, :ptr
                if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
                        raise tok || parser, 'invalid specifier list'
                end
                specifier.delete :signed    # default
        else         # none
                raise tok || parser, 'invalid type' if not specifier.empty?
        end

        @type = BaseType.new(name, *specifier)
        @type.qualifier = qualifier if not qualifier.empty?
end
parse_type_struct(parser, scope) click to toggle source

parses a structure/union/enum declaration

# File metasm/parse_c.rb, line 1948
def parse_type_struct(parser, scope)
        @type.parse_attributes(parser)
        if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
                # anonymous struct, ok
                @type.backtrace = tok
                if @type.kind_of? Enum
                        (scope.anonymous_enums ||= []) << @type
                end
        elsif tok and tok.type == :string
                name = tok.raw
                raise tok, 'bad struct name' if Keyword[name] or (0..9).include?(name[0])
                @type.backtrace = tok
                @type.name = name
                @type.parse_attributes(parser)
                raise parser if not ntok = parser.skipspaces
                if ntok.type != :punct or ntok.raw != '{'
                        raise tok, "struct/union confusion" if scope.struct[name] and scope.struct[name].class != @type.class
                        # variable declaration
                        parser.unreadtok ntok
                        if ntok.type == :punct and ntok.raw == ';'
                                # struct predeclaration
                                # allow redefinition
                                @type = scope.struct[name] ||= @type
                        else
                                # check that the structure exists
                                struct = scope.struct_ancestors[name]
                                # allow incomplete types, usage as var type will raise later
                                struct = scope.struct[name] = @type if not struct
                                raise tok, 'unknown struct' if struct.class != @type.class
                                (struct.attributes ||= []).concat @type.attributes if @type.attributes
                                (struct.qualifier  ||= []).concat @type.qualifier  if @type.qualifier     # XXX const struct foo bar => bar is const, not foo...
                                @type = struct
                        end
                        return
                end
                if scope.struct[name] and scope.struct[name].members
                        # redefinition of an existing struct, save for later comparison
                        oldstruct = scope.struct[name]
                        raise tok, "struct/union confusion" if oldstruct.class != @type.class
                elsif struct = scope.struct[name]
                        raise tok, "struct/union confusion" if struct.class != @type.class
                        (struct.attributes ||= []).concat @type.attributes if @type.attributes
                        (struct.qualifier  ||= []).concat @type.qualifier  if @type.qualifier
                        struct.backtrace = @type.backtrace
                        struct.name = @type.name
                        @type = struct
                else
                        scope.struct[name] = @type
                end
        else
                raise tok || parser, 'struct name or "{" expected'
        end

        @type.parse_members(parser, scope)

        if oldstruct
                if not @type.compare_deep(oldstruct)
                        raise tok, "conflicting struct redefinition (old at #{oldstruct.backtrace.exception(nil).message rescue :unknown})"
                end
                @type = oldstruct
        end
end
to_s() click to toggle source
# File metasm/parse_c.rb, line 3488
def to_s
        dump(Block.new(nil))[0].join(' ')
end