metasm 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/Gemfile +3 -2
  5. data/metasm.gemspec +3 -2
  6. data/metasm.rb +4 -1
  7. data/metasm/compile_c.rb +2 -2
  8. data/metasm/cpu/arc/decode.rb +0 -21
  9. data/metasm/cpu/arc/main.rb +4 -4
  10. data/metasm/cpu/arm/decode.rb +1 -5
  11. data/metasm/cpu/arm/main.rb +3 -3
  12. data/metasm/cpu/arm64/decode.rb +2 -6
  13. data/metasm/cpu/arm64/main.rb +5 -5
  14. data/metasm/cpu/bpf/decode.rb +3 -35
  15. data/metasm/cpu/bpf/main.rb +5 -5
  16. data/metasm/cpu/bpf/render.rb +1 -12
  17. data/metasm/cpu/cy16/decode.rb +0 -6
  18. data/metasm/cpu/cy16/main.rb +3 -3
  19. data/metasm/cpu/cy16/render.rb +0 -11
  20. data/metasm/cpu/dalvik/decode.rb +4 -26
  21. data/metasm/cpu/dalvik/main.rb +20 -2
  22. data/metasm/cpu/dalvik/opcodes.rb +3 -2
  23. data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
  24. data/metasm/cpu/ebpf/debug.rb +61 -0
  25. data/metasm/cpu/ebpf/decode.rb +142 -0
  26. data/metasm/cpu/ebpf/main.rb +58 -0
  27. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  28. data/metasm/cpu/ebpf/render.rb +36 -0
  29. data/metasm/cpu/ia32/debug.rb +39 -1
  30. data/metasm/cpu/ia32/decode.rb +111 -90
  31. data/metasm/cpu/ia32/decompile.rb +45 -37
  32. data/metasm/cpu/ia32/main.rb +10 -0
  33. data/metasm/cpu/ia32/parse.rb +6 -0
  34. data/metasm/cpu/mcs51/decode.rb +1 -1
  35. data/metasm/cpu/mcs51/main.rb +11 -0
  36. data/metasm/cpu/mips/decode.rb +8 -18
  37. data/metasm/cpu/mips/main.rb +3 -3
  38. data/metasm/cpu/mips/opcodes.rb +1 -1
  39. data/metasm/cpu/msp430/decode.rb +2 -6
  40. data/metasm/cpu/msp430/main.rb +3 -3
  41. data/metasm/cpu/openrisc.rb +11 -0
  42. data/metasm/cpu/openrisc/debug.rb +106 -0
  43. data/metasm/cpu/openrisc/decode.rb +182 -0
  44. data/metasm/cpu/openrisc/decompile.rb +350 -0
  45. data/metasm/cpu/openrisc/main.rb +70 -0
  46. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  47. data/metasm/cpu/openrisc/render.rb +37 -0
  48. data/metasm/cpu/ppc/decode.rb +0 -25
  49. data/metasm/cpu/ppc/main.rb +6 -6
  50. data/metasm/cpu/ppc/opcodes.rb +3 -4
  51. data/metasm/cpu/python/decode.rb +0 -20
  52. data/metasm/cpu/python/main.rb +1 -1
  53. data/metasm/cpu/sh4/decode.rb +2 -6
  54. data/metasm/cpu/sh4/main.rb +25 -23
  55. data/metasm/cpu/st20/decode.rb +0 -7
  56. data/metasm/cpu/webasm.rb +11 -0
  57. data/metasm/cpu/webasm/debug.rb +31 -0
  58. data/metasm/cpu/webasm/decode.rb +321 -0
  59. data/metasm/cpu/webasm/decompile.rb +386 -0
  60. data/metasm/cpu/webasm/encode.rb +104 -0
  61. data/metasm/cpu/webasm/main.rb +81 -0
  62. data/metasm/cpu/webasm/opcodes.rb +214 -0
  63. data/metasm/cpu/x86_64/compile_c.rb +13 -9
  64. data/metasm/cpu/x86_64/parse.rb +1 -1
  65. data/metasm/cpu/z80/decode.rb +0 -27
  66. data/metasm/cpu/z80/main.rb +3 -3
  67. data/metasm/cpu/z80/render.rb +0 -11
  68. data/metasm/debug.rb +43 -8
  69. data/metasm/decode.rb +62 -14
  70. data/metasm/decompile.rb +793 -466
  71. data/metasm/disassemble.rb +188 -131
  72. data/metasm/disassemble_api.rb +30 -17
  73. data/metasm/dynldr.rb +2 -2
  74. data/metasm/encode.rb +8 -2
  75. data/metasm/exe_format/autoexe.rb +2 -0
  76. data/metasm/exe_format/coff.rb +21 -3
  77. data/metasm/exe_format/coff_decode.rb +12 -0
  78. data/metasm/exe_format/coff_encode.rb +6 -3
  79. data/metasm/exe_format/dex.rb +13 -3
  80. data/metasm/exe_format/elf.rb +12 -2
  81. data/metasm/exe_format/elf_decode.rb +59 -1
  82. data/metasm/exe_format/main.rb +2 -0
  83. data/metasm/exe_format/mz.rb +1 -0
  84. data/metasm/exe_format/pe.rb +25 -3
  85. data/metasm/exe_format/wasm.rb +402 -0
  86. data/metasm/gui/dasm_decomp.rb +171 -95
  87. data/metasm/gui/dasm_graph.rb +61 -2
  88. data/metasm/gui/dasm_hex.rb +2 -2
  89. data/metasm/gui/dasm_main.rb +45 -19
  90. data/metasm/gui/debug.rb +13 -4
  91. data/metasm/gui/gtk.rb +12 -4
  92. data/metasm/main.rb +108 -103
  93. data/metasm/os/emulator.rb +175 -0
  94. data/metasm/os/main.rb +11 -6
  95. data/metasm/parse.rb +23 -12
  96. data/metasm/parse_c.rb +189 -135
  97. data/metasm/preprocessor.rb +16 -1
  98. data/misc/openrisc-parser.rb +79 -0
  99. data/samples/dasm-plugins/scanxrefs.rb +6 -4
  100. data/samples/dasm-plugins/selfmodify.rb +8 -8
  101. data/samples/dbg-plugins/trace_func.rb +1 -1
  102. data/samples/disassemble-gui.rb +14 -3
  103. data/samples/emubios.rb +251 -0
  104. data/samples/emudbg.rb +127 -0
  105. data/samples/lindebug.rb +79 -78
  106. data/samples/metasm-shell.rb +8 -8
  107. data/tests/all.rb +1 -1
  108. data/tests/expression.rb +2 -0
  109. data/tests/graph_layout.rb +1 -1
  110. data/tests/ia32.rb +1 -0
  111. data/tests/mips.rb +1 -1
  112. data/tests/preprocessor.rb +18 -0
  113. metadata +124 -6
  114. metadata.gz.sig +0 -0
