metasm 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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