metasm/parse_c.rb in metasm-1.0.3 vs metasm/parse_c.rb in metasm-1.0.4

- old
+ new

@@ -20,11 +20,21 @@ inline __inline __inline__ __volatile__ __int8 __int16 __int32 __int64 __builtin_offsetof ].inject({}) { |h, w| h.update w => true } + module Misc + attr_accessor :misc + + def with_misc(m) + @misc = m if m or misc + self + end + end + class Statement + include Misc end module Typed # allows quick testing whether an object is an CExpr or a Variable end @@ -112,10 +122,11 @@ end end class Type include Attributes + include Misc attr_accessor :qualifier # const volatile def pointer? ; false end def arithmetic? ; false end def integral? ; false end @@ -756,10 +767,11 @@ end class Variable include Attributes include Typed + include Misc attr_accessor :type attr_accessor :initializer # CExpr / Block (for Functions) attr_accessor :name attr_accessor :storage # auto register static extern typedef @@ -771,10 +783,12 @@ end # found in a block's Statements, used to know the initialization order # eg { int i; i = 4; struct foo { int k; } toto = {i}; } class Declaration + include Misc + attr_accessor :var def initialize(var) @var = var end end @@ -944,12 +958,12 @@ if tok.type == :punct and tok.raw == '{' loop do raise ftok, 'unterminated asm block' if not tok = parser.lexer.readtok break if tok.type == :punct and tok.raw == '}' case tok.type - when :space; body << ' ' - when :eol; body << "\n" + when :space; body << ' ' unless body.empty? + when :eol; body << "\n" unless body.empty? when :punct; body << tok.raw when :quoted; body << CExpression.string_inspect(tok.value) # concat adjacent c strings when :string body << \ case tok.raw @@ -1054,16 +1068,19 @@ end class CExpression < Statement include Typed + AssignOp = [:'=', :'+=', :'-=', :'*=', :'/=', :'%=', :'^=', :'&=', :'|=', :'>>=', :'<<=', :'++', :'--'] + # may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast) attr_accessor :op # nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block attr_accessor :lexpr, :rexpr # a Type attr_accessor :type + 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 @@ -1090,11 +1107,11 @@ # CExpr[some_cexpr] returns some_cexpr 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] + args.shift while args.length > 0 and args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla] case args.length when 4 op = args[1] if op == :funcall or op == :'?:' @@ -1112,13 +1129,13 @@ 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) + 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 :'.', :'->' @@ -3174,11 +3191,11 @@ values.each { |k, v| st[k] = v } if values st end # parse a given String as an AllocCStruct - # offset is an optionnal offset from the string start + # offset is an optional offset from the string start # modification to the structure will modify the underlying string def decode_c_struct(structname, str, offset=0) struct = find_c_struct(structname) AllocCStruct.new(self, struct, str, offset) end @@ -3319,23 +3336,53 @@ def to_s @toplevel.dump(nil)[0].join("\n") end end + # used to render a C to a source string, while keeping the information of which each character comes from which C object + class CRenderString < ::String + attr_accessor :my_c # default C obj with which raw characters are associated + # hash offset => C::Statement, means bytes from this offset to the next entry comes from rendering this C object + def c_at_offset + @c_at_offset ||= {} + end + + # concatenate another CRenderString: merge @c_at_offset + def <<(o) + if o.kind_of?(self.class) + o.c_at_offset.each { |k, v| + c_at_offset[length+k] ||= v + } + elsif my_c + c_at_offset[length] ||= my_c + end + super(o) + end + + def initialize(*a) + if cs = a.grep(Statement).first + a -= [cs] + @my_c = cs + c_at_offset[0] = cs + end + super(*a) + end + end + class Statement - def self.dump(e, scope, r=[''], dep=[]) + def self.dump(e, scope, r=[CRenderString.new], dep=[]) case e when nil; r.last << ';' when Block r.last << ' ' if not r.last.empty? r.last << '{' - tr, dep = e.dump(scope, [''], dep) + tr, dep = e.dump(scope, [CRenderString.new], dep) tr.pop if tr.last.empty? r.concat tr.map { |s| Case.dump_indent(s) } - (r.last[-1] == ?{ ? r.last : r) << '}' + (r.last[-1] == ?{ ? r.last : r) << CRenderString.new('}') else - tr, dep = e.dump(scope, [''], dep) + tr, dep = e.dump(scope, [CRenderString.new], dep) r.concat tr.map { |s| Case.dump_indent(s) } end [r, dep] end @@ -3346,11 +3393,11 @@ class Block def to_s() dump(nil)[0].join("\n") end # return array of c source lines and array of dependencies (objects) - def dump(scp, r=[''], dep=[]) + def dump(scp, r=[CRenderString.new], dep=[]) mydefs = @symbol.values.grep(TypeDef) + @struct.values + anonymous_enums.to_a todo_rndr = {} todo_deps = {} mydefs.each { |t| # filter out Enum values todo_rndr[t], todo_deps[t] = t.dump_def(self) @@ -3358,11 +3405,11 @@ r, dep = dump_reorder(mydefs, todo_rndr, todo_deps, r, dep) dep -= @symbol.values + @struct.values [r, dep] end - def dump_reorder(mydefs, todo_rndr, todo_deps, r=[''], dep=[]) + def dump_reorder(mydefs, todo_rndr, todo_deps, r=[CRenderString.new], dep=[]) val = todo_deps.values.flatten.uniq dep |= val dep -= mydefs | todo_deps.keys todo_deps.each { |k, v| v.delete k } ext = val - mydefs @@ -3373,20 +3420,21 @@ end # predeclare structs involved in cyclic dependencies dep_cycle = lambda { |ary| # sexyness inside (c) + # XXX 5 years later, i have no idea whats going on here deps = todo_deps[ary.last] if deps.include? ary.first; ary elsif (deps-ary).find { |d| deps = dep_cycle[ary + [d]] }; deps end } todo_rndr.keys.grep(Union).find_all { |t| t.name }.sort_by { |t| t.name }.each { |t| oldc = nil while c = dep_cycle[[t]] break if oldc == c - r << "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};" if not oldc + r << CRenderString.new(t, "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};") if not oldc oldc = c c.each { |s| # XXX struct z { struct a* }; struct a { void (*foo)(struct z); }; todo_deps[s].delete t unless s.kind_of? Union and s.members.find { |sm| sm.type.untypedef == t } @@ -3401,40 +3449,40 @@ r << '// dependency problem, this may not compile' todo_now = todo_deps.keys end todo_now.sort_by { |k| k.name || '0' }.each { |k| if k.kind_of? Variable and k.type.kind_of? Function and k.initializer - r << '' + r << CRenderString.new r.concat todo_rndr.delete(k) else r.pop if r.last == '' r.concat todo_rndr.delete(k) r.last << ';' end todo_deps.delete k } todo_deps.each_key { |k| todo_deps[k] -= todo_now } - r << '' << '' << '' + r << CRenderString.new << CRenderString.new << CRenderString.new end @statements.each { |s| - r << '' if not r.last.empty? - if s.kind_of? Block + r << CRenderString.new if not r.last.empty? + if s.kind_of?(Block) r, dep = Statement.dump(s, self, r, dep) else r, dep = s.dump(self, r, dep) end } [r, dep] end end class Declaration - def dump(scope, r=[''], dep=[]) - tr, dep = @var.dump_def(scope, [''], dep) - if @var.kind_of? Variable and @var.type.kind_of? Function and @var.initializer - r << '' + def dump(scope, r=[CRenderString.new], dep=[]) + tr, dep = @var.dump_def(scope, [CRenderString.new], dep) + if @var.kind_of?(Variable) and @var.type.kind_of?(Function) and @var.initializer + r << CRenderString.new r.concat tr else r.pop if r.last == '' r.concat tr r.last << ';' @@ -3459,26 +3507,26 @@ else '' end end end class Variable - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) if name dep |= [scope.symbol_ancestors[@name]] r.last << @name end [r, dep] end - def dump_def(scope, r=[''], dep=[], skiptype=false) + 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([(name ? @name.dup : '') << dump_attributes], scope, r, dep) + 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 @@ -3488,11 +3536,11 @@ def to_s dump(Block.new(nil))[0].join(' ') end end class Type - def dump_initializer(init, scope, r=[''], dep=[]) + def dump_initializer(init, scope, r=[CRenderString.new], dep=[]) case init when ::Numeric r.last << init.to_s [r, dep] when ::Array @@ -3500,79 +3548,80 @@ [r, dep] else init.dump_inner(scope, r, dep) end end - def dump_declarator(decl, scope, r=[''], dep=[]) + def dump_declarator(decl, scope, r=[CRenderString.new], dep=[]) r.last << decl.shift - r.concat decl + r.concat decl.map { |d| CRenderString.new << d } [r, dep] end def dump_def(*a) dump(*a) end - def dump_cast(scope, r=[''], dep=[]) + def dump_cast(scope, r=[CRenderString.new], dep=[]) r.last << '(' r.last << dump_attributes_pre if not kind_of? TypeDef r, dep = base.dump(scope, r, dep) - r, dep = dump_declarator([kind_of?(TypeDef) ? '' : dump_attributes], scope, r, dep) + r, dep = dump_declarator([CRenderString.new(kind_of?(TypeDef) ? '' : dump_attributes)], scope, r, dep) r.last << ')' [r, dep] end def to_s dump_cast(Block.new(nil))[0].join(' ') end end class Pointer - def dump_declarator(decl, scope, r=[''], dep=[]) + def dump_declarator(decl, scope, r=[CRenderString.new], dep=[]) d = decl[0] - decl[0] = '*' + decl[0] = CRenderString.new + decl[0] << '*' decl[0] << ' ' << @qualifier.map { |q| q.to_s }.join(' ') << ' ' if qualifier decl[0] << d - if @type.kind_of? Function or @type.kind_of? Array - decl[0] = '(' << decl[0] + if @type.kind_of?(Function) or @type.kind_of?(Array) + decl[0] = CRenderString.new << '(' << decl[0] decl.last << ')' end @type.dump_declarator(decl, scope, r, dep) end end class Array - def dump_declarator(decl, scope, r=[''], dep=[]) + def dump_declarator(decl, scope, r=[CRenderString.new], dep=[]) decl.last << '()' if decl.last.empty? decl.last << '[' decl, dep = CExpression.dump(@length, scope, decl, dep) if length decl.last << ']' @type.dump_declarator(decl, scope, r, dep) end - def dump_initializer(init, scope, r=[''], dep=[]) - return super(init, scope, r, dep) if not init.kind_of? ::Array + def dump_initializer(init, scope, r=[CRenderString.new], dep=[]) + return super(init, scope, r, dep) if not init.kind_of?(::Array) r.last << '{ ' showname = false init.each_with_index { |v, i| if not v showname = true next end r.last << ', ' if r.last[-2, 2] != '{ ' - rt = [''] + rt = [CRenderString.new] if showname showname = false - rt << "[#{i}] = " + rt << CRenderString.new("[#{i}] = ") end rt, dep = @type.dump_initializer(v, scope, rt, dep) r.last << rt.shift - r.concat rt.map { |s| "\t" << s } + r.concat rt.map { |s| CRenderString.new << "\t" << s } } r.last << ' }' [r, dep] end end class Function - def dump_declarator(decl, scope, r=[''], dep=[]) + def dump_declarator(decl, scope, r=[CRenderString.new], dep=[]) decl.last << '()' if decl.last.empty? decl.last << '(' if args @args.each { |arg| decl.last << ', ' if decl.last[-1] != ?( @@ -3587,16 +3636,16 @@ end decl.last << ')' @type.dump_declarator(decl, scope, r, dep) end - def dump_initializer(init, scope, r=[''], dep=[]) - Statement.dump(init, scope, r << '', dep) + def dump_initializer(init, scope, r=[CRenderString.new], dep=[]) + Statement.dump(init, scope, r << CRenderString.new, dep) end end class BaseType - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << @specifier.to_s << ' ' if specifier and @name != :ptr r.last << case @name when :longlong; 'long long' when :longdouble; 'long double' @@ -3605,85 +3654,85 @@ end [r, dep] end end class TypeDef - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << @name dep |= [scope.symbol_ancestors[@name]] [r, dep] end - def dump_def(scope, r=[''], dep=[]) + def dump_def(scope, r=[CRenderString.new], dep=[]) r.last << 'typedef ' r.last << dump_attributes_pre r, dep = @type.base.dump(scope, r, dep) r.last << ' ' - @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep) + @type.dump_declarator([CRenderString.new(name ? @name.dup : '') << dump_attributes], scope, r, dep) end - def dump_initializer(init, scope, r=[''], dep=[]) + def dump_initializer(init, scope, r=[CRenderString.new], dep=[]) @type.dump_initializer(init, scope, r, dep) end end class Union - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], 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 - def dump_def(scope, r=[''], dep=[]) - r << '' + def dump_def(scope, r=[CRenderString.new], dep=[]) + r << CRenderString.new 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, dep = m.dump_def(scope, [CRenderString.new], dep) tr.last << ':' << @bits[i].to_s if bits and @bits[i] tr.last << ';' - r.concat tr.map { |s| "\t" << s } + r.concat tr.map { |s| CRenderString.new << "\t" << s } } - r << '}' + r << CRenderString.new('}') end r.last << dump_attributes [r, dep] end - def dump_initializer(init, scope, r=[''], dep=[]) + def dump_initializer(init, scope, r=[CRenderString.new], 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 = [''] + rt = [CRenderString.new] 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.concat rt.map { |s| CRenderString.new << "\t" << s } } r.last << ' }' [r, dep] end end class Struct - def dump_def(scope, r=[''], dep=[]) + def dump_def(scope, r=[CRenderString.new], dep=[]) if pack r, dep = super(scope, r, dep) r.last << if @pack == 1; (has_attribute('packed') or has_attribute_var('pack')) ? '' : " __attribute__((packed))" else has_attribute_var('pack') ? '' : " __attribute__((pack(#@pack)))" @@ -3693,22 +3742,22 @@ super(scope, r, dep) end end end class Enum - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) if name r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << 'enum ' << @name dep |= [scope.struct_ancestors[@name]] [r, dep] else dump_def(scope, r, dep) end end - def dump_def(scope, r=[''], dep=[]) + def dump_def(scope, r=[CRenderString.new], dep=[]) r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier r.last << 'enum' r.last << ' ' << @name if name if members r.last << ' { ' @@ -3724,11 +3773,11 @@ r.last << ' }' end [r, dep] end - def dump_initializer(init, scope, r=[''], dep=[]) + def dump_initializer(init, scope, r=[CRenderString.new], dep=[]) if members and ( k = @members.index(init) or (init.kind_of? CExpression and not init.op and k = @members.index(init.rexpr)) ) r.last << k @@ -3737,18 +3786,18 @@ else super(init, scope, r, dep) end end end class If - def dump(scope, r=[''], dep=[]) - r.last << 'if (' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'if (') r, dep = CExpression.dump(@test, scope, r, dep) r.last << ')' r, dep = Statement.dump(@bthen, scope, r, dep) if belse - @bthen.kind_of?(Block) ? (r.last << ' else') : (r << 'else') - if @belse.kind_of? If + @bthen.kind_of?(Block) ? (r.last << CRenderString.new(' else')) : (r << CRenderString.new(self, 'else')) + if @belse.kind_of?(If) # skip indent r.last << ' ' r, dep = @belse.dump(scope, r, dep) else r, dep = Statement.dump(@belse, scope, r, dep) @@ -3756,13 +3805,13 @@ end [r, dep] end end class For - def dump(scope, r=[''], dep=[]) - r.last << 'for (' - if @init.kind_of? Block + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'for (') + if @init.kind_of?(Block) scope = @init skiptype = false @init.symbol.each_value { |s| r.last << ', ' if skiptype r, dep = s.dump_def(scope, r, dep, skiptype) @@ -3782,73 +3831,74 @@ r.last << ')' Statement.dump(@body, scope, r, dep) end end class While - def dump(scope, r=[''], dep=[]) - r.last << 'while (' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'while (') r, dep = CExpression.dump(@test, scope, r, dep) r.last << ')' Statement.dump(@body, scope, r, dep) end end class DoWhile - def dump(scope, r=[''], dep=[]) - r.last << 'do' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'do') r, dep = Statement.dump(@body, scope, r, dep) - @body.kind_of?(Block) ? (r.last << ' while (') : (r << 'while (') + r << CRenderString.new if not @body.kind_of?(Block) + r.last << CRenderString.new(self, ' while (') r, dep = CExpression.dump(@test, scope, r, dep) r.last << ');' [r, dep] end end class Switch - def dump(scope, r=[''], dep=[]) - r.last << 'switch (' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'switch (') r, dep = CExpression.dump(@test, scope, r, dep) r.last << ')' - r.last << ' {' if @body.kind_of? Block - tr, dep = @body.dump(scope, [''], dep) + r.last << ' {' if @body.kind_of?(Block) + tr, dep = @body.dump(scope, [CRenderString.new], dep) r.concat tr.map { |s| Case.dump_indent(s, true) } - r << '}' if @body.kind_of? Block + r << CRenderString.new('}') if @body.kind_of? Block [r, dep] end end class Continue - def dump(scope, r=[''], dep=[]) - r.last << 'continue;' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'continue;') [r, dep] end end class Break - def dump(scope, r=[''], dep=[]) - r.last << 'break;' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'break;') [r, dep] end end class Goto - def dump(scope, r=[''], dep=[]) - r.last << "goto #@target;" + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, "goto #@target;") [r, dep] end end class Return - def dump(scope, r=[''], dep=[]) - r.last << 'return ' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'return ') r, dep = CExpression.dump(@value, scope, r, dep) r.last.chop! if r.last[-1] == ?\ # the space character r.last << ';' [r, dep] end end class Case - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) case @expr when 'default' r.last << @expr else - r.last << 'case ' + r.last << CRenderString.new(self, 'case ') r, dep = CExpression.dump(@expr, scope, r, dep) if exprup r.last << ' ... ' r, dep = CExpression.dump(@exprup, scope, r, dep) end @@ -3857,96 +3907,100 @@ dump_inner(scope, r, dep) end def self.dump_indent(s, short=false) case s - when /^(case|default)\W/; (short ? ' ' : "\t") << s - when /^\s+(case|default)\W/; "\t" << s + when /^(case|default)\W/; CRenderString.new(short ? ' ' : "\t") << s + when /^\s+(case|default)\W/; CRenderString.new("\t") << s when /:$/; s - else "\t" << s + else CRenderString.new("\t") << s end end end class Label - def dump(scope, r=[''], dep=[]) - r.last << @name << ':' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, @name) << ':' dump_inner(scope, r, dep) end - def dump_inner(scope, r=[''], dep=[]) + def dump_inner(scope, r=[CRenderString.new], dep=[]) if not @statement; [r, dep] - elsif @statement.kind_of? Block; Statement.dump(@statement, scope, r, dep) - else @statement.dump(scope, r << '', dep) + elsif @statement.kind_of?(Block); Statement.dump(@statement, scope, r, dep) + else @statement.dump(scope, r << CRenderString.new, dep) end end end class Asm - def dump(scope, r=[''], dep=[]) - r.last << 'asm ' + def dump(scope, r=[CRenderString.new], dep=[]) + r.last << CRenderString.new(self, 'asm ') r.last << 'volatile ' if @volatile r.last << '(' - r.last << CExpression.string_inspect(@body) + r.last << CRenderString.new(self, CExpression.string_inspect(@body)) if @output or @input or @clobber if @output and @output != [] # TODO - r << ': /* todo */' + r << CRenderString.new(': /* todo */') elsif (@input and @input != []) or (@clobber and @clobber != []) r.last << ' :' end end if @input or @clobber if @input and @input != [] # TODO - r << ': /* todo */' + r << CRenderString.new(': /* todo */') elsif @clobber and @clobber != [] r.last << ' :' end end if @clobber and @clobber != [] - r << (': ' << @clobber.map { |c| CExpression.string_inspect(c) }.join(', ')) + r << (CRenderString.new(': ') << @clobber.map { |c| CExpression.string_inspect(c) }.join(', ')) end r.last << ');' [r, dep] end end class CExpression def self.string_inspect(s) # keep all ascii printable except \ and " - '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o| '\\x' + o.unpack('H*').first } + '"' + '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o| + case hex = o.unpack('H*').first.downcase + when '00'; '\\0' + when '0a'; '\\n' + when '0d'; '\\r' + when '1b'; '\\e' + when '22'; '\\"' + when '5c'; '\\\\' + else "\\x#{hex}" + end + } + '"' end - 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 + def self.dump(e, scope, r=[CRenderString.new], dep=[], brace = false) 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 - def dump(scope, r=[''], dep=[]) + def dump(scope, r=[CRenderString.new], dep=[]) r, dep = dump_inner(scope, r, dep) - r.last << ';' + r.last << CRenderString.new(self, ';') [r, dep] end - 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) + def dump_inner(scope, r=[CRenderString.new], dep=[], brace = false) + r.last << CRenderString.new(self) + if misc and misc[:custom_display] + r.last << misc[:custom_display] + return [r, dep] + end + 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 @@ -3958,20 +4012,20 @@ if re >= 0x1000 r.last << ("0x%X" % re) else r.last << re.to_s end - if @type.kind_of? BaseType + 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 << '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 @@ -3980,32 +4034,32 @@ r.last << '(' r, dep = Statement.dump(@rexpr, scope, r, dep) r.last << ' )' when Label r.last << '&&' << @rexpr.name - else raise "wtf? #{inspect}" + 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)) + 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 + r.last << CRenderString.new(self, @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 == :'->' + 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 + 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) @@ -4014,25 +4068,25 @@ 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, dep = CExpression.dump(arg, scope, r, dep, (arg.kind_of?(CExpression) and arg.op == :',')) } r.last << ')' when :'?:' r, dep = CExpression.dump(@lexpr, scope, r, dep, true) - r.last << ' ? ' + r.last << CRenderString.new(self, ' ? ') r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true) - r.last << ' : ' + r.last << CRenderString.new(self, ' : ') 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)) + r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of?(CExpression) and @lexpr.lexpr and @lexpr.op != @op and @lexpr.op != :funcall)) + r.last << CRenderString.new(self, ' ' << @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 and @op != :'=')) end end - r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression) + r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of?(CExpression)) [r, dep] end def to_s dump_inner(Block.new(nil))[0].join(' ')