@@ -0,0 +1,175 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/os/main'
8
+ require 'metasm/debug'
9
+
10
+ module Metasm
11
+ # a VirtualString mapping the segments from a disassembler
12
+ class VirtualMemoryDasm < VirtualString
13
+ attr_accessor :disassembler
14
+
15
+ def initialize(disassembler, addr_start = 0, length = nil)
16
+ @disassembler = disassembler
17
+ length ||= disassembler.sections.map { |k, v| k.kind_of?(Integer) ? k + v.length : 0 }.max
18
+ super(addr_start, length)
19
+ end
20
+
21
+ def dup(addr = @addr_start, len = @length)
22
+ self.class.new(@disassembler, addr, len)
23
+ end
24
+
25
+ # reads an aligned page from the file, at file offset addr
26
+ def read_range(addr, len=@pagelength)
27
+ @disassembler.read_raw_data(addr, len)
28
+ end
29
+
30
+ def page_invalid?(addr)
31
+ !@disassembler.get_section_at(addr)
32
+ end
33
+
34
+ # overwrite a section of the file
35
+ def rewrite_at(addr, data)
36
+ if e = @disassembler.get_section_at(addr)
37
+ e[0].data[addr - e[1], data.length] = data
38
+ end
39
+ end
40
+
41
+ def decode_imm(addr, len, cpu)
42
+ @disassembler.decode_int(addr, len)
43
+ end
44
+ end
45
+
46
+ # this class implements a virtual debugger over an emulated cpu (based on cpu#get_backtrace_binding)
47
+ class EmuDebugger < Debugger
48
+ attr_accessor :ctx
49
+ # lambda called everytime we emulate a di
50
+ # receives the di as parameter
51
+ # if it returns nil, the di is emulated as usual, if it returns true no further processing is done for this di
52
+ # dont forget to handle reg_pc !
53
+ attr_accessor :callback_emulate_di
54
+ # lambda called everytime we cannot find an instruction at the current PC
55
+ # return true if the context was fixed
56
+ attr_accessor :callback_unknown_pc
57
+
58
+ def initialize(disassembler)
59
+ @pid = @tid = 0
60
+ attach(disassembler)
61
+ end
62
+
63
+ def shortname; 'emudbg'; end
64
+
65
+ def attach(disassembler)
66
+ @memory = VirtualMemoryDasm.new(disassembler)
67
+ @cpu = disassembler.cpu
68
+ @disassembler = disassembler
69
+ @ctx = {}
70
+ @state = :stopped
71
+ @symbols = disassembler.prog_binding.invert
72
+ @symbols_len = @symbols.keys.inject({}) { |h, s| h.update s => 1 }
73
+ @modulemap = {}
74
+ @breakpoint = {}
75
+ @breakpoint_memory = {}
76
+ @breakpoint_thread = {}
77
+ end
78
+
79
+ def detach
80
+ end
81
+
82
+ def initialize_disassembler
83
+ end
84
+ def initialize_cpu
85
+ end
86
+ def initialize_memory
87
+ end
88
+ def invalidate
89
+ end
90
+
91
+ def memory_get_page(addr, len)
92
+ @memory[addr, len]
93
+ end
94
+
95
+ def get_reg_value(r)
96
+ if r.to_s =~ /flags?_(.+)/i
97
+ f = $1.downcase.to_sym
98
+ get_flag_value(f)
99
+ else
100
+ @ctx[r] || 0
101
+ end
102
+ end
103
+
104
+ def set_reg_value(r, v)
105
+ if r.to_s =~ /flags?_(.+)/i
106
+ f = $1.downcase.to_sym
107
+ set_flag_value(f, v)
108
+ else
109
+ @ctx[r] = v
110
+ end
111
+ end
112
+
113
+ def do_check_target
114
+ true
115
+ end
116
+
117
+ def do_wait_target
118
+ true
119
+ end
120
+
121
+ def do_continue
122
+ while not @breakpoint[pc] and do_singlestep # TODO check bp#enabled
123
+ end
124
+ end
125
+
126
+ def do_enable_bp(b) # no need to actually patch code in memory
127
+ end
128
+
129
+ def do_disable_bp(b)
130
+ end
131
+
132
+ def do_singlestep
133
+ di = @disassembler.di_at(pc)
134
+ if not di
135
+ @disassembler.disassemble_fast(pc)
136
+ di = @disassembler.di_at(pc)
137
+ end
138
+ if not di
139
+ if callback_unknown_pc and callback_unknown_pc.call()
140
+ return true
141
+ end
142
+ return
143
+ end
144
+
145
+ if callback_emulate_di
146
+ ret = callback_emulate_di.call(di)
147
+ return true if ret
148
+ end
149
+
150
+ return if di.opcode.props[:stopexec] and not di.opcode.props[:setip]
151
+
152
+ # 2-pass to respect binding atomicity
153
+ fbd = @disassembler.get_fwdemu_binding(di, register_pc, self)
154
+
155
+ fbd.map { |k, v|
156
+ if k.kind_of?(Indirection)
157
+ k = Indirection.new(resolve(k.pointer), k.len, k.origin)
158
+ end
159
+ [k, resolve(v)]
160
+ }.each { |k, v|
161
+ case k
162
+ when Indirection
163
+ v = v & ((1 << (k.len*8)) - 1)
164
+ memory_write_int(k.pointer, v, k.len)
165
+ when Symbol
166
+ set_reg_value(k, v)
167
+ when /^dummy_metasm_/
168
+ else
169
+ puts "singlestep: badkey #{k.inspect} = #{v}"
170
+ end
171
+ }
172
+ true
173
+ end
174
+ end
175
+ end
@@ -246,14 +246,14 @@ class VirtualString
246
246
  def read_range(from, len)
247
247
  from += @addr_start
248
248
  if not len
249
- base, page = cache_get_page(from)
250
- page[from - base]
249
+ base, page, inval = cache_get_page(from)
250
+ page[from - base] if not inval
251
251
  elsif len <= @pagelength
252
- base, page = cache_get_page(from)
253
- s = page[from - base, len]
252
+ base, page, inval = cache_get_page(from)
253
+ s = page[from - base, len] if not inval
254
254
  if from+len-base > @pagelength # request crosses a page boundary
255
- base, page = cache_get_page(from+len)
256
- s << page[0, from+len-base]
255
+ base, page, inval = cache_get_page(from+len)
256
+ s << page[0, from+len-base] if s and not inval
257
257
  end
258
258
  s
259
259
  else
@@ -272,6 +272,11 @@ class VirtualString
272
272
  # overwrites a section of the original data
273
273
  #def rewrite_at(addr, content)
274
274
  #end
275
+
276
+ # decode an integer
277
+ def decode_imm(addr, len, cpu)
278
+ Expression.decode_imm(self, len, cpu, addr)
279
+ end
275
280
  end
276
281
 
277
282
  # on-demand reading of a file
@@ -26,18 +26,10 @@ class CPU
26
26
  while tok = lexer.readtok and parse_prefix(i, tok.raw)
