there is only one instance of a given named struct per parser so we just compare struct names here for comparison between parsers, see compare_deep
# File metasm/parse_c.rb, line 222 def ==(o) o.object_id == self.object_id or (o.class == self.class and o.name == self.name and ((o.name and true) or compare_deep(o))) end
# File metasm/parse_c.rb, line 217 def align(parser) @members.to_a.map { |m| m.type.align(parser) }.max end
# File metasm/parse_c.rb, line 286 def bitoffsetof(parser, name) raise parser, 'undefined structure' if not members update_member_cache(parser) if not fldlist return if @fldlist[name] or @members.include?(name) raise parser, 'undefined union' if not @members raise parser, 'unknown union member' if not findmember(name) @members.find { |m| m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name) }.type.untypedef.bitoffsetof(parser, name) end
compare to another structure, comparing members recursively (names and type) returns true if the self is same as o
# File metasm/parse_c.rb, line 229 def compare_deep(o, seen = []) return true if o.object_id == self.object_id return if o.class != self.class or o.name != self.name or o.attributes != self.attributes o.members.to_a.zip(self.members.to_a).each { |om, sm| return if om.name != sm.name return if om.type != sm.type if om.type.pointer? ot = om.type st = sm.type 500.times { # limit for selfpointers (shouldnt happen) break if not ot.pointer? ot = ot.pointed.untypedef st = st.pointed.untypedef } if ot.kind_of?(C::Union) and ot.name and not seen.include?(ot) return if not st.compare_deep(ot, seen+[ot]) end end } true end
# File metasm/parse_c.rb, line 3630 def dump(scope, r=[''], dep=[]) if name r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1] << ' ' << @name dep |= [scope.struct_ancestors[@name]] [r, dep] else dump_def(scope, r, dep) end end
# File metasm/parse_c.rb, line 3641 def dump_def(scope, r=[''], dep=[]) r << '' r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1] r.last << ' ' << @name if name if members r.last << ' {' @members.each_with_index { |m,i| tr, dep = m.dump_def(scope, [''], dep) tr.last << ':' << @bits[i].to_s if bits and @bits[i] tr.last << ';' r.concat tr.map { |s| "\t" << s } } r << '}' end r.last << dump_attributes [r, dep] end
# File metasm/parse_c.rb, line 3660 def dump_initializer(init, scope, r=[''], dep=[]) return super(init, scope, r, dep) if not init.kind_of? ::Array r.last << '{ ' showname = false @members.zip(init) { |m, i| if not i showname = true next end r.last << ', ' if r.last[-2, 2] != '{ ' rt = [''] if showname showname = false rt << ".#{m.name} = " end rt, dep = m.type.dump_initializer(i, scope, rt, dep) r.last << rt.shift r.concat rt.map { |s| "\t" << s } } r.last << ' }' [r, dep] end
resolve structptr + offset into 'str.membername' handles 'var.substruct1.array.foo' updates str returns the final member type itself works for Struct/Union/Array
# File metasm/parse_c.rb, line 399 def expand_member_offset(c_parser, off, str) # XXX choose in members, check sizeof / prefer structs m = @members.first str << '.' << m.name if m.name if m.type.respond_to?(:expand_member_offset) m.type.expand_member_offset(c_parser, off, str) else m.type end end
# File metasm/parse_c.rb, line 251 def findmember(name, igncase=false) raise 'undefined structure' if not members return @fldlist[name] if fldlist and @fldlist[name] name = name.downcase if igncase if m = @members.find { |m_| (n = m_.name) and (igncase ? n.downcase : n) == name } return m else @members.each { |m_| if t = m_.type.untypedef and t.kind_of? Union and mm = t.findmember(name, igncase) return mm end } end nil end
# File metasm/parse_c.rb, line 269 def offsetof(parser, name) raise parser, 'undefined structure' if not members update_member_cache(parser) if not fldlist return 0 if @fldlist[name] if name.kind_of?(Variable) return 0 if @members.include? name raise ParseError, 'unknown union member' end raise parser, 'unknown union member' if not findmember(name) @members.find { |m| m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name) }.type.untypedef.offsetof(parser, name) end
# File metasm/parse_c.rb, line 347 def parse_initializer(parser, scope) if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{' # struct x toto = { 1, .4, .member[0][6].bla = 12 }; raise tok, 'undefined struct' if not @members ret = [] if tok = parser.skipspaces and (tok.type != :punct or tok.raw != '}') parser.unreadtok tok idx = 0 loop do idx = parse_initializer_designator(parser, scope, ret, idx, true) raise tok || parser, '"," or "}" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != '}' and tok.raw != ',') break if tok.raw == '}' raise tok, 'struct is smaller than that' if idx >= @members.length end end ret else parser.unreadtok tok super(parser, scope) end end
parses a designator+initializer eg '.toto = 4' or '.tutu[12].bla = 16' or (root ? '4' : '=4')
# File metasm/parse_c.rb, line 370 def parse_initializer_designator(parser, scope, value, idx, root=true) if nt = parser.skipspaces and nt.type == :punct and nt.raw == '.' and nnt = parser.skipspaces and nnt.type == :string and findmember(nnt.raw) raise nnt, 'unhandled indirect initializer' if not nidx = @members.index(@members.find { |m| m.name == nnt.raw }) # TODO if not root value[idx] ||= [] # AryRecorder may change [] to AryRec.new, can't do v = v[i] ||= [] value = value[idx] end idx = nidx @members[idx].type.untypedef.parse_initializer_designator(parser, scope, value, idx, false) else parser.unreadtok nnt if root parser.unreadtok nt value[idx] = @members[idx].type.parse_initializer(parser, scope) else raise nt || parser, '"=" expected' if not nt or nt.type != :punct or nt.raw != '=' value[idx] = parse_initializer(parser, scope) end end idx + 1 end
# File metasm/parse_c.rb, line 298 def parse_members(parser, scope) @fldlist = nil if fldlist # invalidate fld offset cache @members = [] # parse struct/union members in definition loop do raise parser if not tok = parser.skipspaces break if tok.type == :punct and tok.raw == '}' parser.unreadtok tok raise tok, 'invalid struct member type' if not basetype = Variable.parse_type(parser, scope) loop do member = basetype.dup member.parse_declarator(parser, scope) member.type.length ||= 0 if member.type.kind_of?(Array) # struct { char blarg[]; }; raise member.backtrace, 'member redefinition' if member.name and @members.find { |m| m.name == member.name } @members << member raise tok || parser if not tok = parser.skipspaces or tok.type != :punct if tok.raw == ':' # bits raise tok, 'bad type for bitslice' if not member.type.integral? bits = nil raise tok, "bad bit count #{bits.inspect}" if not bits = CExpression.parse(parser, scope, false) or not bits.constant? or !(bits = bits.reduce(parser)).kind_of? ::Integer #raise tok, 'need more bits' if bits > 8*parser.sizeof(member) # WORD wReserved:17; => yay windows.h (@bits ||= [])[@members.length-1] = bits raise tok || parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct end case tok.raw when ';'; break when ',' when '}'; parser.unreadtok(tok); break else raise tok, '"," or ";" expected' end end end parse_attributes(parser) end
updates the @fldoffset / @fldbitoffset hash storing the offset of members
# File metasm/parse_c.rb, line 340 def update_member_cache(parser) @fldlist = {} @members.to_a.each { |m| @fldlist[m.name] = m if m.name } end