metasm 1.0.0
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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,3938 @@
|
|
|
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/main'
|
|
8
|
+
require 'metasm/preprocessor'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
# c parser
|
|
12
|
+
# inspired from http://www.math.grin.edu/~stone/courses/languages/C-syntax.xhtml
|
|
13
|
+
module C
|
|
14
|
+
Keyword = %w[struct union enum if else for while do switch goto
|
|
15
|
+
register extern auto static typedef const volatile
|
|
16
|
+
void int float double char signed unsigned long short
|
|
17
|
+
case continue break return default __attribute__
|
|
18
|
+
asm __asm __asm__ sizeof typeof
|
|
19
|
+
__declspec __cdecl __stdcall __fastcall __noreturn
|
|
20
|
+
inline __inline __inline__ __volatile__
|
|
21
|
+
__int8 __int16 __int32 __int64
|
|
22
|
+
__builtin_offsetof
|
|
23
|
+
].inject({}) { |h, w| h.update w => true }
|
|
24
|
+
|
|
25
|
+
class Statement
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module Typed # allows quick testing whether an object is an CExpr or a Variable
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Block < Statement
|
|
32
|
+
attr_accessor :symbol # hash name => Type/Variable/enum value
|
|
33
|
+
attr_accessor :struct # hash name => Struct/Union/Enum
|
|
34
|
+
attr_accessor :outer # parent block
|
|
35
|
+
attr_accessor :statements # array of Statement/Declaration
|
|
36
|
+
attr_accessor :anonymous_enums # array of anonymous Enum
|
|
37
|
+
|
|
38
|
+
def initialize(outer, statements=[], symbol={}, struct={})
|
|
39
|
+
@outer = outer
|
|
40
|
+
@statements = statements
|
|
41
|
+
@symbol = symbol
|
|
42
|
+
@struct = struct
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def struct_ancestors
|
|
46
|
+
@outer ? @outer.struct_ancestors.merge(@struct) : @struct
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def symbol_ancestors
|
|
50
|
+
@outer ? @outer.symbol_ancestors.merge(@symbol) : @symbol
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module Attributes
|
|
55
|
+
attr_accessor :attributes
|
|
56
|
+
|
|
57
|
+
DECLSPECS = %w[cdecl stdcall fastcall inline naked thiscall noreturn]
|
|
58
|
+
|
|
59
|
+
# parses a sequence of __attribute__((anything)) into self.attributes (array of string)
|
|
60
|
+
def parse_attributes(parser, allow_declspec = false)
|
|
61
|
+
while tok = parser.skipspaces and tok.type == :string
|
|
62
|
+
case keyword = tok.raw
|
|
63
|
+
when '__attribute__', '__declspec' # synonymous: __attribute__((foo)) == __declspec(foo)
|
|
64
|
+
raise tok || parser if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
65
|
+
raise tok || parser if keyword == '__attribute__' and (not tok = parser.skipspaces or tok.type != :punct or tok.raw != '(')
|
|
66
|
+
nest = 0
|
|
67
|
+
attrib = ''
|
|
68
|
+
loop do
|
|
69
|
+
raise parser if not tok = parser.skipspaces
|
|
70
|
+
if tok.type == :punct and tok.raw == ')'
|
|
71
|
+
if nest == 0
|
|
72
|
+
raise tok || parser if keyword == '__attribute__' and (not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')')
|
|
73
|
+
break
|
|
74
|
+
else
|
|
75
|
+
nest -= 1
|
|
76
|
+
end
|
|
77
|
+
elsif tok.type == :punct and tok.raw == '('
|
|
78
|
+
nest += 1
|
|
79
|
+
elsif nest == 0 and tok.type == :punct and tok.raw == ','
|
|
80
|
+
raise tok || parser if not allow_declspec and DECLSPECS.include? attrib
|
|
81
|
+
add_attribute attrib
|
|
82
|
+
attrib = ''
|
|
83
|
+
next
|
|
84
|
+
end
|
|
85
|
+
attrib << tok.raw
|
|
86
|
+
end
|
|
87
|
+
raise tok || parser, "attr #{attrib.inspect} not allowed here" if not allow_declspec and DECLSPECS.include? attrib
|
|
88
|
+
else
|
|
89
|
+
if allow_declspec and DECLSPECS.include? keyword.gsub('_', '')
|
|
90
|
+
attrib = keyword.gsub('_', '')
|
|
91
|
+
else break
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
add_attribute(attrib)
|
|
95
|
+
end
|
|
96
|
+
parser.unreadtok tok
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# checks if the object has an attribute in its attribute list
|
|
100
|
+
def has_attribute(attr)
|
|
101
|
+
attributes.to_a.include? attr
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# adds an attribute to the object attribute list if it is not already in it
|
|
105
|
+
def add_attribute(attr)
|
|
106
|
+
(@attributes ||= []) << attr if not has_attribute(attr)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# checks if the object has an attributes a la __attribute__((attr(stuff))), returns 'stuff' (raw, no split on ',' or anything)
|
|
110
|
+
def has_attribute_var(attr)
|
|
111
|
+
$1 if attributes.to_a.find { |a| a =~ /^#{attr}\((.*)\)$/ }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class Type
|
|
116
|
+
include Attributes
|
|
117
|
+
attr_accessor :qualifier # const volatile
|
|
118
|
+
|
|
119
|
+
def pointer? ; false end
|
|
120
|
+
def arithmetic? ; false end
|
|
121
|
+
def integral? ; false end
|
|
122
|
+
def float? ; false end
|
|
123
|
+
def void? ; false end
|
|
124
|
+
def base ; self end
|
|
125
|
+
def untypedef ; self end
|
|
126
|
+
|
|
127
|
+
def parse_initializer(parser, scope)
|
|
128
|
+
raise parser, 'expr expected' if not ret = CExpression.parse(parser, scope, false)
|
|
129
|
+
p, i = pointer?, integral?
|
|
130
|
+
r = ret.reduce(parser) if p or i
|
|
131
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
|
132
|
+
parser.check_compatible_type(parser, ret.type, self)
|
|
133
|
+
end
|
|
134
|
+
ret
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
|
138
|
+
if not root and (not nt = parser.skipspaces or nt.type != :punct or nt.raw != '=')
|
|
139
|
+
raise nt || parser, '"=" expected'
|
|
140
|
+
end
|
|
141
|
+
value[idx] = parse_initializer(parser, scope)
|
|
142
|
+
idx + 1
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
class BaseType < Type
|
|
146
|
+
attr_accessor :name # :int :long :longlong :short :double :longdouble :float :char :void :__int8/16/32/64
|
|
147
|
+
attr_accessor :specifier # sign specifier only
|
|
148
|
+
|
|
149
|
+
def arithmetic? ; @name != :void end
|
|
150
|
+
def integral? ; [:char, :short, :int, :long, :longlong, :ptr,
|
|
151
|
+
:__int8, :__int16, :__int32, :__int64].include? @name end
|
|
152
|
+
def signed? ; specifier != :unsigned end
|
|
153
|
+
def float? ; [:float, :double, :longdouble].include? @name end
|
|
154
|
+
def void? ; @name == :void end
|
|
155
|
+
def align(parser) @name == :double ? 4 : parser.typesize[@name] end
|
|
156
|
+
|
|
157
|
+
def initialize(name, *specs)
|
|
158
|
+
@name = name
|
|
159
|
+
specs.each { |s|
|
|
160
|
+
case s
|
|
161
|
+
when :const, :volatile; (@qualifier ||= []) << s
|
|
162
|
+
when :signed, :unsigned; @specifier = s
|
|
163
|
+
when nil
|
|
164
|
+
else raise "internal error, got #{name.inspect} #{specs.inspect}"
|
|
165
|
+
end
|
|
166
|
+
}
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def ==(o)
|
|
170
|
+
o.object_id == self.object_id or
|
|
171
|
+
(o.class == self.class and o.name == self.name and o.specifier == self.specifier and o.attributes == self.attributes)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
class TypeDef < Type
|
|
175
|
+
attr_accessor :name
|
|
176
|
+
attr_accessor :type
|
|
177
|
+
attr_accessor :backtrace
|
|
178
|
+
|
|
179
|
+
def initialize(name, type, backtrace)
|
|
180
|
+
@name, @type, @backtrace = name, type, backtrace
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def parse_initializer(parser, scope)
|
|
184
|
+
@type.parse_initializer(parser, scope)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def pointer? ; @type.pointer? end
|
|
188
|
+
def arithmetic? ; @type.arithmetic? end
|
|
189
|
+
def integral? ; @type.integral? end
|
|
190
|
+
def signed? ; @type.signed? end # relevant only if integral? returns true
|
|
191
|
+
def float? ; @type.float? end
|
|
192
|
+
def void? ; @type.void? end
|
|
193
|
+
def untypedef ; @type.untypedef end
|
|
194
|
+
def align(parser) @type.align(parser) end # XXX __attribute__ ?
|
|
195
|
+
def pointed ; @type.pointed end
|
|
196
|
+
end
|
|
197
|
+
class Function < Type
|
|
198
|
+
attr_accessor :type # return type
|
|
199
|
+
attr_accessor :args # [name, Variable]
|
|
200
|
+
attr_accessor :varargs # true/false
|
|
201
|
+
|
|
202
|
+
def initialize(type=nil, args=nil)
|
|
203
|
+
@type = type
|
|
204
|
+
@args = args if args
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def base ; @type.base ; end
|
|
208
|
+
end
|
|
209
|
+
class Union < Type
|
|
210
|
+
attr_accessor :members # [Variable]
|
|
211
|
+
attr_accessor :bits # [bits] or nil
|
|
212
|
+
attr_accessor :name
|
|
213
|
+
attr_accessor :backtrace
|
|
214
|
+
|
|
215
|
+
attr_accessor :fldoffset, :fldbitoffset, :fldlist
|
|
216
|
+
|
|
217
|
+
def align(parser) @members.to_a.map { |m| m.type.align(parser) }.max end
|
|
218
|
+
|
|
219
|
+
# there is only one instance of a given named struct per parser
|
|
220
|
+
# so we just compare struct names here
|
|
221
|
+
# for comparison between parsers, see #compare_deep
|
|
222
|
+
def ==(o)
|
|
223
|
+
o.object_id == self.object_id or
|
|
224
|
+
(o.class == self.class and o.name == self.name and ((o.name and true) or compare_deep(o)))
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# compare to another structure, comparing members recursively (names and type)
|
|
228
|
+
# returns true if the self is same as o
|
|
229
|
+
def compare_deep(o, seen = [])
|
|
230
|
+
return true if o.object_id == self.object_id
|
|
231
|
+
return if o.class != self.class or o.name != self.name or o.attributes != self.attributes
|
|
232
|
+
o.members.to_a.zip(self.members.to_a).each { |om, sm|
|
|
233
|
+
return if om.name != sm.name
|
|
234
|
+
return if om.type != sm.type
|
|
235
|
+
if om.type.pointer?
|
|
236
|
+
ot = om.type
|
|
237
|
+
st = sm.type
|
|
238
|
+
500.times { # limit for selfpointers (shouldnt happen)
|
|
239
|
+
break if not ot.pointer?
|
|
240
|
+
ot = ot.pointed.untypedef
|
|
241
|
+
st = st.pointed.untypedef
|
|
242
|
+
}
|
|
243
|
+
if ot.kind_of?(C::Union) and ot.name and not seen.include?(ot)
|
|
244
|
+
return if not st.compare_deep(ot, seen+[ot])
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
}
|
|
248
|
+
true
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def findmember(name, igncase=false)
|
|
252
|
+
raise parser, 'undefined structure' if not @members
|
|
253
|
+
return @fldlist[name] if fldlist and @fldlist[name]
|
|
254
|
+
|
|
255
|
+
name = name.downcase if igncase
|
|
256
|
+
if m = @members.find { |m_| (n = m_.name) and (igncase ? n.downcase : n) == name }
|
|
257
|
+
return m
|
|
258
|
+
else
|
|
259
|
+
@members.each { |m_|
|
|
260
|
+
if t = m_.type.untypedef and t.kind_of? Union and mm = t.findmember(name, igncase)
|
|
261
|
+
return mm
|
|
262
|
+
end
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
nil
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def offsetof(parser, name)
|
|
270
|
+
raise parser, 'undefined structure' if not @members
|
|
271
|
+
update_member_cache(parser) if not fldlist
|
|
272
|
+
return 0 if @fldlist[name]
|
|
273
|
+
|
|
274
|
+
if name.kind_of?(Variable)
|
|
275
|
+
return 0 if @members.include? name
|
|
276
|
+
raise ParseError, 'unknown union member'
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
raise parser, 'unknown union member' if not findmember(name)
|
|
280
|
+
|
|
281
|
+
@members.find { |m|
|
|
282
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
|
283
|
+
}.type.untypedef.offsetof(parser, name)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def bitoffsetof(parser, name)
|
|
287
|
+
raise parser, 'undefined structure' if not @members
|
|
288
|
+
update_member_cache(parser) if not fldlist
|
|
289
|
+
return if @fldlist[name] or @members.include?(name)
|
|
290
|
+
raise parser, 'undefined union' if not @members
|
|
291
|
+
raise parser, 'unknown union member' if not findmember(name)
|
|
292
|
+
|
|
293
|
+
@members.find { |m|
|
|
294
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
|
295
|
+
}.type.untypedef.bitoffsetof(parser, name)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def parse_members(parser, scope)
|
|
299
|
+
@fldlist = nil if fldlist # invalidate fld offset cache
|
|
300
|
+
@members = []
|
|
301
|
+
# parse struct/union members in definition
|
|
302
|
+
loop do
|
|
303
|
+
raise parser if not tok = parser.skipspaces
|
|
304
|
+
break if tok.type == :punct and tok.raw == '}'
|
|
305
|
+
parser.unreadtok tok
|
|
306
|
+
|
|
307
|
+
raise tok, 'invalid struct member type' if not basetype = Variable.parse_type(parser, scope)
|
|
308
|
+
loop do
|
|
309
|
+
member = basetype.dup
|
|
310
|
+
member.parse_declarator(parser, scope)
|
|
311
|
+
member.type.length ||= 0 if member.type.kind_of?(Array) # struct { char blarg[]; };
|
|
312
|
+
raise member.backtrace, 'member redefinition' if member.name and @members.find { |m| m.name == member.name }
|
|
313
|
+
@members << member
|
|
314
|
+
|
|
315
|
+
raise tok || parser if not tok = parser.skipspaces or tok.type != :punct
|
|
316
|
+
|
|
317
|
+
if tok.raw == ':' # bits
|
|
318
|
+
raise tok, 'bad type for bitslice' if not member.type.integral?
|
|
319
|
+
bits = nil
|
|
320
|
+
raise tok, "bad bit count #{bits.inspect}" if not bits = CExpression.parse(parser, scope, false) or
|
|
321
|
+
not bits.constant? or !(bits = bits.reduce(parser)).kind_of? ::Integer
|
|
322
|
+
#raise tok, 'need more bits' if bits > 8*parser.sizeof(member)
|
|
323
|
+
# WORD wReserved:17; => yay windows.h
|
|
324
|
+
(@bits ||= [])[@members.length-1] = bits
|
|
325
|
+
raise tok || parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
case tok.raw
|
|
329
|
+
when ';'; break
|
|
330
|
+
when ','
|
|
331
|
+
when '}'; parser.unreadtok(tok); break
|
|
332
|
+
else raise tok, '"," or ";" expected'
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
parse_attributes(parser)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# updates the @fldoffset / @fldbitoffset hash storing the offset of members
|
|
340
|
+
def update_member_cache(parser)
|
|
341
|
+
@fldlist = {}
|
|
342
|
+
@members.to_a.each { |m|
|
|
343
|
+
@fldlist[m.name] = m if m.name
|
|
344
|
+
}
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def parse_initializer(parser, scope)
|
|
348
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
|
349
|
+
# struct x toto = { 1, .4, .member[0][6].bla = 12 };
|
|
350
|
+
raise tok, 'undefined struct' if not @members
|
|
351
|
+
ret = []
|
|
352
|
+
if tok = parser.skipspaces and (tok.type != :punct or tok.raw != '}')
|
|
353
|
+
parser.unreadtok tok
|
|
354
|
+
idx = 0
|
|
355
|
+
loop do
|
|
356
|
+
idx = parse_initializer_designator(parser, scope, ret, idx, true)
|
|
357
|
+
raise tok || parser, '"," or "}" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != '}' and tok.raw != ',')
|
|
358
|
+
break if tok.raw == '}'
|
|
359
|
+
raise tok, 'struct is smaller than that' if idx >= @members.length
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
ret
|
|
363
|
+
else
|
|
364
|
+
parser.unreadtok tok
|
|
365
|
+
super(parser, scope)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# parses a designator+initializer eg '.toto = 4' or '.tutu[42][12].bla = 16' or (root ? '4' : '=4')
|
|
370
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
|
371
|
+
if nt = parser.skipspaces and nt.type == :punct and nt.raw == '.' and
|
|
372
|
+
nnt = parser.skipspaces and nnt.type == :string and
|
|
373
|
+
findmember(nnt.raw)
|
|
374
|
+
raise nnt, 'unhandled indirect initializer' if not nidx = @members.index(@members.find { |m| m.name == nnt.raw }) # TODO
|
|
375
|
+
if not root
|
|
376
|
+
value[idx] ||= [] # AryRecorder may change [] to AryRec.new, can't do v = v[i] ||= []
|
|
377
|
+
value = value[idx]
|
|
378
|
+
end
|
|
379
|
+
idx = nidx
|
|
380
|
+
@members[idx].type.untypedef.parse_initializer_designator(parser, scope, value, idx, false)
|
|
381
|
+
else
|
|
382
|
+
parser.unreadtok nnt
|
|
383
|
+
if root
|
|
384
|
+
parser.unreadtok nt
|
|
385
|
+
value[idx] = @members[idx].type.parse_initializer(parser, scope)
|
|
386
|
+
else
|
|
387
|
+
raise nt || parser, '"=" expected' if not nt or nt.type != :punct or nt.raw != '='
|
|
388
|
+
value[idx] = parse_initializer(parser, scope)
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
idx + 1
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
class Struct < Union
|
|
395
|
+
attr_accessor :pack
|
|
396
|
+
|
|
397
|
+
def align(parser) [@members.to_a.map { |m| m.type.align(parser) }.max || 1, (pack || 8)].min end
|
|
398
|
+
|
|
399
|
+
def offsetof(parser, name)
|
|
400
|
+
raise parser, 'undefined structure' if not @members
|
|
401
|
+
update_member_cache(parser) if not fldlist
|
|
402
|
+
return @fldoffset[name] if @fldoffset[name]
|
|
403
|
+
|
|
404
|
+
# this is almost never reached, only for <struct>.offsetof(anonymoussubstructmembername)
|
|
405
|
+
raise parser, 'unknown structure member' if (name.kind_of?(::String) ? !findmember(name) : !@members.include?(name))
|
|
406
|
+
|
|
407
|
+
indirect = true if name.kind_of?(::String) and not @fldlist[name]
|
|
408
|
+
|
|
409
|
+
al = align(parser)
|
|
410
|
+
off = 0
|
|
411
|
+
bit_off = 0
|
|
412
|
+
isz = nil
|
|
413
|
+
|
|
414
|
+
@members.each_with_index { |m, i|
|
|
415
|
+
if bits and b = @bits[i]
|
|
416
|
+
if not isz
|
|
417
|
+
mal = [m.type.align(parser), al].min
|
|
418
|
+
off = (off + mal - 1) / mal * mal
|
|
419
|
+
end
|
|
420
|
+
isz = parser.sizeof(m)
|
|
421
|
+
if b == 0 or (bit_off > 0 and bit_off + b > 8*isz)
|
|
422
|
+
bit_off = 0
|
|
423
|
+
mal = [m.type.align(parser), al].min
|
|
424
|
+
off = (off + isz + mal - 1) / mal * mal
|
|
425
|
+
end
|
|
426
|
+
break if m.name == name or m == name
|
|
427
|
+
bit_off += b
|
|
428
|
+
else
|
|
429
|
+
if isz
|
|
430
|
+
off += isz
|
|
431
|
+
bit_off = 0
|
|
432
|
+
isz = nil
|
|
433
|
+
end
|
|
434
|
+
mal = [m.type.align(parser), al].min
|
|
435
|
+
off = (off + mal - 1) / mal * mal
|
|
436
|
+
if m.name == name or m == name
|
|
437
|
+
break
|
|
438
|
+
elsif indirect and m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
|
439
|
+
off += m.type.untypedef.offsetof(parser, name)
|
|
440
|
+
break
|
|
441
|
+
else
|
|
442
|
+
off += parser.sizeof(m)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
}
|
|
446
|
+
off
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# returns the [bitoffset, bitlength] of the field if it is a bitfield
|
|
450
|
+
# this should be added to the offsetof(field)
|
|
451
|
+
def bitoffsetof(parser, name)
|
|
452
|
+
raise parser, 'undefined structure' if not @members
|
|
453
|
+
update_member_cache(parser) if not fldlist
|
|
454
|
+
return @fldbitoffset[name] if fldbitoffset and @fldbitoffset[name]
|
|
455
|
+
return if @fldlist[name] or @members.include?(name)
|
|
456
|
+
raise parser, 'undefined union' if not @members
|
|
457
|
+
raise parser, 'unknown union member' if not findmember(name)
|
|
458
|
+
|
|
459
|
+
@members.find { |m|
|
|
460
|
+
m.type.untypedef.kind_of? Union and m.type.untypedef.findmember(name)
|
|
461
|
+
}.type.untypedef.bitoffsetof(parser, name)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def parse_members(parser, scope)
|
|
465
|
+
super(parser, scope)
|
|
466
|
+
|
|
467
|
+
if has_attribute 'packed'
|
|
468
|
+
@pack = 1
|
|
469
|
+
elsif p = has_attribute_var('pack')
|
|
470
|
+
@pack = p[/\d+/].to_i
|
|
471
|
+
raise parser, "illegal struct pack(#{p})" if @pack == 0
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# updates the @fldoffset / @fldbitoffset hash storing the offset of members
|
|
476
|
+
def update_member_cache(parser)
|
|
477
|
+
super(parser)
|
|
478
|
+
|
|
479
|
+
@fldoffset = {}
|
|
480
|
+
@fldbitoffset = {} if fldbitoffset
|
|
481
|
+
|
|
482
|
+
al = align(parser)
|
|
483
|
+
off = 0
|
|
484
|
+
bit_off = 0
|
|
485
|
+
isz = nil
|
|
486
|
+
|
|
487
|
+
@members.each_with_index { |m, i|
|
|
488
|
+
if bits and b = @bits[i]
|
|
489
|
+
if not isz
|
|
490
|
+
mal = [m.type.align(parser), al].min
|
|
491
|
+
off = (off + mal - 1) / mal * mal
|
|
492
|
+
end
|
|
493
|
+
isz = parser.sizeof(m)
|
|
494
|
+
if b == 0 or (bit_off > 0 and bit_off + b > 8*isz)
|
|
495
|
+
bit_off = 0
|
|
496
|
+
mal = [m.type.align(parser), al].min
|
|
497
|
+
off = (off + isz + mal - 1) / mal * mal
|
|
498
|
+
end
|
|
499
|
+
if m.name
|
|
500
|
+
@fldoffset[m.name] = off
|
|
501
|
+
@fldbitoffset ||= {}
|
|
502
|
+
@fldbitoffset[m.name] = [bit_off, b]
|
|
503
|
+
end
|
|
504
|
+
bit_off += b
|
|
505
|
+
else
|
|
506
|
+
if isz
|
|
507
|
+
off += isz
|
|
508
|
+
bit_off = 0
|
|
509
|
+
isz = nil
|
|
510
|
+
end
|
|
511
|
+
mal = [m.type.align(parser), al].min
|
|
512
|
+
off = (off + mal - 1) / mal * mal
|
|
513
|
+
@fldoffset[m.name] = off if m.name
|
|
514
|
+
off += parser.sizeof(m)
|
|
515
|
+
end
|
|
516
|
+
}
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
class Enum < Type
|
|
520
|
+
# name => value
|
|
521
|
+
attr_accessor :members
|
|
522
|
+
attr_accessor :name
|
|
523
|
+
attr_accessor :backtrace
|
|
524
|
+
|
|
525
|
+
def align(parser) BaseType.new(:int).align(parser) end
|
|
526
|
+
|
|
527
|
+
def arithmetic?; true end
|
|
528
|
+
def integral?; true end
|
|
529
|
+
def signed?; false end
|
|
530
|
+
|
|
531
|
+
def parse_members(parser, scope)
|
|
532
|
+
val = -1
|
|
533
|
+
@members = {}
|
|
534
|
+
loop do
|
|
535
|
+
raise parser if not tok = parser.skipspaces
|
|
536
|
+
break if tok.type == :punct and tok.raw == '}'
|
|
537
|
+
|
|
538
|
+
name = tok.raw
|
|
539
|
+
raise tok, 'bad enum name' if tok.type != :string or Keyword[name] or (?0..?9).include?(name[0])
|
|
540
|
+
|
|
541
|
+
raise parser if not tok = parser.skipspaces
|
|
542
|
+
if tok.type == :punct and tok.raw == '='
|
|
543
|
+
raise tok || parser if not val = CExpression.parse(parser, scope, false) or not val = val.reduce(parser) or not tok = parser.skipspaces
|
|
544
|
+
else
|
|
545
|
+
val += 1
|
|
546
|
+
end
|
|
547
|
+
raise tok, "enum value #{name} redefinition" if scope.symbol[name] and scope.symbol[name] != val
|
|
548
|
+
@members[name] = val
|
|
549
|
+
scope.symbol[name] = val
|
|
550
|
+
|
|
551
|
+
if tok.type == :punct and tok.raw == '}'
|
|
552
|
+
break
|
|
553
|
+
elsif tok.type == :punct and tok.raw == ','
|
|
554
|
+
else raise tok, '"," or "}" expected'
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
parse_attributes(parser)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
end
|
|
561
|
+
class Pointer < Type
|
|
562
|
+
attr_accessor :type
|
|
563
|
+
|
|
564
|
+
def initialize(type=nil)
|
|
565
|
+
@type = type
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def pointer? ; true ; end
|
|
569
|
+
def arithmetic? ; true ; end
|
|
570
|
+
def base ; @type.base ; end
|
|
571
|
+
def align(parser) BaseType.new(:ptr).align(parser) end
|
|
572
|
+
def pointed ; @type end
|
|
573
|
+
|
|
574
|
+
def ==(o)
|
|
575
|
+
o.class == self.class and o.type == self.type
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
class Array < Pointer
|
|
579
|
+
attr_accessor :length
|
|
580
|
+
|
|
581
|
+
def initialize(type=nil, length=nil)
|
|
582
|
+
super(type)
|
|
583
|
+
@length = length if length
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def align(parser) @type.align(parser) end
|
|
587
|
+
|
|
588
|
+
def parse_initializer(parser, scope)
|
|
589
|
+
raise parser, 'cannot initialize dynamic array' if @length.kind_of? CExpression
|
|
590
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
|
591
|
+
# struct x foo[] = { { 4 }, [12].tutu = 2 };
|
|
592
|
+
ret = []
|
|
593
|
+
if tok = parser.skipspaces and (tok.type != :punct or tok.raw != '}')
|
|
594
|
+
parser.unreadtok tok
|
|
595
|
+
idx = 0
|
|
596
|
+
loop do
|
|
597
|
+
idx = parse_initializer_designator(parser, scope, ret, idx, true)
|
|
598
|
+
raise tok || parser, '"," or "}" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != '}' and tok.raw != ',')
|
|
599
|
+
break if tok.raw == '}'
|
|
600
|
+
# allow int x[] = {1, 2, 3, };
|
|
601
|
+
break if tok = parser.skipspaces and tok.type == :punct and tok.raw == '}'
|
|
602
|
+
parser.unreadtok tok
|
|
603
|
+
raise tok, 'array is smaller than that' if length and idx >= @length
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
ret
|
|
607
|
+
else
|
|
608
|
+
parser.unreadtok tok
|
|
609
|
+
i = super(parser, scope)
|
|
610
|
+
if i.kind_of? CExpression and not i.op and i.rexpr.kind_of? String and @length and i.rexpr.length > @length
|
|
611
|
+
puts tok.exception("initializer is too long (#{i.rexpr.length} for #@length)").message if $VERBOSE
|
|
612
|
+
i.rexpr = i.rexpr[0, @length]
|
|
613
|
+
end
|
|
614
|
+
i
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# this class is a hack to support [1 ... 4] array initializer
|
|
619
|
+
# it stores the effects of subsequent initializers (eg [1 ... 4].toto[48].bla[2 ... 57] = 12)
|
|
620
|
+
# which are later played back on the range
|
|
621
|
+
class AryRecorder
|
|
622
|
+
attr_accessor :log
|
|
623
|
+
def initialize
|
|
624
|
+
@log = []
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def []=(idx, val)
|
|
628
|
+
val = self.class.new if val == []
|
|
629
|
+
@log[idx] = val
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def [](idx)
|
|
633
|
+
@log[idx]
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def playback_idx(i)
|
|
637
|
+
case v = @log[i]
|
|
638
|
+
when self.class; v.playback
|
|
639
|
+
else v
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def playback(ary=[])
|
|
644
|
+
@log.each_with_index { |v, i| ary[i] = playback_idx(i) }
|
|
645
|
+
ary
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
# parses a designator+initializer eg '[12] = 4' or '[42].bla = 16' or '[3 ... 8] = 28'
|
|
650
|
+
def parse_initializer_designator(parser, scope, value, idx, root=true)
|
|
651
|
+
# root = true for 1st invocation (x = { 4 }) => immediate value allowed
|
|
652
|
+
# or false for recursive invocations (x = { .y = 4 }) => need '=' sign before immediate
|
|
653
|
+
if nt = parser.skipspaces and nt.type == :punct and nt.raw == '['
|
|
654
|
+
if not root
|
|
655
|
+
value[idx] ||= [] # AryRecorder may change [] to AryRec.new, can't do v = v[i] ||= []
|
|
656
|
+
value = value[idx]
|
|
657
|
+
end
|
|
658
|
+
raise nt, 'const expected' if not idx = CExpression.parse(parser, scope) or not idx.constant? or not idx = idx.reduce(parser) or not idx.kind_of? ::Integer
|
|
659
|
+
nt = parser.skipspaces
|
|
660
|
+
if nt and nt.type == :punct and nt.raw == '.' # range
|
|
661
|
+
raise nt || parser, '".." expected' if not nt = parser.skipspaces or nt.type != :punct or nt.raw != '.'
|
|
662
|
+
raise nt || parser, '"." expected' if not nt = parser.skipspaces or nt.type != :punct or nt.raw != '.'
|
|
663
|
+
raise nt, 'const expected' if not eidx = CExpression.parse(parser, scope) or not eidx.constant? or not eidx = eidx.reduce(parser) or not eidx.kind_of? ::Integer
|
|
664
|
+
raise nt, 'bad range' if eidx < idx
|
|
665
|
+
nt = parser.skipspaces
|
|
666
|
+
realvalue = value
|
|
667
|
+
value = AryRecorder.new
|
|
668
|
+
end
|
|
669
|
+
raise nt || parser, '"]" expected' if not nt or nt.type != :punct or nt.raw != ']'
|
|
670
|
+
raise nt, 'array is smaller than that' if length and (eidx||idx) >= @length
|
|
671
|
+
@type.untypedef.parse_initializer_designator(parser, scope, value, idx, false)
|
|
672
|
+
if eidx
|
|
673
|
+
(idx..eidx).each { |i| realvalue[i] = value.playback_idx(idx) }
|
|
674
|
+
idx = eidx # next default value = eidx+1 (eg int x[] = { [1 ... 3] = 4, 5 } => x[4] = 5)
|
|
675
|
+
end
|
|
676
|
+
else
|
|
677
|
+
if root
|
|
678
|
+
parser.unreadtok nt
|
|
679
|
+
value[idx] = @type.parse_initializer(parser, scope)
|
|
680
|
+
else
|
|
681
|
+
raise nt || parser, '"=" expected' if not nt or nt.type != :punct or nt.raw != '='
|
|
682
|
+
value[idx] = parse_initializer(parser, scope)
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
idx + 1
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
class Variable
|
|
690
|
+
include Attributes
|
|
691
|
+
include Typed
|
|
692
|
+
|
|
693
|
+
attr_accessor :type
|
|
694
|
+
attr_accessor :initializer # CExpr / Block (for Functions)
|
|
695
|
+
attr_accessor :name
|
|
696
|
+
attr_accessor :storage # auto register static extern typedef
|
|
697
|
+
attr_accessor :backtrace # definition backtrace info (the name token)
|
|
698
|
+
|
|
699
|
+
def initialize(name=nil, type=nil)
|
|
700
|
+
@name, @type = name, type
|
|
701
|
+
end
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
# found in a block's Statements, used to know the initialization order
|
|
705
|
+
# eg { int i; i = 4; struct foo { int k; } toto = {i}; }
|
|
706
|
+
class Declaration
|
|
707
|
+
attr_accessor :var
|
|
708
|
+
def initialize(var)
|
|
709
|
+
@var = var
|
|
710
|
+
end
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
class If < Statement
|
|
714
|
+
attr_accessor :test # expression
|
|
715
|
+
attr_accessor :bthen, :belse # statements
|
|
716
|
+
def initialize(test, bthen, belse=nil)
|
|
717
|
+
@test = test
|
|
718
|
+
@bthen = bthen
|
|
719
|
+
@belse = belse if belse
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
def self.parse(parser, scope, nest)
|
|
723
|
+
tok = nil
|
|
724
|
+
raise tok || self, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
725
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
|
726
|
+
raise tok || self, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
727
|
+
bthen = parser.parse_statement scope, nest
|
|
728
|
+
if tok = parser.skipspaces and tok.type == :string and tok.raw == 'else'
|
|
729
|
+
belse = parser.parse_statement scope, nest
|
|
730
|
+
else
|
|
731
|
+
parser.unreadtok tok
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
new expr, bthen, belse
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
class For < Statement
|
|
738
|
+
attr_accessor :init, :test, :iter # CExpressions, init may be Block
|
|
739
|
+
attr_accessor :body
|
|
740
|
+
def initialize(init, test, iter, body)
|
|
741
|
+
@init, @test, @iter, @body = init, test, iter, body
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def self.parse(parser, scope, nest)
|
|
745
|
+
tok = nil
|
|
746
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
747
|
+
init = forscope = Block.new(scope)
|
|
748
|
+
if not parser.parse_definition(forscope)
|
|
749
|
+
forscope = scope
|
|
750
|
+
init = CExpression.parse(parser, forscope)
|
|
751
|
+
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
|
752
|
+
end
|
|
753
|
+
test = CExpression.parse(parser, forscope)
|
|
754
|
+
raise tok || parser, '";" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ';'
|
|
755
|
+
raise tok, 'bad test expression in for loop' if test and not test.type.arithmetic?
|
|
756
|
+
iter = CExpression.parse(parser, forscope)
|
|
757
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
758
|
+
|
|
759
|
+
new init, test, iter, parser.parse_statement(forscope, nest + [:loop])
|
|
760
|
+
end
|
|
761
|
+
end
|
|
762
|
+
class While < Statement
|
|
763
|
+
attr_accessor :test
|
|
764
|
+
attr_accessor :body
|
|
765
|
+
|
|
766
|
+
def initialize(test, body)
|
|
767
|
+
@test = test
|
|
768
|
+
@body = body
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
def self.parse(parser, scope, nest)
|
|
772
|
+
tok = nil
|
|
773
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
774
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
|
775
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
776
|
+
|
|
777
|
+
new expr, parser.parse_statement(scope, nest + [:loop])
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
class DoWhile < While
|
|
781
|
+
def self.parse(parser, scope, nest)
|
|
782
|
+
body = parser.parse_statement(scope, nest + [:loop])
|
|
783
|
+
tok = nil
|
|
784
|
+
raise tok || parser, '"while" expected' if not tok = parser.skipspaces or tok.type != :string or tok.raw != 'while'
|
|
785
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
786
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.arithmetic?
|
|
787
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
788
|
+
parser.checkstatementend(tok)
|
|
789
|
+
|
|
790
|
+
new expr, body
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
class Switch < Statement
|
|
794
|
+
attr_accessor :test, :body
|
|
795
|
+
|
|
796
|
+
def initialize(test, body)
|
|
797
|
+
@test = test
|
|
798
|
+
@body = body
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def self.parse(parser, scope, nest)
|
|
802
|
+
raise tok || parser, '"(" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '('
|
|
803
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(parser, scope) or not expr.type.integral?
|
|
804
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
805
|
+
|
|
806
|
+
new expr, parser.parse_statement(scope, nest + [:switch])
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
class Continue < Statement
|
|
811
|
+
end
|
|
812
|
+
class Break < Statement
|
|
813
|
+
end
|
|
814
|
+
class Goto < Statement
|
|
815
|
+
attr_accessor :target
|
|
816
|
+
def initialize(target)
|
|
817
|
+
@target = target
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
class Return < Statement
|
|
821
|
+
attr_accessor :value
|
|
822
|
+
def initialize(value)
|
|
823
|
+
@value = value
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
class Label < Statement
|
|
827
|
+
attr_accessor :name
|
|
828
|
+
attr_accessor :statement
|
|
829
|
+
def initialize(name, statement=nil)
|
|
830
|
+
@name, @statement = name, statement
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
class Case < Label
|
|
834
|
+
attr_accessor :expr, :exprup # exprup if range, expr may be 'default'
|
|
835
|
+
def initialize(expr, exprup, statement)
|
|
836
|
+
@expr, @statement = expr, statement
|
|
837
|
+
@exprup = exprup if exprup
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
def self.parse(parser, scope, nest)
|
|
841
|
+
raise parser, 'invalid case' if not expr = CExpression.parse(parser, scope) or not expr.constant? or not expr.type.integral?
|
|
842
|
+
raise tok || parser, '":" or "..." expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ':' and tok.raw != '.')
|
|
843
|
+
if tok.raw == '.'
|
|
844
|
+
raise tok || parser, '".." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
|
845
|
+
raise tok || parser, '"." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
|
846
|
+
raise tok, 'invalid case range' if not exprup = CExpression.parse(parser, scope) or not exprup.constant? or not exprup.type.integral?
|
|
847
|
+
raise tok || parser, '":" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ':'
|
|
848
|
+
end
|
|
849
|
+
body = parser.parse_statement scope, nest
|
|
850
|
+
new expr, exprup, body
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
# inline asm statement
|
|
855
|
+
class Asm < Statement
|
|
856
|
+
include Attributes
|
|
857
|
+
attr_accessor :body # asm source (::String)
|
|
858
|
+
attr_accessor :output, :input, :clobber # I/O, gcc-style (::Array)
|
|
859
|
+
attr_accessor :backtrace # body Token
|
|
860
|
+
attr_accessor :volatile
|
|
861
|
+
|
|
862
|
+
def initialize(body, backtrace, output=nil, input=nil, clobber=nil, volatile=nil)
|
|
863
|
+
@body, @backtrace, @output, @input, @clobber, @volatile = body, backtrace, output, input, clobber, volatile
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def self.parse(parser, scope)
|
|
867
|
+
if tok = parser.skipspaces and tok.type == :string and (tok.raw == 'volatile' or tok.raw == '__volatile__')
|
|
868
|
+
volatile = true
|
|
869
|
+
tok = parser.skipspaces
|
|
870
|
+
end
|
|
871
|
+
if not tok or tok.type != :punct or tok.raw != '('
|
|
872
|
+
# detect MS-style inline asm: "__asm .* __asm .*" or "asm { [\s.]* }"
|
|
873
|
+
ftok = tok
|
|
874
|
+
body = ''
|
|
875
|
+
if tok.type == :punct and tok.raw == '{'
|
|
876
|
+
loop do
|
|
877
|
+
raise ftok, 'unterminated asm block' if not tok = parser.lexer.readtok
|
|
878
|
+
break if tok.type == :punct and tok.raw == '}'
|
|
879
|
+
case tok.type
|
|
880
|
+
when :space; body << ' '
|
|
881
|
+
when :eol; body << "\n"
|
|
882
|
+
when :punct; body << tok.raw
|
|
883
|
+
when :quoted; body << tok.value.inspect # concat adjacent c strings
|
|
884
|
+
when :string
|
|
885
|
+
body << \
|
|
886
|
+
case tok.raw
|
|
887
|
+
when 'asm', '__asm', '__asm__'; "\n"
|
|
888
|
+
when '_emit'; 'db'
|
|
889
|
+
else tok.raw
|
|
890
|
+
end
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
# allow shell-style heredoc: asm <<EOS\n<asm>\nEOS
|
|
894
|
+
elsif tok.type == :punct and tok.raw == '<'
|
|
895
|
+
raise ftok, 'bad asm heredoc' if not tok = parser.lexer.readtok or tok.type != :punct or tok.raw != '<'
|
|
896
|
+
delimiter = parser.lexer.readtok
|
|
897
|
+
if delimiter.type == :punct and delimiter.raw == '-'
|
|
898
|
+
skipspc = true
|
|
899
|
+
delimiter = parser.lexer.readtok
|
|
900
|
+
end
|
|
901
|
+
raise ftok, 'bad asm heredoc delim' if delimiter.type != :string or not tok = parser.lexer.readtok or tok.type != :eol
|
|
902
|
+
nl = true
|
|
903
|
+
loop do
|
|
904
|
+
raise ftok, 'unterminated heredoc' if not tok = parser.lexer.readtok
|
|
905
|
+
break if nl and tok.raw == delimiter.raw
|
|
906
|
+
raw = tok.raw
|
|
907
|
+
raw = "\n" if skipspc and tok.type == :eol
|
|
908
|
+
body << raw
|
|
909
|
+
nl = (tok.type == :eol and (raw[-1] == ?\n or raw[-1] == ?\r))
|
|
910
|
+
end
|
|
911
|
+
# MS single-instr: asm inc eax;
|
|
912
|
+
# also allow asm "foo bar\nbaz";
|
|
913
|
+
else
|
|
914
|
+
parser.lexer.unreadtok tok
|
|
915
|
+
loop do
|
|
916
|
+
break if not tok = parser.lexer.readtok or tok.type == :eol
|
|
917
|
+
case tok.type
|
|
918
|
+
when :space; body << ' '
|
|
919
|
+
when :punct
|
|
920
|
+
case tok.raw
|
|
921
|
+
when '}'
|
|
922
|
+
parser.lexer.unreadtok tok
|
|
923
|
+
break
|
|
924
|
+
else body << tok.raw
|
|
925
|
+
end
|
|
926
|
+
when :quoted; body << (body.empty? ? tok.value : tok.value.inspect) # asm "pop\nret" VS asm add al, 'z'
|
|
927
|
+
when :string
|
|
928
|
+
body << \
|
|
929
|
+
case tok.raw
|
|
930
|
+
when 'asm', '__asm', '__asm__'; "\n"
|
|
931
|
+
when '_emit'; 'db'
|
|
932
|
+
else tok.raw
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
return new(body, ftok, nil, nil, nil, volatile)
|
|
938
|
+
end
|
|
939
|
+
raise tok || parser, '"(" expected' if not tok or tok.type != :punct or tok.raw != '('
|
|
940
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
|
941
|
+
body = tok
|
|
942
|
+
ret = new body.value, body
|
|
943
|
+
tok = parser.skipspaces
|
|
944
|
+
raise tok || parser, '":" or ")" expected' if not tok or tok.type != :punct or (tok.raw != ':' and tok.raw != ')')
|
|
945
|
+
|
|
946
|
+
if tok.raw == ':'
|
|
947
|
+
ret.output = []
|
|
948
|
+
raise parser if not tok = parser.skipspaces
|
|
949
|
+
while tok.type == :quoted
|
|
950
|
+
type = tok.value
|
|
951
|
+
raise tok, 'expr expected' if not var = CExpression.parse_value(parser, scope)
|
|
952
|
+
ret.output << [type, var]
|
|
953
|
+
raise tok || parser, '":" or "," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')' and tok.raw != ':')
|
|
954
|
+
break if tok.raw == ':' or tok.raw == ')'
|
|
955
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
if tok.raw == ':'
|
|
959
|
+
ret.input = []
|
|
960
|
+
raise parser if not tok = parser.skipspaces
|
|
961
|
+
while tok.type == :quoted
|
|
962
|
+
type = tok.value
|
|
963
|
+
raise tok, 'expr expected' if not var = CExpression.parse_value(parser, scope)
|
|
964
|
+
ret.input << [type, var]
|
|
965
|
+
raise tok || parser, '":" or "," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')' and tok.raw != ':')
|
|
966
|
+
break if tok.raw == ':' or tok.raw == ')'
|
|
967
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
|
968
|
+
end
|
|
969
|
+
end
|
|
970
|
+
if tok.raw == ':'
|
|
971
|
+
ret.clobber = []
|
|
972
|
+
raise parser if not tok = parser.skipspaces
|
|
973
|
+
while tok.type == :quoted
|
|
974
|
+
ret.clobber << tok.value
|
|
975
|
+
raise tok || parser, '"," or ")" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ',' and tok.raw != ')')
|
|
976
|
+
break if tok.raw == ')'
|
|
977
|
+
raise tok || parser, 'qstring expected' if not tok = parser.skipspaces or tok.type != :quoted
|
|
978
|
+
end
|
|
979
|
+
end
|
|
980
|
+
raise tok || parser, '")" expected' if not tok or tok.type != :punct or tok.raw != ')'
|
|
981
|
+
ret.parse_attributes(parser)
|
|
982
|
+
parser.checkstatementend(tok)
|
|
983
|
+
ret
|
|
984
|
+
end
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
class CExpression < Statement
|
|
988
|
+
include Typed
|
|
989
|
+
|
|
990
|
+
# may be :,, :., :'->', :funcall (function, [arglist]), :[] (array indexing), nil (cast)
|
|
991
|
+
attr_accessor :op
|
|
992
|
+
# nil/CExpr/Variable/Label/::String( = :quoted/struct member name)/::Integer/::Float/Block
|
|
993
|
+
attr_accessor :lexpr, :rexpr
|
|
994
|
+
# a Type
|
|
995
|
+
attr_accessor :type
|
|
996
|
+
def initialize(l, o, r, t)
|
|
997
|
+
raise "invalid CExpr #{[l, o, r, t].inspect}" if (o and not o.kind_of? ::Symbol) or not t.kind_of? Type
|
|
998
|
+
@lexpr, @op, @rexpr, @type = l, o, r, t
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
# overwrites @lexpr @op @rexpr @type from the arg
|
|
1002
|
+
def replace(o)
|
|
1003
|
+
@lexpr, @op, @rexpr, @type = o.lexpr, o.op, o.rexpr, o.type
|
|
1004
|
+
self
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
# deep copy of the object
|
|
1008
|
+
# recurses only within CExpressions, anything else is copied by reference
|
|
1009
|
+
def deep_dup
|
|
1010
|
+
n = dup
|
|
1011
|
+
n.lexpr = n.lexpr.deep_dup if n.lexpr.kind_of? CExpression
|
|
1012
|
+
n.rexpr = n.rexpr.deep_dup if n.rexpr.kind_of? CExpression
|
|
1013
|
+
n.rexpr = n.rexpr.map { |e| e.kind_of?(CExpression) ? e.deep_dup : e } if n.rexpr.kind_of? ::Array
|
|
1014
|
+
n
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
# recursive constructor with automatic type inference
|
|
1018
|
+
# e.g. CExpression[foo, :+, [:*, bar]]
|
|
1019
|
+
# assumes root args are correctly typed (eg *foo => foo must be a pointer)
|
|
1020
|
+
# take care to use [int] with immediates, e.g. CExpression[foo, :+, [2]]
|
|
1021
|
+
# CExpr[some_cexpr] returns some_cexpr
|
|
1022
|
+
def self.[](*args)
|
|
1023
|
+
# sub-arrays in args are to be passed to self.[] recursively (syntaxic sugar)
|
|
1024
|
+
splat = lambda { |e| e.kind_of?(::Array) ? self[*e] : e }
|
|
1025
|
+
|
|
1026
|
+
args.shift while args.first == nil # CExpr[nil, :&, bla] => CExpr[:&, bla]
|
|
1027
|
+
|
|
1028
|
+
case args.length
|
|
1029
|
+
when 4
|
|
1030
|
+
op = args[1]
|
|
1031
|
+
if op == :funcall or op == :'?:'
|
|
1032
|
+
x2 = args[2].map { |a| splat[a] } if args[2]
|
|
1033
|
+
else
|
|
1034
|
+
x2 = splat[args[2]]
|
|
1035
|
+
end
|
|
1036
|
+
new(splat[args[0]], op, x2, args[3])
|
|
1037
|
+
when 3
|
|
1038
|
+
op = args[1]
|
|
1039
|
+
x1 = splat[args[0]]
|
|
1040
|
+
if op == :funcall or op == :'?:'
|
|
1041
|
+
x2 = args[2].map { |a| splat[a] } if args[2]
|
|
1042
|
+
else
|
|
1043
|
+
x2 = splat[args[2]]
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
case op
|
|
1047
|
+
when :funcall
|
|
1048
|
+
rt = x1.type.untypedef
|
|
1049
|
+
rt = rt.type.untypedef if rt.pointer?
|
|
1050
|
+
new(x1, op, x2, rt.type)
|
|
1051
|
+
when :[]; new(x1, op, x2, x1.type.untypedef.type)
|
|
1052
|
+
when :+; new(x1, op, x2, (x2.type.pointer? ? x2.type : x1.type))
|
|
1053
|
+
when :-; new(x1, op, x2, ((x1.type.pointer? and x2.type.pointer?) ? BaseType.new(:int) : x2.type.pointer? ? x2.type : x1.type))
|
|
1054
|
+
when :'&&', :'||', :==, :'!=', :>, :<, :<=, :>=; new(x1, op, x2, BaseType.new(:int))
|
|
1055
|
+
when :'.', :'->'
|
|
1056
|
+
t = x1.type.untypedef
|
|
1057
|
+
t = t.type.untypedef if op == :'->' and x1.type.pointer?
|
|
1058
|
+
raise "parse error: #{t} has no member #{x2}" if not t.kind_of? Union or not m = t.findmember(x2)
|
|
1059
|
+
new(x1, op, x2, m.type)
|
|
1060
|
+
when :'?:'; new(x1, op, x2, x2[0].type)
|
|
1061
|
+
when :','; new(x1, op, x2, x2.type)
|
|
1062
|
+
else new(x1, op, x2, x1.type)
|
|
1063
|
+
end
|
|
1064
|
+
when 2
|
|
1065
|
+
x0 = splat[args[0]]
|
|
1066
|
+
x1 = splat[args[1]]
|
|
1067
|
+
x0, x1 = x1, x0 if x0.kind_of? Type
|
|
1068
|
+
if x1.kind_of? Type; new(nil, nil, x0, x1) # (cast)r
|
|
1069
|
+
elsif x0 == :*; new(nil, x0, x1, x1.type.untypedef.type) # *r
|
|
1070
|
+
elsif x0 == :& and x1.kind_of? CExpression and x1.type.kind_of? C::Array; new(nil, nil, x1, Pointer.new(x1.type.type))
|
|
1071
|
+
elsif x0 == :&; new(nil, x0, x1, Pointer.new(x1.type)) # &r
|
|
1072
|
+
elsif x0 == :'!'; new(nil, x0, x1, BaseType.new(:int)) # &r
|
|
1073
|
+
elsif x1.kind_of? ::Symbol; new(x0, x1, nil, x0.type) # l++
|
|
1074
|
+
else new(nil, x0, x1, x1.type) # +r
|
|
1075
|
+
end
|
|
1076
|
+
when 1
|
|
1077
|
+
x = splat[args[0]]
|
|
1078
|
+
case x
|
|
1079
|
+
when CExpression; x
|
|
1080
|
+
when ::Integer; new(nil, nil, x, BaseType.new(:int)) # XXX range => __int64 ?
|
|
1081
|
+
when ::Float; new(nil, nil, x, BaseType.new(:double))
|
|
1082
|
+
when ::String; new(nil, nil, x, Pointer.new(BaseType.new(:char)))
|
|
1083
|
+
else new(nil, nil, x, x.type)
|
|
1084
|
+
end
|
|
1085
|
+
else raise "parse error CExpr[*#{args.inspect}]"
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
class Parser
|
|
1092
|
+
# creates a new CParser, parses all top-level statements
|
|
1093
|
+
def self.parse(text)
|
|
1094
|
+
new.parse text
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
# parses the current lexer content (or the text arg) for toplevel definitions
|
|
1098
|
+
def parse(text=nil, filename='<unk>', lineno=1)
|
|
1099
|
+
@lexer.feed text, filename, lineno if text
|
|
1100
|
+
nil while not @lexer.eos? and (parse_definition(@toplevel) or parse_toplevel_statement(@toplevel))
|
|
1101
|
+
raise @lexer.readtok || self, 'invalid definition' if not @lexer.eos?
|
|
1102
|
+
sanity_checks
|
|
1103
|
+
self
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
# parses a C file
|
|
1107
|
+
def parse_file(file)
|
|
1108
|
+
parse(File.read(file), file)
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
attr_accessor :lexer, :toplevel, :typesize, :pragma_pack
|
|
1112
|
+
attr_accessor :endianness
|
|
1113
|
+
attr_accessor :allow_bad_c
|
|
1114
|
+
# allowed arguments: ExeFormat, CPU, Preprocessor, Symbol (for the data model)
|
|
1115
|
+
def initialize(*args)
|
|
1116
|
+
model = args.grep(Symbol).first || :ilp32
|
|
1117
|
+
lexer = args.grep(Preprocessor).first || Preprocessor.new
|
|
1118
|
+
exe = args.grep(ExeFormat).first
|
|
1119
|
+
cpu = args.grep(CPU).first
|
|
1120
|
+
cpu ||= exe.cpu if exe
|
|
1121
|
+
@lexer = lexer
|
|
1122
|
+
@prev_pragma_callback = @lexer.pragma_callback
|
|
1123
|
+
@lexer.pragma_callback = lambda { |tok| parse_pragma_callback(tok) }
|
|
1124
|
+
@toplevel = Block.new(nil)
|
|
1125
|
+
@unreadtoks = []
|
|
1126
|
+
@endianness = cpu ? cpu.endianness : :big
|
|
1127
|
+
@typesize = { :void => 1, :__int8 => 1, :__int16 => 2, :__int32 => 4, :__int64 => 8,
|
|
1128
|
+
:char => 1, :float => 4, :double => 8, :longdouble => 12 }
|
|
1129
|
+
send model
|
|
1130
|
+
cpu.tune_cparser(self) if cpu
|
|
1131
|
+
exe.tune_cparser(self) if exe
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
def ilp16
|
|
1135
|
+
@typesize.update :short => 2, :ptr => 2,
|
|
1136
|
+
:int => 2, :long => 4, :longlong => 4
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
def lp32
|
|
1140
|
+
@typesize.update :short => 2, :ptr => 4,
|
|
1141
|
+
:int => 2, :long => 4, :longlong => 8
|
|
1142
|
+
end
|
|
1143
|
+
def ilp32
|
|
1144
|
+
@typesize.update :short => 2, :ptr => 4,
|
|
1145
|
+
:int => 4, :long => 4, :longlong => 8
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
def llp64
|
|
1149
|
+
@typesize.update :short => 2, :ptr => 8,
|
|
1150
|
+
:int => 4, :long => 4, :longlong => 8
|
|
1151
|
+
end
|
|
1152
|
+
def lp64
|
|
1153
|
+
@typesize.update :short => 2, :ptr => 8,
|
|
1154
|
+
:int => 4, :long => 8, :longlong => 8
|
|
1155
|
+
end
|
|
1156
|
+
def ilp64
|
|
1157
|
+
@typesize.update :short => 2, :ptr => 8,
|
|
1158
|
+
:int => 8, :long => 8, :longlong => 8
|
|
1159
|
+
end
|
|
1160
|
+
|
|
1161
|
+
def parse_pragma_callback(otok)
|
|
1162
|
+
case otok.raw
|
|
1163
|
+
when 'pack'
|
|
1164
|
+
nil while lp = @lexer.readtok and lp.type == :space
|
|
1165
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
|
1166
|
+
if not rp or rp.type != :punct or rp.raw != ')'
|
|
1167
|
+
v1 = rp
|
|
1168
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
|
1169
|
+
end
|
|
1170
|
+
if rp and rp.type == :punct and rp.raw == ','
|
|
1171
|
+
nil while v2 = @lexer.readtok and v2.type == :space
|
|
1172
|
+
nil while rp = @lexer.readtok and rp.type == :space
|
|
1173
|
+
end
|
|
1174
|
+
raise otok if not rp or lp.type != :punct or rp.type != :punct or lp.raw != '(' or rp.raw != ')'
|
|
1175
|
+
raise otok if (v1 and v1.type != :string) or (v2 and (v2.type != :string or v2.raw =~ /[^\d]/))
|
|
1176
|
+
if not v1
|
|
1177
|
+
@pragma_pack = nil
|
|
1178
|
+
elsif v1.raw == 'push'
|
|
1179
|
+
@pragma_pack_stack ||= []
|
|
1180
|
+
@pragma_pack_stack << pragma_pack
|
|
1181
|
+
@pragma_pack = v2.raw.to_i if v2
|
|
1182
|
+
raise v2, 'bad pack value' if pragma_pack == 0
|
|
1183
|
+
elsif v1.raw == 'pop'
|
|
1184
|
+
@pragma_pack_stack ||= []
|
|
1185
|
+
raise v1, 'pack stack empty' if @pragma_pack_stack.empty?
|
|
1186
|
+
@pragma_pack = @pragma_pack_stack.pop
|
|
1187
|
+
@pragma_pack = v2.raw.to_i if v2 and v2.raw # #pragma pack(pop, 4) => pop stack, but use 4 as pack value (imho)
|
|
1188
|
+
raise v2, 'bad pack value' if @pragma_pack == 0
|
|
1189
|
+
elsif v1.raw =~ /^\d+$/
|
|
1190
|
+
raise v2, '2nd arg unexpected' if v2
|
|
1191
|
+
@pragma_pack = v1.raw.to_i
|
|
1192
|
+
raise v1, 'bad pack value' if @pragma_pack == 0
|
|
1193
|
+
else raise otok
|
|
1194
|
+
end
|
|
1195
|
+
# the caller checks for :eol
|
|
1196
|
+
when 'warning'
|
|
1197
|
+
if $DEBUG
|
|
1198
|
+
@prev_pragma_callback[otok]
|
|
1199
|
+
else
|
|
1200
|
+
# silent discard
|
|
1201
|
+
nil while tok = @lexer.readtok_nopp and tok.type != :eol
|
|
1202
|
+
@lexer.unreadtok tok
|
|
1203
|
+
end
|
|
1204
|
+
when 'prepare_visualstudio'
|
|
1205
|
+
prepare_visualstudio
|
|
1206
|
+
when 'prepare_gcc'
|
|
1207
|
+
prepare_gcc
|
|
1208
|
+
when 'data_model' # XXX use carefully, should be the very first thing parsed
|
|
1209
|
+
nil while lp = @lexer.readtok and lp.type == :space
|
|
1210
|
+
if lp.type != :string or lp.raw !~ /^s?[il]?lp(16|32|64)$/ or not respond_to? lp.raw
|
|
1211
|
+
raise lp, "invalid data model (use lp32/lp64/llp64/ilp64)"
|
|
1212
|
+
else
|
|
1213
|
+
send lp.raw
|
|
1214
|
+
end
|
|
1215
|
+
else @prev_pragma_callback[otok]
|
|
1216
|
+
end
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
def prepare_visualstudio
|
|
1220
|
+
@lexer.define_weak('_WIN32')
|
|
1221
|
+
@lexer.define_weak('_WIN32_WINNT', 0x500)
|
|
1222
|
+
@lexer.define_weak('_INTEGRAL_MAX_BITS', 64)
|
|
1223
|
+
@lexer.define_weak('__w64')
|
|
1224
|
+
@lexer.define_weak('_cdecl', '__cdecl') # typo ? seen in winreg.h
|
|
1225
|
+
@lexer.define_weak('_fastcall', '__fastcall') # typo ? seen in ntddk.h
|
|
1226
|
+
@lexer.define_weak('_MSC_VER', 1300) # handle '#pragma once' and _declspec(noreturn)
|
|
1227
|
+
@lexer.define_weak('__forceinline', '__inline')
|
|
1228
|
+
@lexer.define_weak('__ptr32') # needed with msc_ver 1300, don't understand their use
|
|
1229
|
+
@lexer.define_weak('__ptr64')
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
def prepare_gcc
|
|
1233
|
+
@lexer.define_weak('__GNUC__', 2) # otherwise __attribute__ is defined to void..
|
|
1234
|
+
@lexer.define_weak('__STDC__')
|
|
1235
|
+
@lexer.define_weak('__const', 'const')
|
|
1236
|
+
@lexer.define_weak('__signed', 'signed')
|
|
1237
|
+
@lexer.define_weak('__signed__', 'signed')
|
|
1238
|
+
@lexer.define_weak('__volatile', 'volatile')
|
|
1239
|
+
if not @lexer.definition['__builtin_constant_p']
|
|
1240
|
+
# magic macro to check if its arg is an immediate value
|
|
1241
|
+
@lexer.define_weak('__builtin_constant_p', '0')
|
|
1242
|
+
@lexer.definition['__builtin_constant_p'].args = [Preprocessor::Token.new([])]
|
|
1243
|
+
end
|
|
1244
|
+
@lexer.nodefine_strong('alloca') # TODO __builtin_alloca
|
|
1245
|
+
@lexer.hooked_include['stddef.h'] = <<EOH
|
|
1246
|
+
/* simplified, define all at first invocation. may break things... */
|
|
1247
|
+
#undef __need_ptrdiff_t
|
|
1248
|
+
#undef __need_size_t
|
|
1249
|
+
#undef __need_wint_t
|
|
1250
|
+
#undef __need_wchar_t
|
|
1251
|
+
#undef __need_NULL
|
|
1252
|
+
#undef NULL
|
|
1253
|
+
#if !defined (_STDDEF_H)
|
|
1254
|
+
#define _STDDEF_H
|
|
1255
|
+
#define __PTRDIFF_TYPE__ long int
|
|
1256
|
+
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
|
1257
|
+
#define __SIZE_TYPE__ long unsigned int
|
|
1258
|
+
typedef __SIZE_TYPE__ size_t;
|
|
1259
|
+
#define __WINT_TYPE__ unsigned int
|
|
1260
|
+
typedef __WINT_TYPE__ wint_t;
|
|
1261
|
+
#define __WCHAR_TYPE__ int
|
|
1262
|
+
typedef __WCHAR_TYPE__ wchar_t;
|
|
1263
|
+
#define NULL 0
|
|
1264
|
+
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
|
|
1265
|
+
#endif
|
|
1266
|
+
EOH
|
|
1267
|
+
# TODO va_args
|
|
1268
|
+
@lexer.hooked_include['stdarg.h'] = <<EOH
|
|
1269
|
+
// TODO
|
|
1270
|
+
typedef void* __gnuc_va_list;
|
|
1271
|
+
/*
|
|
1272
|
+
typedef void* va_list;
|
|
1273
|
+
#define va_start(v, l)
|
|
1274
|
+
#define va_end(v)
|
|
1275
|
+
#define va_arg(v, l)
|
|
1276
|
+
#define va_copy(d, s)
|
|
1277
|
+
*/
|
|
1278
|
+
EOH
|
|
1279
|
+
@lexer.hooked_include['limits.h'] = <<EOH
|
|
1280
|
+
#define CHAR_BIT 8
|
|
1281
|
+
#define SCHAR_MIN (-128)
|
|
1282
|
+
#define SCHAR_MAX 127
|
|
1283
|
+
#define UCHAR_MAX 255
|
|
1284
|
+
#ifdef __CHAR_UNSIGNED__
|
|
1285
|
+
# define CHAR_MIN 0
|
|
1286
|
+
# define CHAR_MAX UCHAR_MAX
|
|
1287
|
+
#else
|
|
1288
|
+
# define CHAR_MIN SCHAR_MIN
|
|
1289
|
+
# define CHAR_MAX SCHAR_MAX
|
|
1290
|
+
#endif
|
|
1291
|
+
#define UINT_MAX #{(1 << (8*@typesize[:int]))-1}U
|
|
1292
|
+
#define INT_MAX (UINT_MAX >> 1)
|
|
1293
|
+
#define INT_MIN (-INT_MAX - 1)
|
|
1294
|
+
#define ULONG_MAX #{(1 << (8*@typesize[:long]))-1}UL
|
|
1295
|
+
#define LONG_MAX (ULONG_MAX >> 1L)
|
|
1296
|
+
#define LONG_MIN (-LONG_MAX - 1L)
|
|
1297
|
+
EOH
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
# C sanity checks
|
|
1301
|
+
def sanity_checks
|
|
1302
|
+
return if not $VERBOSE
|
|
1303
|
+
# TODO
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
# checks that the types are compatible (variable predeclaration, function argument..)
|
|
1307
|
+
# strict = false for func call/assignment (eg char compatible with int -- but int is incompatible with char)
|
|
1308
|
+
# output warnings only
|
|
1309
|
+
def check_compatible_type(tok, oldtype, newtype, strict = false, checked = [])
|
|
1310
|
+
return if not $VERBOSE
|
|
1311
|
+
oldtype = oldtype.untypedef
|
|
1312
|
+
newtype = newtype.untypedef
|
|
1313
|
+
oldtype = BaseType.new(:int) if oldtype.kind_of? Enum
|
|
1314
|
+
newtype = BaseType.new(:int) if newtype.kind_of? Enum
|
|
1315
|
+
|
|
1316
|
+
puts tok.exception('type qualifier mismatch').message if oldtype.qualifier.to_a.uniq.length > newtype.qualifier.to_a.uniq.length
|
|
1317
|
+
|
|
1318
|
+
# avoid infinite recursion
|
|
1319
|
+
return if checked.include? oldtype
|
|
1320
|
+
checked = checked + [oldtype]
|
|
1321
|
+
|
|
1322
|
+
begin
|
|
1323
|
+
case newtype
|
|
1324
|
+
when Function
|
|
1325
|
+
raise tok, 'type error' if not oldtype.kind_of? Function
|
|
1326
|
+
check_compatible_type tok, oldtype.type, newtype.type, strict, checked
|
|
1327
|
+
if oldtype.args and newtype.args
|
|
1328
|
+
if oldtype.args.length != newtype.args.length or
|
|
1329
|
+
oldtype.varargs != newtype.varargs
|
|
1330
|
+
raise tok, 'type error'
|
|
1331
|
+
end
|
|
1332
|
+
oldtype.args.zip(newtype.args) { |oa, na|
|
|
1333
|
+
# begin ; rescue ParseError: raise $!.message + "in parameter #{oa.name}" end
|
|
1334
|
+
check_compatible_type tok, oa.type, na.type, strict, checked
|
|
1335
|
+
}
|
|
1336
|
+
end
|
|
1337
|
+
when Pointer
|
|
1338
|
+
if oldtype.kind_of? BaseType and oldtype.integral?
|
|
1339
|
+
puts tok.exception('making pointer from integer without a cast').message
|
|
1340
|
+
return
|
|
1341
|
+
end
|
|
1342
|
+
raise tok, 'type error' if not oldtype.kind_of? Pointer
|
|
1343
|
+
hasvoid = true if (t = newtype.type.untypedef).kind_of? BaseType and t.name == :void
|
|
1344
|
+
hasvoid = true if (t = oldtype.type.untypedef).kind_of? BaseType and t.name == :void # struct foo *f = NULL;
|
|
1345
|
+
if strict and not hasvoid
|
|
1346
|
+
check_compatible_type tok, oldtype.type, newtype.type, strict, checked
|
|
1347
|
+
end
|
|
1348
|
+
when Union
|
|
1349
|
+
raise tok, 'type error' if not oldtype.class == newtype.class
|
|
1350
|
+
if oldtype.members and newtype.members
|
|
1351
|
+
if oldtype.members.length != newtype.members.length
|
|
1352
|
+
raise tok, 'bad member count'
|
|
1353
|
+
end
|
|
1354
|
+
oldtype.members.zip(newtype.members) { |om, nm|
|
|
1355
|
+
# raise tok if om.name and nm.name and om.name != nm.name # don't care
|
|
1356
|
+
check_compatible_type tok, om.type, nm.type, strict, checked
|
|
1357
|
+
}
|
|
1358
|
+
end
|
|
1359
|
+
when BaseType
|
|
1360
|
+
raise tok, 'type error' if not oldtype.kind_of? BaseType
|
|
1361
|
+
if strict
|
|
1362
|
+
if oldtype.name != newtype.name or
|
|
1363
|
+
oldtype.specifier != newtype.specifier
|
|
1364
|
+
raise tok, 'type error'
|
|
1365
|
+
end
|
|
1366
|
+
else
|
|
1367
|
+
raise tok, 'type error' if @typesize[newtype.name] == 0 and @typesize[oldtype.name] > 0
|
|
1368
|
+
puts tok.exception('type size mismatch, may lose bits').message if @typesize[oldtype.name] > @typesize[newtype.name]
|
|
1369
|
+
puts tok.exception('sign mismatch').message if oldtype.specifier != newtype.specifier and @typesize[newtype.name] == @typesize[oldtype.name]
|
|
1370
|
+
end
|
|
1371
|
+
end
|
|
1372
|
+
rescue ParseError
|
|
1373
|
+
raise $! if checked.length != 1 # bubble up
|
|
1374
|
+
oname = (oldtype.to_s rescue oldtype.class.name)
|
|
1375
|
+
nname = (newtype.to_s rescue newtype.class.name)
|
|
1376
|
+
puts $!.message + " incompatible type #{oname} to #{nname}"
|
|
1377
|
+
end
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1380
|
+
# allows 'raise self'
|
|
1381
|
+
def exception(msg='EOF unexpected')
|
|
1382
|
+
@lexer.exception msg
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1385
|
+
# reads a token, convert 'L"foo"' to a :quoted
|
|
1386
|
+
def readtok_longstr
|
|
1387
|
+
if t = @lexer.readtok and t.type == :string and t.raw == 'L' and
|
|
1388
|
+
nt = @lexer.readtok and nt.type == :quoted and nt.raw[0] == ?"
|
|
1389
|
+
nt.raw[0, 0] = 'L'
|
|
1390
|
+
nt
|
|
1391
|
+
elsif t and t.type == :punct and t.raw == '/' and
|
|
1392
|
+
# nt has not been read
|
|
1393
|
+
nt = @lexer.readtok and nt.type == :punct and nt.raw == '/'
|
|
1394
|
+
# windows.h has a #define some_type_name /##/, and VS interprets this as a comment..
|
|
1395
|
+
puts @lexer.exception('#defined //').message if $VERBOSE
|
|
1396
|
+
t = @lexer.readtok while t and t.type != :eol
|
|
1397
|
+
t
|
|
1398
|
+
else
|
|
1399
|
+
@lexer.unreadtok nt
|
|
1400
|
+
t
|
|
1401
|
+
end
|
|
1402
|
+
end
|
|
1403
|
+
private :readtok_longstr
|
|
1404
|
+
|
|
1405
|
+
# reads a token from self.lexer
|
|
1406
|
+
# concatenates strings, merges spaces/eol to ' ', handles wchar strings, allows $@_ in :string
|
|
1407
|
+
def readtok
|
|
1408
|
+
if not t = @unreadtoks.pop
|
|
1409
|
+
return if not t = readtok_longstr
|
|
1410
|
+
case t.type
|
|
1411
|
+
when :space, :eol
|
|
1412
|
+
# merge consecutive :space/:eol
|
|
1413
|
+
t = t.dup
|
|
1414
|
+
t.type = :space
|
|
1415
|
+
t.raw = ' '
|
|
1416
|
+
nil while nt = @lexer.readtok and (nt.type == :eol or nt.type == :space)
|
|
1417
|
+
@lexer.unreadtok nt
|
|
1418
|
+
|
|
1419
|
+
when :quoted
|
|
1420
|
+
# merge consecutive :quoted
|
|
1421
|
+
t = t.dup
|
|
1422
|
+
while nt = readtok_longstr
|
|
1423
|
+
case nt.type
|
|
1424
|
+
when :quoted
|
|
1425
|
+
if t.raw[0] == ?" and nt.raw[0, 2] == 'L"'
|
|
1426
|
+
# ensure wide prefix is set
|
|
1427
|
+
t.raw[0, 0] = 'L'
|
|
1428
|
+
end
|
|
1429
|
+
t.raw << ' ' << nt.raw
|
|
1430
|
+
t.value << nt.value
|
|
1431
|
+
when :space, :eol
|
|
1432
|
+
else break
|
|
1433
|
+
end
|
|
1434
|
+
end
|
|
1435
|
+
@lexer.unreadtok nt
|
|
1436
|
+
else
|
|
1437
|
+
if (t.type == :punct and (t.raw == '_' or t.raw == '@' or t.raw == '$')) or t.type == :string
|
|
1438
|
+
t = t.dup
|
|
1439
|
+
t.type = :string
|
|
1440
|
+
nt = nil
|
|
1441
|
+
t.raw << nt.raw while nt = @lexer.readtok and ((nt.type == :punct and (nt.raw == '_' or nt.raw == '@' or nt.raw == '$')) or nt.type == :string)
|
|
1442
|
+
@lexer.unreadtok nt
|
|
1443
|
+
end
|
|
1444
|
+
end
|
|
1445
|
+
end
|
|
1446
|
+
t
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
def eos?
|
|
1450
|
+
@unreadtoks.empty? and @lexer.eos?
|
|
1451
|
+
end
|
|
1452
|
+
|
|
1453
|
+
def unreadtok(tok)
|
|
1454
|
+
@unreadtoks << tok if tok
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1457
|
+
# returns the next non-space/non-eol token
|
|
1458
|
+
def skipspaces
|
|
1459
|
+
nil while t = readtok and t.type == :space
|
|
1460
|
+
t
|
|
1461
|
+
end
|
|
1462
|
+
|
|
1463
|
+
# checks that we are at the end of a statement, ie an ';' character (consumed), or a '}' (not consumed)
|
|
1464
|
+
# otherwise, raise either the given token or self.
|
|
1465
|
+
def checkstatementend(tok=nil)
|
|
1466
|
+
raise tok || self, '";" expected' if not tok = skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != '}')
|
|
1467
|
+
unreadtok tok if tok.raw == '}'
|
|
1468
|
+
end
|
|
1469
|
+
|
|
1470
|
+
# returns the size of a type in bytes
|
|
1471
|
+
def sizeof(var, type=nil)
|
|
1472
|
+
var, type = nil, var if var.kind_of? Type and not type
|
|
1473
|
+
type ||= var.type
|
|
1474
|
+
# XXX double-check class apparition order ('when' checks inheritance)
|
|
1475
|
+
case type
|
|
1476
|
+
when Array
|
|
1477
|
+
case type.length
|
|
1478
|
+
when nil
|
|
1479
|
+
if var.kind_of? CExpression and not var.lexpr and not var.op and var.rexpr.kind_of? Variable
|
|
1480
|
+
var = var.rexpr
|
|
1481
|
+
end
|
|
1482
|
+
raise self, 'unknown array size' if not var.kind_of? Variable or not var.initializer
|
|
1483
|
+
init = var.initializer
|
|
1484
|
+
init = init.rexpr if init.kind_of? C::CExpression and not init.op and init.rexpr.kind_of? ::String
|
|
1485
|
+
case init
|
|
1486
|
+
when ::String; sizeof(nil, type.type) * (init.length + 1)
|
|
1487
|
+
when ::Array
|
|
1488
|
+
v = init.compact.first
|
|
1489
|
+
v ? (sizeof(nil, type.type) * init.length) : 0
|
|
1490
|
+
else sizeof(init)
|
|
1491
|
+
end
|
|
1492
|
+
when ::Integer; type.length * sizeof(type.type)
|
|
1493
|
+
when CExpression
|
|
1494
|
+
len = type.length.reduce(self)
|
|
1495
|
+
raise self, 'unknown array size' if not len.kind_of? ::Integer
|
|
1496
|
+
len * sizeof(type)
|
|
1497
|
+
else raise self, 'unknown array size'
|
|
1498
|
+
end
|
|
1499
|
+
when Pointer
|
|
1500
|
+
if var.kind_of? CExpression and not var.op and var.rexpr.kind_of? ::String
|
|
1501
|
+
# sizeof("lolz") => 5
|
|
1502
|
+
sizeof(nil, type.type) * (var.rexpr.length + 1)
|
|
1503
|
+
else
|
|
1504
|
+
@typesize[:ptr]
|
|
1505
|
+
end
|
|
1506
|
+
when Function
|
|
1507
|
+
# raise
|
|
1508
|
+
1 # gcc
|
|
1509
|
+
when BaseType
|
|
1510
|
+
@typesize[type.name]
|
|
1511
|
+
when Enum
|
|
1512
|
+
@typesize[:int]
|
|
1513
|
+
when Struct
|
|
1514
|
+
raise self, "unknown structure size #{type.name}" if not type.members
|
|
1515
|
+
al = type.align(self)
|
|
1516
|
+
lm = type.members.last
|
|
1517
|
+
lm ? (type.offsetof(self, lm) + sizeof(lm) + al - 1) / al * al : 0
|
|
1518
|
+
when Union
|
|
1519
|
+
raise self, "unknown structure size #{type.name}" if not type.members
|
|
1520
|
+
type.members.map { |m| sizeof(m) }.max || 0
|
|
1521
|
+
when TypeDef
|
|
1522
|
+
sizeof(var, type.type)
|
|
1523
|
+
end
|
|
1524
|
+
end
|
|
1525
|
+
|
|
1526
|
+
# parses variable/function definition/declaration/initialization
|
|
1527
|
+
# populates scope.symbols and scope.struct
|
|
1528
|
+
# raises on redefinitions
|
|
1529
|
+
# returns false if no definition found
|
|
1530
|
+
def parse_definition(scope)
|
|
1531
|
+
return false if not basetype = Variable.parse_type(self, scope, true)
|
|
1532
|
+
|
|
1533
|
+
# check struct predeclaration
|
|
1534
|
+
tok = skipspaces
|
|
1535
|
+
if tok and tok.type == :punct and tok.raw == ';' and basetype.type and
|
|
1536
|
+
(basetype.type.kind_of? Union or basetype.type.kind_of? Enum)
|
|
1537
|
+
return true
|
|
1538
|
+
else unreadtok tok
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1541
|
+
nofunc = false
|
|
1542
|
+
loop do
|
|
1543
|
+
var = basetype.dup
|
|
1544
|
+
var.parse_declarator(self, scope)
|
|
1545
|
+
|
|
1546
|
+
raise var.backtrace if not var.name # barrel roll
|
|
1547
|
+
|
|
1548
|
+
if prev = scope.symbol[var.name]
|
|
1549
|
+
if prev.kind_of? TypeDef and var.storage == :typedef
|
|
1550
|
+
check_compatible_type(var.backtrace, prev.type, var.type, true)
|
|
1551
|
+
# windows.h redefines many typedefs with the same definition
|
|
1552
|
+
puts "redefining typedef #{var.name}" if $VERBOSE
|
|
1553
|
+
var = prev
|
|
1554
|
+
elsif not prev.kind_of?(Variable) or
|
|
1555
|
+
prev.initializer or
|
|
1556
|
+
(prev.storage != :extern and prev.storage != var.storage) or
|
|
1557
|
+
(scope != @toplevel and prev.storage != :static)
|
|
1558
|
+
if prev.kind_of? ::Integer # enum value
|
|
1559
|
+
prev = (scope.struct.values.grep(Enum) + scope.anonymous_enums.to_a).find { |e| e.members.index(prev) }
|
|
1560
|
+
end
|
|
1561
|
+
raise var.backtrace, "redefinition, previous is #{prev.backtrace.exception(nil).message rescue :unknown}"
|
|
1562
|
+
else
|
|
1563
|
+
check_compatible_type var.backtrace, prev.type, var.type, true
|
|
1564
|
+
(var.attributes ||= []).concat prev.attributes if prev.attributes
|
|
1565
|
+
end
|
|
1566
|
+
elsif var.storage == :typedef
|
|
1567
|
+
attrs = var.attributes
|
|
1568
|
+
var = TypeDef.new var.name, var.type, var.backtrace
|
|
1569
|
+
var.attributes = attrs if attrs
|
|
1570
|
+
end
|
|
1571
|
+
scope.statements << Declaration.new(var) unless var.kind_of? TypeDef
|
|
1572
|
+
|
|
1573
|
+
raise tok || self, 'punctuation expected' if not tok = skipspaces or (tok.type != :punct and not %w[asm __asm __asm__].include? tok.raw)
|
|
1574
|
+
|
|
1575
|
+
case tok.raw
|
|
1576
|
+
when '{'
|
|
1577
|
+
# function body
|
|
1578
|
+
raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
|
|
1579
|
+
scope.symbol[var.name] = var
|
|
1580
|
+
body = var.initializer = Block.new(scope)
|
|
1581
|
+
var.type.args ||= []
|
|
1582
|
+
var.type.args.each { |v|
|
|
1583
|
+
# put func parameters in func body scope
|
|
1584
|
+
# arg redefinition is checked in parse_declarator
|
|
1585
|
+
if not v.name
|
|
1586
|
+
puts "unnamed argument in definition of #{var.name}" if $DEBUG
|
|
1587
|
+
next # should raise to be compliant
|
|
1588
|
+
end
|
|
1589
|
+
body.symbol[v.name] = v # XXX will need special check in stack allocator
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
loop do
|
|
1593
|
+
raise tok || self, var.backtrace.exception('"}" expected for end of function') if not tok = skipspaces
|
|
1594
|
+
break if tok.type == :punct and tok.raw == '}'
|
|
1595
|
+
unreadtok tok
|
|
1596
|
+
if not parse_definition(body)
|
|
1597
|
+
body.statements << parse_statement(body, [var.type.type])
|
|
1598
|
+
end
|
|
1599
|
+
end
|
|
1600
|
+
if $VERBOSE and not body.statements.last.kind_of? Return and not body.statements.last.kind_of? Asm
|
|
1601
|
+
puts tok.exception('missing function return value').message if not var.type.type.untypedef.kind_of? BaseType or var.type.type.untypedef.name != :void
|
|
1602
|
+
end
|
|
1603
|
+
break
|
|
1604
|
+
when 'asm', '__asm', '__asm__'
|
|
1605
|
+
# GCC function redirection
|
|
1606
|
+
# void foo(void) __asm__("bar"); => when code uses 'foo', silently redirect to 'bar' instead
|
|
1607
|
+
raise tok if nofunc or not var.kind_of? Variable or not var.type.kind_of? Function
|
|
1608
|
+
# most of the time, 'bar' is not defined anywhere, so we support it only
|
|
1609
|
+
# to allow parsing of headers using it, hoping noone will actually use them
|
|
1610
|
+
unused = Asm.parse(self, scope)
|
|
1611
|
+
puts "unsupported gcc-style __asm__ function redirect #{var.name.inspect} => #{unused.body.inspect}" if $VERBOSE
|
|
1612
|
+
break
|
|
1613
|
+
when '='
|
|
1614
|
+
# variable initialization
|
|
1615
|
+
raise tok, '"{" or ";" expected' if var.type.kind_of? Function
|
|
1616
|
+
raise tok, 'cannot initialize extern variable' if var.storage == :extern
|
|
1617
|
+
scope.symbol[var.name] = var # allow initializer to reference var, eg 'void *ptr = &ptr;'
|
|
1618
|
+
var.initializer = var.type.parse_initializer(self, scope)
|
|
1619
|
+
if var.initializer.kind_of?(CExpression) and (scope == @toplevel or var.storage == :static)
|
|
1620
|
+
raise tok, "initializer for static #{var.name} is not constant" if not var.initializer.constant?
|
|
1621
|
+
end
|
|
1622
|
+
reference_value = lambda { |e, v|
|
|
1623
|
+
found = false
|
|
1624
|
+
case e
|
|
1625
|
+
when Variable; found = true if e == v
|
|
1626
|
+
when CExpression; e.walk { |ee| found ||= reference_value[ee, v] } if e.op != :& or e.lexpr
|
|
1627
|
+
end
|
|
1628
|
+
found
|
|
1629
|
+
}
|
|
1630
|
+
raise tok, "initializer for #{var.name} is not constant (selfreference)" if reference_value[var.initializer, var]
|
|
1631
|
+
raise tok || self, '"," or ";" expected' if not tok = skipspaces or tok.type != :punct
|
|
1632
|
+
else
|
|
1633
|
+
scope.symbol[var.name] = var
|
|
1634
|
+
end
|
|
1635
|
+
|
|
1636
|
+
case tok.raw
|
|
1637
|
+
when ','; nofunc = true
|
|
1638
|
+
when ';'; break
|
|
1639
|
+
when '}'; unreadtok(tok); break
|
|
1640
|
+
else raise tok, '";" or "," expected'
|
|
1641
|
+
end
|
|
1642
|
+
end
|
|
1643
|
+
true
|
|
1644
|
+
end
|
|
1645
|
+
|
|
1646
|
+
# parses toplevel statements, return nil if none found
|
|
1647
|
+
# toplevel statements are ';' and 'asm <..>'
|
|
1648
|
+
def parse_toplevel_statement(scope)
|
|
1649
|
+
if tok = skipspaces and tok.type == :punct and tok.raw == ';'
|
|
1650
|
+
true
|
|
1651
|
+
elsif tok and tok.type == :punct and tok.raw == '{'
|
|
1652
|
+
raise tok || self, '"}" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != '}'
|
|
1653
|
+
true
|
|
1654
|
+
elsif tok and tok.type == :string and %w[asm __asm __asm__].include? tok.raw
|
|
1655
|
+
scope.statements << Asm.parse(self, scope)
|
|
1656
|
+
true
|
|
1657
|
+
end
|
|
1658
|
+
end
|
|
1659
|
+
|
|
1660
|
+
# returns a statement or raise
|
|
1661
|
+
def parse_statement(scope, nest)
|
|
1662
|
+
raise self, 'statement expected' if not tok = skipspaces
|
|
1663
|
+
|
|
1664
|
+
if tok.type == :punct and tok.raw == '{'
|
|
1665
|
+
body = Block.new scope
|
|
1666
|
+
loop do
|
|
1667
|
+
raise tok || self, '"}" expected' if not tok = skipspaces
|
|
1668
|
+
break if tok.type == :punct and tok.raw == '}'
|
|
1669
|
+
unreadtok tok
|
|
1670
|
+
if not parse_definition(body)
|
|
1671
|
+
body.statements << parse_statement(body, nest)
|
|
1672
|
+
end
|
|
1673
|
+
end
|
|
1674
|
+
return body
|
|
1675
|
+
elsif tok.type == :punct and tok.raw == ';'
|
|
1676
|
+
return Block.new(scope)
|
|
1677
|
+
elsif tok.type != :string
|
|
1678
|
+
unreadtok tok
|
|
1679
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
|
1680
|
+
checkstatementend(tok)
|
|
1681
|
+
|
|
1682
|
+
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
|
1683
|
+
puts tok.exception("statement with no effect : #{expr}").message
|
|
1684
|
+
end
|
|
1685
|
+
return expr
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1688
|
+
case tok.raw
|
|
1689
|
+
when 'if'
|
|
1690
|
+
If.parse self, scope, nest
|
|
1691
|
+
when 'while'
|
|
1692
|
+
While.parse self, scope, nest
|
|
1693
|
+
when 'do'
|
|
1694
|
+
DoWhile.parse self, scope, nest
|
|
1695
|
+
when 'for'
|
|
1696
|
+
For.parse self, scope, nest
|
|
1697
|
+
when 'switch'
|
|
1698
|
+
Switch.parse self, scope, nest
|
|
1699
|
+
when 'goto'
|
|
1700
|
+
raise tok || self, 'label expected' if not tok = skipspaces or tok.type != :string
|
|
1701
|
+
name = tok.raw
|
|
1702
|
+
checkstatementend(tok)
|
|
1703
|
+
Goto.new name
|
|
1704
|
+
when 'return'
|
|
1705
|
+
expr = CExpression.parse(self, scope) # nil allowed
|
|
1706
|
+
p, i = nest[0].pointer?, nest[0].integral? if expr
|
|
1707
|
+
r = expr.reduce(self) if p or i
|
|
1708
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
|
1709
|
+
check_compatible_type(tok, (expr ? expr.type : BaseType.new(:void)), nest[0])
|
|
1710
|
+
end
|
|
1711
|
+
checkstatementend(tok)
|
|
1712
|
+
Return.new expr
|
|
1713
|
+
when 'case'
|
|
1714
|
+
raise tok, 'case out of switch' if not nest.include? :switch
|
|
1715
|
+
Case.parse self, scope, nest
|
|
1716
|
+
when 'default'
|
|
1717
|
+
raise tok || self, '":" expected' if not tok = skipspaces or tok.type != :punct or tok.raw != ':'
|
|
1718
|
+
raise tok, 'case out of switch' if not nest.include? :switch
|
|
1719
|
+
Case.new 'default', nil, parse_statement(scope, nest)
|
|
1720
|
+
when 'continue'
|
|
1721
|
+
checkstatementend(tok)
|
|
1722
|
+
raise tok, 'continue out of loop' if not nest.include? :loop
|
|
1723
|
+
Continue.new
|
|
1724
|
+
when 'break'
|
|
1725
|
+
checkstatementend(tok)
|
|
1726
|
+
raise tok, 'break out of loop' if not nest.include? :loop and not nest.include? :switch
|
|
1727
|
+
Break.new
|
|
1728
|
+
when 'asm', '__asm', '__asm__'
|
|
1729
|
+
Asm.parse self, scope
|
|
1730
|
+
else
|
|
1731
|
+
if ntok = skipspaces and ntok.type == :punct and ntok.raw == ':'
|
|
1732
|
+
begin
|
|
1733
|
+
st = parse_statement(scope, nest)
|
|
1734
|
+
rescue ParseError
|
|
1735
|
+
puts "label without statement, #{$!.message}" if $VERBOSE
|
|
1736
|
+
end
|
|
1737
|
+
Label.new tok.raw, st
|
|
1738
|
+
else
|
|
1739
|
+
unreadtok ntok
|
|
1740
|
+
unreadtok tok
|
|
1741
|
+
raise tok, 'expr expected' if not expr = CExpression.parse(self, scope)
|
|
1742
|
+
checkstatementend(tok)
|
|
1743
|
+
|
|
1744
|
+
if $VERBOSE and not nest.include?(:expression) and (expr.op or not expr.type.untypedef.kind_of? BaseType or expr.type.untypedef.name != :void) and CExpression.constant?(expr)
|
|
1745
|
+
puts tok.exception("statement with no effect : #{expr}").message
|
|
1746
|
+
end
|
|
1747
|
+
expr
|
|
1748
|
+
end
|
|
1749
|
+
end
|
|
1750
|
+
end
|
|
1751
|
+
|
|
1752
|
+
# check if a macro definition has a numeric value
|
|
1753
|
+
# returns this value or nil
|
|
1754
|
+
def macro_numeric(m)
|
|
1755
|
+
d = @lexer.definition[m]
|
|
1756
|
+
return if not d.kind_of? Preprocessor::Macro or d.args or d.varargs
|
|
1757
|
+
# filter metasm-defined vars (eg __PE__ / _M_IX86)
|
|
1758
|
+
return if not d.name or not bt = d.name.backtrace or (bt[0][0] != ?" and bt[0][0] != ?<)
|
|
1759
|
+
raise 'cannot macro_numeric with unparsed data' if not eos?
|
|
1760
|
+
@lexer.feed m
|
|
1761
|
+
if e = CExpression.parse(self, Block.new(@toplevel)) and eos?
|
|
1762
|
+
v = e.reduce(self)
|
|
1763
|
+
return v if v.kind_of? ::Numeric
|
|
1764
|
+
end
|
|
1765
|
+
readtok until eos?
|
|
1766
|
+
nil
|
|
1767
|
+
rescue ParseError
|
|
1768
|
+
readtok until eos?
|
|
1769
|
+
nil
|
|
1770
|
+
end
|
|
1771
|
+
|
|
1772
|
+
# returns all numeric constants defined with their value, either macros or enums
|
|
1773
|
+
def numeric_constants
|
|
1774
|
+
ret = []
|
|
1775
|
+
# macros
|
|
1776
|
+
@lexer.definition.each_key { |k|
|
|
1777
|
+
if v = macro_numeric(k)
|
|
1778
|
+
ret << [k, v]
|
|
1779
|
+
end
|
|
1780
|
+
}
|
|
1781
|
+
# enums
|
|
1782
|
+
@toplevel.symbol.each { |k, v|
|
|
1783
|
+
ret << [k, v] if v.kind_of? ::Numeric
|
|
1784
|
+
}
|
|
1785
|
+
ret
|
|
1786
|
+
end
|
|
1787
|
+
end
|
|
1788
|
+
|
|
1789
|
+
class Variable
|
|
1790
|
+
# parses a variable basetype/qualifier/(storage if allow_value), returns a new variable of this type
|
|
1791
|
+
# populates scope.struct
|
|
1792
|
+
def self.parse_type(parser, scope, allow_value = false)
|
|
1793
|
+
var = new
|
|
1794
|
+
qualifier = []
|
|
1795
|
+
tok = nil
|
|
1796
|
+
loop do
|
|
1797
|
+
var.parse_attributes(parser, true)
|
|
1798
|
+
break if not tok = parser.skipspaces
|
|
1799
|
+
if tok.type != :string
|
|
1800
|
+
parser.unreadtok tok
|
|
1801
|
+
break
|
|
1802
|
+
end
|
|
1803
|
+
|
|
1804
|
+
case tok.raw
|
|
1805
|
+
when 'const', 'volatile'
|
|
1806
|
+
qualifier << tok.raw.to_sym
|
|
1807
|
+
next
|
|
1808
|
+
when 'register', 'auto', 'static', 'typedef', 'extern'
|
|
1809
|
+
raise tok, 'storage specifier not allowed here' if not allow_value
|
|
1810
|
+
raise tok, 'multiple storage class' if var.storage
|
|
1811
|
+
var.storage = tok.raw.to_sym
|
|
1812
|
+
next
|
|
1813
|
+
when 'struct'
|
|
1814
|
+
var.type = Struct.new
|
|
1815
|
+
var.type.pack = parser.pragma_pack if parser.pragma_pack
|
|
1816
|
+
var.parse_type_struct(parser, scope)
|
|
1817
|
+
when 'union'
|
|
1818
|
+
var.type = Union.new
|
|
1819
|
+
var.parse_type_struct(parser, scope)
|
|
1820
|
+
when 'enum'
|
|
1821
|
+
var.type = Enum.new
|
|
1822
|
+
var.parse_type_struct(parser, scope)
|
|
1823
|
+
when 'typeof'
|
|
1824
|
+
if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
|
|
1825
|
+
# check type
|
|
1826
|
+
if v = parse_type(parser, scope)
|
|
1827
|
+
v.parse_declarator(parser, scope)
|
|
1828
|
+
raise tok if v.name != false
|
|
1829
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
1830
|
+
else
|
|
1831
|
+
raise tok, 'expr expected' if not v = CExpression.parse(parser, scope)
|
|
1832
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
1833
|
+
end
|
|
1834
|
+
else
|
|
1835
|
+
parser.unreadtok ntok
|
|
1836
|
+
raise tok, 'expr expected' if not v = CExpression.parse_value(parser, scope)
|
|
1837
|
+
end
|
|
1838
|
+
var.type = v.type # TypeDef.new('typeof', v.type, tok)
|
|
1839
|
+
when 'long', 'short', 'signed', 'unsigned', 'int', 'char', 'float', 'double',
|
|
1840
|
+
'void', '__int8', '__int16', '__int32', '__int64',
|
|
1841
|
+
'intptr_t', 'uintptr_t'
|
|
1842
|
+
parser.unreadtok tok
|
|
1843
|
+
var.parse_type_base(parser, scope)
|
|
1844
|
+
else
|
|
1845
|
+
if type = scope.symbol_ancestors[tok.raw] and type.kind_of? TypeDef
|
|
1846
|
+
var.type = type.dup
|
|
1847
|
+
else
|
|
1848
|
+
parser.unreadtok tok
|
|
1849
|
+
end
|
|
1850
|
+
end
|
|
1851
|
+
|
|
1852
|
+
break
|
|
1853
|
+
end
|
|
1854
|
+
|
|
1855
|
+
if not var.type
|
|
1856
|
+
raise tok || parser, 'bad type name' if not qualifier.empty? or var.storage
|
|
1857
|
+
nil
|
|
1858
|
+
else
|
|
1859
|
+
var.type.qualifier = var.type.qualifier.to_a | qualifier if not qualifier.empty?
|
|
1860
|
+
var.type.parse_attributes(parser, true)
|
|
1861
|
+
var
|
|
1862
|
+
end
|
|
1863
|
+
end
|
|
1864
|
+
|
|
1865
|
+
# parses a structure/union/enum declaration
|
|
1866
|
+
def parse_type_struct(parser, scope)
|
|
1867
|
+
@type.parse_attributes(parser)
|
|
1868
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '{'
|
|
1869
|
+
# anonymous struct, ok
|
|
1870
|
+
@type.backtrace = tok
|
|
1871
|
+
if @type.kind_of? Enum
|
|
1872
|
+
(scope.anonymous_enums ||= []) << @type
|
|
1873
|
+
end
|
|
1874
|
+
elsif tok and tok.type == :string
|
|
1875
|
+
name = tok.raw
|
|
1876
|
+
raise tok, 'bad struct name' if Keyword[name] or (?0..?9).include?(name[0])
|
|
1877
|
+
@type.backtrace = tok
|
|
1878
|
+
@type.name = name
|
|
1879
|
+
@type.parse_attributes(parser)
|
|
1880
|
+
raise parser if not ntok = parser.skipspaces
|
|
1881
|
+
if ntok.type != :punct or ntok.raw != '{'
|
|
1882
|
+
raise tok, "struct/union confusion" if scope.struct[name] and scope.struct[name].class != @type.class
|
|
1883
|
+
# variable declaration
|
|
1884
|
+
parser.unreadtok ntok
|
|
1885
|
+
if ntok.type == :punct and ntok.raw == ';'
|
|
1886
|
+
# struct predeclaration
|
|
1887
|
+
# allow redefinition
|
|
1888
|
+
@type = scope.struct[name] ||= @type
|
|
1889
|
+
else
|
|
1890
|
+
# check that the structure exists
|
|
1891
|
+
struct = scope.struct_ancestors[name]
|
|
1892
|
+
# allow incomplete types, usage as var type will raise later
|
|
1893
|
+
struct = scope.struct[name] = @type if not struct
|
|
1894
|
+
raise tok, 'unknown struct' if struct.class != @type.class
|
|
1895
|
+
(struct.attributes ||= []).concat @type.attributes if @type.attributes
|
|
1896
|
+
(struct.qualifier ||= []).concat @type.qualifier if @type.qualifier # XXX const struct foo bar => bar is const, not foo...
|
|
1897
|
+
@type = struct
|
|
1898
|
+
end
|
|
1899
|
+
return
|
|
1900
|
+
end
|
|
1901
|
+
if scope.struct[name] and scope.struct[name].members
|
|
1902
|
+
# redefinition of an existing struct, save for later comparison
|
|
1903
|
+
oldstruct = scope.struct[name]
|
|
1904
|
+
raise tok, "struct/union confusion" if oldstruct.class != @type.class
|
|
1905
|
+
elsif struct = scope.struct[name]
|
|
1906
|
+
raise tok, "struct/union confusion" if struct.class != @type.class
|
|
1907
|
+
(struct.attributes ||= []).concat @type.attributes if @type.attributes
|
|
1908
|
+
(struct.qualifier ||= []).concat @type.qualifier if @type.qualifier
|
|
1909
|
+
struct.backtrace = @type.backtrace
|
|
1910
|
+
struct.name = @type.name
|
|
1911
|
+
@type = struct
|
|
1912
|
+
else
|
|
1913
|
+
scope.struct[name] = @type
|
|
1914
|
+
end
|
|
1915
|
+
else
|
|
1916
|
+
raise tok || parser, 'struct name or "{" expected'
|
|
1917
|
+
end
|
|
1918
|
+
|
|
1919
|
+
@type.parse_members(parser, scope)
|
|
1920
|
+
|
|
1921
|
+
if oldstruct
|
|
1922
|
+
if not @type.compare_deep(oldstruct)
|
|
1923
|
+
raise tok, "conflicting struct redefinition (old at #{oldstruct.backtrace.exception(nil).message rescue :unknown})"
|
|
1924
|
+
end
|
|
1925
|
+
@type = oldstruct
|
|
1926
|
+
end
|
|
1927
|
+
end
|
|
1928
|
+
|
|
1929
|
+
# parses int/long int/long long/double etc
|
|
1930
|
+
def parse_type_base(parser, scope)
|
|
1931
|
+
specifier = []
|
|
1932
|
+
qualifier = []
|
|
1933
|
+
name = :int
|
|
1934
|
+
tok = nil
|
|
1935
|
+
loop do
|
|
1936
|
+
raise parser if not tok = parser.skipspaces
|
|
1937
|
+
if tok.type != :string
|
|
1938
|
+
parser.unreadtok tok
|
|
1939
|
+
break
|
|
1940
|
+
end
|
|
1941
|
+
case tok.raw
|
|
1942
|
+
when 'const', 'volatile'
|
|
1943
|
+
qualifier << tok.raw.to_sym
|
|
1944
|
+
when 'long', 'short', 'signed', 'unsigned'
|
|
1945
|
+
specifier << tok.raw.to_sym
|
|
1946
|
+
when 'int', 'char', 'void', 'float', 'double', '__int8', '__int16', '__int32', '__int64'
|
|
1947
|
+
name = tok.raw.to_sym
|
|
1948
|
+
break
|
|
1949
|
+
when 'intptr_t', 'uintptr_t'
|
|
1950
|
+
name = :ptr
|
|
1951
|
+
specifier << :unsigned if tok.raw == 'uintptr_t'
|
|
1952
|
+
break
|
|
1953
|
+
else
|
|
1954
|
+
parser.unreadtok tok
|
|
1955
|
+
break
|
|
1956
|
+
end
|
|
1957
|
+
end
|
|
1958
|
+
|
|
1959
|
+
case name
|
|
1960
|
+
when :double # long double
|
|
1961
|
+
if specifier == [:long]
|
|
1962
|
+
name = :longdouble
|
|
1963
|
+
specifier.clear
|
|
1964
|
+
elsif not specifier.empty?
|
|
1965
|
+
raise tok || parser, 'invalid specifier list'
|
|
1966
|
+
end
|
|
1967
|
+
when :int # short, long, long long X signed, unsigned
|
|
1968
|
+
# Array#count not available on old ruby (eg 1.8.4), so use ary.len - (ary-stuff).len
|
|
1969
|
+
specifier = specifier - [:long] + [:longlong] if specifier.length - (specifier-[:long]).length == 2
|
|
1970
|
+
if specifier.length - (specifier-[:signed, :unsigned]).length > 1 or specifier.length - (specifier-[:short, :long, :longlong]).length > 1
|
|
1971
|
+
raise tok || parser, 'invalid specifier list'
|
|
1972
|
+
else
|
|
1973
|
+
name = (specifier & [:longlong, :long, :short])[0] || :int
|
|
1974
|
+
specifier -= [:longlong, :long, :short]
|
|
1975
|
+
end
|
|
1976
|
+
specifier.delete :signed # default
|
|
1977
|
+
when :char # signed, unsigned
|
|
1978
|
+
# signed char != char and unsigned char != char
|
|
1979
|
+
if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
|
|
1980
|
+
raise tok || parser, 'invalid specifier list'
|
|
1981
|
+
end
|
|
1982
|
+
when :__int8, :__int16, :__int32, :__int64, :ptr
|
|
1983
|
+
if (specifier & [:signed, :unsigned]).length > 1 or (specifier & [:short, :long]).length > 0
|
|
1984
|
+
raise tok || parser, 'invalid specifier list'
|
|
1985
|
+
end
|
|
1986
|
+
specifier.delete :signed # default
|
|
1987
|
+
else # none
|
|
1988
|
+
raise tok || parser, 'invalid type' if not specifier.empty?
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1991
|
+
@type = BaseType.new(name, *specifier)
|
|
1992
|
+
@type.qualifier = qualifier if not qualifier.empty?
|
|
1993
|
+
end
|
|
1994
|
+
|
|
1995
|
+
# updates @type and @name, parses pointer/arrays/function declarations
|
|
1996
|
+
# parses anonymous declarators (@name will be false)
|
|
1997
|
+
# the caller is responsible for detecting redefinitions
|
|
1998
|
+
# scope used only in CExpression.parse for array sizes and function prototype argument types
|
|
1999
|
+
# rec for internal use only
|
|
2000
|
+
def parse_declarator(parser, scope, rec = false)
|
|
2001
|
+
parse_attributes(parser, true)
|
|
2002
|
+
tok = parser.skipspaces
|
|
2003
|
+
# read upto name
|
|
2004
|
+
if tok and tok.type == :punct and tok.raw == '*'
|
|
2005
|
+
ptr = Pointer.new
|
|
2006
|
+
ptr.parse_attributes(parser)
|
|
2007
|
+
while ntok = parser.skipspaces and ntok.type == :string
|
|
2008
|
+
case ntok.raw
|
|
2009
|
+
when 'const', 'volatile'
|
|
2010
|
+
(ptr.qualifier ||= []) << ntok.raw.to_sym
|
|
2011
|
+
ptr.parse_attributes(parser)
|
|
2012
|
+
else break
|
|
2013
|
+
end
|
|
2014
|
+
end
|
|
2015
|
+
parser.unreadtok ntok
|
|
2016
|
+
parse_declarator(parser, scope, true)
|
|
2017
|
+
t = self
|
|
2018
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
|
2019
|
+
ptr.type = t.type
|
|
2020
|
+
t.type = ptr
|
|
2021
|
+
if t.kind_of? Function and ptr.attributes
|
|
2022
|
+
@attributes ||= []
|
|
2023
|
+
@attributes |= ptr.attributes
|
|
2024
|
+
ptr.attributes = nil
|
|
2025
|
+
end
|
|
2026
|
+
return
|
|
2027
|
+
elsif tok and tok.type == :punct and tok.raw == '('
|
|
2028
|
+
parse_declarator(parser, scope, true)
|
|
2029
|
+
raise tok || parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
2030
|
+
elsif tok and tok.type == :string
|
|
2031
|
+
case tok.raw
|
|
2032
|
+
when 'const', 'volatile'
|
|
2033
|
+
(@type.qualifier ||= []) << tok.raw.to_sym
|
|
2034
|
+
return parse_declarator(parser, scope, rec)
|
|
2035
|
+
when 'register', 'auto', 'static', 'typedef', 'extern'
|
|
2036
|
+
raise tok, 'multiple storage class' if storage
|
|
2037
|
+
@storage = tok.raw.to_sym
|
|
2038
|
+
puts tok.exception('misplaced storage specifier').message if $VERBOSE
|
|
2039
|
+
return parse_declarator(parser, scope, rec)
|
|
2040
|
+
end
|
|
2041
|
+
raise tok if name or name == false
|
|
2042
|
+
raise tok, 'bad var name' if Keyword[tok.raw] or (?0..?9).include?(tok.raw[0])
|
|
2043
|
+
@name = tok.raw
|
|
2044
|
+
@backtrace = tok
|
|
2045
|
+
parse_attributes(parser, true)
|
|
2046
|
+
else
|
|
2047
|
+
# unnamed
|
|
2048
|
+
raise tok || parser if name or name == false
|
|
2049
|
+
@name = false
|
|
2050
|
+
@backtrace = tok
|
|
2051
|
+
parser.unreadtok tok
|
|
2052
|
+
parse_attributes(parser, true)
|
|
2053
|
+
end
|
|
2054
|
+
parse_declarator_postfix(parser, scope)
|
|
2055
|
+
if not rec
|
|
2056
|
+
raise @backtrace, 'void type is invalid' if name and (t = @type.untypedef).kind_of? BaseType and
|
|
2057
|
+
t.name == :void and storage != :typedef
|
|
2058
|
+
raise @backtrace, "incomplete type #{@type.name}" if (@type.kind_of? Union or @type.kind_of? Enum) and
|
|
2059
|
+
not @type.members and storage != :typedef and storage != :extern # gcc uses an undefined extern struct just to cast it later (_IO_FILE_plus)
|
|
2060
|
+
end
|
|
2061
|
+
end
|
|
2062
|
+
|
|
2063
|
+
# parses array/function type
|
|
2064
|
+
def parse_declarator_postfix(parser, scope)
|
|
2065
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == '['
|
|
2066
|
+
# array indexing
|
|
2067
|
+
idx = CExpression.parse(parser, scope) # may be nil
|
|
2068
|
+
if idx and (scope == parser.toplevel or storage == :static)
|
|
2069
|
+
raise tok, 'array size is not constant' if not idx.constant?
|
|
2070
|
+
idx = idx.reduce(parser)
|
|
2071
|
+
elsif idx and nidx = idx.reduce(parser) and nidx.kind_of? ::Integer
|
|
2072
|
+
idx = nidx
|
|
2073
|
+
end
|
|
2074
|
+
t = self
|
|
2075
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
|
2076
|
+
t.type = Array.new t.type
|
|
2077
|
+
t.type.length = idx
|
|
2078
|
+
raise tok || parser, '"]" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
|
|
2079
|
+
parse_attributes(parser) # should be type.attrs, but this is should be more compiler-compatible
|
|
2080
|
+
elsif tok and tok.type == :punct and tok.raw == '('
|
|
2081
|
+
# function prototype
|
|
2082
|
+
# void __attribute__((noreturn)) func() => attribute belongs to func
|
|
2083
|
+
if @type and @type.attributes
|
|
2084
|
+
@attributes ||= []
|
|
2085
|
+
@attributes |= @type.attributes
|
|
2086
|
+
@type.attributes = nil
|
|
2087
|
+
end
|
|
2088
|
+
t = self
|
|
2089
|
+
t = t.type while t.type and (t.type.kind_of?(Pointer) or t.type.kind_of?(Function))
|
|
2090
|
+
t.type = Function.new t.type
|
|
2091
|
+
if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
2092
|
+
parser.unreadtok tok
|
|
2093
|
+
t.type.args = []
|
|
2094
|
+
oldstyle = false # int func(a, b) int a; double b; { stuff; }
|
|
2095
|
+
loop do
|
|
2096
|
+
raise parser if not tok = parser.skipspaces
|
|
2097
|
+
if tok.type == :punct and tok.raw == '.' # variadic function
|
|
2098
|
+
raise parser, '".." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
|
2099
|
+
raise parser, '"." expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '.'
|
|
2100
|
+
raise parser, '")" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ')'
|
|
2101
|
+
t.type.varargs = true
|
|
2102
|
+
break
|
|
2103
|
+
elsif tok.type == :string and tok.raw == 'register'
|
|
2104
|
+
storage = :register
|
|
2105
|
+
else
|
|
2106
|
+
parser.unreadtok tok
|
|
2107
|
+
end
|
|
2108
|
+
|
|
2109
|
+
if oldstyle or not v = Variable.parse_type(parser, scope)
|
|
2110
|
+
raise tok if not @name # no oldstyle in casts
|
|
2111
|
+
tok = parser.skipspaces
|
|
2112
|
+
oldstyle ||= [tok] # arg to raise() later
|
|
2113
|
+
oldstyle << tok.raw
|
|
2114
|
+
else
|
|
2115
|
+
v.storage = storage if storage
|
|
2116
|
+
v.parse_declarator(parser, scope)
|
|
2117
|
+
v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
|
|
2118
|
+
v.type = Pointer.new(v.type) if v.type.kind_of? Function
|
|
2119
|
+
|
|
2120
|
+
t.type.args << v if not v.type.untypedef.kind_of? BaseType or v.type.untypedef.name != :void
|
|
2121
|
+
end
|
|
2122
|
+
|
|
2123
|
+
if tok = parser.skipspaces and tok.type == :punct and tok.raw == ','
|
|
2124
|
+
raise tok, '")" expected' if v and t.type.args.last != v # last arg of type :void
|
|
2125
|
+
elsif tok and tok.type == :punct and tok.raw == ')'
|
|
2126
|
+
break
|
|
2127
|
+
else raise tok || parser, '"," or ")" expected'
|
|
2128
|
+
end
|
|
2129
|
+
end
|
|
2130
|
+
if oldstyle
|
|
2131
|
+
parse_attributes(parser, true)
|
|
2132
|
+
ra = oldstyle.shift
|
|
2133
|
+
while t.type.args.compact.length != oldstyle.length
|
|
2134
|
+
raise ra, "invalid prototype" if not vb = Variable.parse_type(parser, scope)
|
|
2135
|
+
loop do
|
|
2136
|
+
v = vb.dup
|
|
2137
|
+
v.parse_declarator(parser, scope)
|
|
2138
|
+
v.type = Pointer.new(v.type.type) if v.type.kind_of? Array
|
|
2139
|
+
v.type = Pointer.new(v.type) if v.type.kind_of? Function
|
|
2140
|
+
raise parser, "unknown arg #{v.name.inspect}" if not i = oldstyle.index(v.name)
|
|
2141
|
+
t.type.args[i] = v
|
|
2142
|
+
raise parser, '"," or ";" expected' if not tok = parser.skipspaces or tok.type != :punct or (tok.raw != ';' and tok.raw != ',')
|
|
2143
|
+
break if tok.raw == ';'
|
|
2144
|
+
end
|
|
2145
|
+
end
|
|
2146
|
+
parse_attributes(parser, true)
|
|
2147
|
+
raise parser, '"{" expected' if not tok = parser.skipspaces or tok.type != :punct or tok.raw != '{'
|
|
2148
|
+
parser.unreadtok tok
|
|
2149
|
+
end
|
|
2150
|
+
namedargs = t.type.args.map { |a| a.name }.compact - [false]
|
|
2151
|
+
raise tok, "duplicate argument name #{namedargs.find { |a| namedargs.index(a) != namedargs.rindex(a) }.inspect}" if namedargs.length != namedargs.uniq.length
|
|
2152
|
+
end
|
|
2153
|
+
parse_attributes(parser, true) # should be type.attrs, but this should be more existing-compiler-compatible
|
|
2154
|
+
else
|
|
2155
|
+
parser.unreadtok tok
|
|
2156
|
+
return
|
|
2157
|
+
end
|
|
2158
|
+
parse_declarator_postfix(parser, scope)
|
|
2159
|
+
end
|
|
2160
|
+
end
|
|
2161
|
+
|
|
2162
|
+
class Variable
|
|
2163
|
+
def ===(o)
|
|
2164
|
+
self == o or (o.class == CExpression and not o.op and o.rexpr == self)
|
|
2165
|
+
end
|
|
2166
|
+
end
|
|
2167
|
+
|
|
2168
|
+
class CExpression
|
|
2169
|
+
def self.lvalue?(e)
|
|
2170
|
+
e.kind_of?(self) ? e.lvalue? : (e.kind_of? Variable and e.name)
|
|
2171
|
+
end
|
|
2172
|
+
def lvalue?
|
|
2173
|
+
case @op
|
|
2174
|
+
when :*; true if not @lexpr
|
|
2175
|
+
when :'[]', :'.', :'->'; true
|
|
2176
|
+
when nil # cast
|
|
2177
|
+
CExpression.lvalue?(@rexpr)
|
|
2178
|
+
else false
|
|
2179
|
+
end
|
|
2180
|
+
end
|
|
2181
|
+
|
|
2182
|
+
def self.constant?(e)
|
|
2183
|
+
e.kind_of?(self) ? e.constant? : true
|
|
2184
|
+
end
|
|
2185
|
+
def constant?
|
|
2186
|
+
# gcc considers '1, 2' not constant
|
|
2187
|
+
if [:',', :funcall, :'=', :'--', :'++', :'+=', :'-=', :'*=', :'/=', :'>>=', :'<<=', :'&=', :'|=', :'^=', :'%=', :'->', :'[]'].include?(@op)
|
|
2188
|
+
false
|
|
2189
|
+
elsif @op == :'*' and not @lexpr; false
|
|
2190
|
+
elsif not @lexpr and not @op and @rexpr.kind_of? Block; false
|
|
2191
|
+
else
|
|
2192
|
+
out = true
|
|
2193
|
+
walk { |e| break out = false if not CExpression.constant?(e) }
|
|
2194
|
+
out
|
|
2195
|
+
end
|
|
2196
|
+
end
|
|
2197
|
+
|
|
2198
|
+
def self.reduce(parser, e)
|
|
2199
|
+
e.kind_of?(self) ? e.reduce(parser) : e
|
|
2200
|
+
end
|
|
2201
|
+
def reduce(parser)
|
|
2202
|
+
# parser used for arithmetic overflows (need basic type sizes)
|
|
2203
|
+
case @op
|
|
2204
|
+
when :'&&'
|
|
2205
|
+
case l = CExpression.reduce(parser, @lexpr)
|
|
2206
|
+
when 0; 0
|
|
2207
|
+
when ::Integer
|
|
2208
|
+
case r = CExpression.reduce(parser, @rexpr)
|
|
2209
|
+
when 0; 0
|
|
2210
|
+
when ::Integer; 1
|
|
2211
|
+
else CExpression.new(l, @op, r, @type)
|
|
2212
|
+
end
|
|
2213
|
+
else CExpression.new(l, @op, @rexpr, @type)
|
|
2214
|
+
end
|
|
2215
|
+
when :'||'
|
|
2216
|
+
case l = CExpression.reduce(parser, @lexpr)
|
|
2217
|
+
when 0
|
|
2218
|
+
case r = CExpression.reduce(parser, @rexpr)
|
|
2219
|
+
when 0; 0
|
|
2220
|
+
when ::Integer; 1
|
|
2221
|
+
else CExpression.new(l, @op, r, @type)
|
|
2222
|
+
end
|
|
2223
|
+
when ::Integer; 1
|
|
2224
|
+
else CExpression.new(l, @op, @rexpr, @type)
|
|
2225
|
+
end
|
|
2226
|
+
when :'!'
|
|
2227
|
+
case r = CExpression.reduce(parser, @rexpr)
|
|
2228
|
+
when 0; 1
|
|
2229
|
+
when ::Integer; 0
|
|
2230
|
+
else CExpression.new(nil, @op, r, @type)
|
|
2231
|
+
end
|
|
2232
|
+
when :'!=', :'==', :'<', :'>', :'>=', :'<='
|
|
2233
|
+
l = CExpression.reduce(parser, @lexpr)
|
|
2234
|
+
r = CExpression.reduce(parser, @rexpr)
|
|
2235
|
+
if l.kind_of?(::Integer) and r.kind_of?(::Integer)
|
|
2236
|
+
if @op == :'!='; l != r ? 1 : 0
|
|
2237
|
+
else l.send(@op, r) ? 1 : 0
|
|
2238
|
+
end
|
|
2239
|
+
else
|
|
2240
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
|
2241
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
|
2242
|
+
CExpression.new(l, @op, r, @type)
|
|
2243
|
+
end
|
|
2244
|
+
when :'.'
|
|
2245
|
+
le = CExpression.reduce(parser, @lexpr)
|
|
2246
|
+
if le.kind_of? Variable and le.initializer.kind_of? ::Array
|
|
2247
|
+
midx = le.type.members.index(le.type.findmember(@rexpr))
|
|
2248
|
+
CExpression.reduce(parser, le.initializer[midx] || 0)
|
|
2249
|
+
else
|
|
2250
|
+
CExpression.new(le, @op, @rexpr, @type)
|
|
2251
|
+
end
|
|
2252
|
+
when :'?:'
|
|
2253
|
+
case c = CExpression.reduce(parser, @lexpr)
|
|
2254
|
+
when 0; CExpression.reduce(parser, @rexpr[0])
|
|
2255
|
+
when ::Integer; CExpression.reduce(parser, @rexpr[1])
|
|
2256
|
+
else CExpression.new(c, @op, @rexpr, @type)
|
|
2257
|
+
end
|
|
2258
|
+
when :'+', :'-', :'*', :'/', :'^', :'%', :'&', :'|', :'>>', :'<<', :'~', nil
|
|
2259
|
+
t = @type.untypedef
|
|
2260
|
+
case t
|
|
2261
|
+
when BaseType
|
|
2262
|
+
when Pointer; return self if @op
|
|
2263
|
+
else
|
|
2264
|
+
return @rexpr if not @op and not @lexpr and @rexpr.kind_of? Variable and @rexpr.type == @type
|
|
2265
|
+
return self # raise parser, 'not arithmetic type'
|
|
2266
|
+
end
|
|
2267
|
+
|
|
2268
|
+
# compute value
|
|
2269
|
+
r = CExpression.reduce(parser, @rexpr)
|
|
2270
|
+
ret = \
|
|
2271
|
+
if not @lexpr
|
|
2272
|
+
# unary
|
|
2273
|
+
case @op
|
|
2274
|
+
when :'+', nil, :'-', :'~'
|
|
2275
|
+
return CExpression.new(nil, @op, r, @type) if not r.kind_of? ::Numeric
|
|
2276
|
+
case @op
|
|
2277
|
+
when :'-'; -r
|
|
2278
|
+
when :'~'; ~r
|
|
2279
|
+
else r
|
|
2280
|
+
end
|
|
2281
|
+
else return CExpression.new(nil, @op, r, @type)
|
|
2282
|
+
end
|
|
2283
|
+
else
|
|
2284
|
+
l = CExpression.reduce(parser, @lexpr)
|
|
2285
|
+
if not l.kind_of?(::Numeric) or not r.kind_of?(::Numeric)
|
|
2286
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
|
2287
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
|
2288
|
+
return CExpression.new(l, @op, r, @type)
|
|
2289
|
+
end
|
|
2290
|
+
l.send(@op, r)
|
|
2291
|
+
end
|
|
2292
|
+
|
|
2293
|
+
# overflow
|
|
2294
|
+
tn = (t.pointer? ? :ptr : t.name)
|
|
2295
|
+
case tn
|
|
2296
|
+
when :char, :short, :int, :long, :ptr, :longlong, :__int8, :__int16, :__int32, :__int64
|
|
2297
|
+
max = 1 << (8*parser.typesize[tn])
|
|
2298
|
+
ret = ret.to_i & (max-1)
|
|
2299
|
+
if not t.pointer? and t.specifier != :unsigned and (ret & (max >> 1)) > 0 # char == unsigned char
|
|
2300
|
+
ret - max
|
|
2301
|
+
else
|
|
2302
|
+
ret
|
|
2303
|
+
end
|
|
2304
|
+
when :float, :double, :longdouble
|
|
2305
|
+
ret.to_f # TODO
|
|
2306
|
+
end
|
|
2307
|
+
when :funcall
|
|
2308
|
+
l = CExpression.reduce(parser, @lexpr)
|
|
2309
|
+
r = @rexpr.map { |rr|
|
|
2310
|
+
rr = CExpression.reduce(parser, rr)
|
|
2311
|
+
rr = CExpression.new(nil, nil, rr, BaseType.new(:int)) if rr.kind_of? ::Integer
|
|
2312
|
+
rr
|
|
2313
|
+
}
|
|
2314
|
+
CExpression.new(l, @op, r, @type)
|
|
2315
|
+
else
|
|
2316
|
+
l = CExpression.reduce(parser, @lexpr) if @lexpr
|
|
2317
|
+
r = CExpression.reduce(parser, @rexpr) if @rexpr
|
|
2318
|
+
l = CExpression.new(nil, nil, l, BaseType.new(:int)) if l.kind_of? ::Integer
|
|
2319
|
+
r = CExpression.new(nil, nil, r, BaseType.new(:int)) if r.kind_of? ::Integer
|
|
2320
|
+
CExpression.new(l, @op, r, @type)
|
|
2321
|
+
end
|
|
2322
|
+
end
|
|
2323
|
+
|
|
2324
|
+
def ==(o)
|
|
2325
|
+
o.object_id == self.object_id or
|
|
2326
|
+
(self.class == o.class and op == o.op and lexpr == o.lexpr and rexpr == o.rexpr)
|
|
2327
|
+
end
|
|
2328
|
+
|
|
2329
|
+
def ===(o)
|
|
2330
|
+
(self.class == o.class and op == o.op and lexpr === o.lexpr and rexpr === o.rexpr) or
|
|
2331
|
+
(o.class == Variable and not @op and @rexpr == o)
|
|
2332
|
+
end
|
|
2333
|
+
|
|
2334
|
+
NegateOp = { :== => :'!=', :'!=' => :==, :> => :<=, :>= => :<, :< => :>=, :<= => :> }
|
|
2335
|
+
# returns a CExpr negating this one (eg 'x' => '!x', 'a > b' => 'a <= b'...)
|
|
2336
|
+
def self.negate(e)
|
|
2337
|
+
e.kind_of?(self) ? e.negate : CExpression[:'!', e]
|
|
2338
|
+
end
|
|
2339
|
+
def negate
|
|
2340
|
+
if @op == :'!'
|
|
2341
|
+
CExpression[@rexpr]
|
|
2342
|
+
elsif nop = NegateOp[@op]
|
|
2343
|
+
if nop == :== and @rexpr.kind_of? CExpression and not @rexpr.op and @rexpr.rexpr == 0 and
|
|
2344
|
+
@lexpr.kind_of? CExpression and [:==, :'!=', :>, :<, :>=, :<=, :'!'].include? @lexpr.op
|
|
2345
|
+
# (a > b) != 0 => (a > b)
|
|
2346
|
+
CExpression[@lexpr]
|
|
2347
|
+
else
|
|
2348
|
+
CExpression.new(@lexpr, nop, @rexpr, @type)
|
|
2349
|
+
end
|
|
2350
|
+
elsif nop = { :'||' => :'&&', :'&&' => :'||' }[@op]
|
|
2351
|
+
CExpression.new(CExpression.negate(@lexpr), nop, CExpression.negate(@rexpr), @type)
|
|
2352
|
+
else
|
|
2353
|
+
CExpression[:'!', self]
|
|
2354
|
+
end
|
|
2355
|
+
end
|
|
2356
|
+
|
|
2357
|
+
def walk
|
|
2358
|
+
case @op
|
|
2359
|
+
when :funcall, :'?:'
|
|
2360
|
+
yield @lexpr
|
|
2361
|
+
@rexpr.each { |arg| yield arg }
|
|
2362
|
+
when :'->', :'.'
|
|
2363
|
+
yield @lexpr
|
|
2364
|
+
else
|
|
2365
|
+
yield @lexpr if @lexpr
|
|
2366
|
+
yield @rexpr if @rexpr
|
|
2367
|
+
end
|
|
2368
|
+
end
|
|
2369
|
+
|
|
2370
|
+
def complexity
|
|
2371
|
+
cx = 1
|
|
2372
|
+
walk { |e| cx += e.complexity if e.kind_of?(CExpression) }
|
|
2373
|
+
cx
|
|
2374
|
+
end
|
|
2375
|
+
|
|
2376
|
+
RIGHTASSOC = [:'=', :'+=', :'-=', :'*=', :'/=', :'%=', :'&=',
|
|
2377
|
+
:'|=', :'^=', :'<<=', :'>>=', :'?:'
|
|
2378
|
+
].inject({}) { |h, op| h.update op => true }
|
|
2379
|
+
|
|
2380
|
+
# key = operator, value = hash regrouping operators of lower precedence
|
|
2381
|
+
# funcall/array index/member dereference/sizeof are handled in parse_value
|
|
2382
|
+
OP_PRIO = [[:','], [:'?:'], [:'=', :'+=', :'-=', :'*=', :'/=',
|
|
2383
|
+
:'%=', :'&=', :'|=', :'^=', :'<<=', :'>>='], [:'||'],
|
|
2384
|
+
[:'&&'], [:|], [:^], [:&], [:'==', :'!='],
|
|
2385
|
+
[:'<', :'>', :'<=', :'>='], [:<<, :>>], [:+, :-],
|
|
2386
|
+
[:*, :/, :%], ].inject({}) { |h, oplist|
|
|
2387
|
+
lessprio = h.keys.inject({}) { |hh, op| hh.update op => true }
|
|
2388
|
+
oplist.each { |op| lessprio.update op => true } if RIGHTASSOC[oplist.first]
|
|
2389
|
+
oplist.each { |op| h[op] = lessprio }
|
|
2390
|
+
h }
|
|
2391
|
+
|
|
2392
|
+
class << self
|
|
2393
|
+
# reads a binary operator from the parser, returns the corresponding symbol or nil
|
|
2394
|
+
def readop(parser)
|
|
2395
|
+
if not op = parser.skipspaces or op.type != :punct
|
|
2396
|
+
parser.unreadtok op
|
|
2397
|
+
return
|
|
2398
|
+
end
|
|
2399
|
+
|
|
2400
|
+
case op.raw
|
|
2401
|
+
when '>', '<', '|', '&' # << >> || &&
|
|
2402
|
+
if ntok = parser.readtok and ntok.type == :punct and ntok.raw == op.raw
|
|
2403
|
+
op.raw << ntok.raw
|
|
2404
|
+
else
|
|
2405
|
+
parser.unreadtok ntok
|
|
2406
|
+
end
|
|
2407
|
+
when '!' # != (mandatory)
|
|
2408
|
+
if not ntok = parser.readtok or ntok.type != :punct and ntok.raw != '='
|
|
2409
|
+
parser.unreadtok op
|
|
2410
|
+
return
|
|
2411
|
+
end
|
|
2412
|
+
op.raw << ntok.raw
|
|
2413
|
+
when '+', '-', '*', '/', '%', '^', '=', ',', '?', ':', '>>', '<<', '||', '&&',
|
|
2414
|
+
'+=','-=','*=','/=','%=','^=','==','&=','|=','!=' # ok
|
|
2415
|
+
else # bad
|
|
2416
|
+
parser.unreadtok op
|
|
2417
|
+
return
|
|
2418
|
+
end
|
|
2419
|
+
|
|
2420
|
+
# may be followed by '='
|
|
2421
|
+
case op.raw
|
|
2422
|
+
when '+', '-', '*', '/', '%', '^', '&', '|', '>>', '<<', '<', '>', '='
|
|
2423
|
+
if ntok = parser.readtok and ntok.type == :punct and ntok.raw == '='
|
|
2424
|
+
op.raw << ntok.raw
|
|
2425
|
+
else
|
|
2426
|
+
parser.unreadtok ntok
|
|
2427
|
+
end
|
|
2428
|
+
end
|
|
2429
|
+
|
|
2430
|
+
op.value = op.raw.to_sym
|
|
2431
|
+
op
|
|
2432
|
+
end
|
|
2433
|
+
|
|
2434
|
+
# parse sizeof offsetof float immediate etc into tok.value
|
|
2435
|
+
def parse_intfloat(parser, scope, tok)
|
|
2436
|
+
if tok.type == :string and not tok.value
|
|
2437
|
+
case tok.raw
|
|
2438
|
+
when 'sizeof'
|
|
2439
|
+
if ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '('
|
|
2440
|
+
# check type
|
|
2441
|
+
if v = Variable.parse_type(parser, scope)
|
|
2442
|
+
v.parse_declarator(parser, scope)
|
|
2443
|
+
raise tok if v.name != false
|
|
2444
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2445
|
+
else
|
|
2446
|
+
raise tok, 'expr expected' if not v = parse(parser, scope)
|
|
2447
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2448
|
+
end
|
|
2449
|
+
else
|
|
2450
|
+
parser.unreadtok ntok
|
|
2451
|
+
raise tok, 'expr expected' if not v = parse_value(parser, scope)
|
|
2452
|
+
end
|
|
2453
|
+
tok.value = parser.sizeof(v)
|
|
2454
|
+
return
|
|
2455
|
+
when '__builtin_offsetof'
|
|
2456
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != '('
|
|
2457
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string or ntok.raw != 'struct'
|
|
2458
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string
|
|
2459
|
+
raise tok, 'unknown structure' if not struct = scope.struct_ancestors[ntok.raw] or not struct.kind_of? Union or not struct.members
|
|
2460
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
|
|
2461
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :string
|
|
2462
|
+
tok.value = struct.offsetof(parser, ntok.raw)
|
|
2463
|
+
raise tok if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2464
|
+
return
|
|
2465
|
+
end
|
|
2466
|
+
end
|
|
2467
|
+
|
|
2468
|
+
Expression.parse_num_value(parser, tok)
|
|
2469
|
+
end
|
|
2470
|
+
|
|
2471
|
+
# returns the next value from parser (parenthesised expression, immediate, variable, unary operators)
|
|
2472
|
+
def parse_value(parser, scope)
|
|
2473
|
+
return if not tok = parser.skipspaces
|
|
2474
|
+
case tok.type
|
|
2475
|
+
when :string
|
|
2476
|
+
parse_intfloat(parser, scope, tok)
|
|
2477
|
+
val = tok.value || tok.raw
|
|
2478
|
+
if val.kind_of? ::String
|
|
2479
|
+
raise tok, 'undefined variable' if not val = scope.symbol_ancestors[val]
|
|
2480
|
+
end
|
|
2481
|
+
case val
|
|
2482
|
+
when Type
|
|
2483
|
+
raise tok, 'invalid variable'
|
|
2484
|
+
when Variable
|
|
2485
|
+
val = parse_value_postfix(parser, scope, val)
|
|
2486
|
+
when ::Float
|
|
2487
|
+
# parse suffix
|
|
2488
|
+
type = :double
|
|
2489
|
+
if (?0..?9).include?(tok.raw[0])
|
|
2490
|
+
case tok.raw.downcase[-1]
|
|
2491
|
+
when ?l; type = :longdouble
|
|
2492
|
+
when ?f; type = :float
|
|
2493
|
+
end
|
|
2494
|
+
end
|
|
2495
|
+
val = CExpression[val, BaseType.new(type)]
|
|
2496
|
+
|
|
2497
|
+
when ::Integer
|
|
2498
|
+
# parse suffix
|
|
2499
|
+
# XXX 010h ?
|
|
2500
|
+
type = :int
|
|
2501
|
+
specifier = []
|
|
2502
|
+
if (?0..?9).include?(tok.raw[0])
|
|
2503
|
+
suffix = tok.raw.downcase[-3, 3] || tok.raw.downcase[-2, 2] || tok.raw.downcase[-1, 1] # short string
|
|
2504
|
+
specifier << :unsigned if suffix.include?('u') # XXX or tok.raw.downcase[1] == ?x
|
|
2505
|
+
type = :longlong if suffix.count('l') == 2
|
|
2506
|
+
type = :long if suffix.count('l') == 1
|
|
2507
|
+
end
|
|
2508
|
+
val = CExpression[val, BaseType.new(type, *specifier)]
|
|
2509
|
+
else raise parser, "internal error #{val.inspect}"
|
|
2510
|
+
end
|
|
2511
|
+
|
|
2512
|
+
when :quoted
|
|
2513
|
+
if tok.raw[0] == ?'
|
|
2514
|
+
raise tok, 'invalid character constant' if not [1, 2, 4, 8].include? tok.value.length # TODO 0fill
|
|
2515
|
+
val = CExpression[Expression.decode_imm(tok.value, tok.value.length, :big), BaseType.new(:int)]
|
|
2516
|
+
else
|
|
2517
|
+
val = CExpression[tok.value, Pointer.new(BaseType.new(tok.raw[0, 2] == 'L"' ? :short : :char))]
|
|
2518
|
+
val = parse_value_postfix(parser, scope, val)
|
|
2519
|
+
end
|
|
2520
|
+
|
|
2521
|
+
when :punct
|
|
2522
|
+
case tok.raw
|
|
2523
|
+
when '('
|
|
2524
|
+
ntok = nil
|
|
2525
|
+
# check type casting
|
|
2526
|
+
if v = Variable.parse_type(parser, scope)
|
|
2527
|
+
v.parse_declarator(parser, scope)
|
|
2528
|
+
(v.type.attributes ||= []).concat v.attributes if v.attributes
|
|
2529
|
+
raise tok, 'bad cast' if v.name != false
|
|
2530
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2531
|
+
raise ntok, 'expr expected' if not val = parse_value(parser, scope) # parses postfix too
|
|
2532
|
+
#raise ntok, 'unable to cast a struct' if val.type.untypedef.kind_of? Union
|
|
2533
|
+
val = CExpression[[val], v.type]
|
|
2534
|
+
# check compound statement expression
|
|
2535
|
+
elsif ntok = parser.skipspaces and ntok.type == :punct and ntok.raw == '{'
|
|
2536
|
+
parser.unreadtok ntok
|
|
2537
|
+
blk = parser.parse_statement(scope, [:expression]) # XXX nesting ?
|
|
2538
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2539
|
+
type = blk.statements.last.kind_of?(CExpression) ? blk.statements.last.type : BaseType.new(:void)
|
|
2540
|
+
val = CExpression[blk, type]
|
|
2541
|
+
else
|
|
2542
|
+
parser.unreadtok ntok
|
|
2543
|
+
if not val = parse(parser, scope)
|
|
2544
|
+
parser.unreadtok tok
|
|
2545
|
+
return
|
|
2546
|
+
end
|
|
2547
|
+
raise ntok || tok, 'no ")" found' if not ntok = parser.readtok or ntok.type != :punct or ntok.raw != ')'
|
|
2548
|
+
val = parse_value_postfix(parser, scope, val)
|
|
2549
|
+
end
|
|
2550
|
+
when '.' # float
|
|
2551
|
+
parse_intfloat(parser, scope, tok)
|
|
2552
|
+
if not tok.value
|
|
2553
|
+
parser.unreadtok tok
|
|
2554
|
+
return
|
|
2555
|
+
end
|
|
2556
|
+
val = tok.value || tok.raw
|
|
2557
|
+
type = :double
|
|
2558
|
+
case tok.raw.downcase[-1]
|
|
2559
|
+
when ?l; type = :longdouble
|
|
2560
|
+
when ?f; type = :float
|
|
2561
|
+
end
|
|
2562
|
+
val = CExpression.new[val, BaseType.new(type)]
|
|
2563
|
+
|
|
2564
|
+
when '+', '-', '&', '!', '~', '*', '--', '++', '&&'
|
|
2565
|
+
# unary prefix
|
|
2566
|
+
# may have been read ahead
|
|
2567
|
+
|
|
2568
|
+
raise parser if not ntok = parser.readtok
|
|
2569
|
+
# check for -- ++ &&
|
|
2570
|
+
if ntok.type == :punct and ntok.raw == tok.raw and %w[+ - &].include?(tok.raw)
|
|
2571
|
+
tok.raw << ntok.raw
|
|
2572
|
+
else
|
|
2573
|
+
parser.unreadtok ntok
|
|
2574
|
+
end
|
|
2575
|
+
|
|
2576
|
+
case tok.raw
|
|
2577
|
+
when '&'
|
|
2578
|
+
val = parse_value(parser, scope)
|
|
2579
|
+
if val.kind_of? CExpression and val.op == :& and not val.lexpr and
|
|
2580
|
+
(val.rexpr.kind_of? Variable or val.rexpr.kind_of? CExpression) and val.rexpr.type.kind_of? Function
|
|
2581
|
+
# &&function == &function
|
|
2582
|
+
elsif (val.kind_of? CExpression or val.kind_of? Variable) and val.type.kind_of? Array
|
|
2583
|
+
# &ary = ary
|
|
2584
|
+
else
|
|
2585
|
+
raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
|
|
2586
|
+
raise val.backtrace, 'cannot take addr of register' if val.kind_of? Variable and val.storage == :register and not parser.allow_bad_c
|
|
2587
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, Pointer.new(val.type))
|
|
2588
|
+
end
|
|
2589
|
+
when '++', '--'
|
|
2590
|
+
val = parse_value(parser, scope)
|
|
2591
|
+
raise parser, "invalid lvalue #{val}" if not CExpression.lvalue?(val) and not parser.allow_bad_c
|
|
2592
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
|
|
2593
|
+
when '&&'
|
|
2594
|
+
raise tok, 'label name expected' if not val = parser.skipspaces or val.type != :string
|
|
2595
|
+
val = CExpression.new(nil, nil, Label.new(val.raw, nil), Pointer.new(BaseType.new(:void)))
|
|
2596
|
+
when '*'
|
|
2597
|
+
raise tok, 'expr expected' if not val = parse_value(parser, scope)
|
|
2598
|
+
raise tok, 'not a pointer' if not val.type.pointer? and not parser.allow_bad_c
|
|
2599
|
+
newtype = val.type.pointer? ? val.type.pointed : BaseType.new(:int)
|
|
2600
|
+
if not newtype.untypedef.kind_of? Function # *fptr == fptr
|
|
2601
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, newtype)
|
|
2602
|
+
end
|
|
2603
|
+
when '~', '!', '+', '-'
|
|
2604
|
+
raise tok, 'expr expected' if not val = parse_value(parser, scope)
|
|
2605
|
+
raise tok, 'type not arithmetic' if not val.type.arithmetic? and not parser.allow_bad_c
|
|
2606
|
+
val = CExpression.new(nil, tok.raw.to_sym, val, val.type)
|
|
2607
|
+
val.type = BaseType.new(:int) if tok.raw == '!'
|
|
2608
|
+
else raise tok, 'internal error'
|
|
2609
|
+
end
|
|
2610
|
+
else
|
|
2611
|
+
parser.unreadtok tok
|
|
2612
|
+
return
|
|
2613
|
+
end
|
|
2614
|
+
else
|
|
2615
|
+
parser.unreadtok tok
|
|
2616
|
+
return
|
|
2617
|
+
end
|
|
2618
|
+
|
|
2619
|
+
if val.kind_of? Variable and val.type.kind_of? Function
|
|
2620
|
+
# void (*bla)() = printf; => ...= &printf;
|
|
2621
|
+
val = CExpression[:&, val]
|
|
2622
|
+
end
|
|
2623
|
+
|
|
2624
|
+
val
|
|
2625
|
+
end
|
|
2626
|
+
|
|
2627
|
+
# parse postfix forms (postincrement, array index, struct member dereference)
|
|
2628
|
+
def parse_value_postfix(parser, scope, val)
|
|
2629
|
+
tok = parser.skipspaces
|
|
2630
|
+
nval = \
|
|
2631
|
+
if tok and tok.type == :punct
|
|
2632
|
+
case tok.raw
|
|
2633
|
+
when '+', '++', '-', '--', '->'
|
|
2634
|
+
ntok = parser.readtok
|
|
2635
|
+
if (tok.raw == '+' or tok.raw == '-') and ntok and ntok.type == :punct and
|
|
2636
|
+
(ntok.raw == tok.raw or (tok.raw == '-' and ntok.raw == '>'))
|
|
2637
|
+
tok.raw << ntok.raw
|
|
2638
|
+
else
|
|
2639
|
+
parser.unreadtok ntok
|
|
2640
|
+
end
|
|
2641
|
+
case tok.raw
|
|
2642
|
+
when '+', '-'
|
|
2643
|
+
nil
|
|
2644
|
+
when '++', '--'
|
|
2645
|
+
raise parser, "#{val}: invalid lvalue" if not CExpression.lvalue?(val)
|
|
2646
|
+
CExpression.new(val, tok.raw.to_sym, nil, val.type)
|
|
2647
|
+
when '->'
|
|
2648
|
+
# XXX allow_bad_c..
|
|
2649
|
+
raise tok, "#{val}: not a pointer" if not val.type.pointer?
|
|
2650
|
+
type = val.type.pointed.untypedef
|
|
2651
|
+
raise tok, "#{val}: bad pointer" if not type.kind_of? Union
|
|
2652
|
+
raise tok, "#{val}: incomplete type" if not type.members
|
|
2653
|
+
raise tok, "#{val}: invalid member" if not tok = parser.skipspaces or tok.type != :string or not m = type.findmember(tok.raw)
|
|
2654
|
+
CExpression.new(val, :'->', tok.raw, m.type)
|
|
2655
|
+
end
|
|
2656
|
+
when '.'
|
|
2657
|
+
type = val.type.untypedef
|
|
2658
|
+
if not ntok = parser.skipspaces or ntok.type != :string or not type.kind_of? Union
|
|
2659
|
+
parser.unreadtok ntok
|
|
2660
|
+
nil
|
|
2661
|
+
else
|
|
2662
|
+
raise ntok, "#{val}: incomplete type" if not type.members
|
|
2663
|
+
raise ntok, "#{val}: invalid member" if not m = type.findmember(ntok.raw)
|
|
2664
|
+
CExpression.new(val, :'.', ntok.raw, m.type)
|
|
2665
|
+
end
|
|
2666
|
+
when '['
|
|
2667
|
+
raise tok, "#{val}: index expected" if not idx = parse(parser, scope)
|
|
2668
|
+
val, idx = idx, val if not val.type.pointer? # fake support of "4[tab]"
|
|
2669
|
+
raise tok, "#{val}: not a pointer" if not val.type.pointer?
|
|
2670
|
+
raise tok, "#{val}: invalid index" if not idx.type.integral?
|
|
2671
|
+
raise tok, "#{val}: get perpendicular ! (elsewhere)" if idx.kind_of?(CExpression) and idx.op == :','
|
|
2672
|
+
raise tok || parser, "']' expected" if not tok = parser.skipspaces or tok.type != :punct or tok.raw != ']'
|
|
2673
|
+
type = val.type.untypedef.type
|
|
2674
|
+
# TODO boundscheck (and become king of the universe)
|
|
2675
|
+
CExpression.new(val, :'[]', idx, type)
|
|
2676
|
+
when '('
|
|
2677
|
+
type = val.type.untypedef
|
|
2678
|
+
type = type.type.untypedef if type.kind_of? Pointer
|
|
2679
|
+
raise tok, "#{val}: not a function" if not type.kind_of? Function
|
|
2680
|
+
|
|
2681
|
+
args = []
|
|
2682
|
+
loop do
|
|
2683
|
+
a = parse(parser, scope, false)
|
|
2684
|
+
break if not a
|
|
2685
|
+
args << a
|
|
2686
|
+
if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ','
|
|
2687
|
+
parser.unreadtok ntok
|
|
2688
|
+
break
|
|
2689
|
+
end
|
|
2690
|
+
end
|
|
2691
|
+
raise ntok || parser, "#{val}: ')' expected" if not ntok = parser.skipspaces or ntok.type != :punct or ntok.raw != ')'
|
|
2692
|
+
|
|
2693
|
+
type.args ||= []
|
|
2694
|
+
raise tok, "#{val}: bad argument count: #{args.length} for #{type.args.length}" if (type.varargs ? (args.length < type.args.length) : (args.length != type.args.length))
|
|
2695
|
+
type.args.zip(args) { |ta, a|
|
|
2696
|
+
p, i = ta.type.pointer?, ta.type.integral?
|
|
2697
|
+
r = a.reduce(parser) if p or i
|
|
2698
|
+
if (not p and not i) or (i and not r.kind_of? ::Integer) or (p and r != 0)
|
|
2699
|
+
tok = tok.dup ; tok.raw = a.to_s
|
|
2700
|
+
parser.check_compatible_type(tok, a.type, ta.type)
|
|
2701
|
+
end
|
|
2702
|
+
}
|
|
2703
|
+
CExpression.new(val, :funcall, args, type.type)
|
|
2704
|
+
end
|
|
2705
|
+
end
|
|
2706
|
+
|
|
2707
|
+
if nval
|
|
2708
|
+
parse_value_postfix(parser, scope, nval)
|
|
2709
|
+
else
|
|
2710
|
+
parser.unreadtok tok
|
|
2711
|
+
val
|
|
2712
|
+
end
|
|
2713
|
+
end
|
|
2714
|
+
|
|
2715
|
+
def parse(parser, scope, allow_coma = true)
|
|
2716
|
+
opstack = []
|
|
2717
|
+
stack = []
|
|
2718
|
+
|
|
2719
|
+
popstack = lambda {
|
|
2720
|
+
r, l = stack.pop, stack.pop
|
|
2721
|
+
case op = opstack.pop
|
|
2722
|
+
when :'?:'
|
|
2723
|
+
stack << CExpression.new(stack.pop, op, [l, r], l.type)
|
|
2724
|
+
when :','
|
|
2725
|
+
stack << CExpression.new(l, op, r, r.type)
|
|
2726
|
+
when :'='
|
|
2727
|
+
parser.check_compatible_type(parser, r.type, l.type)
|
|
2728
|
+
stack << CExpression.new(l, op, r, l.type)
|
|
2729
|
+
when :'&&', :'||'
|
|
2730
|
+
stack << CExpression.new(l, op, r, BaseType.new(:int))
|
|
2731
|
+
else
|
|
2732
|
+
# XXX struct == struct ?
|
|
2733
|
+
raise parser, "invalid type #{l.type} #{l} for #{op.inspect}" if not l.type.arithmetic? and not parser.allow_bad_c
|
|
2734
|
+
raise parser, "invalid type #{r.type} #{r} for #{op.inspect}" if not r.type.arithmetic? and not parser.allow_bad_c
|
|
2735
|
+
|
|
2736
|
+
if l.type.pointer? and r.type.pointer?
|
|
2737
|
+
type = \
|
|
2738
|
+
case op
|
|
2739
|
+
when :'-'; BaseType.new(:long) # addr_t or sumthin ?
|
|
2740
|
+
when :'-='; l.type
|
|
2741
|
+
when :'>', :'>=', :'<', :'<=', :'==', :'!='; BaseType.new(:long)
|
|
2742
|
+
else raise parser, "cannot do #{op.inspect} on pointers" unless parser.allow_bad_c ; l.type
|
|
2743
|
+
end
|
|
2744
|
+
elsif l.type.pointer? or r.type.pointer?
|
|
2745
|
+
puts parser.exception("should not #{op.inspect} a pointer").message if $VERBOSE and not [:'+', :'-', :'=', :'+=', :'-=', :==, :'!='].include? op
|
|
2746
|
+
type = l.type.pointer? ? l.type : r.type
|
|
2747
|
+
else
|
|
2748
|
+
# yay integer promotion
|
|
2749
|
+
lt = l.type.untypedef
|
|
2750
|
+
rt = r.type.untypedef
|
|
2751
|
+
if (t = lt).name == :longdouble or (t = rt).name == :longdouble or
|
|
2752
|
+
(t = lt).name == :double or (t = rt).name == :double or
|
|
2753
|
+
(t = lt).name == :float or (t = rt).name == :float
|
|
2754
|
+
# long double > double > float ...
|
|
2755
|
+
type = t
|
|
2756
|
+
elsif true
|
|
2757
|
+
# custom integer rules based on type sizes
|
|
2758
|
+
lts = parser.typesize[lt.name]
|
|
2759
|
+
rts = parser.typesize[rt.name]
|
|
2760
|
+
its = parser.typesize[:int]
|
|
2761
|
+
if not lts or not rts
|
|
2762
|
+
type = BaseType.new(:int)
|
|
2763
|
+
elsif lts > rts and lts >= its
|
|
2764
|
+
type = lt
|
|
2765
|
+
elsif rts > lts and rts >= its
|
|
2766
|
+
type = rt
|
|
2767
|
+
elsif lts == rts and lts >= its
|
|
2768
|
+
type = lt
|
|
2769
|
+
type = rt if rt.specifier == :unsigned
|
|
2770
|
+
else
|
|
2771
|
+
type = BaseType.new(:int)
|
|
2772
|
+
end
|
|
2773
|
+
# end of custom rules
|
|
2774
|
+
elsif ((t = lt).name == :long and t.specifier == :unsigned) or
|
|
2775
|
+
((t = rt).name == :long and t.specifier == :unsigned)
|
|
2776
|
+
# ... ulong ...
|
|
2777
|
+
type = t
|
|
2778
|
+
elsif (lt.name == :long and rt.name == :int and rt.specifier == :unsigned) or
|
|
2779
|
+
(rt.name == :long and lt.name == :int and lt.specifier == :unsigned)
|
|
2780
|
+
# long+uint => ulong
|
|
2781
|
+
type = BaseType.new(:long, :unsigned)
|
|
2782
|
+
elsif (t = lt).name == :long or (t = rt).name == :long or
|
|
2783
|
+
((t = lt).name == :int and t.specifier == :unsigned) or
|
|
2784
|
+
((t = rt).name == :int and t.specifier == :unsigned)
|
|
2785
|
+
# ... long > uint ...
|
|
2786
|
+
type = t
|
|
2787
|
+
else
|
|
2788
|
+
# int
|
|
2789
|
+
type = BaseType.new(:int)
|
|
2790
|
+
end
|
|
2791
|
+
end
|
|
2792
|
+
|
|
2793
|
+
case op
|
|
2794
|
+
when :'>', :'>=', :'<', :'<=', :'==', :'!='
|
|
2795
|
+
# cast both sides
|
|
2796
|
+
l = CExpression[l, type] if l.type != type
|
|
2797
|
+
r = CExpression[r, type] if r.type != type
|
|
2798
|
+
stack << CExpression.new(l, op, r, BaseType.new(:int))
|
|
2799
|
+
else
|
|
2800
|
+
# promote result
|
|
2801
|
+
stack << CExpression.new(l, op, r, type)
|
|
2802
|
+
end
|
|
2803
|
+
end
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
return if not e = parse_value(parser, scope)
|
|
2807
|
+
|
|
2808
|
+
stack << e
|
|
2809
|
+
|
|
2810
|
+
while op = readop(parser)
|
|
2811
|
+
case op.value
|
|
2812
|
+
when :'?'
|
|
2813
|
+
# a, b ? c, d : e, f == a, (b ? (c, d) : e), f
|
|
2814
|
+
until opstack.empty? or OP_PRIO[opstack.last][:'?:']
|
|
2815
|
+
popstack[]
|
|
2816
|
+
end
|
|
2817
|
+
stack << parse(parser, scope)
|
|
2818
|
+
raise op || parser, '":" expected' if not op = readop(parser) or op.value != :':'
|
|
2819
|
+
op = op.dup
|
|
2820
|
+
op.value = :'?:'
|
|
2821
|
+
when :':'
|
|
2822
|
+
parser.unreadtok op
|
|
2823
|
+
break
|
|
2824
|
+
else
|
|
2825
|
+
if not allow_coma and op.value == :','
|
|
2826
|
+
parser.unreadtok op
|
|
2827
|
+
break
|
|
2828
|
+
end
|
|
2829
|
+
until opstack.empty? or OP_PRIO[op.value][opstack.last]
|
|
2830
|
+
popstack[]
|
|
2831
|
+
end
|
|
2832
|
+
end
|
|
2833
|
+
|
|
2834
|
+
raise op, 'need rhs' if not e = parse_value(parser, scope)
|
|
2835
|
+
stack << e
|
|
2836
|
+
opstack << op.value
|
|
2837
|
+
end
|
|
2838
|
+
|
|
2839
|
+
until opstack.empty?
|
|
2840
|
+
popstack[]
|
|
2841
|
+
end
|
|
2842
|
+
|
|
2843
|
+
CExpression[stack.first]
|
|
2844
|
+
end
|
|
2845
|
+
end
|
|
2846
|
+
end
|
|
2847
|
+
|
|
2848
|
+
|
|
2849
|
+
|
|
2850
|
+
#
|
|
2851
|
+
# AllocCStruct: ruby interpreter memory-mapped structure
|
|
2852
|
+
#
|
|
2853
|
+
|
|
2854
|
+
# this maps a C structure from a C parser to a ruby String
|
|
2855
|
+
# struct members are accessed through obj['fldname']
|
|
2856
|
+
# obj.fldname is an alias
|
|
2857
|
+
class AllocCStruct
|
|
2858
|
+
# str is a reference to the underlying ruby String
|
|
2859
|
+
# stroff is the offset from the start of this string (non-nul for nested structs)
|
|
2860
|
+
# cp is a reference to the C::Parser
|
|
2861
|
+
# struct to the C::Union/Struct/Array
|
|
2862
|
+
# sizeof is the byte size of the C struct
|
|
2863
|
+
attr_accessor :str, :stroff, :cp, :struct
|
|
2864
|
+
attr_writer :sizeof
|
|
2865
|
+
def initialize(cp, struct, str=nil, stroff=0)
|
|
2866
|
+
@cp, @struct = cp, struct
|
|
2867
|
+
@str = str || [0].pack('C')*sizeof
|
|
2868
|
+
@stroff = stroff
|
|
2869
|
+
end
|
|
2870
|
+
|
|
2871
|
+
def sizeof
|
|
2872
|
+
@sizeof ||= @cp.sizeof(@struct)
|
|
2873
|
+
end
|
|
2874
|
+
|
|
2875
|
+
def [](*a)
|
|
2876
|
+
if @struct.kind_of? C::Array and a.length == 1 and @struct.length and a[0].kind_of? Integer
|
|
2877
|
+
i = a[0]
|
|
2878
|
+
raise "#{i} out of bounds 0...#{@struct.length}" if i < 0 or i >= @struct.length
|
|
2879
|
+
off = @stroff + i*@cp.sizeof(@struct.type)
|
|
2880
|
+
return @cp.decode_c_value(@str, @struct.type, off)
|
|
2881
|
+
end
|
|
2882
|
+
|
|
2883
|
+
return @str[@stroff..-1][*a] if a.length != 1
|
|
2884
|
+
a = a.first
|
|
2885
|
+
return @str[@stroff..-1][a] if not a.kind_of? Symbol and not a.kind_of? String and not a.kind_of? C::Variable
|
|
2886
|
+
f = a
|
|
2887
|
+
raise "#{a.inspect} not a member" if not f.kind_of? C::Variable and not f = @struct.findmember(a.to_s, true)
|
|
2888
|
+
a = f.name || f
|
|
2889
|
+
off = @stroff + @struct.offsetof(@cp, a)
|
|
2890
|
+
if bf = @struct.bitoffsetof(@cp, a)
|
|
2891
|
+
ft = C::BaseType.new((bf[0] + bf[1] > 32) ? :__int64 : :__int32)
|
|
2892
|
+
v = @cp.decode_c_value(@str, ft, off)
|
|
2893
|
+
(v >> bf[0]) & ((1 << bf[1])-1)
|
|
2894
|
+
else
|
|
2895
|
+
@cp.decode_c_value(@str, f, off)
|
|
2896
|
+
end
|
|
2897
|
+
end
|
|
2898
|
+
|
|
2899
|
+
def []=(*a)
|
|
2900
|
+
if @struct.kind_of? C::Array and a.length == 2 and @struct.length and a[0].kind_of? Integer
|
|
2901
|
+
i = a[0]
|
|
2902
|
+
raise "#{i} out of bounds 0...#{@struct.length}" if i < 0 or i >= @struct.length
|
|
2903
|
+
off = @stroff + i*@cp.sizeof(@struct.type)
|
|
2904
|
+
val = @cp.encode_c_value(@struct.type, a[1])
|
|
2905
|
+
@str[off, val.length] = val
|
|
2906
|
+
return
|
|
2907
|
+
end
|
|
2908
|
+
|
|
2909
|
+
if not a.first.kind_of? Symbol and not a.first.kind_of? String and not a.first.kind_of? C::Variable
|
|
2910
|
+
# patch @str[@stroff..-1] like a string
|
|
2911
|
+
# so we must find the intended start offset, and add @stroff to it
|
|
2912
|
+
if @stroff != 0
|
|
2913
|
+
case a.first
|
|
2914
|
+
when Range
|
|
2915
|
+
if a.first.begin >= 0
|
|
2916
|
+
a[0] = ::Range.new(a[0].begin+@stroff, a[0].end+@stroff, a[0].exclude_end?)
|
|
2917
|
+
else raise 'no can do, use positive index'
|
|
2918
|
+
end
|
|
2919
|
+
when Integer
|
|
2920
|
+
if a.first >= 0
|
|
2921
|
+
a[0] += @stroff
|
|
2922
|
+
else raise 'no can do, use positive index'
|
|
2923
|
+
end
|
|
2924
|
+
else raise 'no can do'
|
|
2925
|
+
end
|
|
2926
|
+
end
|
|
2927
|
+
|
|
2928
|
+
return @str.send(:'[]=', *a) # XXX *should* work...
|
|
2929
|
+
end
|
|
2930
|
+
|
|
2931
|
+
a, val = a
|
|
2932
|
+
raise "#{a.inspect} not a struct member" if not a.kind_of? C::Variable and not f = @struct.findmember(a.to_s, true)
|
|
2933
|
+
a = f.name if a.kind_of? String or a.kind_of? Symbol
|
|
2934
|
+
val = sizeof if val == :size
|
|
2935
|
+
off = @stroff + @struct.offsetof(@cp, a)
|
|
2936
|
+
|
|
2937
|
+
if bf = @struct.bitoffsetof(@cp, a)
|
|
2938
|
+
raise "only Integers supported in bitfield #{a}, got #{val.inspect}" if not val.kind_of?(::Integer)
|
|
2939
|
+
# struct { int i:8; }; => size 8 or 32 ?
|
|
2940
|
+
ft = C::BaseType.new((bf[0] + bf[1] > 32) ? :__int64 : :__int32)
|
|
2941
|
+
mask = ((1 << bf[1]) - 1) << bf[0]
|
|
2942
|
+
preval = @cp.decode_c_value(@str, ft, off)
|
|
2943
|
+
val = (preval & ~mask) | ((val << bf[0]) & mask)
|
|
2944
|
+
f = ft
|
|
2945
|
+
end
|
|
2946
|
+
|
|
2947
|
+
val = @cp.encode_c_value(f, val)
|
|
2948
|
+
@str[off, val.length] = val
|
|
2949
|
+
end
|
|
2950
|
+
|
|
2951
|
+
# virtual accessors to members
|
|
2952
|
+
# struct.foo is aliased to struct['foo'],
|
|
2953
|
+
# struct.foo = 42 aliased to struct['foo'] = 42
|
|
2954
|
+
def method_missing(on, *a)
|
|
2955
|
+
n = on.to_s
|
|
2956
|
+
if n[-1] == ?=
|
|
2957
|
+
send :[]=, n[0...-1], *a
|
|
2958
|
+
else
|
|
2959
|
+
super(on, *a) if not @struct.kind_of?(C::Union) or not @struct.findmember(n, true)
|
|
2960
|
+
send :[], n, *a
|
|
2961
|
+
end
|
|
2962
|
+
end
|
|
2963
|
+
|
|
2964
|
+
def to_s(off=nil, maxdepth=500)
|
|
2965
|
+
return '{ /* ... */ }' if maxdepth <= 0
|
|
2966
|
+
str = ['']
|
|
2967
|
+
if @struct.kind_of?(C::Array)
|
|
2968
|
+
str.last << "#{@struct.type} x[#{@struct.length}] = " if not off
|
|
2969
|
+
mlist = (0...@struct.length)
|
|
2970
|
+
fldoff = mlist.inject({}) { |h, i| h.update i => i*@cp.sizeof(@struct.type) }
|
|
2971
|
+
elsif @struct.kind_of?(C::Struct)
|
|
2972
|
+
str.last << "struct #{@struct.name || '_'} x = " if not off
|
|
2973
|
+
@struct.update_member_cache(@cp) if not @struct.fldlist
|
|
2974
|
+
fldoff = @struct.fldoffset
|
|
2975
|
+
fbo = @struct.fldbitoffset || {}
|
|
2976
|
+
mlist = @struct.members.map { |m| m.name || m }
|
|
2977
|
+
else
|
|
2978
|
+
str.last << "union #{@struct.name || '_'} x = " if not off
|
|
2979
|
+
mlist = @struct.members.map { |m| m.name || m }
|
|
2980
|
+
end
|
|
2981
|
+
str.last << '{'
|
|
2982
|
+
mlist.each { |k|
|
|
2983
|
+
if k.kind_of? Variable # anonymous member
|
|
2984
|
+
curoff = off.to_i + @struct.offsetof(@cp, k)
|
|
2985
|
+
val = self[k]
|
|
2986
|
+
k = '?'
|
|
2987
|
+
else
|
|
2988
|
+
curoff = off.to_i + (fldoff ? fldoff[k].to_i : 0)
|
|
2989
|
+
val = self[k]
|
|
2990
|
+
end
|
|
2991
|
+
if val.kind_of?(::Integer)
|
|
2992
|
+
if val >= 0x100
|
|
2993
|
+
val = '0x%X, // +%x' % [val, curoff]
|
|
2994
|
+
elsif val <= -0x100
|
|
2995
|
+
val = '-0x%X, // +%x' % [-val, curoff]
|
|
2996
|
+
else
|
|
2997
|
+
val = '%d, // +%x' % [val, curoff]
|
|
2998
|
+
end
|
|
2999
|
+
elsif val.kind_of? AllocCStruct
|
|
3000
|
+
val = val.to_s(curoff, maxdepth-1)
|
|
3001
|
+
elsif not val
|
|
3002
|
+
val = 'NULL, // +%x' % curoff # pointer with NULL value
|
|
3003
|
+
else
|
|
3004
|
+
val = val.to_s.sub(/$/, ', // +%x' % curoff)
|
|
3005
|
+
end
|
|
3006
|
+
val = val.gsub("\n", "\n\t")
|
|
3007
|
+
str << "\t#{k.kind_of?(::Integer) ? "[#{k}]" : ".#{k}"} = #{val}"
|
|
3008
|
+
}
|
|
3009
|
+
str << '}'
|
|
3010
|
+
str.last << (off ? ',' : ';')
|
|
3011
|
+
str.join("\n")
|
|
3012
|
+
end
|
|
3013
|
+
|
|
3014
|
+
def to_array
|
|
3015
|
+
raise NoMethodError, "Not an Array" if not @struct.kind_of?(C::Array)
|
|
3016
|
+
ary = []
|
|
3017
|
+
@struct.length.times { |i| ary << self[i] }
|
|
3018
|
+
ary
|
|
3019
|
+
end
|
|
3020
|
+
|
|
3021
|
+
def to_strz
|
|
3022
|
+
raise NoMethodError, "Not an Array" if not @struct.kind_of?(C::Array)
|
|
3023
|
+
a = to_array
|
|
3024
|
+
a[a.index(0)..-1] = [] if a.index(0)
|
|
3025
|
+
a.pack('C*')
|
|
3026
|
+
end
|
|
3027
|
+
end
|
|
3028
|
+
|
|
3029
|
+
class Parser
|
|
3030
|
+
# find a Struct/Union object from a struct name/typedef name
|
|
3031
|
+
# raises if it cant find it
|
|
3032
|
+
def find_c_struct(structname)
|
|
3033
|
+
structname = structname.to_s if structname.kind_of?(::Symbol)
|
|
3034
|
+
if structname.kind_of?(::String) and not struct = @toplevel.struct[structname]
|
|
3035
|
+
struct = @toplevel.symbol[structname]
|
|
3036
|
+
raise "unknown struct #{structname.inspect}" if not struct
|
|
3037
|
+
struct = struct.type.untypedef
|
|
3038
|
+
struct = struct.pointed while struct.pointer?
|
|
3039
|
+
raise "unknown struct #{structname.inspect}" if not struct.kind_of? C::Union
|
|
3040
|
+
end
|
|
3041
|
+
struct = structname if structname.kind_of? C::Union
|
|
3042
|
+
raise "unknown struct #{structname.inspect}" if not struct.kind_of? C::Union
|
|
3043
|
+
struct
|
|
3044
|
+
end
|
|
3045
|
+
|
|
3046
|
+
# find a C::Type (struct/union/typedef/basetype) from a string
|
|
3047
|
+
def find_c_type(typename)
|
|
3048
|
+
typename = typename.to_s if typename.kind_of? ::Symbol
|
|
3049
|
+
if typename.kind_of?(::String) and not type = @toplevel.struct[typename]
|
|
3050
|
+
if type = @toplevel.symbol[typename]
|
|
3051
|
+
type = type.type.untypedef
|
|
3052
|
+
else
|
|
3053
|
+
begin
|
|
3054
|
+
lexer.feed(typename)
|
|
3055
|
+
b = C::Block.new(@toplevel)
|
|
3056
|
+
var = Variable.parse_type(self, b)
|
|
3057
|
+
var.parse_declarator(self, b)
|
|
3058
|
+
type = var.type
|
|
3059
|
+
rescue
|
|
3060
|
+
end
|
|
3061
|
+
end
|
|
3062
|
+
end
|
|
3063
|
+
type = typename if typename.kind_of?(C::Type)
|
|
3064
|
+
raise "unknown type #{typename.inspect}" if not type.kind_of? C::Type
|
|
3065
|
+
type
|
|
3066
|
+
end
|
|
3067
|
+
|
|
3068
|
+
# allocate a new AllocCStruct from the struct/struct typedef name of the current toplevel
|
|
3069
|
+
# optionally populate the fields using the 'values' hash
|
|
3070
|
+
def alloc_c_struct(structname, values=nil)
|
|
3071
|
+
struct = find_c_struct(structname)
|
|
3072
|
+
st = AllocCStruct.new(self, struct)
|
|
3073
|
+
values.each { |k, v| st[k] = v } if values
|
|
3074
|
+
st
|
|
3075
|
+
end
|
|
3076
|
+
|
|
3077
|
+
# parse a given String as an AllocCStruct
|
|
3078
|
+
# offset is an optionnal offset from the string start
|
|
3079
|
+
# modification to the structure will modify the underlying string
|
|
3080
|
+
def decode_c_struct(structname, str, offset=0)
|
|
3081
|
+
struct = find_c_struct(structname)
|
|
3082
|
+
AllocCStruct.new(self, struct, str, offset)
|
|
3083
|
+
end
|
|
3084
|
+
|
|
3085
|
+
# allocate an array of types
|
|
3086
|
+
# init is either the length of the array, or an array of initial values
|
|
3087
|
+
def alloc_c_ary(typename, init=1)
|
|
3088
|
+
type = find_c_type(typename)
|
|
3089
|
+
len = init.kind_of?(Integer) ? init : init.length
|
|
3090
|
+
struct = C::Array.new(type, len)
|
|
3091
|
+
st = AllocCStruct.new(self, struct)
|
|
3092
|
+
if init.kind_of?(::Array)
|
|
3093
|
+
init.each_with_index { |v, i|
|
|
3094
|
+
st[i] = v
|
|
3095
|
+
}
|
|
3096
|
+
end
|
|
3097
|
+
st
|
|
3098
|
+
end
|
|
3099
|
+
|
|
3100
|
+
# "cast" a string to C::Array
|
|
3101
|
+
def decode_c_ary(typename, len, str, offset=0)
|
|
3102
|
+
type = find_c_type(typename)
|
|
3103
|
+
struct = C::Array.new(type, len)
|
|
3104
|
+
AllocCStruct.new(self, struct, str, offset)
|
|
3105
|
+
end
|
|
3106
|
+
|
|
3107
|
+
# convert (pack) a ruby value into a C buffer
|
|
3108
|
+
# packs integers, converts Strings to their C pointer (using DynLdr)
|
|
3109
|
+
def encode_c_value(type, val)
|
|
3110
|
+
type = type.type if type.kind_of? Variable
|
|
3111
|
+
|
|
3112
|
+
case val
|
|
3113
|
+
when nil; val = 0
|
|
3114
|
+
when ::Integer
|
|
3115
|
+
when ::String
|
|
3116
|
+
val = DynLdr.str_ptr(val)
|
|
3117
|
+
when ::Hash
|
|
3118
|
+
type = type.pointed while type.pointer?
|
|
3119
|
+
raise "need a struct ptr for #{type} #{val.inspect}" if not type.kind_of? Union
|
|
3120
|
+
buf = alloc_c_struct(type, val)
|
|
3121
|
+
val.instance_variable_set('@rb2c', buf) # GC trick
|
|
3122
|
+
val = buf
|
|
3123
|
+
when ::Proc
|
|
3124
|
+
val = DynLdr.convert_rb2c(type, val) # allocate a DynLdr callback
|
|
3125
|
+
when AllocCStruct
|
|
3126
|
+
val = DynLdr.str_ptr(val.str) + val.stroff
|
|
3127
|
+
#when ::Float # TODO
|
|
3128
|
+
else raise "TODO #{val.inspect}"
|
|
3129
|
+
end
|
|
3130
|
+
|
|
3131
|
+
val = Expression.encode_immediate(val, sizeof(type), @endianness) if val.kind_of?(::Integer)
|
|
3132
|
+
|
|
3133
|
+
val
|
|
3134
|
+
end
|
|
3135
|
+
|
|
3136
|
+
def decode_c_value(str, type, off=0)
|
|
3137
|
+
type = type.type if type.kind_of? Variable
|
|
3138
|
+
type = type.untypedef
|
|
3139
|
+
if type.kind_of? C::Union or type.kind_of? C::Array
|
|
3140
|
+
return AllocCStruct.new(self, type, str, off)
|
|
3141
|
+
end
|
|
3142
|
+
val = Expression.decode_immediate(str, sizeof(type), @endianness, off)
|
|
3143
|
+
val = Expression.make_signed(val, sizeof(type)*8) if type.integral? and type.signed?
|
|
3144
|
+
val = nil if val == 0 and type.pointer?
|
|
3145
|
+
val
|
|
3146
|
+
end
|
|
3147
|
+
end
|
|
3148
|
+
|
|
3149
|
+
|
|
3150
|
+
#
|
|
3151
|
+
# Dumper : objects => C source
|
|
3152
|
+
#
|
|
3153
|
+
|
|
3154
|
+
class Parser
|
|
3155
|
+
# returns a big string containing all definitions from headers used in the source (including macros)
|
|
3156
|
+
def factorize(*a)
|
|
3157
|
+
factorize_init
|
|
3158
|
+
parse(*a)
|
|
3159
|
+
raise @lexer.readtok || self, 'eof expected' if not @lexer.eos?
|
|
3160
|
+
factorize_final
|
|
3161
|
+
end
|
|
3162
|
+
|
|
3163
|
+
def factorize_init
|
|
3164
|
+
@lexer.traced_macros = []
|
|
3165
|
+
end
|
|
3166
|
+
|
|
3167
|
+
def factorize_final
|
|
3168
|
+
# now find all types/defs not coming from the standard headers
|
|
3169
|
+
# all
|
|
3170
|
+
all = @toplevel.struct.values + @toplevel.symbol.values
|
|
3171
|
+
all -= all.grep(::Integer) # Enum values
|
|
3172
|
+
|
|
3173
|
+
# list of definitions of user-defined objects
|
|
3174
|
+
userdefined = all.find_all { |t|
|
|
3175
|
+
t.backtrace.backtrace.grep(::String).grep(/^</).empty?
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
@toplevel.statements.clear # don't want all Declarations
|
|
3179
|
+
|
|
3180
|
+
# a macro is fine too
|
|
3181
|
+
@lexer.dump_macros(@lexer.traced_macros, false) + "\n\n" +
|
|
3182
|
+
dump_definitions(userdefined, userdefined)
|
|
3183
|
+
end
|
|
3184
|
+
|
|
3185
|
+
# returns a big string representing the definitions of all terms appearing in +list+, excluding +exclude+
|
|
3186
|
+
# includes dependencies
|
|
3187
|
+
def dump_definitions(list, exclude=[])
|
|
3188
|
+
# recurse all dependencies
|
|
3189
|
+
todo_rndr = {}
|
|
3190
|
+
todo_deps = {}
|
|
3191
|
+
list.each { |t|
|
|
3192
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(@toplevel)
|
|
3193
|
+
}
|
|
3194
|
+
# c.toplevel.anonymous_enums.to_a.each { |t| todo_rndr[t], todo_deps[t] = t.dump_def(c.toplevel) }
|
|
3195
|
+
while !(ar = (todo_deps.values.flatten - todo_deps.keys)).empty?
|
|
3196
|
+
ar.each { |t|
|
|
3197
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(@toplevel)
|
|
3198
|
+
}
|
|
3199
|
+
end
|
|
3200
|
+
exclude.each { |t| todo_deps.delete t ; todo_rndr.delete t }
|
|
3201
|
+
todo_deps.each_key { |t| todo_deps[t] -= exclude }
|
|
3202
|
+
|
|
3203
|
+
all = @toplevel.struct.values + @toplevel.symbol.values
|
|
3204
|
+
all -= all.grep(::Integer) # Enum values
|
|
3205
|
+
|
|
3206
|
+
r, dep = @toplevel.dump_reorder(all, todo_rndr, todo_deps)
|
|
3207
|
+
r.join("\n")
|
|
3208
|
+
end
|
|
3209
|
+
|
|
3210
|
+
# returns a string containing the C definition(s) of toplevel functions, with their dependencies
|
|
3211
|
+
def dump_definition(*funcnames)
|
|
3212
|
+
oldst = @toplevel.statements
|
|
3213
|
+
@toplevel.statements = []
|
|
3214
|
+
dump_definitions(funcnames.map { |f| @toplevel.symbol[f] })
|
|
3215
|
+
ensure
|
|
3216
|
+
@toplevel.statements = oldst
|
|
3217
|
+
end
|
|
3218
|
+
|
|
3219
|
+
def to_s
|
|
3220
|
+
@toplevel.dump(nil)[0].join("\n")
|
|
3221
|
+
end
|
|
3222
|
+
end
|
|
3223
|
+
|
|
3224
|
+
class Statement
|
|
3225
|
+
def self.dump(e, scope, r=[''], dep=[])
|
|
3226
|
+
case e
|
|
3227
|
+
when nil; r.last << ';'
|
|
3228
|
+
when Block
|
|
3229
|
+
r.last << ' ' if not r.last.empty?
|
|
3230
|
+
r.last << '{'
|
|
3231
|
+
tr, dep = e.dump(scope, [''], dep)
|
|
3232
|
+
tr.pop if tr.last.empty?
|
|
3233
|
+
r.concat tr.map { |s| Case.dump_indent(s) }
|
|
3234
|
+
(r.last[-1] == ?{ ? r.last : r) << '}'
|
|
3235
|
+
else
|
|
3236
|
+
tr, dep = e.dump(scope, [''], dep)
|
|
3237
|
+
r.concat tr.map { |s| Case.dump_indent(s) }
|
|
3238
|
+
end
|
|
3239
|
+
[r, dep]
|
|
3240
|
+
end
|
|
3241
|
+
|
|
3242
|
+
def to_s
|
|
3243
|
+
dump(Block.new(nil))[0].join(' ')
|
|
3244
|
+
end
|
|
3245
|
+
end
|
|
3246
|
+
|
|
3247
|
+
class Block
|
|
3248
|
+
def to_s() dump(nil)[0].join("\n") end
|
|
3249
|
+
|
|
3250
|
+
# return array of c source lines and array of dependencies (objects)
|
|
3251
|
+
def dump(scp, r=[''], dep=[])
|
|
3252
|
+
mydefs = @symbol.values.grep(TypeDef) + @struct.values + anonymous_enums.to_a
|
|
3253
|
+
todo_rndr = {}
|
|
3254
|
+
todo_deps = {}
|
|
3255
|
+
mydefs.each { |t| # filter out Enum values
|
|
3256
|
+
todo_rndr[t], todo_deps[t] = t.dump_def(self)
|
|
3257
|
+
}
|
|
3258
|
+
r, dep = dump_reorder(mydefs, todo_rndr, todo_deps, r, dep)
|
|
3259
|
+
dep -= @symbol.values + @struct.values
|
|
3260
|
+
[r, dep]
|
|
3261
|
+
end
|
|
3262
|
+
|
|
3263
|
+
def dump_reorder(mydefs, todo_rndr, todo_deps, r=[''], dep=[])
|
|
3264
|
+
val = todo_deps.values.flatten.uniq
|
|
3265
|
+
dep |= val
|
|
3266
|
+
dep -= mydefs | todo_deps.keys
|
|
3267
|
+
todo_deps.each { |k, v| v.delete k }
|
|
3268
|
+
ext = val - mydefs
|
|
3269
|
+
if ext.length > todo_deps.length
|
|
3270
|
+
todo_deps.each_key { |k| todo_deps[k] = todo_deps[k] & mydefs }
|
|
3271
|
+
else
|
|
3272
|
+
ext.each { |k| todo_deps.each_value { |v| v.delete k } }
|
|
3273
|
+
end
|
|
3274
|
+
|
|
3275
|
+
# predeclare structs involved in cyclic dependencies
|
|
3276
|
+
dep_cycle = lambda { |ary|
|
|
3277
|
+
# sexyness inside (c)
|
|
3278
|
+
deps = todo_deps[ary.last]
|
|
3279
|
+
if deps.include? ary.first; ary
|
|
3280
|
+
elsif (deps-ary).find { |d| deps = dep_cycle[ary + [d]] }; deps
|
|
3281
|
+
end
|
|
3282
|
+
}
|
|
3283
|
+
todo_rndr.keys.grep(Union).find_all { |t| t.name }.sort_by { |t| t.name }.each { |t|
|
|
3284
|
+
oldc = nil
|
|
3285
|
+
while c = dep_cycle[[t]]
|
|
3286
|
+
break if oldc == c
|
|
3287
|
+
r << "#{t.kind_of?(Struct) ? 'struct' : 'union'} #{t.name};" if not oldc
|
|
3288
|
+
oldc = c
|
|
3289
|
+
c.each { |s|
|
|
3290
|
+
# XXX struct z { struct a* }; struct a { void (*foo)(struct z); };
|
|
3291
|
+
todo_deps[s].delete t unless s.kind_of? Union and
|
|
3292
|
+
s.members.find { |sm| sm.type.untypedef == t }
|
|
3293
|
+
}
|
|
3294
|
+
end
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
loop do
|
|
3298
|
+
break if todo_rndr.empty?
|
|
3299
|
+
todo_now = todo_deps.keys.find_all { |k| todo_deps[k].empty? }
|
|
3300
|
+
if todo_now.empty?
|
|
3301
|
+
r << '// dependency problem, this may not compile'
|
|
3302
|
+
todo_now = todo_deps.keys
|
|
3303
|
+
end
|
|
3304
|
+
todo_now.sort_by { |k| k.name || '0' }.each { |k|
|
|
3305
|
+
if k.kind_of? Variable and k.type.kind_of? Function and k.initializer
|
|
3306
|
+
r << ''
|
|
3307
|
+
r.concat todo_rndr.delete(k)
|
|
3308
|
+
else
|
|
3309
|
+
r.pop if r.last == ''
|
|
3310
|
+
r.concat todo_rndr.delete(k)
|
|
3311
|
+
r.last << ';'
|
|
3312
|
+
end
|
|
3313
|
+
todo_deps.delete k
|
|
3314
|
+
}
|
|
3315
|
+
todo_deps.each_key { |k| todo_deps[k] -= todo_now }
|
|
3316
|
+
r << '' << '' << ''
|
|
3317
|
+
end
|
|
3318
|
+
|
|
3319
|
+
@statements.each { |s|
|
|
3320
|
+
r << '' if not r.last.empty?
|
|
3321
|
+
if s.kind_of? Block
|
|
3322
|
+
r, dep = Statement.dump(s, self, r, dep)
|
|
3323
|
+
else
|
|
3324
|
+
r, dep = s.dump(self, r, dep)
|
|
3325
|
+
end
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
[r, dep]
|
|
3329
|
+
end
|
|
3330
|
+
end
|
|
3331
|
+
class Declaration
|
|
3332
|
+
def dump(scope, r=[''], dep=[])
|
|
3333
|
+
tr, dep = @var.dump_def(scope, [''], dep)
|
|
3334
|
+
if @var.kind_of? Variable and @var.type.kind_of? Function and @var.initializer
|
|
3335
|
+
r << ''
|
|
3336
|
+
r.concat tr
|
|
3337
|
+
else
|
|
3338
|
+
r.pop if r.last == ''
|
|
3339
|
+
r.concat tr
|
|
3340
|
+
r.last << ';'
|
|
3341
|
+
end
|
|
3342
|
+
[r, dep]
|
|
3343
|
+
end
|
|
3344
|
+
|
|
3345
|
+
def to_s
|
|
3346
|
+
dump(Block.new(nil))[0].join(' ')
|
|
3347
|
+
end
|
|
3348
|
+
end
|
|
3349
|
+
module Attributes
|
|
3350
|
+
def dump_attributes
|
|
3351
|
+
if attributes
|
|
3352
|
+
(attributes - DECLSPECS).map { |a| " __attribute__((#{a}))" }.join
|
|
3353
|
+
else ''
|
|
3354
|
+
end
|
|
3355
|
+
end
|
|
3356
|
+
def dump_attributes_pre
|
|
3357
|
+
if attributes
|
|
3358
|
+
(attributes & DECLSPECS).map { |a| "__#{a} " }.join
|
|
3359
|
+
else ''
|
|
3360
|
+
end
|
|
3361
|
+
end
|
|
3362
|
+
end
|
|
3363
|
+
class Variable
|
|
3364
|
+
def dump(scope, r=[''], dep=[])
|
|
3365
|
+
if name
|
|
3366
|
+
dep |= [scope.symbol_ancestors[@name]]
|
|
3367
|
+
r.last << @name
|
|
3368
|
+
end
|
|
3369
|
+
[r, dep]
|
|
3370
|
+
end
|
|
3371
|
+
def dump_def(scope, r=[''], dep=[], skiptype=false)
|
|
3372
|
+
# int a=1, b=2;
|
|
3373
|
+
r.last << dump_attributes_pre
|
|
3374
|
+
if not skiptype
|
|
3375
|
+
r.last << @storage.to_s << ' ' if storage
|
|
3376
|
+
r, dep = @type.base.dump(scope, r, dep)
|
|
3377
|
+
r.last << ' ' if name
|
|
3378
|
+
end
|
|
3379
|
+
r, dep = @type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
|
|
3380
|
+
|
|
3381
|
+
if initializer
|
|
3382
|
+
r.last << ' = ' if not @type.kind_of?(Function)
|
|
3383
|
+
r, dep = @type.dump_initializer(@initializer, scope, r, dep)
|
|
3384
|
+
end
|
|
3385
|
+
[r, dep]
|
|
3386
|
+
end
|
|
3387
|
+
|
|
3388
|
+
def to_s
|
|
3389
|
+
dump(Block.new(nil))[0].join(' ')
|
|
3390
|
+
end
|
|
3391
|
+
end
|
|
3392
|
+
class Type
|
|
3393
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3394
|
+
case init
|
|
3395
|
+
when ::Numeric
|
|
3396
|
+
r.last << init.to_s
|
|
3397
|
+
[r, dep]
|
|
3398
|
+
when ::Array
|
|
3399
|
+
r.last << init.inspect
|
|
3400
|
+
[r, dep]
|
|
3401
|
+
else init.dump_inner(scope, r, dep)
|
|
3402
|
+
end
|
|
3403
|
+
end
|
|
3404
|
+
|
|
3405
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
|
3406
|
+
r.last << decl.shift
|
|
3407
|
+
r.concat decl
|
|
3408
|
+
[r, dep]
|
|
3409
|
+
end
|
|
3410
|
+
|
|
3411
|
+
def dump_def(*a)
|
|
3412
|
+
dump(*a)
|
|
3413
|
+
end
|
|
3414
|
+
|
|
3415
|
+
def dump_cast(scope, r=[''], dep=[])
|
|
3416
|
+
r.last << '('
|
|
3417
|
+
r.last << dump_attributes_pre if not kind_of? TypeDef
|
|
3418
|
+
r, dep = base.dump(scope, r, dep)
|
|
3419
|
+
r, dep = dump_declarator([kind_of?(TypeDef) ? '' : dump_attributes], scope, r, dep)
|
|
3420
|
+
r.last << ')'
|
|
3421
|
+
[r, dep]
|
|
3422
|
+
end
|
|
3423
|
+
|
|
3424
|
+
def to_s
|
|
3425
|
+
dump_cast(Block.new(nil))[0].join(' ')
|
|
3426
|
+
end
|
|
3427
|
+
end
|
|
3428
|
+
class Pointer
|
|
3429
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
|
3430
|
+
d = decl[0]
|
|
3431
|
+
decl[0] = '*'
|
|
3432
|
+
decl[0] << ' ' << @qualifier.map { |q| q.to_s }.join(' ') << ' ' if qualifier
|
|
3433
|
+
decl[0] << d
|
|
3434
|
+
if @type.kind_of? Function or @type.kind_of? Array
|
|
3435
|
+
decl[0] = '(' << decl[0]
|
|
3436
|
+
decl.last << ')'
|
|
3437
|
+
end
|
|
3438
|
+
@type.dump_declarator(decl, scope, r, dep)
|
|
3439
|
+
end
|
|
3440
|
+
end
|
|
3441
|
+
class Array
|
|
3442
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
|
3443
|
+
decl.last << '()' if decl.last.empty?
|
|
3444
|
+
decl.last << '['
|
|
3445
|
+
decl, dep = CExpression.dump(@length, scope, decl, dep) if length
|
|
3446
|
+
decl.last << ']'
|
|
3447
|
+
@type.dump_declarator(decl, scope, r, dep)
|
|
3448
|
+
end
|
|
3449
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3450
|
+
return super(init, scope, r, dep) if not init.kind_of? ::Array
|
|
3451
|
+
r.last << '{ '
|
|
3452
|
+
showname = false
|
|
3453
|
+
init.each_with_index { |v, i|
|
|
3454
|
+
if not v
|
|
3455
|
+
showname = true
|
|
3456
|
+
next
|
|
3457
|
+
end
|
|
3458
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
|
3459
|
+
rt = ['']
|
|
3460
|
+
if showname
|
|
3461
|
+
showname = false
|
|
3462
|
+
rt << "[#{i}] = "
|
|
3463
|
+
end
|
|
3464
|
+
rt, dep = @type.dump_initializer(v, scope, rt, dep)
|
|
3465
|
+
r.last << rt.shift
|
|
3466
|
+
r.concat rt.map { |s| "\t" << s }
|
|
3467
|
+
}
|
|
3468
|
+
r.last << ' }'
|
|
3469
|
+
[r, dep]
|
|
3470
|
+
end
|
|
3471
|
+
end
|
|
3472
|
+
class Function
|
|
3473
|
+
def dump_declarator(decl, scope, r=[''], dep=[])
|
|
3474
|
+
decl.last << '()' if decl.last.empty?
|
|
3475
|
+
decl.last << '('
|
|
3476
|
+
if args
|
|
3477
|
+
@args.each { |arg|
|
|
3478
|
+
decl.last << ', ' if decl.last[-1] != ?(
|
|
3479
|
+
decl, dep = arg.dump_def(scope, decl, dep)
|
|
3480
|
+
}
|
|
3481
|
+
if varargs
|
|
3482
|
+
decl.last << ', ' if decl.last[-1] != ?(
|
|
3483
|
+
decl.last << '...'
|
|
3484
|
+
else
|
|
3485
|
+
decl.last << 'void' if @args.empty?
|
|
3486
|
+
end
|
|
3487
|
+
end
|
|
3488
|
+
decl.last << ')'
|
|
3489
|
+
@type.dump_declarator(decl, scope, r, dep)
|
|
3490
|
+
end
|
|
3491
|
+
|
|
3492
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3493
|
+
Statement.dump(init, scope, r << '', dep)
|
|
3494
|
+
end
|
|
3495
|
+
end
|
|
3496
|
+
class BaseType
|
|
3497
|
+
def dump(scope, r=[''], dep=[])
|
|
3498
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3499
|
+
r.last << @specifier.to_s << ' ' if specifier and @name != :ptr
|
|
3500
|
+
r.last << case @name
|
|
3501
|
+
when :longlong; 'long long'
|
|
3502
|
+
when :longdouble; 'long double'
|
|
3503
|
+
when :ptr; specifier == :unsigned ? 'uintptr_t' : 'intptr_t'
|
|
3504
|
+
else @name.to_s
|
|
3505
|
+
end
|
|
3506
|
+
[r, dep]
|
|
3507
|
+
end
|
|
3508
|
+
end
|
|
3509
|
+
class TypeDef
|
|
3510
|
+
def dump(scope, r=[''], dep=[])
|
|
3511
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3512
|
+
r.last << @name
|
|
3513
|
+
dep |= [scope.symbol_ancestors[@name]]
|
|
3514
|
+
[r, dep]
|
|
3515
|
+
end
|
|
3516
|
+
|
|
3517
|
+
def dump_def(scope, r=[''], dep=[])
|
|
3518
|
+
r.last << 'typedef '
|
|
3519
|
+
r.last << dump_attributes_pre
|
|
3520
|
+
r, dep = @type.base.dump(scope, r, dep)
|
|
3521
|
+
r.last << ' '
|
|
3522
|
+
@type.dump_declarator([(name ? @name.dup : '') << dump_attributes], scope, r, dep)
|
|
3523
|
+
end
|
|
3524
|
+
|
|
3525
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3526
|
+
@type.dump_initializer(init, scope, r, dep)
|
|
3527
|
+
end
|
|
3528
|
+
end
|
|
3529
|
+
class Union
|
|
3530
|
+
def dump(scope, r=[''], dep=[])
|
|
3531
|
+
if name
|
|
3532
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3533
|
+
r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1] << ' ' << @name
|
|
3534
|
+
dep |= [scope.struct_ancestors[@name]]
|
|
3535
|
+
[r, dep]
|
|
3536
|
+
else
|
|
3537
|
+
dump_def(scope, r, dep)
|
|
3538
|
+
end
|
|
3539
|
+
end
|
|
3540
|
+
|
|
3541
|
+
def dump_def(scope, r=[''], dep=[])
|
|
3542
|
+
r << ''
|
|
3543
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3544
|
+
r.last << self.class.name.downcase[/(?:.*::)?(.*)/, 1]
|
|
3545
|
+
r.last << ' ' << @name if name
|
|
3546
|
+
if members
|
|
3547
|
+
r.last << ' {'
|
|
3548
|
+
@members.each_with_index { |m,i|
|
|
3549
|
+
tr, dep = m.dump_def(scope, [''], dep)
|
|
3550
|
+
tr.last << ':' << @bits[i].to_s if bits and @bits[i]
|
|
3551
|
+
tr.last << ';'
|
|
3552
|
+
r.concat tr.map { |s| "\t" << s }
|
|
3553
|
+
}
|
|
3554
|
+
r << '}'
|
|
3555
|
+
end
|
|
3556
|
+
r.last << dump_attributes
|
|
3557
|
+
[r, dep]
|
|
3558
|
+
end
|
|
3559
|
+
|
|
3560
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3561
|
+
return super(init, scope, r, dep) if not init.kind_of? ::Array
|
|
3562
|
+
r.last << '{ '
|
|
3563
|
+
showname = false
|
|
3564
|
+
@members.zip(init) { |m, i|
|
|
3565
|
+
if not i
|
|
3566
|
+
showname = true
|
|
3567
|
+
next
|
|
3568
|
+
end
|
|
3569
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
|
3570
|
+
rt = ['']
|
|
3571
|
+
if showname
|
|
3572
|
+
showname = false
|
|
3573
|
+
rt << ".#{m.name} = "
|
|
3574
|
+
end
|
|
3575
|
+
rt, dep = m.type.dump_initializer(i, scope, rt, dep)
|
|
3576
|
+
r.last << rt.shift
|
|
3577
|
+
r.concat rt.map { |s| "\t" << s }
|
|
3578
|
+
}
|
|
3579
|
+
r.last << ' }'
|
|
3580
|
+
[r, dep]
|
|
3581
|
+
end
|
|
3582
|
+
end
|
|
3583
|
+
class Struct
|
|
3584
|
+
def dump_def(scope, r=[''], dep=[])
|
|
3585
|
+
if pack
|
|
3586
|
+
r, dep = super(scope, r, dep)
|
|
3587
|
+
r.last <<
|
|
3588
|
+
if @pack == 1; (has_attribute('packed') or has_attribute_var('pack')) ? '' : " __attribute__((packed))"
|
|
3589
|
+
else has_attribute_var('pack') ? '' : " __attribute__((pack(#@pack)))"
|
|
3590
|
+
end
|
|
3591
|
+
[r, dep]
|
|
3592
|
+
else
|
|
3593
|
+
super(scope, r, dep)
|
|
3594
|
+
end
|
|
3595
|
+
end
|
|
3596
|
+
end
|
|
3597
|
+
class Enum
|
|
3598
|
+
def dump(scope, r=[''], dep=[])
|
|
3599
|
+
if name
|
|
3600
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3601
|
+
r.last << 'enum ' << @name
|
|
3602
|
+
dep |= [scope.struct_ancestors[@name]]
|
|
3603
|
+
[r, dep]
|
|
3604
|
+
else
|
|
3605
|
+
dump_def(scope, r, dep)
|
|
3606
|
+
end
|
|
3607
|
+
end
|
|
3608
|
+
|
|
3609
|
+
def dump_def(scope, r=[''], dep=[])
|
|
3610
|
+
r.last << @qualifier.map { |q| q.to_s << ' ' }.join if qualifier
|
|
3611
|
+
r.last << 'enum'
|
|
3612
|
+
r.last << ' ' << @name if name
|
|
3613
|
+
if members
|
|
3614
|
+
r.last << ' { '
|
|
3615
|
+
val = -1
|
|
3616
|
+
@members.sort_by { |m, v| v }.each { |m, v|
|
|
3617
|
+
r.last << ', ' if r.last[-2, 2] != '{ '
|
|
3618
|
+
r.last << m
|
|
3619
|
+
if v != (val += 1)
|
|
3620
|
+
val = v
|
|
3621
|
+
r.last << ' = ' << val.to_s
|
|
3622
|
+
end
|
|
3623
|
+
}
|
|
3624
|
+
r.last << ' }'
|
|
3625
|
+
end
|
|
3626
|
+
[r, dep]
|
|
3627
|
+
end
|
|
3628
|
+
|
|
3629
|
+
def dump_initializer(init, scope, r=[''], dep=[])
|
|
3630
|
+
if members and (
|
|
3631
|
+
k = @members.index(init) or
|
|
3632
|
+
(init.kind_of? CExpression and not init.op and k = @members.index(init.rexpr))
|
|
3633
|
+
)
|
|
3634
|
+
r.last << k
|
|
3635
|
+
dep |= [scope.struct_ancestors[@name]]
|
|
3636
|
+
[r, dep]
|
|
3637
|
+
else super(init, scope, r, dep)
|
|
3638
|
+
end
|
|
3639
|
+
end
|
|
3640
|
+
end
|
|
3641
|
+
class If
|
|
3642
|
+
def dump(scope, r=[''], dep=[])
|
|
3643
|
+
r.last << 'if ('
|
|
3644
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
|
3645
|
+
r.last << ')'
|
|
3646
|
+
r, dep = Statement.dump(@bthen, scope, r, dep)
|
|
3647
|
+
if belse
|
|
3648
|
+
@bthen.kind_of?(Block) ? (r.last << ' else') : (r << 'else')
|
|
3649
|
+
if @belse.kind_of? If
|
|
3650
|
+
# skip indent
|
|
3651
|
+
r.last << ' '
|
|
3652
|
+
r, dep = @belse.dump(scope, r, dep)
|
|
3653
|
+
else
|
|
3654
|
+
r, dep = Statement.dump(@belse, scope, r, dep)
|
|
3655
|
+
end
|
|
3656
|
+
end
|
|
3657
|
+
[r, dep]
|
|
3658
|
+
end
|
|
3659
|
+
end
|
|
3660
|
+
class For
|
|
3661
|
+
def dump(scope, r=[''], dep=[])
|
|
3662
|
+
r.last << 'for ('
|
|
3663
|
+
if @init.kind_of? Block
|
|
3664
|
+
scope = @init
|
|
3665
|
+
skiptype = false
|
|
3666
|
+
@init.symbol.each_value { |s|
|
|
3667
|
+
r.last << ', ' if skiptype
|
|
3668
|
+
r, dep = s.dump_def(scope, r, dep, skiptype)
|
|
3669
|
+
skiptype = true
|
|
3670
|
+
}
|
|
3671
|
+
else
|
|
3672
|
+
r, dep = CExpression.dump(@init, scope, r, dep)
|
|
3673
|
+
end
|
|
3674
|
+
r.last << ' ' if @init
|
|
3675
|
+
r.last << ';'
|
|
3676
|
+
r.last << ' ' if @test
|
|
3677
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
|
3678
|
+
r.last << ' ' if @test
|
|
3679
|
+
r.last << ';'
|
|
3680
|
+
r.last << ' ' if @iter
|
|
3681
|
+
r, dep = CExpression.dump(@iter, scope, r, dep)
|
|
3682
|
+
r.last << ')'
|
|
3683
|
+
Statement.dump(@body, scope, r, dep)
|
|
3684
|
+
end
|
|
3685
|
+
end
|
|
3686
|
+
class While
|
|
3687
|
+
def dump(scope, r=[''], dep=[])
|
|
3688
|
+
r.last << 'while ('
|
|
3689
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
|
3690
|
+
r.last << ')'
|
|
3691
|
+
Statement.dump(@body, scope, r, dep)
|
|
3692
|
+
end
|
|
3693
|
+
end
|
|
3694
|
+
class DoWhile
|
|
3695
|
+
def dump(scope, r=[''], dep=[])
|
|
3696
|
+
r.last << 'do'
|
|
3697
|
+
r, dep = Statement.dump(@body, scope, r, dep)
|
|
3698
|
+
@body.kind_of?(Block) ? (r.last << ' while (') : (r << 'while (')
|
|
3699
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
|
3700
|
+
r.last << ');'
|
|
3701
|
+
[r, dep]
|
|
3702
|
+
end
|
|
3703
|
+
end
|
|
3704
|
+
class Switch
|
|
3705
|
+
def dump(scope, r=[''], dep=[])
|
|
3706
|
+
r.last << 'switch ('
|
|
3707
|
+
r, dep = CExpression.dump(@test, scope, r, dep)
|
|
3708
|
+
r.last << ')'
|
|
3709
|
+
r.last << ' {' if @body.kind_of? Block
|
|
3710
|
+
tr, dep = @body.dump(scope, [''], dep)
|
|
3711
|
+
r.concat tr.map { |s| Case.dump_indent(s, true) }
|
|
3712
|
+
r << '}' if @body.kind_of? Block
|
|
3713
|
+
[r, dep]
|
|
3714
|
+
end
|
|
3715
|
+
end
|
|
3716
|
+
class Continue
|
|
3717
|
+
def dump(scope, r=[''], dep=[])
|
|
3718
|
+
r.last << 'continue;'
|
|
3719
|
+
[r, dep]
|
|
3720
|
+
end
|
|
3721
|
+
end
|
|
3722
|
+
class Break
|
|
3723
|
+
def dump(scope, r=[''], dep=[])
|
|
3724
|
+
r.last << 'break;'
|
|
3725
|
+
[r, dep]
|
|
3726
|
+
end
|
|
3727
|
+
end
|
|
3728
|
+
class Goto
|
|
3729
|
+
def dump(scope, r=[''], dep=[])
|
|
3730
|
+
r.last << "goto #@target;"
|
|
3731
|
+
[r, dep]
|
|
3732
|
+
end
|
|
3733
|
+
end
|
|
3734
|
+
class Return
|
|
3735
|
+
def dump(scope, r=[''], dep=[])
|
|
3736
|
+
r.last << 'return '
|
|
3737
|
+
r, dep = CExpression.dump(@value, scope, r, dep)
|
|
3738
|
+
r.last.chop! if r.last[-1] == ?\ # the space character
|
|
3739
|
+
r.last << ';'
|
|
3740
|
+
[r, dep]
|
|
3741
|
+
end
|
|
3742
|
+
end
|
|
3743
|
+
class Case
|
|
3744
|
+
def dump(scope, r=[''], dep=[])
|
|
3745
|
+
case @expr
|
|
3746
|
+
when 'default'
|
|
3747
|
+
r.last << @expr
|
|
3748
|
+
else
|
|
3749
|
+
r.last << 'case '
|
|
3750
|
+
r, dep = CExpression.dump(@expr, scope, r, dep)
|
|
3751
|
+
if exprup
|
|
3752
|
+
r.last << ' ... '
|
|
3753
|
+
r, dep = CExpression.dump(@exprup, scope, r, dep)
|
|
3754
|
+
end
|
|
3755
|
+
end
|
|
3756
|
+
r.last << ':'
|
|
3757
|
+
dump_inner(scope, r, dep)
|
|
3758
|
+
end
|
|
3759
|
+
|
|
3760
|
+
def self.dump_indent(s, short=false)
|
|
3761
|
+
case s
|
|
3762
|
+
when /^(case|default)\W/; (short ? ' ' : "\t") << s
|
|
3763
|
+
when /^\s+(case|default)\W/; "\t" << s
|
|
3764
|
+
when /:$/; s
|
|
3765
|
+
else "\t" << s
|
|
3766
|
+
end
|
|
3767
|
+
end
|
|
3768
|
+
end
|
|
3769
|
+
class Label
|
|
3770
|
+
def dump(scope, r=[''], dep=[])
|
|
3771
|
+
r.last << @name << ':'
|
|
3772
|
+
dump_inner(scope, r, dep)
|
|
3773
|
+
end
|
|
3774
|
+
def dump_inner(scope, r=[''], dep=[])
|
|
3775
|
+
if not @statement; [r, dep]
|
|
3776
|
+
elsif @statement.kind_of? Block; Statement.dump(@statement, scope, r, dep)
|
|
3777
|
+
else @statement.dump(scope, r << '', dep)
|
|
3778
|
+
end
|
|
3779
|
+
end
|
|
3780
|
+
end
|
|
3781
|
+
class Asm
|
|
3782
|
+
def dump(scope, r=[''], dep=[])
|
|
3783
|
+
r.last << 'asm '
|
|
3784
|
+
r.last << 'volatile ' if @volatile
|
|
3785
|
+
r.last << '('
|
|
3786
|
+
r.last << @body.inspect
|
|
3787
|
+
if @output or @input or @clobber
|
|
3788
|
+
if @output and @output != []
|
|
3789
|
+
# TODO
|
|
3790
|
+
r << ': /* todo */'
|
|
3791
|
+
elsif (@input and @input != []) or (@clobber and @clobber != [])
|
|
3792
|
+
r.last << ' :'
|
|
3793
|
+
end
|
|
3794
|
+
end
|
|
3795
|
+
if @input or @clobber
|
|
3796
|
+
if @input and @input != []
|
|
3797
|
+
# TODO
|
|
3798
|
+
r << ': /* todo */'
|
|
3799
|
+
elsif @clobber and @clobber != []
|
|
3800
|
+
r.last << ' :'
|
|
3801
|
+
end
|
|
3802
|
+
end
|
|
3803
|
+
if @clobber and @clobber != []
|
|
3804
|
+
r << (': ' << @clobber.map { |c| c.inspect }.join(', '))
|
|
3805
|
+
end
|
|
3806
|
+
r.last << ');'
|
|
3807
|
+
[r, dep]
|
|
3808
|
+
end
|
|
3809
|
+
end
|
|
3810
|
+
class CExpression
|
|
3811
|
+
def self.dump(e, scope, r=[''], dep=[], brace = false)
|
|
3812
|
+
if $DEBUG
|
|
3813
|
+
brace = false
|
|
3814
|
+
case e
|
|
3815
|
+
when CExpression, Variable
|
|
3816
|
+
r, dep = e.type.dump_cast(scope, r, dep)
|
|
3817
|
+
end
|
|
3818
|
+
r.last << '('
|
|
3819
|
+
end
|
|
3820
|
+
r, dep = \
|
|
3821
|
+
case e
|
|
3822
|
+
when ::Numeric; r.last << e.to_s ; [r, dep]
|
|
3823
|
+
when ::String; r.last << e.inspect ; [r, dep]
|
|
3824
|
+
when CExpression; e.dump_inner(scope, r, dep, brace)
|
|
3825
|
+
when Variable; e.dump(scope, r, dep)
|
|
3826
|
+
when nil; [r, dep]
|
|
3827
|
+
else raise 'wtf?' + e.inspect
|
|
3828
|
+
end
|
|
3829
|
+
if $DEBUG
|
|
3830
|
+
r.last << ')'
|
|
3831
|
+
end
|
|
3832
|
+
[r, dep]
|
|
3833
|
+
end
|
|
3834
|
+
|
|
3835
|
+
def dump(scope, r=[''], dep=[])
|
|
3836
|
+
r, dep = dump_inner(scope, r, dep)
|
|
3837
|
+
r.last << ';'
|
|
3838
|
+
[r, dep]
|
|
3839
|
+
end
|
|
3840
|
+
|
|
3841
|
+
def dump_inner(scope, r=[''], dep=[], brace = false)
|
|
3842
|
+
r.last << '(' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
|
|
3843
|
+
if not @lexpr
|
|
3844
|
+
if not @op
|
|
3845
|
+
case @rexpr
|
|
3846
|
+
when ::Numeric
|
|
3847
|
+
if @rexpr < 0
|
|
3848
|
+
r.last << ?-
|
|
3849
|
+
re = -@rexpr
|
|
3850
|
+
else
|
|
3851
|
+
re = @rexpr
|
|
3852
|
+
end
|
|
3853
|
+
if re >= 0x1000
|
|
3854
|
+
r.last << ("0x%X" % re)
|
|
3855
|
+
else
|
|
3856
|
+
r.last << re.to_s
|
|
3857
|
+
end
|
|
3858
|
+
if @type.kind_of? BaseType
|
|
3859
|
+
r.last << 'U' if @type.specifier == :unsigned
|
|
3860
|
+
case @type.name
|
|
3861
|
+
when :longlong, :__int64; r.last << 'LL'
|
|
3862
|
+
when :long, :longdouble; r.last << 'L'
|
|
3863
|
+
when :float; r.last << 'F'
|
|
3864
|
+
end
|
|
3865
|
+
end
|
|
3866
|
+
when ::String
|
|
3867
|
+
r.last << 'L' if @type.kind_of? Pointer and @type.type.kind_of? BaseType and @type.type.name == :short
|
|
3868
|
+
r.last << @rexpr.inspect
|
|
3869
|
+
when CExpression # cast
|
|
3870
|
+
r, dep = @type.dump_cast(scope, r, dep)
|
|
3871
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, true)
|
|
3872
|
+
when Variable
|
|
3873
|
+
r, dep = @rexpr.dump(scope, r, dep)
|
|
3874
|
+
when Block
|
|
3875
|
+
r.last << '('
|
|
3876
|
+
r, dep = Statement.dump(@rexpr, scope, r, dep)
|
|
3877
|
+
r.last << ' )'
|
|
3878
|
+
when Label
|
|
3879
|
+
r.last << '&&' << @rexpr.name
|
|
3880
|
+
else raise "wtf? #{inspect}"
|
|
3881
|
+
end
|
|
3882
|
+
else
|
|
3883
|
+
r.last << @op.to_s
|
|
3884
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? C::CExpression and @rexpr.lexpr))
|
|
3885
|
+
end
|
|
3886
|
+
elsif not @rexpr
|
|
3887
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep)
|
|
3888
|
+
r.last << @op.to_s
|
|
3889
|
+
else
|
|
3890
|
+
case @op
|
|
3891
|
+
when :'->', :'.'
|
|
3892
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
|
3893
|
+
r.last << @op.to_s << @rexpr
|
|
3894
|
+
when :'[]'
|
|
3895
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
|
3896
|
+
r.last << '['
|
|
3897
|
+
l = lexpr if lexpr.kind_of? Variable
|
|
3898
|
+
l = lexpr.lexpr.type.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'.'
|
|
3899
|
+
l = lexpr.lexpr.type.pointed.untypedef.findmember(lexpr.rexpr) if lexpr.kind_of? CExpression and lexpr.op == :'->'
|
|
3900
|
+
# honor __attribute__((indexenum(enumname)))
|
|
3901
|
+
if l and l.attributes and rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr.kind_of? ::Integer and
|
|
3902
|
+
n = l.has_attribute_var('indexenum') and enum = scope.struct_ancestors[n] and i = enum.members.index(rexpr.rexpr)
|
|
3903
|
+
r.last << i
|
|
3904
|
+
dep |= [enum]
|
|
3905
|
+
else
|
|
3906
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep)
|
|
3907
|
+
end
|
|
3908
|
+
r.last << ']'
|
|
3909
|
+
when :funcall
|
|
3910
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
|
3911
|
+
r.last << '('
|
|
3912
|
+
@rexpr.each { |arg|
|
|
3913
|
+
r.last << ', ' if r.last[-1] != ?(
|
|
3914
|
+
r, dep = CExpression.dump(arg, scope, r, dep)
|
|
3915
|
+
}
|
|
3916
|
+
r.last << ')'
|
|
3917
|
+
when :'?:'
|
|
3918
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, true)
|
|
3919
|
+
r.last << ' ? '
|
|
3920
|
+
r, dep = CExpression.dump(@rexpr[0], scope, r, dep, true)
|
|
3921
|
+
r.last << ' : '
|
|
3922
|
+
r, dep = CExpression.dump(@rexpr[1], scope, r, dep, true)
|
|
3923
|
+
else
|
|
3924
|
+
r, dep = CExpression.dump(@lexpr, scope, r, dep, (@lexpr.kind_of? CExpression and @lexpr.lexpr and @lexpr.op != @op))
|
|
3925
|
+
r.last << ' ' << @op.to_s << ' '
|
|
3926
|
+
r, dep = CExpression.dump(@rexpr, scope, r, dep, (@rexpr.kind_of? CExpression and @rexpr.lexpr and @rexpr.op != @op and @rexpr.op != :funcall))
|
|
3927
|
+
end
|
|
3928
|
+
end
|
|
3929
|
+
r.last << ')' if brace and @op != :'->' and @op != :'.' and @op != :'[]' and (@op or @rexpr.kind_of? CExpression)
|
|
3930
|
+
[r, dep]
|
|
3931
|
+
end
|
|
3932
|
+
|
|
3933
|
+
def to_s
|
|
3934
|
+
dump_inner(Block.new(nil))[0].join(' ')
|
|
3935
|
+
end
|
|
3936
|
+
end
|
|
3937
|
+
end
|
|
3938
|
+
end
|