27
27
  lexer.skip_space_eol
28
28
  end
29
- return if not tok
30
-
31
- # allow '.' in opcode name
32
- tok = tok.dup
33
- while ntok = lexer.nexttok and ntok.type == :punct and ntok.raw == '.'
34
- tok.raw << lexer.readtok.raw
35
- ntok = lexer.readtok
36
- raise tok, 'invalid opcode name' if not ntok or ntok.type != :string
37
- tok.raw << ntok.raw
38
- end
39
29
 
40
- raise tok, 'invalid opcode' if not opcode_list_byname[tok.raw]
30
+ lexer.unreadtok(tok)
31
+ tok = parse_instruction_mnemonic(lexer)
32
+ return if not tok
41
33
 
42
34
  i.opname = tok.raw
43
35
  i.backtrace = tok.backtrace
@@ -63,6 +55,23 @@ class CPU
63
55
  i
64
56
  end
65
57
 
58
+ # return a lexer token with an instruction mnemonic in #raw
59
+ # allows '.' in opcode name
60
+ # return nil at eof
61
+ def parse_instruction_mnemonic(lexer)
62
+ return if not tok = lexer.readtok
63
+ tok = tok.dup
64
+ while ntok = lexer.nexttok and ntok.type == :punct and ntok.raw == '.'
65
+ tok.raw << lexer.readtok.raw
66
+ ntok = lexer.readtok
67
+ raise tok, 'invalid opcode name' if not ntok or ntok.type != :string
68
+ tok.raw << ntok.raw
69
+ end
70
+
71
+ raise tok, 'invalid opcode' if not opcode_list_byname[tok.raw]
72
+ tok
73
+ end
74
+
66
75
  def parse_instruction_checkproto(i)
67
76
  opcode_list_byname[i.opname].to_a.find { |o|
68
77
  o.args.length == i.args.length and o.args.zip(i.args).all? { |f, a| parse_arg_valid?(o, f, a) }
@@ -421,7 +430,7 @@ class ExeFormat
421
430
  @cursource << Padding.new(fillwith, tok.backtrace) << Offset.new(e, tok.backtrace)
422
431
 
423
432
  else
424
- @cpu.parse_parser_instruction(self, tok)
433
+ @cpu.parse_parser_instruction(@lexer, tok)
425
434
  end
426
435
  end
427
436
 
@@ -746,6 +755,8 @@ class Expression
746
755
 
747
756
  # for boolean operators, true is 1 (or anything != 0), false is 0
748
757
  def parse(lexer)
758
+ lexer = Preprocessor.new(lexer) if lexer.kind_of?(::String)
759
+
749
760
  opstack = []
750
761
  stack = []
751
762
 
@@ -22,7 +22,17 @@ module C
22
22
  __builtin_offsetof
23
23
  ].inject({}) { |h, w| h.update w => true }
24
24
 
25
+ module Misc
26
+ attr_accessor :misc
27
+
28
+ def with_misc(m)
29
+ @misc = m if m or misc
30
+ self
31
+ end
32
+ end
33
+
25
34
  class Statement
35
+ include Misc
26
36
  end
27
37
 
28
38
  module Typed # allows quick testing whether an object is an CExpr or a Variable
@@ -114,6 +124,7 @@ module C
114
124
 
115
125
  class Type
116
126
  include Attributes
127
+ include Misc
117
128
  attr_accessor :qualifier # const volatile
118
129
 
119
130
  def pointer? ; false end
@@ -758,6 +769,7 @@ module C
758
769
  class Variable
759
770
  include Attributes
760
771
  include Typed
772
+ include Misc
761
773
 
762
774
  attr_accessor :type
763
775
  attr_accessor :initializer # CExpr / Block (for Functions)
@@ -773,6 +785,8 @@ module C
773
785
  # found in a block's Statements, used to know the initialization order
774
786
  # eg { int i; i = 4; struct foo { int k; } toto = {i}; }
775
787
  class Declaration
788
+ include Misc
789
+
776
790
  attr_accessor :var
777
791
  def initialize(var)
778
792
  @var = var
@@ -946,8 +960,8 @@ module C
946
960
  raise ftok, 'unterminated asm block' if not tok = parser.lexer.readtok
947
961
  break if tok.type == :punct and tok.raw == '}'
948
962
  case tok.type
949
- when :space; body << ' '
950
- when :eol; body << "\n"
963
+ when :space; body << ' ' unless body.empty?
964
+ when :eol; body << "\n" unless body.empty?
951
965
  when :punct; body << tok.raw
952
966
  when :quoted; body << CExpression.string_inspect(tok.value) # concat adjacent c strings
953
967
  when :string
@@ -1056,12 +1070,15 @@ module C
1056
1070
  class CExpression < Statement
1057
1071
  include Typed
1058
1072
 
1073
+ AssignOp = [:'=', :'+=', :'-=', :'*=', :'/=', :'%=', :'^=', :'&=', :'|=', :'>>=', :'<<=', :'++', :'--']
1074
+
1059
1075
  # may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast)
1060
1076
  attr_accessor :op
1061
1077
  # nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block
1062
1078
  attr_accessor :lexpr, :rexpr
1063
1079
  # a Type
1064
1080
  attr_accessor :type
1081
+
1065
1082
  def initialize(l, o, r, t)
1066
1083
  raise "invalid CExpr #{[l, o, r, t].inspect}" if (o and not o.kind_of? ::Symbol) or not t.kind_of? Type
1067
1084
  @lexpr, @op, @rexpr, @type = l, o, r, t
@@ -1092,7 +1109,7 @@ module C
1092
1109
  # sub-arrays in args are to be passed to self.[] recursively (syntaxic sugar)
1093
1110
  splat = lambda { |e| e.kind_of?(::Array) ? self[*e] : e }
1094
1111
 
1095
- args.shift while args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla]
1112
+ args.shift while args.length > 0 and args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla]
1096
1113
 
1097
1114
  case args.length
1098
1115
  when 4
@@ -1114,9 +1131,9 @@ module C
1114
1131
 
1115
1132
  case op
1116
1133
  when :funcall
1117
- rt = x1.type.untypedef
1118
- rt = rt.type.untypedef if rt.pointer?
1119
- new(x1, op, x2, rt.type)
1134
+ rt = x1.type.untypedef
1135
+ rt = rt.type.untypedef if rt.pointer?
1136
+ new(x1, op, x2, rt.type)
1120
1137
  when :[]; new(x1, op, x2, x1.type.untypedef.type)
1121
1138
  when :+; new(x1, op, x2, (x2.type.pointer? ? x2.type : x1.type))
1122
1139
  when :-; new(x1, op, x2, ((x1.type.pointer? and x2.type.pointer?) ? BaseType.new(:int) : x2.type.pointer? ? x2.type : x1.type))
@@ -3176,7 +3193,7 @@ EOH
3176
3193
  end
3177
3194
 
