1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
|
import sys
from itertools import izip
from collections import OrderedDict
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rlib.rfile import FILEP
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rtyper.tool import rfficache, rffi_platform
from rpython.flowspace.model import Constant, const
from rpython.flowspace.specialcase import register_flow_sc
from rpython.flowspace.flowcontext import FlowingError
from . import model
from .cparser import Parser
CNAME_TO_LLTYPE = {
'char': rffi.CHAR,
'double': rffi.DOUBLE, 'long double': rffi.LONGDOUBLE,
'float': rffi.FLOAT, 'FILE': FILEP.TO}
def add_inttypes():
for name in rffi.TYPES:
if name.startswith('unsigned'):
rname = 'u' + name[9:]
else:
rname = name
rname = rname.replace(' ', '').upper()
CNAME_TO_LLTYPE[name] = rfficache.platform.types[rname]
add_inttypes()
CNAME_TO_LLTYPE['int'] = rffi.INT_real
CNAME_TO_LLTYPE['wchar_t'] = lltype.UniChar
if 'ssize_t' not in CNAME_TO_LLTYPE: # on Windows
CNAME_TO_LLTYPE['ssize_t'] = rffi.SIGNED
def cname_to_lltype(name):
return CNAME_TO_LLTYPE[name]
class DelayedStruct(object):
def __init__(self, name, fields, TYPE):
self.struct_name = name
self.type_name = None
self.fields = fields
self.TYPE = TYPE
def get_type_name(self):
if self.type_name is not None:
return self.type_name
elif not self.struct_name.startswith('$'):
return 'struct %s' % self.struct_name
else:
raise ValueError('Anonymous struct')
def __repr__(self):
return "<struct {struct_name}>".format(**vars(self))
class CTypeSpace(object):
def __init__(self, parser=None, definitions=None, macros=None,
headers=None, includes=None):
self.definitions = definitions if definitions is not None else {}
self.macros = macros if macros is not None else {}
self.structs = {}
self.ctx = parser if parser else Parser()
self.headers = headers if headers is not None else ['sys/types.h']
self.parsed_headers = []
self.sources = []
self._config_entries = OrderedDict()
self.includes = []
self.struct_typedefs = {}
self._handled = set()
self._frozen = False
self._cdecl_type_cache = {} # {cdecl: TYPE} cache
if includes is not None:
for header in includes:
self.include(header)
def include(self, other):
self.ctx.include(other.ctx)
self.structs.update(other.structs)
self.includes.append(other)
def parse_source(self, source, configure=True):
self.sources.append(source)
self.ctx.parse(source)
if configure:
self.configure_types()
def parse_header(self, header_path, configure=True):
self.headers.append(str(header_path))
self.parsed_headers.append(header_path)
self.ctx.parse(header_path.read())
if configure:
self.configure_types()
def add_typedef(self, name, obj, quals):
assert name not in self.definitions
tp = self.convert_type(obj, quals)
if isinstance(tp, DelayedStruct):
if tp.type_name is None:
tp.type_name = name
tp = self.realize_struct(tp)
self.structs[obj.realtype] = tp
self.definitions[name] = tp
def add_macro(self, name, value):
assert name not in self.macros
self.macros[name] = value
def add_struct(self, name, obj, quals):
tp = self.convert_type(obj, quals)
if isinstance(tp, DelayedStruct):
tp = self.realize_struct(tp)
self.structs[obj] = tp
def new_struct(self, obj):
if obj.name == '_IO_FILE': # cffi weirdness
return cname_to_lltype('FILE')
struct = DelayedStruct(obj.name, None, lltype.ForwardReference())
# Cache it early, to avoid infinite recursion
self.structs[obj] = struct
if obj.fldtypes is not None:
struct.fields = zip(
obj.fldnames,
[self.convert_field(field) for field in obj.fldtypes])
return struct
def convert_field(self, obj):
tp = self.convert_type(obj)
if isinstance(tp, DelayedStruct):
tp = tp.TYPE
elif isinstance(tp, type) and issubclass(tp, Enum):
tp = rffi.INT_real
return tp
def realize_struct(self, struct):
type_name = struct.get_type_name()
if struct.fields is None:
raise ValueError('Missing definition for %s' % type_name)
entry = rffi_platform.Struct(type_name, struct.fields)
self._config_entries[entry] = struct.TYPE
return struct.TYPE
def build_eci(self):
all_sources = []
for cts in self.includes:
all_sources.extend(cts.sources)
all_sources.extend(self.sources)
all_headers = self.headers
for x in self.includes:
for hdr in x.headers:
if hdr not in all_headers:
all_headers.append(hdr)
if sys.platform == 'win32':
if sys.maxint > 2**32:
compile_extra = ['-Dssize_t=__int64']
else:
compile_extra = ['-Dssize_t=long']
else:
compile_extra = []
return ExternalCompilationInfo(
post_include_bits=all_sources, includes=all_headers,
compile_extra=compile_extra)
def configure_types(self):
for name, (obj, quals) in self.ctx._declarations.iteritems():
if obj in self.ctx._included_declarations:
continue
if name in self._handled:
continue
self._handled.add(name)
if name.startswith('typedef '):
name = name[8:]
self.add_typedef(name, obj, quals)
elif name.startswith('macro '):
name = name[6:]
self.add_macro(name, obj)
elif name.startswith('struct '):
name = name[7:]
self.add_struct(name, obj, quals)
if not self._config_entries:
return
eci = self.build_eci()
result = rffi_platform.configure_entries(list(self._config_entries), eci)
for entry, TYPE in izip(self._config_entries, result):
# hack: prevent the source from being pasted into common_header.h
del TYPE._hints['eci']
self._config_entries[entry].become(TYPE)
self._config_entries.clear()
def convert_type(self, obj, quals=0):
if isinstance(obj, model.DefinedType):
return self.convert_type(obj.realtype, obj.quals)
if isinstance(obj, model.PrimitiveType):
return cname_to_lltype(obj.name)
elif isinstance(obj, model.StructType):
if obj in self.structs:
return self.structs[obj]
return self.new_struct(obj)
elif isinstance(obj, model.PointerType):
TO = self.convert_type(obj.totype)
if TO is lltype.Void:
return rffi.VOIDP
elif isinstance(TO, DelayedStruct):
TO = TO.TYPE
if isinstance(TO, lltype.ContainerType):
return lltype.Ptr(TO)
else:
if obj.quals & model.Q_CONST:
return lltype.Ptr(lltype.Array(
TO, hints={'nolength': True, 'render_as_const': True}))
else:
return rffi.CArrayPtr(TO)
elif isinstance(obj, model.FunctionPtrType):
if obj.ellipsis:
raise NotImplementedError
args = [self.convert_type(arg) for arg in obj.args]
res = self.convert_type(obj.result)
return lltype.Ptr(lltype.FuncType(args, res))
elif isinstance(obj, model.VoidType):
return lltype.Void
elif isinstance(obj, model.ArrayType):
return rffi.CFixedArray(self.convert_type(obj.item), obj.length)
elif isinstance(obj, model.EnumType):
enum = type(obj.forcename, (Enum,), {})
for key, value in zip(obj.enumerators, obj.enumvalues):
setattr(enum, key, value)
return enum
else:
raise NotImplementedError
def gettype(self, cdecl):
try:
return self._cdecl_type_cache[cdecl]
except KeyError:
result = self._real_gettype(cdecl)
self._cdecl_type_cache[cdecl] = result
return result
def _real_gettype(self, cdecl):
obj = self.ctx.parse_type(cdecl)
result = self.convert_type(obj)
if isinstance(result, DelayedStruct):
result = result.TYPE
return result
def cast(self, cdecl, value):
return rffi.cast(self.gettype(cdecl), value)
def parse_func(self, cdecl):
cdecl = cdecl.strip()
if cdecl[-1] != ';':
cdecl += ';'
ast, _, _ = self.ctx._parse(cdecl)
decl = ast.ext[-1]
tp, quals = self.ctx._get_type_and_quals(decl.type, name=decl.name)
return FunctionDeclaration(decl.name, tp)
def _freeze_(self):
if self._frozen:
return True
@register_flow_sc(self.cast)
def sc_cast(ctx, v_decl, v_arg):
if not isinstance(v_decl, Constant):
raise FlowingError(
"The first argument of cts.cast() must be a constant.")
TP = self.gettype(v_decl.value)
return ctx.appcall(rffi.cast, const(TP), v_arg)
@register_flow_sc(self.gettype)
def sc_gettype(ctx, v_decl):
if not isinstance(v_decl, Constant):
raise FlowingError(
"The argument of cts.gettype() must be a constant.")
return const(self.gettype(v_decl.value))
self._frozen = True
return True
class Enum(object):
pass
class FunctionDeclaration(object):
def __init__(self, name, tp):
self.name = name
self.tp = tp
def get_llargs(self, cts):
return [cts.convert_type(arg) for arg in self.tp.args]
def get_llresult(self, cts):
return cts.convert_type(self.tp.result)
def parse_source(source, includes=None, headers=None, configure_now=True):
cts = CTypeSpace(headers=headers, includes=includes)
cts.parse_source(source)
return cts
|