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 780 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 1891 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 2265 def ===(o) self == o or (o.class == CExpression and not o.op and o.rexpr == self) end
dump(scope, r=[CRenderString.new], dep=[])
click to toggle source
# File metasm/parse_c.rb, line 3512 def dump(scope, r=[CRenderString.new], dep=[]) if name dep |= [scope.symbol_ancestors[@name]] r.last << @name end [r, dep] end
dump_def(scope, r=[CRenderString.new], dep=[], skiptype=false)
click to toggle source
# File metasm/parse_c.rb, line 3519 def dump_def(scope, r=[CRenderString.new], 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([CRenderString.new(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 CExpression.parse
for array sizes and function prototype argument types rec for internal use only
# File metasm/parse_c.rb, line 2102 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 2166 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 2029 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 1965 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 3536 def to_s dump(Block.new(nil))[0].join(' ') end