3178
3195
  # parse a given String as an AllocCStruct
3179
- # offset is an optionnal offset from the string start
3196
+ # offset is an optional offset from the string start
3180
3197
  # modification to the structure will modify the underlying string
3181
3198
  def decode_c_struct(structname, str, offset=0)
3182
3199
  struct = find_c_struct(structname)
@@ -3321,19 +3338,49 @@ EOH
3321
3338
  end
3322
3339
  end
3323
3340
 
3341
+ # used to render a C to a source string, while keeping the information of which each character comes from which C object
3342
+ class CRenderString < ::String
3343
+ attr_accessor :my_c # default C obj with which raw characters are associated
3344
+ # hash offset => C::Statement, means bytes from this offset to the next entry comes from rendering this C object
3345
+ def c_at_offset
3346
+ @c_at_offset ||= {}
3347
+ end
3348
+
3349
+ # concatenate another CRenderString: merge @c_at_offset
3350
+ def <<(o)
3351
+ if o.kind_of?(self.class)
3352
+ o.c_at_offset.each { |k, v|
3353
+ c_at_offset[length+k] ||= v
3354
+ }
3355
+ elsif my_c
3356
+ c_at_offset[length] ||= my_c
3357
+ end
3358
+ super(o)
3359
+ end
3360
+
3361
+ def initialize(*a)
3362
+ if cs = a.grep(Statement).first
3363
+ a -= [cs]
3364
+ @my_c = cs
3365
+ c_at_offset[0] = cs
3366
+ end
3367
+ super(*a)
3368
+ end
3369
+ end
3370
+
3324
3371
  class Statement
3325
- def self.dump(e, scope, r=[''], dep=[])
3372
+ def self.dump(e, scope, r=[CRenderString.new], dep=[])
3326
3373
  case e
3327
3374
  when nil; r.last << ';'
3328
3375
  when Block
3329
3376
  r.last << ' ' if not r.last.empty?
3330
3377
  r.last << '{'
3331
- tr, dep = e.dump(scope, [''], dep)
3378
+ tr, dep = e.dump(scope, [CRenderString.new], dep)
3332
3379
  tr.pop if tr.last.empty?
3333
3380
  r.concat tr.map { |s| Case.dump_indent(s) }
3334
- (r.last[-1] == ?{ ? r.last : r) << '}'
3381
+ (r.last[-1] == ?{ ? r.last : r) << CRenderString.new('}')
3335
3382
  else
3336
- tr, dep = e.dump(scope, [''], dep)
3383
+ tr, dep = e.dump(scope, [CRenderString.new], dep)
3337
3384
  r.concat tr.map { |s| Case.dump_indent(s) }
3338
3385
  end
3339
3386
  [r, dep]
@@ -3348,7 +3395,7 @@ EOH
3348
3395
  def to_s() dump(nil)[0].join("\n") end
3349
3396
 
3350
3397
  # return array of c source lines and array of dependencies (objects)
3351
- def dump(scp, r=[''], dep=[])
3398
+ def dump(scp, r=[CRenderString.new], dep=[])
3352
3399
  mydefs = @symbol.values.grep(TypeDef) + @struct.values + anonymous_enums.to_a
3353
3400
  todo_rndr = {}
3354
3401
  todo_deps = {}
@@ -3360,7 +3407,7 @@ EOH
3360
3407
  [r, dep]
3361
3408
  end
3362
3409
 
3363
- def dump_reorder(mydefs, todo_rndr, todo_deps, r=[''], dep=[])
3410
+ def dump_reorder(mydefs, todo_rndr, todo_deps, r=[CRenderString.new], dep=[])
3364
3411
  val = todo_deps.values.flatten.uniq
3365
3412
  dep |= val
3366
3413
  dep -= mydefs | todo_deps.keys
@@ -3375,6 +3422,7 @@ EOH
3375
3422
  # predeclare structs involved in cyclic dependencies
3376
3423
  dep_cycle = lambda { |ary|
3377
3424
  # sexyness inside (c)
3425
+ # XXX 5 years later, i have no idea whats going on here
3378
3426
  deps = todo_deps[ary.last]
3379
3427
  if deps.include? ary.first; ary
3380
3428
  elsif (deps-ary).find { |d| deps = dep_cycle[ary + [d]] }; deps
@@ -3384,7 +3432,7 @@ EOH
3384
3432
  oldc = nil
3385
3433
  while c = dep_cycle[[t]]
3386
3434
  break if oldc == c
3387
- r << "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};" if not oldc
3435
+ r << CRenderString.new(t, "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};") if not oldc
3388
3436
  oldc = c
3389
3437
  c.each { |s|
3390
3438
  # XXX struct z { struct a* }; struct a { void (*foo)(struct z); };
@@ -3403,7 +3451,7 @@ EOH
3403
3451
  end
3404
3452
  todo_now.sort_by { |k| k.name || '0' }.each { |k|
3405
3453
  if k.kind_of? Variable and k.type.kind_of? Function and k.initializer
3406
- r << ''
3454
+ r << CRenderString.new
3407
3455
  r.concat todo_rndr.delete(k)
3408
3456
  else
3409
3457
  r.pop if r.last == ''
@@ -3413,12 +3461,12 @@ EOH
3413
3461
  todo_deps.delete k
3414
3462
  }
3415
3463
  todo_deps.each_key { |k| todo_deps[k] -= todo_now }
3416
- r << '' << '' << ''
3464
+ r << CRenderString.new << CRenderString.new << CRenderString.new
3417
3465
  end
3418
3466
 
3419
3467
  @statements.each { |s|
3420
- r << '' if not r.last.empty?
3421
- if s.kind_of? Block
3468
+ r << CRenderString.new if not r.last.empty?
3469
+ if s.kind_of?(Block)
3422
3470
  r, dep = Statement.dump(s, self, r, dep)
3423
3471
  else
3424
3472
  r, dep = s.dump(self, r, dep)
@@ -3429,10 +3477,10 @@ EOH
3429
3477
  end
3430
3478
  end
3431
3479
  class Declaration
3432
- def dump(scope, r=[''], dep=[])
3433
- tr, dep = @var.dump_def(scope, [''], dep)
3434
- if @var.kind_of? Variable and @var.type.kind_of? Function and @var.initializer
3435
- r << ''
3480
+ def dump(scope, r=[CRenderString.new], dep=[])
3481
+ tr, dep = @var.dump_def(scope, [CRenderString.new], dep)
3482
+ if @var.kind_of?(Variable) and @var.type.kind_of?(Function) and @var.initializer
3483
+ r << CRenderString.new
3436
3484
  r.concat tr
3437
3485
  else
3438
3486
  r.pop if r.last == ''
@@ -3461,14 +3509,14 @@ EOH
3461
3509
  end
3462
3510
  end
3463
3511
  class Variable
3464
- def dump(scope, r=[''], dep=[])
3512
+ def dump(scope, r=[CRenderString.new], dep=[])
3465
3513
  if name
3466
3514
  dep |= [scope.symbol_ancestors[@name]]
3467
3515
  r.last << @name
3468
3516
  end
3469
3517
  [r, dep]
3470
3518
  end
3471
- def dump_def(scope, r=[''], dep=[], skiptype=false)
3519
+ def dump_def(scope, r=[CRenderString.new], dep=[], skiptype=false)
3472
3520
  # int a=1, b=2;
3473
3521
  r.last << dump_attributes_pre
3474
3522
  if not skiptype
@@ -3476,7 +3524,7 @@ EOH
3476
3524
  r, dep = @type.base.dump(scope, r, dep)
3477
3525
  r.last << ' ' if name
3478
3526
  end
3479
- r, dep = @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
3527
+ r, dep = @type.dump_declarator([CRenderString.new(name ? @name.dup : '') << dump_attributes], scope, r, dep)
3480
3528
 
3481
3529
  if initializer
3482
3530
  r.last << ' = ' if not @type.kind_of?(Function)
@@ -3490,7 +3538,7 @@ EOH
3490
3538
  end
3491
3539
  end
3492
3540
  class Type
3493
- def dump_initializer(init, scope, r=[''], dep=[])
3541
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3494
3542
  case init
3495
3543
  when ::Numeric
3496
3544
  r.last << init.to_s
@@ -3502,9 +3550,9 @@ EOH
3502
3550
  end
3503
3551
  end
3504
3552
 
3505
- def dump_declarator(decl, scope, r=[''], dep=[])
3553
+ def dump_declarator(decl, scope, r=[CRenderString.new], dep=[])
3506
3554
  r.last << decl.shift
3507
- r.concat decl
3555
+ r.concat decl.map { |d| CRenderString.new << d }
3508
3556
  [r, dep]
3509
3557
  end
3510
3558
 
@@ -3512,11 +3560,11 @@ EOH
3512
3560
  dump(*a)
3513
3561
  end
3514
3562
 
3515
- def dump_cast(scope, r=[''], dep=[])
3563
+ def dump_cast(scope, r=[CRenderString.new], dep=[])
3516
3564
  r.last << '('
3517
3565
  r.last << dump_attributes_pre if not kind_of? TypeDef
3518
3566
  r, dep = base.dump(scope, r, dep)
3519
- r, dep = dump_declarator([kind_of?(TypeDef) ? '' : dump_attributes], scope, r, dep)
3567
+ r, dep = dump_declarator([CRenderString.new(kind_of?(TypeDef) ? '' : dump_attributes)], scope, r, dep)
3520
3568
  r.last << ')'
3521
3569
  [r, dep]
3522
3570
  end
@@ -3526,28 +3574,29 @@ EOH
3526
3574
  end
3527
3575
  end
3528
3576
  class Pointer
3529
- def dump_declarator(decl, scope, r=[''], dep=[])
3577
+ def dump_declarator(decl, scope, r=[CRenderString.new], dep=[])
3530
3578
  d = decl[0]
3531
- decl[0] = '*'
3579
+ decl[0] = CRenderString.new
3580
+ decl[0] << '*'
3532
3581
  decl[0] << ' ' << @qualifier.map { |q| q.to_s }.join(' ') << ' ' if qualifier
3533
3582
  decl[0] << d
3534
- if @type.kind_of? Function or @type.kind_of? Array
3535
- decl[0] = '(' << decl[0]
3583
+ if @type.kind_of?(Function) or @type.kind_of?(Array)
3584
+ decl[0] = CRenderString.new << '(' << decl[0]
3536
3585
  decl.last << ')'
3537
3586
  end
3538
3587
  @type.dump_declarator(decl, scope, r, dep)
3539
3588
  end
3540
3589
  end
3541
3590
  class Array
3542
- def dump_declarator(decl, scope, r=[''], dep=[])
3591
+ def dump_declarator(decl, scope, r=[CRenderString.new], dep=[])
3543
3592
  decl.last << '()' if decl.last.empty?
3544
3593
  decl.last << '['
3545
3594
  decl, dep = CExpression.dump(@length, scope, decl, dep) if length
3546
3595
  decl.last << ']'
3547
3596
  @type.dump_declarator(decl, scope, r, dep)
3548
3597
  end
3549
- def dump_initializer(init, scope, r=[''], dep=[])
3550
- return super(init, scope, r, dep) if not init.kind_of? ::Array
3598
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3599
+ return super(init, scope, r, dep) if not init.kind_of?(::Array)
3551
3600
  r.last << '{ '
3552
3601
  showname = false
3553
3602
  init.each_with_index { |v, i|
@@ -3556,21 +3605,21 @@ EOH
3556
3605
  next
3557
3606
  end
3558
3607
  r.last << ', ' if r.last[-2, 2] != '{ '
3559
- rt = ['']
3608
+ rt = [CRenderString.new]
3560
3609
  if showname
3561
3610
  showname = false
3562
- rt << "[#{i}] = "
3611
+ rt << CRenderString.new("[#{i}] = ")
3563
3612
  end
3564
3613
  rt, dep = @type.dump_initializer(v, scope, rt, dep)
3565
3614
  r.last << rt.shift
3566
- r.concat rt.map { |s| "\t" << s }
3615
+ r.concat rt.map { |s| CRenderString.new << "\t" << s }
3567
3616
  }
3568
3617
  r.last << ' }'
3569
3618
  [r, dep]
3570
3619
  end
3571
3620
  end
3572
3621
  class Function
3573
- def dump_declarator(decl, scope, r=[''], dep=[])
3622
+ def dump_declarator(decl, scope, r=[CRenderString.new], dep=[])
3574
3623
  decl.last << '()' if decl.last.empty?
3575
3624
  decl.last << '('
3576
3625
  if args
@@ -3589,12 +3638,12 @@ EOH
3589
3638
  @type.dump_declarator(decl, scope, r, dep)
3590
3639
  end
3591
3640
 
3592
- def dump_initializer(init, scope, r=[''], dep=[])
3593
- Statement.dump(init, scope, r << '', dep)
3641
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3642
+ Statement.dump(init, scope, r << CRenderString.new, dep)
3594
3643
  end
3595
3644
  end
3596
3645
  class BaseType
3597
- def dump(scope, r=[''], dep=[])
3646
+ def dump(scope, r=[CRenderString.new], dep=[])
3598
3647
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3599
3648
  r.last << @specifier.to_s << ' ' if specifier and @name != :ptr
3600
3649
  r.last << case @name
@@ -3607,27 +3656,27 @@ EOH
3607
3656
  end
3608
3657
  end
3609
3658
  class TypeDef
3610
- def dump(scope, r=[''], dep=[])
3659
+ def dump(scope, r=[CRenderString.new], dep=[])
3611
3660
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3612
3661
  r.last << @name
3613
3662
  dep |= [scope.symbol_ancestors[@name]]
3614
3663
  [r, dep]
3615
3664
  end
3616
3665
 
3617
- def dump_def(scope, r=[''], dep=[])
3666
+ def dump_def(scope, r=[CRenderString.new], dep=[])
3618
3667
  r.last << 'typedef '
3619
3668
  r.last << dump_attributes_pre
3620
3669
  r, dep = @type.base.dump(scope, r, dep)
3621
3670
  r.last << ' '
3622
- @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
3671
+ @type.dump_declarator([CRenderString.new(name ? @name.dup : '') << dump_attributes], scope, r, dep)
3623
3672
  end
3624
3673
 
3625
- def dump_initializer(init, scope, r=[''], dep=[])
3674
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3626
3675
  @type.dump_initializer(init, scope, r, dep)
3627
3676
  end
3628
3677
  end
3629
3678
  class Union
3630
- def dump(scope, r=[''], dep=[])
3679
+ def dump(scope, r=[CRenderString.new], dep=[])
3631
3680
  if name
3632
3681
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3633
3682
  r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1] << ' ' << @name
@@ -3638,26 +3687,26 @@ EOH
3638
3687
  end
3639
3688
  end
3640
3689
 
3641
- def dump_def(scope, r=[''], dep=[])
3642
- r << ''
3690
+ def dump_def(scope, r=[CRenderString.new], dep=[])
3691
+ r << CRenderString.new
3643
3692
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3644
3693
  r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1]
3645
3694
  r.last << ' ' << @name if name
3646
3695
  if members
3647
3696
  r.last << ' {'
3648
3697
  @members.each_with_index { |m,i|
3649
- tr, dep = m.dump_def(scope, [''], dep)
3698
+ tr, dep = m.dump_def(scope, [CRenderString.new], dep)
3650
3699
  tr.last << ':' << @bits[i].to_s if bits and @bits[i]
3651
3700
  tr.last << ';'
3652
- r.concat tr.map { |s| "\t" << s }
3701
+ r.concat tr.map { |s| CRenderString.new << "\t" << s }
3653
3702
  }
3654
- r << '}'
3703
+ r << CRenderString.new('}')
3655
3704
  end
3656
3705
  r.last << dump_attributes
3657
3706
  [r, dep]
3658
3707
  end
3659
3708
 
3660
- def dump_initializer(init, scope, r=[''], dep=[])
3709
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3661
3710
  return super(init, scope, r, dep) if not init.kind_of? ::Array
3662
3711
  r.last << '{ '
3663
3712
  showname = false
@@ -3667,21 +3716,21 @@ EOH
3667
3716
  next
3668
3717
  end
3669
3718
  r.last << ', ' if r.last[-2, 2] != '{ '
3670
- rt = ['']
3719
+ rt = [CRenderString.new]
3671
3720
  if showname
3672
3721
  showname = false
3673
3722
  rt << ".#{m.name} = "
3674
3723
  end
3675
3724
  rt, dep = m.type.dump_initializer(i, scope, rt, dep)
3676
3725
  r.last << rt.shift
3677
- r.concat rt.map { |s| "\t" << s }
3726
+ r.concat rt.map { |s| CRenderString.new << "\t" << s }
3678
3727
  }
3679
3728
  r.last << ' }'
3680
3729
  [r, dep]
3681
3730
  end
3682
3731
  end
3683
3732
  class Struct
3684
- def dump_def(scope, r=[''], dep=[])
3733
+ def dump_def(scope, r=[CRenderString.new], dep=[])
3685
3734
  if pack
3686
3735
  r, dep = super(scope, r, dep)
3687
3736
  r.last <<
@@ -3695,7 +3744,7 @@ EOH
3695
3744
  end
3696
3745
  end
3697
3746
  class Enum
3698
- def dump(scope, r=[''], dep=[])
3747
+ def dump(scope, r=[CRenderString.new], dep=[])
3699
3748
  if name
3700
3749
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3701
3750
  r.last << 'enum ' << @name
@@ -3706,7 +3755,7 @@ EOH
3706
3755
  end
3707
3756
  end
3708
3757
 
3709
- def dump_def(scope, r=[''], dep=[])
3758
+ def dump_def(scope, r=[CRenderString.new], dep=[])
3710
3759
  r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
3711
3760
  r.last << 'enum'
3712
3761
  r.last << ' ' << @name if name
@@ -3726,7 +3775,7 @@ EOH
3726
3775
  [r, dep]
3727
3776
  end
3728
3777
 
3729
- def dump_initializer(init, scope, r=[''], dep=[])
3778
+ def dump_initializer(init, scope, r=[CRenderString.new], dep=[])
3730
3779
  if members and (
3731
3780
  k = @members.index(init) or
3732
3781
  (init.kind_of? CExpression and not init.op and k = @members.index(init.rexpr))
@@ -3739,14 +3788,14 @@ EOH
3739
3788
  end
3740
3789
  end
3741
3790
  class If
3742
- def dump(scope, r=[''], dep=[])
3743
- r.last << 'if ('
3791
+ def dump(scope, r=[CRenderString.new], dep=[])
3792
+ r.last << CRenderString.new(self, 'if (')
3744
3793
  r, dep = CExpression.dump(@test, scope, r, dep)
3745
3794
  r.last << ')'
3746
3795
  r, dep = Statement.dump(@bthen, scope, r, dep)
3747
3796
  if belse
3748
- @bthen.kind_of?(Block) ? (r.last << ' else') : (r << 'else')
3749
- if @belse.kind_of? If
3797
+ @bthen.kind_of?(Block) ? (r.last << CRenderString.new(' else')) : (r << CRenderString.new(self, 'else'))
3798
+ if @belse.kind_of?(If)
3750
3799
  # skip indent
3751
3800
  r.last << ' '
3752
3801
  r, dep = @belse.dump(scope, r, dep)
@@ -3758,9 +3807,9 @@ EOH
3758
3807
  end
3759
3808
  end
3760
3809
  class For
3761
- def dump(scope, r=[''], dep=[])
3762
- r.last << 'for ('
3763
- if @init.kind_of? Block
3810
+ def dump(scope, r=[CRenderString.new], dep=[])
3811
+ r.last << CRenderString.new(self, 'for (')
3812
+ if @init.kind_of?(Block)
3764
3813
  scope = @init
3765
3814
  skiptype = false
3766
3815
  @init.symbol.each_value { |s|
@@ -3784,56 +3833,57 @@ EOH
3784
3833
  end
3785
3834
  end
3786
3835
  class While
3787
- def dump(scope, r=[''], dep=[])
3788
- r.last << 'while ('
3836
+ def dump(scope, r=[CRenderString.new], dep=[])
3837
+ r.last << CRenderString.new(self, 'while (')
3789
3838
  r, dep = CExpression.dump(@test, scope, r, dep)
3790
3839
  r.last << ')'
3791
3840
  Statement.dump(@body, scope, r, dep)
3792
3841
  end
3793
3842
  end
3794
3843
  class DoWhile
3795
- def dump(scope, r=[''], dep=[])
3796
- r.last << 'do'
3844
+ def dump(scope, r=[CRenderString.new], dep=[])
3845
+ r.last << CRenderString.new(self, 'do')
3797
3846
  r, dep = Statement.dump(@body, scope, r, dep)
3798
- @body.kind_of?(Block) ? (r.last << ' while (') : (r << 'while (')
3847
+ r << CRenderString.new if not @body.kind_of?(Block)
3848
+ r.last << CRenderString.new(self, ' while (')
3799
3849
  r, dep = CExpression.dump(@test, scope, r, dep)
3800
3850
  r.last << ');'
3801
3851
  [r, dep]
3802
3852
  end
3803
3853
  end
3804
3854
  class Switch
3805
- def dump(scope, r=[''], dep=[])
3806
- r.last << 'switch ('
3855
+ def dump(scope, r=[CRenderString.new], dep=[])
3856
+ r.last << CRenderString.new(self, 'switch (')
3807
3857
  r, dep = CExpression.dump(@test, scope, r, dep)
3808
3858
  r.last << ')'
3809
- r.last << ' {' if @body.kind_of? Block
3810
- tr, dep = @body.dump(scope, [''], dep)
3859
+ r.last << ' {' if @body.kind_of?(Block)
3860
+ tr, dep = @body.dump(scope, [CRenderString.new], dep)
3811
3861
  r.concat tr.map { |s| Case.dump_indent(s, true) }
3812
- r << '}' if @body.kind_of? Block
3862
+ r << CRenderString.new('}') if @body.kind_of? Block
3813
3863
  [r, dep]
3814
3864
  end
3815
3865
  end
3816
3866
  class Continue
3817
- def dump(scope, r=[''], dep=[])
3818
- r.last << 'continue;'
3867
+ def dump(scope, r=[CRenderString.new], dep=[])
3868
+ r.last << CRenderString.new(self, 'continue;')
3819
3869
  [r, dep]
3820
3870
  end
3821
3871
  end
3822
3872
  class Break
3823
- def dump(scope, r=[''], dep=[])
3824
- r.last << 'break;'
3873
+ def dump(scope, r=[CRenderString.new], dep=[])
3874
+ r.last << CRenderString.new(self, 'break;')
3825
3875
  [r, dep]
3826
3876
  end
3827
3877
  end
3828
3878
  class Goto
3829
- def dump(scope, r=[''], dep=[])
3830
- r.last << "goto #@target;"
3879
+ def dump(scope, r=[CRenderString.new], dep=[])
3880
+ r.last << CRenderString.new(self, "goto #@target;")
3831
3881
  [r, dep]
3832
3882
  end
3833
3883
  end
3834
3884
  class Return
3835
- def dump(scope, r=[''], dep=[])
3836
- r.last << 'return '
3885
+ def dump(scope, r=[CRenderString.new], dep=[])
3886
+ r.last << CRenderString.new(self, 'return ')
3837
3887
  r, dep = CExpression.dump(@value, scope, r, dep)
3838
3888
  r.last.chop! if r.last[-1] == ?\ # the space character
3839
3889
  r.last << ';'
@@ -3841,12 +3891,12 @@ EOH
3841
3891
  end
3842
3892
  end
3843
3893
  class Case
3844
- def dump(scope, r=[''], dep=[])
3894
+ def dump(scope, r=[CRenderString.new], dep=[])
3845
3895
  case @expr
3846
3896
  when 'default'
3847
3897
  r.last << @expr
3848
3898
  else
3849
- r.last << 'case '
3899
+ r.last << CRenderString.new(self, 'case ')
3850
3900
  r, dep = CExpression.dump(@expr, scope, r, dep)
3851
3901
  if exprup
3852
3902
  r.last << ' ... '
@@ -3859,35 +3909,35 @@ EOH
3859
3909
 
3860
3910
  def self.dump_indent(s, short=false)
3861
3911
  case s
3862
- when /^(case|default)\W/; (short ? ' ' : "\t") << s
3863
- when /^\s+(case|default)\W/; "\t" << s
3912
+ when /^(case|default)\W/; CRenderString.new(short ? ' ' : "\t") << s
3913
+ when /^\s+(case|default)\W/; CRenderString.new("\t") << s
3864
3914
  when /:$/; s
3865
- else "\t" << s
3915
+ else CRenderString.new("\t") << s
3866
3916
  end
3867
3917
  end
3868
3918
  end
3869
3919
  class Label
3870
- def dump(scope, r=[''], dep=[])
3871
- r.last << @name << ':'
3920
+ def dump(scope, r=[CRenderString.new], dep=[])
3921
+ r.last << CRenderString.new(self, @name) << ':'
3872
3922
  dump_inner(scope, r, dep)
3873
3923
  end
3874
- def dump_inner(scope, r=[''], dep=[])
3924
+ def dump_inner(scope, r=[CRenderString.new], dep=[])
3875
3925
  if not @statement; [r, dep]
3876
- elsif @statement.kind_of? Block; Statement.dump(@statement, scope, r, dep)
3877
- else @statement.dump(scope, r << '', dep)
3926
+ elsif @statement.kind_of?(Block); Statement.dump(@statement, scope, r, dep)
3927
+ else @statement.dump(scope, r << CRenderString.new, dep)
3878
3928
  end
3879
3929
  end
3880
3930
  end
3881
3931
  class Asm
3882
- def dump(scope, r=[''], dep=[])
3883
- r.last << 'asm '
3932
+ def dump(scope, r=[CRenderString.new], dep=[])
3933
+ r.last << CRenderString.new(self, 'asm ')
3884
3934
  r.last << 'volatile ' if @volatile
3885
3935
  r.last << '('
3886
- r.last << CExpression.string_inspect(@body)
3936
+ r.last << CRenderString.new(self, CExpression.string_inspect(@body))
3887
3937
  if @output or @input or @clobber
3888
3938
  if @output and @output != []
3889
3939
  # TODO
3890
- r << ': /* todo */'
3940
+ r << CRenderString.new(': /* todo */')
3891
3941
  elsif (@input and @input != []) or (@clobber and @clobber != [])
3892
3942
  r.last << ' :'
3893
3943
  end
@@ -3895,13 +3945,13 @@ EOH
3895
3945
  if @input or @clobber
3896
3946
  if @input and @input != []
3897
3947
  # TODO
3898
- r << ': /* todo */'
3948
+ r << CRenderString.new(': /* todo */')
3899
3949
  elsif @clobber and @clobber != []
3900
3950
  r.last << ' :'
3901
3951
  end
3902
3952
  end
3903
3953
  if @clobber and @clobber != []
3904
- r << (': ' << @clobber.map { |c| CExpression.string_inspect(c) }.join(', '))
3954
+ r << (CRenderString.new(': ') << @clobber.map { |c| CExpression.string_inspect(c) }.join(', '))
3905
3955
  end
3906
3956
  r.last << ');'
3907
3957
  [r, dep]
@@ -3910,18 +3960,20 @@ EOH
3910
3960
  class CExpression
3911
3961
  def self.string_inspect(s)
3912
3962
  # keep all ascii printable except \ and "
3913
- '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o| '\\x' + o.unpack('H*').first } + '"'
3963
+ '"' + s.gsub(/[^ !\x23-\x5b\x5d-\x7e]/) { |o|
3964
+ case hex = o.unpack('H*').first.downcase
3965
+ when '00'; '\\0'
3966
+ when '0a'; '\\n'
3967
+ when '0d'; '\\r'
3968
+ when '1b'; '\\e'
3969
+ when '22'; '\\"'
3970
+ when '5c'; '\\\\'
3971
+ else "\\x#{hex}"
3972
+ end
3973
+ } + '"'
3914
3974
  end
3915
3975
 
3916
- def self.dump(e, scope, r=[''], dep=[], brace = false)
3917
- if $DEBUG
3918
- brace = false
3919
- case e
3920
- when CExpression, Variable
3921
- r, dep = e.type.dump_cast(scope, r, dep)
3922
- end
3923
- r.last << '('
3924
- end
3976
+ def self.dump(e, scope, r=[CRenderString.new], dep=[], brace = false)
3925
3977
  r, dep = \
3926
3978
  case e
3927
3979
  when ::Numeric; r.last << e.to_s ; [r, dep]
@@ -3931,20 +3983,22 @@ EOH
3931
3983
  when nil; [r, dep]
3932
3984
  else raise 'wtf?' + e.inspect
3933
3985
  end
3934
- if $DEBUG
3935
- r.last << ')'
3936
- end
3937
3986
  [r, dep]
3938
3987
  end
3939
3988
 
3940
- def dump(scope, r=[''], dep=[])
3989
+ def dump(scope, r=[CRenderString.new], dep=[])
3941
3990
  r, dep = dump_inner(scope, r, dep)
3942
- r.last << ';'
3991
+ r.last << CRenderString.new(self, ';')
3943
3992
  [r, dep]
3944
3993
  end
3945
3994
 
3946
- def dump_inner(scope, r=[''], dep=[], brace = false)
3947
- r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
3995
+ def dump_inner(scope, r=[CRenderString.new], dep=[], brace = false)
3996
+ r.last << CRenderString.new(self)
3997
+ if misc and misc[:custom_display]
3998
+ r.last << misc[:custom_display]
3999
+ return [r, dep]
4000
+ end
4001
+ r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of?(CExpression))
3948
4002
  if not @lexpr
3949
4003
  if not @op
3950
4004
  case @rexpr
@@ -3960,7 +4014,7 @@ EOH
3960
4014
  else
3961
4015
  r.last << re.to_s
3962
4016
  end
3963
- if @type.kind_of? BaseType
4017
+ if @type.kind_of?(BaseType)
3964
4018
  r.last << 'U' if @type.specifier == :unsigned
3965
4019
  case @type.name
3966
4020
  when :longlong, :__int64; r.last << 'LL'
@@ -3969,7 +4023,7 @@ EOH
3969
4023
  end
3970
4024
  end
3971
4025
  when ::String
3972
- r.last << 'L' if @type.kind_of? Pointer and @type.type.kind_of? BaseType and @type.type.name == :short
4026
+ r.last << 'L' if @type.kind_of?(Pointer) and @type.type.kind_of?(BaseType) and @type.type.name == :short
3973
4027
  r.last << CExpression.string_inspect(@rexpr)
3974
4028
  when CExpression # cast
3975
4029
  r, dep = @type.dump_cast(scope, r, dep)
@@ -3982,11 +4036,11 @@ EOH
3982
4036
  r.last << ' )'
3983
4037
  when Label
3984
4038
  r.last << '&&' << @rexpr.name
3985
- else raise "wtf? #{inspect}"
4039
+ else raise "(wtf? #{inspect})"
3986
4040
  end
3987
4041
  else
3988
4042
  r.last << @op.to_s
3989
- r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? C::CExpression and @rexpr.lexpr))
4043
+ r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of?(C::CExpression) and @rexpr.lexpr))
3990
4044
  end
3991
4045
  elsif not @rexpr
3992
4046
  r, dep = CExpression.dump(@lexpr, scope, r, dep)
@@ -3995,15 +4049,15 @@ EOH
3995
4049
  case @op
3996
4050
  when :'->', :'.'
3997
4051
  r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
3998
- r.last << @op.to_s << @rexpr
4052
+ r.last << CRenderString.new(self, @op.to_s) << @rexpr
3999
4053
  when :'[]'
4000
4054
  r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
4001
4055
  r.last << '['
4002
- l = lexpr if lexpr.kind_of? Variable
4003
- l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'.'
4004
- l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'->'
4056
+ l = lexpr if lexpr.kind_of?(Variable)
4057
+ l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of?(CExpression) and lexpr.op == :'.'
4058
+ l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of?(CExpression) and lexpr.op == :'->'
4005
4059
  # honor __attribute__((indexenum(enumname)))
4006
- if l and l.attributes and rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr.kind_of? ::Integer and
4060
+ if l and l.attributes and rexpr.kind_of?(CExpression) and not rexpr.op and rexpr.rexpr.kind_of?(::Integer) and
4007
4061
  n = l.has_attribute_var('indexenum') and enum = scope.struct_ancestors[n] and i = enum.members.index(rexpr.rexpr)
4008
4062
  r.last << i
4009
4063
  dep |= [enum]
@@ -4016,22 +4070,22 @@ EOH
4016
4070
  r.last << '('
4017
4071
  @rexpr.each { |arg|
4018
4072
  r.last << ', ' if r.last[-1] != ?(
4019
- r, dep = CExpression.dump(arg, scope, r, dep)
4073
+ r, dep = CExpression.dump(arg, scope, r, dep, (arg.kind_of?(CExpression) and arg.op == :','))
4020
4074
  }
4021
4075
  r.last << ')'
4022
4076
  when :'?:'
4023
4077
  r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
4024
- r.last << ' ? '
4078
+ r.last << CRenderString.new(self, ' ? ')
4025
4079
  r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true)
4026
- r.last << ' : '
4080
+ r.last << CRenderString.new(self, ' : ')
4027
4081
  r, dep = CExpression.dump(@rexpr[1], scope, r, dep, true)
4028
4082
  else
4029
- r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op))
4030
- r.last << ' ' << @op.to_s << ' '
4031
- r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall))
4083
+ r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of?(CExpression) and @lexpr.lexpr and @lexpr.op != @op and @lexpr.op != :funcall))
4084
+ r.last << CRenderString.new(self, ' ' << @op.to_s << ' ')
4085
+ 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 != :'='))
4032
4086
  end
4033
4087
  end
4034
- r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
4088
+ r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of?(CExpression))
4035
4089
  [r, dep]
4036
4090
  end
4037
4091