Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cffi/recompiler.py: 36%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1100 statements  

1import os, sys, io 

2from . import ffiplatform, model 

3from .error import VerificationError 

4from .cffi_opcode import * 

5 

6VERSION_BASE = 0x2601 

7VERSION_EMBEDDED = 0x2701 

8VERSION_CHAR16CHAR32 = 0x2801 

9 

10USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or 

11 sys.version_info >= (3, 5)) 

12 

13 

14class GlobalExpr: 

15 def __init__(self, name, address, type_op, size=0, check_value=0): 

16 self.name = name 

17 self.address = address 

18 self.type_op = type_op 

19 self.size = size 

20 self.check_value = check_value 

21 

22 def as_c_expr(self): 

23 return ' { "%s", (void *)%s, %s, (void *)%s },' % ( 

24 self.name, self.address, self.type_op.as_c_expr(), self.size) 

25 

26 def as_python_expr(self): 

27 return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, 

28 self.check_value) 

29 

30class FieldExpr: 

31 def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): 

32 self.name = name 

33 self.field_offset = field_offset 

34 self.field_size = field_size 

35 self.fbitsize = fbitsize 

36 self.field_type_op = field_type_op 

37 

38 def as_c_expr(self): 

39 spaces = " " * len(self.name) 

40 return (' { "%s", %s,\n' % (self.name, self.field_offset) + 

41 ' %s %s,\n' % (spaces, self.field_size) + 

42 ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) 

43 

44 def as_python_expr(self): 

45 raise NotImplementedError 

46 

47 def as_field_python_expr(self): 

48 if self.field_type_op.op == OP_NOOP: 

49 size_expr = '' 

50 elif self.field_type_op.op == OP_BITFIELD: 

51 size_expr = format_four_bytes(self.fbitsize) 

52 else: 

53 raise NotImplementedError 

54 return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), 

55 size_expr, 

56 self.name) 

57 

58class StructUnionExpr: 

59 def __init__(self, name, type_index, flags, size, alignment, comment, 

60 first_field_index, c_fields): 

61 self.name = name 

62 self.type_index = type_index 

63 self.flags = flags 

64 self.size = size 

65 self.alignment = alignment 

66 self.comment = comment 

67 self.first_field_index = first_field_index 

68 self.c_fields = c_fields 

69 

70 def as_c_expr(self): 

71 return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) 

72 + '\n %s, %s, ' % (self.size, self.alignment) 

73 + '%d, %d ' % (self.first_field_index, len(self.c_fields)) 

74 + ('/* %s */ ' % self.comment if self.comment else '') 

75 + '},') 

76 

77 def as_python_expr(self): 

78 flags = eval(self.flags, G_FLAGS) 

79 fields_expr = [c_field.as_field_python_expr() 

80 for c_field in self.c_fields] 

81 return "(b'%s%s%s',%s)" % ( 

82 format_four_bytes(self.type_index), 

83 format_four_bytes(flags), 

84 self.name, 

85 ','.join(fields_expr)) 

86 

87class EnumExpr: 

88 def __init__(self, name, type_index, size, signed, allenums): 

89 self.name = name 

90 self.type_index = type_index 

91 self.size = size 

92 self.signed = signed 

93 self.allenums = allenums 

94 

95 def as_c_expr(self): 

96 return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' 

97 ' "%s" },' % (self.name, self.type_index, 

98 self.size, self.signed, self.allenums)) 

99 

100 def as_python_expr(self): 

101 prim_index = { 

102 (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, 

103 (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, 

104 (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, 

105 (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, 

106 }[self.size, self.signed] 

107 return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), 

108 format_four_bytes(prim_index), 

109 self.name, self.allenums) 

110 

111class TypenameExpr: 

112 def __init__(self, name, type_index): 

113 self.name = name 

114 self.type_index = type_index 

115 

116 def as_c_expr(self): 

117 return ' { "%s", %d },' % (self.name, self.type_index) 

118 

119 def as_python_expr(self): 

120 return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) 

121 

122 

123# ____________________________________________________________ 

124 

125 

126class Recompiler: 

127 _num_externpy = 0 

128 

129 def __init__(self, ffi, module_name, target_is_python=False): 

130 self.ffi = ffi 

131 self.module_name = module_name 

132 self.target_is_python = target_is_python 

133 self._version = VERSION_BASE 

134 

135 def needs_version(self, ver): 

136 self._version = max(self._version, ver) 

137 

138 def collect_type_table(self): 

139 self._typesdict = {} 

140 self._generate("collecttype") 

141 # 

142 all_decls = sorted(self._typesdict, key=str) 

143 # 

144 # prepare all FUNCTION bytecode sequences first 

145 self.cffi_types = [] 

146 for tp in all_decls: 

147 if tp.is_raw_function: 

148 assert self._typesdict[tp] is None 

149 self._typesdict[tp] = len(self.cffi_types) 

150 self.cffi_types.append(tp) # placeholder 

151 for tp1 in tp.args: 

152 assert isinstance(tp1, (model.VoidType, 

153 model.BasePrimitiveType, 

154 model.PointerType, 

155 model.StructOrUnionOrEnum, 

156 model.FunctionPtrType)) 

157 if self._typesdict[tp1] is None: 

158 self._typesdict[tp1] = len(self.cffi_types) 

159 self.cffi_types.append(tp1) # placeholder 

160 self.cffi_types.append('END') # placeholder 

161 # 

162 # prepare all OTHER bytecode sequences 

163 for tp in all_decls: 

164 if not tp.is_raw_function and self._typesdict[tp] is None: 

165 self._typesdict[tp] = len(self.cffi_types) 

166 self.cffi_types.append(tp) # placeholder 

167 if tp.is_array_type and tp.length is not None: 

168 self.cffi_types.append('LEN') # placeholder 

169 assert None not in self._typesdict.values() 

170 # 

171 # collect all structs and unions and enums 

172 self._struct_unions = {} 

173 self._enums = {} 

174 for tp in all_decls: 

175 if isinstance(tp, model.StructOrUnion): 

176 self._struct_unions[tp] = None 

177 elif isinstance(tp, model.EnumType): 

178 self._enums[tp] = None 

179 for i, tp in enumerate(sorted(self._struct_unions, 

180 key=lambda tp: tp.name)): 

181 self._struct_unions[tp] = i 

182 for i, tp in enumerate(sorted(self._enums, 

183 key=lambda tp: tp.name)): 

184 self._enums[tp] = i 

185 # 

186 # emit all bytecode sequences now 

187 for tp in all_decls: 

188 method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) 

189 method(tp, self._typesdict[tp]) 

190 # 

191 # consistency check 

192 for op in self.cffi_types: 

193 assert isinstance(op, CffiOp) 

194 self.cffi_types = tuple(self.cffi_types) # don't change any more 

195 

196 def _enum_fields(self, tp): 

197 # When producing C, expand all anonymous struct/union fields. 

198 # That's necessary to have C code checking the offsets of the 

199 # individual fields contained in them. When producing Python, 

200 # don't do it and instead write it like it is, with the 

201 # corresponding fields having an empty name. Empty names are 

202 # recognized at runtime when we import the generated Python 

203 # file. 

204 expand_anonymous_struct_union = not self.target_is_python 

205 return tp.enumfields(expand_anonymous_struct_union) 

206 

207 def _do_collect_type(self, tp): 

208 if not isinstance(tp, model.BaseTypeByIdentity): 

209 if isinstance(tp, tuple): 

210 for x in tp: 

211 self._do_collect_type(x) 

212 return 

213 if tp not in self._typesdict: 

214 self._typesdict[tp] = None 

215 if isinstance(tp, model.FunctionPtrType): 

216 self._do_collect_type(tp.as_raw_function()) 

217 elif isinstance(tp, model.StructOrUnion): 

218 if tp.fldtypes is not None and ( 

219 tp not in self.ffi._parser._included_declarations): 

220 for name1, tp1, _, _ in self._enum_fields(tp): 

221 self._do_collect_type(self._field_type(tp, name1, tp1)) 

222 else: 

223 for _, x in tp._get_items(): 

224 self._do_collect_type(x) 

225 

226 def _generate(self, step_name): 

227 lst = self.ffi._parser._declarations.items() 

228 for name, (tp, quals) in sorted(lst): 

229 kind, realname = name.split(' ', 1) 

230 try: 

231 method = getattr(self, '_generate_cpy_%s_%s' % (kind, 

232 step_name)) 

233 except AttributeError: 

234 raise VerificationError( 

235 "not implemented in recompile(): %r" % name) 

236 try: 

237 self._current_quals = quals 

238 method(tp, realname) 

239 except Exception as e: 

240 model.attach_exception_info(e, name) 

241 raise 

242 

243 # ---------- 

244 

245 ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] 

246 

247 def collect_step_tables(self): 

248 # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. 

249 self._lsts = {} 

250 for step_name in self.ALL_STEPS: 

251 self._lsts[step_name] = [] 

252 self._seen_struct_unions = set() 

253 self._generate("ctx") 

254 self._add_missing_struct_unions() 

255 # 

256 for step_name in self.ALL_STEPS: 

257 lst = self._lsts[step_name] 

258 if step_name != "field": 

259 lst.sort(key=lambda entry: entry.name) 

260 self._lsts[step_name] = tuple(lst) # don't change any more 

261 # 

262 # check for a possible internal inconsistency: _cffi_struct_unions 

263 # should have been generated with exactly self._struct_unions 

264 lst = self._lsts["struct_union"] 

265 for tp, i in self._struct_unions.items(): 

266 assert i < len(lst) 

267 assert lst[i].name == tp.name 

268 assert len(lst) == len(self._struct_unions) 

269 # same with enums 

270 lst = self._lsts["enum"] 

271 for tp, i in self._enums.items(): 

272 assert i < len(lst) 

273 assert lst[i].name == tp.name 

274 assert len(lst) == len(self._enums) 

275 

276 # ---------- 

277 

278 def _prnt(self, what=''): 

279 self._f.write(what + '\n') 

280 

281 def write_source_to_f(self, f, preamble): 

282 if self.target_is_python: 

283 assert preamble is None 

284 self.write_py_source_to_f(f) 

285 else: 

286 assert preamble is not None 

287 self.write_c_source_to_f(f, preamble) 

288 

289 def _rel_readlines(self, filename): 

290 g = open(os.path.join(os.path.dirname(__file__), filename), 'r') 

291 lines = g.readlines() 

292 g.close() 

293 return lines 

294 

295 def write_c_source_to_f(self, f, preamble): 

296 self._f = f 

297 prnt = self._prnt 

298 if self.ffi._embedding is not None: 

299 prnt('#define _CFFI_USE_EMBEDDING') 

300 if not USE_LIMITED_API: 

301 prnt('#define _CFFI_NO_LIMITED_API') 

302 # 

303 # first the '#include' (actually done by inlining the file's content) 

304 lines = self._rel_readlines('_cffi_include.h') 

305 i = lines.index('#include "parse_c_type.h"\n') 

306 lines[i:i+1] = self._rel_readlines('parse_c_type.h') 

307 prnt(''.join(lines)) 

308 # 

309 # if we have ffi._embedding != None, we give it here as a macro 

310 # and include an extra file 

311 base_module_name = self.module_name.split('.')[-1] 

312 if self.ffi._embedding is not None: 

313 prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) 

314 prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') 

315 self._print_string_literal_in_array(self.ffi._embedding) 

316 prnt('0 };') 

317 prnt('#ifdef PYPY_VERSION') 

318 prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( 

319 base_module_name,)) 

320 prnt('#elif PY_MAJOR_VERSION >= 3') 

321 prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( 

322 base_module_name,)) 

323 prnt('#else') 

324 prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( 

325 base_module_name,)) 

326 prnt('#endif') 

327 lines = self._rel_readlines('_embedding.h') 

328 i = lines.index('#include "_cffi_errors.h"\n') 

329 lines[i:i+1] = self._rel_readlines('_cffi_errors.h') 

330 prnt(''.join(lines)) 

331 self.needs_version(VERSION_EMBEDDED) 

332 # 

333 # then paste the C source given by the user, verbatim. 

334 prnt('/************************************************************/') 

335 prnt() 

336 prnt(preamble) 

337 prnt() 

338 prnt('/************************************************************/') 

339 prnt() 

340 # 

341 # the declaration of '_cffi_types' 

342 prnt('static void *_cffi_types[] = {') 

343 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) 

344 for i, op in enumerate(self.cffi_types): 

345 comment = '' 

346 if i in typeindex2type: 

347 comment = ' // ' + typeindex2type[i]._get_c_name() 

348 prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) 

349 if not self.cffi_types: 

350 prnt(' 0') 

351 prnt('};') 

352 prnt() 

353 # 

354 # call generate_cpy_xxx_decl(), for every xxx found from 

355 # ffi._parser._declarations. This generates all the functions. 

356 self._seen_constants = set() 

357 self._generate("decl") 

358 # 

359 # the declaration of '_cffi_globals' and '_cffi_typenames' 

360 nums = {} 

361 for step_name in self.ALL_STEPS: 

362 lst = self._lsts[step_name] 

363 nums[step_name] = len(lst) 

364 if nums[step_name] > 0: 

365 prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( 

366 step_name, step_name)) 

367 for entry in lst: 

368 prnt(entry.as_c_expr()) 

369 prnt('};') 

370 prnt() 

371 # 

372 # the declaration of '_cffi_includes' 

373 if self.ffi._included_ffis: 

374 prnt('static const char * const _cffi_includes[] = {') 

375 for ffi_to_include in self.ffi._included_ffis: 

376 try: 

377 included_module_name, included_source = ( 

378 ffi_to_include._assigned_source[:2]) 

379 except AttributeError: 

380 raise VerificationError( 

381 "ffi object %r includes %r, but the latter has not " 

382 "been prepared with set_source()" % ( 

383 self.ffi, ffi_to_include,)) 

384 if included_source is None: 

385 raise VerificationError( 

386 "not implemented yet: ffi.include() of a Python-based " 

387 "ffi inside a C-based ffi") 

388 prnt(' "%s",' % (included_module_name,)) 

389 prnt(' NULL') 

390 prnt('};') 

391 prnt() 

392 # 

393 # the declaration of '_cffi_type_context' 

394 prnt('static const struct _cffi_type_context_s _cffi_type_context = {') 

395 prnt(' _cffi_types,') 

396 for step_name in self.ALL_STEPS: 

397 if nums[step_name] > 0: 

398 prnt(' _cffi_%ss,' % step_name) 

399 else: 

400 prnt(' NULL, /* no %ss */' % step_name) 

401 for step_name in self.ALL_STEPS: 

402 if step_name != "field": 

403 prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) 

404 if self.ffi._included_ffis: 

405 prnt(' _cffi_includes,') 

406 else: 

407 prnt(' NULL, /* no includes */') 

408 prnt(' %d, /* num_types */' % (len(self.cffi_types),)) 

409 flags = 0 

410 if self._num_externpy > 0 or self.ffi._embedding is not None: 

411 flags |= 1 # set to mean that we use extern "Python" 

412 prnt(' %d, /* flags */' % flags) 

413 prnt('};') 

414 prnt() 

415 # 

416 # the init function 

417 prnt('#ifdef __GNUC__') 

418 prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') 

419 prnt('#endif') 

420 prnt() 

421 prnt('#ifdef PYPY_VERSION') 

422 prnt('PyMODINIT_FUNC') 

423 prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) 

424 prnt('{') 

425 if flags & 1: 

426 prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') 

427 prnt(' _cffi_call_python_org = ' 

428 '(void(*)(struct _cffi_externpy_s *, char *))p[1];') 

429 prnt(' }') 

430 prnt(' p[0] = (const void *)0x%x;' % self._version) 

431 prnt(' p[1] = &_cffi_type_context;') 

432 prnt('#if PY_MAJOR_VERSION >= 3') 

433 prnt(' return NULL;') 

434 prnt('#endif') 

435 prnt('}') 

436 # on Windows, distutils insists on putting init_cffi_xyz in 

437 # 'export_symbols', so instead of fighting it, just give up and 

438 # give it one 

439 prnt('# ifdef _MSC_VER') 

440 prnt(' PyMODINIT_FUNC') 

441 prnt('# if PY_MAJOR_VERSION >= 3') 

442 prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) 

443 prnt('# else') 

444 prnt(' init%s(void) { }' % (base_module_name,)) 

445 prnt('# endif') 

446 prnt('# endif') 

447 prnt('#elif PY_MAJOR_VERSION >= 3') 

448 prnt('PyMODINIT_FUNC') 

449 prnt('PyInit_%s(void)' % (base_module_name,)) 

450 prnt('{') 

451 prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( 

452 self.module_name, self._version)) 

453 prnt('}') 

454 prnt('#else') 

455 prnt('PyMODINIT_FUNC') 

456 prnt('init%s(void)' % (base_module_name,)) 

457 prnt('{') 

458 prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( 

459 self.module_name, self._version)) 

460 prnt('}') 

461 prnt('#endif') 

462 prnt() 

463 prnt('#ifdef __GNUC__') 

464 prnt('# pragma GCC visibility pop') 

465 prnt('#endif') 

466 self._version = None 

467 

468 def _to_py(self, x): 

469 if isinstance(x, str): 

470 return "b'%s'" % (x,) 

471 if isinstance(x, (list, tuple)): 

472 rep = [self._to_py(item) for item in x] 

473 if len(rep) == 1: 

474 rep.append('') 

475 return "(%s)" % (','.join(rep),) 

476 return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. 

477 

478 def write_py_source_to_f(self, f): 

479 self._f = f 

480 prnt = self._prnt 

481 # 

482 # header 

483 prnt("# auto-generated file") 

484 prnt("import _cffi_backend") 

485 # 

486 # the 'import' of the included ffis 

487 num_includes = len(self.ffi._included_ffis or ()) 

488 for i in range(num_includes): 

489 ffi_to_include = self.ffi._included_ffis[i] 

490 try: 

491 included_module_name, included_source = ( 

492 ffi_to_include._assigned_source[:2]) 

493 except AttributeError: 

494 raise VerificationError( 

495 "ffi object %r includes %r, but the latter has not " 

496 "been prepared with set_source()" % ( 

497 self.ffi, ffi_to_include,)) 

498 if included_source is not None: 

499 raise VerificationError( 

500 "not implemented yet: ffi.include() of a C-based " 

501 "ffi inside a Python-based ffi") 

502 prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) 

503 prnt() 

504 prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) 

505 prnt(" _version = 0x%x," % (self._version,)) 

506 self._version = None 

507 # 

508 # the '_types' keyword argument 

509 self.cffi_types = tuple(self.cffi_types) # don't change any more 

510 types_lst = [op.as_python_bytes() for op in self.cffi_types] 

511 prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) 

512 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) 

513 # 

514 # the keyword arguments from ALL_STEPS 

515 for step_name in self.ALL_STEPS: 

516 lst = self._lsts[step_name] 

517 if len(lst) > 0 and step_name != "field": 

518 prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) 

519 # 

520 # the '_includes' keyword argument 

521 if num_includes > 0: 

522 prnt(' _includes = (%s,),' % ( 

523 ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) 

524 # 

525 # the footer 

526 prnt(')') 

527 

528 # ---------- 

529 

530 def _gettypenum(self, type): 

531 # a KeyError here is a bug. please report it! :-) 

532 return self._typesdict[type] 

533 

534 def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): 

535 extraarg = '' 

536 if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): 

537 if tp.is_integer_type() and tp.name != '_Bool': 

538 converter = '_cffi_to_c_int' 

539 extraarg = ', %s' % tp.name 

540 elif isinstance(tp, model.UnknownFloatType): 

541 # don't check with is_float_type(): it may be a 'long 

542 # double' here, and _cffi_to_c_double would loose precision 

543 converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) 

544 else: 

545 cname = tp.get_c_name('') 

546 converter = '(%s)_cffi_to_c_%s' % (cname, 

547 tp.name.replace(' ', '_')) 

548 if cname in ('char16_t', 'char32_t'): 

549 self.needs_version(VERSION_CHAR16CHAR32) 

550 errvalue = '-1' 

551 # 

552 elif isinstance(tp, model.PointerType): 

553 self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, 

554 tovar, errcode) 

555 return 

556 # 

557 elif (isinstance(tp, model.StructOrUnionOrEnum) or 

558 isinstance(tp, model.BasePrimitiveType)): 

559 # a struct (not a struct pointer) as a function argument; 

560 # or, a complex (the same code works) 

561 self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' 

562 % (tovar, self._gettypenum(tp), fromvar)) 

563 self._prnt(' %s;' % errcode) 

564 return 

565 # 

566 elif isinstance(tp, model.FunctionPtrType): 

567 converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') 

568 extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) 

569 errvalue = 'NULL' 

570 # 

571 else: 

572 raise NotImplementedError(tp) 

573 # 

574 self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) 

575 self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( 

576 tovar, tp.get_c_name(''), errvalue)) 

577 self._prnt(' %s;' % errcode) 

578 

579 def _extra_local_variables(self, tp, localvars, freelines): 

580 if isinstance(tp, model.PointerType): 

581 localvars.add('Py_ssize_t datasize') 

582 localvars.add('struct _cffi_freeme_s *large_args_free = NULL') 

583 freelines.add('if (large_args_free != NULL)' 

584 ' _cffi_free_array_arguments(large_args_free);') 

585 

586 def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): 

587 self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') 

588 self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( 

589 self._gettypenum(tp), fromvar, tovar)) 

590 self._prnt(' if (datasize != 0) {') 

591 self._prnt(' %s = ((size_t)datasize) <= 640 ? ' 

592 '(%s)alloca((size_t)datasize) : NULL;' % ( 

593 tovar, tp.get_c_name(''))) 

594 self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' 

595 '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) 

596 self._prnt(' datasize, &large_args_free) < 0)') 

597 self._prnt(' %s;' % errcode) 

598 self._prnt(' }') 

599 

600 def _convert_expr_from_c(self, tp, var, context): 

601 if isinstance(tp, model.BasePrimitiveType): 

602 if tp.is_integer_type() and tp.name != '_Bool': 

603 return '_cffi_from_c_int(%s, %s)' % (var, tp.name) 

604 elif isinstance(tp, model.UnknownFloatType): 

605 return '_cffi_from_c_double(%s)' % (var,) 

606 elif tp.name != 'long double' and not tp.is_complex_type(): 

607 cname = tp.name.replace(' ', '_') 

608 if cname in ('char16_t', 'char32_t'): 

609 self.needs_version(VERSION_CHAR16CHAR32) 

610 return '_cffi_from_c_%s(%s)' % (cname, var) 

611 else: 

612 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( 

613 var, self._gettypenum(tp)) 

614 elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): 

615 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( 

616 var, self._gettypenum(tp)) 

617 elif isinstance(tp, model.ArrayType): 

618 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( 

619 var, self._gettypenum(model.PointerType(tp.item))) 

620 elif isinstance(tp, model.StructOrUnion): 

621 if tp.fldnames is None: 

622 raise TypeError("'%s' is used as %s, but is opaque" % ( 

623 tp._get_c_name(), context)) 

624 return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( 

625 var, self._gettypenum(tp)) 

626 elif isinstance(tp, model.EnumType): 

627 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( 

628 var, self._gettypenum(tp)) 

629 else: 

630 raise NotImplementedError(tp) 

631 

632 # ---------- 

633 # typedefs 

634 

635 def _typedef_type(self, tp, name): 

636 return self._global_type(tp, "(*(%s *)0)" % (name,)) 

637 

638 def _generate_cpy_typedef_collecttype(self, tp, name): 

639 self._do_collect_type(self._typedef_type(tp, name)) 

640 

641 def _generate_cpy_typedef_decl(self, tp, name): 

642 pass 

643 

644 def _typedef_ctx(self, tp, name): 

645 type_index = self._typesdict[tp] 

646 self._lsts["typename"].append(TypenameExpr(name, type_index)) 

647 

648 def _generate_cpy_typedef_ctx(self, tp, name): 

649 tp = self._typedef_type(tp, name) 

650 self._typedef_ctx(tp, name) 

651 if getattr(tp, "origin", None) == "unknown_type": 

652 self._struct_ctx(tp, tp.name, approxname=None) 

653 elif isinstance(tp, model.NamedPointerType): 

654 self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, 

655 named_ptr=tp) 

656 

657 # ---------- 

658 # function declarations 

659 

660 def _generate_cpy_function_collecttype(self, tp, name): 

661 self._do_collect_type(tp.as_raw_function()) 

662 if tp.ellipsis and not self.target_is_python: 

663 self._do_collect_type(tp) 

664 

665 def _generate_cpy_function_decl(self, tp, name): 

666 assert not self.target_is_python 

667 assert isinstance(tp, model.FunctionPtrType) 

668 if tp.ellipsis: 

669 # cannot support vararg functions better than this: check for its 

670 # exact type (including the fixed arguments), and build it as a 

671 # constant function pointer (no CPython wrapper) 

672 self._generate_cpy_constant_decl(tp, name) 

673 return 

674 prnt = self._prnt 

675 numargs = len(tp.args) 

676 if numargs == 0: 

677 argname = 'noarg' 

678 elif numargs == 1: 

679 argname = 'arg0' 

680 else: 

681 argname = 'args' 

682 # 

683 # ------------------------------ 

684 # the 'd' version of the function, only for addressof(lib, 'func') 

685 arguments = [] 

686 call_arguments = [] 

687 context = 'argument of %s' % name 

688 for i, type in enumerate(tp.args): 

689 arguments.append(type.get_c_name(' x%d' % i, context)) 

690 call_arguments.append('x%d' % i) 

691 repr_arguments = ', '.join(arguments) 

692 repr_arguments = repr_arguments or 'void' 

693 if tp.abi: 

694 abi = tp.abi + ' ' 

695 else: 

696 abi = '' 

697 name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) 

698 prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) 

699 prnt('{') 

700 call_arguments = ', '.join(call_arguments) 

701 result_code = 'return ' 

702 if isinstance(tp.result, model.VoidType): 

703 result_code = '' 

704 prnt(' %s%s(%s);' % (result_code, name, call_arguments)) 

705 prnt('}') 

706 # 

707 prnt('#ifndef PYPY_VERSION') # ------------------------------ 

708 # 

709 prnt('static PyObject *') 

710 prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) 

711 prnt('{') 

712 # 

713 context = 'argument of %s' % name 

714 for i, type in enumerate(tp.args): 

715 arg = type.get_c_name(' x%d' % i, context) 

716 prnt(' %s;' % arg) 

717 # 

718 localvars = set() 

719 freelines = set() 

720 for type in tp.args: 

721 self._extra_local_variables(type, localvars, freelines) 

722 for decl in sorted(localvars): 

723 prnt(' %s;' % (decl,)) 

724 # 

725 if not isinstance(tp.result, model.VoidType): 

726 result_code = 'result = ' 

727 context = 'result of %s' % name 

728 result_decl = ' %s;' % tp.result.get_c_name(' result', context) 

729 prnt(result_decl) 

730 prnt(' PyObject *pyresult;') 

731 else: 

732 result_decl = None 

733 result_code = '' 

734 # 

735 if len(tp.args) > 1: 

736 rng = range(len(tp.args)) 

737 for i in rng: 

738 prnt(' PyObject *arg%d;' % i) 

739 prnt() 

740 prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( 

741 name, len(rng), len(rng), 

742 ', '.join(['&arg%d' % i for i in rng]))) 

743 prnt(' return NULL;') 

744 prnt() 

745 # 

746 for i, type in enumerate(tp.args): 

747 self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, 

748 'return NULL') 

749 prnt() 

750 # 

751 prnt(' Py_BEGIN_ALLOW_THREADS') 

752 prnt(' _cffi_restore_errno();') 

753 call_arguments = ['x%d' % i for i in range(len(tp.args))] 

754 call_arguments = ', '.join(call_arguments) 

755 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) 

756 prnt(' _cffi_save_errno();') 

757 prnt(' Py_END_ALLOW_THREADS') 

758 prnt() 

759 # 

760 prnt(' (void)self; /* unused */') 

761 if numargs == 0: 

762 prnt(' (void)noarg; /* unused */') 

763 if result_code: 

764 prnt(' pyresult = %s;' % 

765 self._convert_expr_from_c(tp.result, 'result', 'result type')) 

766 for freeline in freelines: 

767 prnt(' ' + freeline) 

768 prnt(' return pyresult;') 

769 else: 

770 for freeline in freelines: 

771 prnt(' ' + freeline) 

772 prnt(' Py_INCREF(Py_None);') 

773 prnt(' return Py_None;') 

774 prnt('}') 

775 # 

776 prnt('#else') # ------------------------------ 

777 # 

778 # the PyPy version: need to replace struct/union arguments with 

779 # pointers, and if the result is a struct/union, insert a first 

780 # arg that is a pointer to the result. We also do that for 

781 # complex args and return type. 

782 def need_indirection(type): 

783 return (isinstance(type, model.StructOrUnion) or 

784 (isinstance(type, model.PrimitiveType) and 

785 type.is_complex_type())) 

786 difference = False 

787 arguments = [] 

788 call_arguments = [] 

789 context = 'argument of %s' % name 

790 for i, type in enumerate(tp.args): 

791 indirection = '' 

792 if need_indirection(type): 

793 indirection = '*' 

794 difference = True 

795 arg = type.get_c_name(' %sx%d' % (indirection, i), context) 

796 arguments.append(arg) 

797 call_arguments.append('%sx%d' % (indirection, i)) 

798 tp_result = tp.result 

799 if need_indirection(tp_result): 

800 context = 'result of %s' % name 

801 arg = tp_result.get_c_name(' *result', context) 

802 arguments.insert(0, arg) 

803 tp_result = model.void_type 

804 result_decl = None 

805 result_code = '*result = ' 

806 difference = True 

807 if difference: 

808 repr_arguments = ', '.join(arguments) 

809 repr_arguments = repr_arguments or 'void' 

810 name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, 

811 repr_arguments) 

812 prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) 

813 prnt('{') 

814 if result_decl: 

815 prnt(result_decl) 

816 call_arguments = ', '.join(call_arguments) 

817 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) 

818 if result_decl: 

819 prnt(' return result;') 

820 prnt('}') 

821 else: 

822 prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) 

823 # 

824 prnt('#endif') # ------------------------------ 

825 prnt() 

826 

827 def _generate_cpy_function_ctx(self, tp, name): 

828 if tp.ellipsis and not self.target_is_python: 

829 self._generate_cpy_constant_ctx(tp, name) 

830 return 

831 type_index = self._typesdict[tp.as_raw_function()] 

832 numargs = len(tp.args) 

833 if self.target_is_python: 

834 meth_kind = OP_DLOPEN_FUNC 

835 elif numargs == 0: 

836 meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' 

837 elif numargs == 1: 

838 meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' 

839 else: 

840 meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' 

841 self._lsts["global"].append( 

842 GlobalExpr(name, '_cffi_f_%s' % name, 

843 CffiOp(meth_kind, type_index), 

844 size='_cffi_d_%s' % name)) 

845 

846 # ---------- 

847 # named structs or unions 

848 

849 def _field_type(self, tp_struct, field_name, tp_field): 

850 if isinstance(tp_field, model.ArrayType): 

851 actual_length = tp_field.length 

852 if actual_length == '...': 

853 ptr_struct_name = tp_struct.get_c_name('*') 

854 actual_length = '_cffi_array_len(((%s)0)->%s)' % ( 

855 ptr_struct_name, field_name) 

856 tp_item = self._field_type(tp_struct, '%s[0]' % field_name, 

857 tp_field.item) 

858 tp_field = model.ArrayType(tp_item, actual_length) 

859 return tp_field 

860 

861 def _struct_collecttype(self, tp): 

862 self._do_collect_type(tp) 

863 if self.target_is_python: 

864 # also requires nested anon struct/unions in ABI mode, recursively 

865 for fldtype in tp.anonymous_struct_fields(): 

866 self._struct_collecttype(fldtype) 

867 

868 def _struct_decl(self, tp, cname, approxname): 

869 if tp.fldtypes is None: 

870 return 

871 prnt = self._prnt 

872 checkfuncname = '_cffi_checkfld_%s' % (approxname,) 

873 prnt('_CFFI_UNUSED_FN') 

874 prnt('static void %s(%s *p)' % (checkfuncname, cname)) 

875 prnt('{') 

876 prnt(' /* only to generate compile-time warnings or errors */') 

877 prnt(' (void)p;') 

878 for fname, ftype, fbitsize, fqual in self._enum_fields(tp): 

879 try: 

880 if ftype.is_integer_type() or fbitsize >= 0: 

881 # accept all integers, but complain on float or double 

882 if fname != '': 

883 prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " 

884 "an integer */" % (fname, cname, fname)) 

885 continue 

886 # only accept exactly the type declared, except that '[]' 

887 # is interpreted as a '*' and so will match any array length. 

888 # (It would also match '*', but that's harder to detect...) 

889 while (isinstance(ftype, model.ArrayType) 

890 and (ftype.length is None or ftype.length == '...')): 

891 ftype = ftype.item 

892 fname = fname + '[0]' 

893 prnt(' { %s = &p->%s; (void)tmp; }' % ( 

894 ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), 

895 fname)) 

896 except VerificationError as e: 

897 prnt(' /* %s */' % str(e)) # cannot verify it, ignore 

898 prnt('}') 

899 prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) 

900 prnt() 

901 

902 def _struct_ctx(self, tp, cname, approxname, named_ptr=None): 

903 type_index = self._typesdict[tp] 

904 reason_for_not_expanding = None 

905 flags = [] 

906 if isinstance(tp, model.UnionType): 

907 flags.append("_CFFI_F_UNION") 

908 if tp.fldtypes is None: 

909 flags.append("_CFFI_F_OPAQUE") 

910 reason_for_not_expanding = "opaque" 

911 if (tp not in self.ffi._parser._included_declarations and 

912 (named_ptr is None or 

913 named_ptr not in self.ffi._parser._included_declarations)): 

914 if tp.fldtypes is None: 

915 pass # opaque 

916 elif tp.partial or any(tp.anonymous_struct_fields()): 

917 pass # field layout obtained silently from the C compiler 

918 else: 

919 flags.append("_CFFI_F_CHECK_FIELDS") 

920 if tp.packed: 

921 if tp.packed > 1: 

922 raise NotImplementedError( 

923 "%r is declared with 'pack=%r'; only 0 or 1 are " 

924 "supported in API mode (try to use \"...;\", which " 

925 "does not require a 'pack' declaration)" % 

926 (tp, tp.packed)) 

927 flags.append("_CFFI_F_PACKED") 

928 else: 

929 flags.append("_CFFI_F_EXTERNAL") 

930 reason_for_not_expanding = "external" 

931 flags = '|'.join(flags) or '0' 

932 c_fields = [] 

933 if reason_for_not_expanding is None: 

934 enumfields = list(self._enum_fields(tp)) 

935 for fldname, fldtype, fbitsize, fqual in enumfields: 

936 fldtype = self._field_type(tp, fldname, fldtype) 

937 self._check_not_opaque(fldtype, 

938 "field '%s.%s'" % (tp.name, fldname)) 

939 # cname is None for _add_missing_struct_unions() only 

940 op = OP_NOOP 

941 if fbitsize >= 0: 

942 op = OP_BITFIELD 

943 size = '%d /* bits */' % fbitsize 

944 elif cname is None or ( 

945 isinstance(fldtype, model.ArrayType) and 

946 fldtype.length is None): 

947 size = '(size_t)-1' 

948 else: 

949 size = 'sizeof(((%s)0)->%s)' % ( 

950 tp.get_c_name('*') if named_ptr is None 

951 else named_ptr.name, 

952 fldname) 

953 if cname is None or fbitsize >= 0: 

954 offset = '(size_t)-1' 

955 elif named_ptr is not None: 

956 offset = '((char *)&((%s)0)->%s) - (char *)0' % ( 

957 named_ptr.name, fldname) 

958 else: 

959 offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) 

960 c_fields.append( 

961 FieldExpr(fldname, offset, size, fbitsize, 

962 CffiOp(op, self._typesdict[fldtype]))) 

963 first_field_index = len(self._lsts["field"]) 

964 self._lsts["field"].extend(c_fields) 

965 # 

966 if cname is None: # unknown name, for _add_missing_struct_unions 

967 size = '(size_t)-2' 

968 align = -2 

969 comment = "unnamed" 

970 else: 

971 if named_ptr is not None: 

972 size = 'sizeof(*(%s)0)' % (named_ptr.name,) 

973 align = '-1 /* unknown alignment */' 

974 else: 

975 size = 'sizeof(%s)' % (cname,) 

976 align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) 

977 comment = None 

978 else: 

979 size = '(size_t)-1' 

980 align = -1 

981 first_field_index = -1 

982 comment = reason_for_not_expanding 

983 self._lsts["struct_union"].append( 

984 StructUnionExpr(tp.name, type_index, flags, size, align, comment, 

985 first_field_index, c_fields)) 

986 self._seen_struct_unions.add(tp) 

987 

988 def _check_not_opaque(self, tp, location): 

989 while isinstance(tp, model.ArrayType): 

990 tp = tp.item 

991 if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: 

992 raise TypeError( 

993 "%s is of an opaque type (not declared in cdef())" % location) 

994 

995 def _add_missing_struct_unions(self): 

996 # not very nice, but some struct declarations might be missing 

997 # because they don't have any known C name. Check that they are 

998 # not partial (we can't complete or verify them!) and emit them 

999 # anonymously. 

1000 lst = list(self._struct_unions.items()) 

1001 lst.sort(key=lambda tp_order: tp_order[1]) 

1002 for tp, order in lst: 

1003 if tp not in self._seen_struct_unions: 

1004 if tp.partial: 

1005 raise NotImplementedError("internal inconsistency: %r is " 

1006 "partial but was not seen at " 

1007 "this point" % (tp,)) 

1008 if tp.name.startswith('$') and tp.name[1:].isdigit(): 

1009 approxname = tp.name[1:] 

1010 elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': 

1011 approxname = 'FILE' 

1012 self._typedef_ctx(tp, 'FILE') 

1013 else: 

1014 raise NotImplementedError("internal inconsistency: %r" % 

1015 (tp,)) 

1016 self._struct_ctx(tp, None, approxname) 

1017 

1018 def _generate_cpy_struct_collecttype(self, tp, name): 

1019 self._struct_collecttype(tp) 

1020 _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype 

1021 

1022 def _struct_names(self, tp): 

1023 cname = tp.get_c_name('') 

1024 if ' ' in cname: 

1025 return cname, cname.replace(' ', '_') 

1026 else: 

1027 return cname, '_' + cname 

1028 

1029 def _generate_cpy_struct_decl(self, tp, name): 

1030 self._struct_decl(tp, *self._struct_names(tp)) 

1031 _generate_cpy_union_decl = _generate_cpy_struct_decl 

1032 

1033 def _generate_cpy_struct_ctx(self, tp, name): 

1034 self._struct_ctx(tp, *self._struct_names(tp)) 

1035 _generate_cpy_union_ctx = _generate_cpy_struct_ctx 

1036 

1037 # ---------- 

1038 # 'anonymous' declarations. These are produced for anonymous structs 

1039 # or unions; the 'name' is obtained by a typedef. 

1040 

1041 def _generate_cpy_anonymous_collecttype(self, tp, name): 

1042 if isinstance(tp, model.EnumType): 

1043 self._generate_cpy_enum_collecttype(tp, name) 

1044 else: 

1045 self._struct_collecttype(tp) 

1046 

1047 def _generate_cpy_anonymous_decl(self, tp, name): 

1048 if isinstance(tp, model.EnumType): 

1049 self._generate_cpy_enum_decl(tp) 

1050 else: 

1051 self._struct_decl(tp, name, 'typedef_' + name) 

1052 

1053 def _generate_cpy_anonymous_ctx(self, tp, name): 

1054 if isinstance(tp, model.EnumType): 

1055 self._enum_ctx(tp, name) 

1056 else: 

1057 self._struct_ctx(tp, name, 'typedef_' + name) 

1058 

1059 # ---------- 

1060 # constants, declared with "static const ..." 

1061 

1062 def _generate_cpy_const(self, is_int, name, tp=None, category='const', 

1063 check_value=None): 

1064 if (category, name) in self._seen_constants: 

1065 raise VerificationError( 

1066 "duplicate declaration of %s '%s'" % (category, name)) 

1067 self._seen_constants.add((category, name)) 

1068 # 

1069 prnt = self._prnt 

1070 funcname = '_cffi_%s_%s' % (category, name) 

1071 if is_int: 

1072 prnt('static int %s(unsigned long long *o)' % funcname) 

1073 prnt('{') 

1074 prnt(' int n = (%s) <= 0;' % (name,)) 

1075 prnt(' *o = (unsigned long long)((%s) | 0);' 

1076 ' /* check that %s is an integer */' % (name, name)) 

1077 if check_value is not None: 

1078 if check_value > 0: 

1079 check_value = '%dU' % (check_value,) 

1080 prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) 

1081 prnt(' n |= 2;') 

1082 prnt(' return n;') 

1083 prnt('}') 

1084 else: 

1085 assert check_value is None 

1086 prnt('static void %s(char *o)' % funcname) 

1087 prnt('{') 

1088 prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) 

1089 prnt('}') 

1090 prnt() 

1091 

1092 def _generate_cpy_constant_collecttype(self, tp, name): 

1093 is_int = tp.is_integer_type() 

1094 if not is_int or self.target_is_python: 

1095 self._do_collect_type(tp) 

1096 

1097 def _generate_cpy_constant_decl(self, tp, name): 

1098 is_int = tp.is_integer_type() 

1099 self._generate_cpy_const(is_int, name, tp) 

1100 

1101 def _generate_cpy_constant_ctx(self, tp, name): 

1102 if not self.target_is_python and tp.is_integer_type(): 

1103 type_op = CffiOp(OP_CONSTANT_INT, -1) 

1104 else: 

1105 if self.target_is_python: 

1106 const_kind = OP_DLOPEN_CONST 

1107 else: 

1108 const_kind = OP_CONSTANT 

1109 type_index = self._typesdict[tp] 

1110 type_op = CffiOp(const_kind, type_index) 

1111 self._lsts["global"].append( 

1112 GlobalExpr(name, '_cffi_const_%s' % name, type_op)) 

1113 

1114 # ---------- 

1115 # enums 

1116 

1117 def _generate_cpy_enum_collecttype(self, tp, name): 

1118 self._do_collect_type(tp) 

1119 

1120 def _generate_cpy_enum_decl(self, tp, name=None): 

1121 for enumerator in tp.enumerators: 

1122 self._generate_cpy_const(True, enumerator) 

1123 

1124 def _enum_ctx(self, tp, cname): 

1125 type_index = self._typesdict[tp] 

1126 type_op = CffiOp(OP_ENUM, -1) 

1127 if self.target_is_python: 

1128 tp.check_not_partial() 

1129 for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): 

1130 self._lsts["global"].append( 

1131 GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, 

1132 check_value=enumvalue)) 

1133 # 

1134 if cname is not None and '$' not in cname and not self.target_is_python: 

1135 size = "sizeof(%s)" % cname 

1136 signed = "((%s)-1) <= 0" % cname 

1137 else: 

1138 basetp = tp.build_baseinttype(self.ffi, []) 

1139 size = self.ffi.sizeof(basetp) 

1140 signed = int(int(self.ffi.cast(basetp, -1)) < 0) 

1141 allenums = ",".join(tp.enumerators) 

1142 self._lsts["enum"].append( 

1143 EnumExpr(tp.name, type_index, size, signed, allenums)) 

1144 

1145 def _generate_cpy_enum_ctx(self, tp, name): 

1146 self._enum_ctx(tp, tp._get_c_name()) 

1147 

1148 # ---------- 

1149 # macros: for now only for integers 

1150 

1151 def _generate_cpy_macro_collecttype(self, tp, name): 

1152 pass 

1153 

1154 def _generate_cpy_macro_decl(self, tp, name): 

1155 if tp == '...': 

1156 check_value = None 

1157 else: 

1158 check_value = tp # an integer 

1159 self._generate_cpy_const(True, name, check_value=check_value) 

1160 

1161 def _generate_cpy_macro_ctx(self, tp, name): 

1162 if tp == '...': 

1163 if self.target_is_python: 

1164 raise VerificationError( 

1165 "cannot use the syntax '...' in '#define %s ...' when " 

1166 "using the ABI mode" % (name,)) 

1167 check_value = None 

1168 else: 

1169 check_value = tp # an integer 

1170 type_op = CffiOp(OP_CONSTANT_INT, -1) 

1171 self._lsts["global"].append( 

1172 GlobalExpr(name, '_cffi_const_%s' % name, type_op, 

1173 check_value=check_value)) 

1174 

1175 # ---------- 

1176 # global variables 

1177 

1178 def _global_type(self, tp, global_name): 

1179 if isinstance(tp, model.ArrayType): 

1180 actual_length = tp.length 

1181 if actual_length == '...': 

1182 actual_length = '_cffi_array_len(%s)' % (global_name,) 

1183 tp_item = self._global_type(tp.item, '%s[0]' % global_name) 

1184 tp = model.ArrayType(tp_item, actual_length) 

1185 return tp 

1186 

1187 def _generate_cpy_variable_collecttype(self, tp, name): 

1188 self._do_collect_type(self._global_type(tp, name)) 

1189 

1190 def _generate_cpy_variable_decl(self, tp, name): 

1191 prnt = self._prnt 

1192 tp = self._global_type(tp, name) 

1193 if isinstance(tp, model.ArrayType) and tp.length is None: 

1194 tp = tp.item 

1195 ampersand = '' 

1196 else: 

1197 ampersand = '&' 

1198 # This code assumes that casts from "tp *" to "void *" is a 

1199 # no-op, i.e. a function that returns a "tp *" can be called 

1200 # as if it returned a "void *". This should be generally true 

1201 # on any modern machine. The only exception to that rule (on 

1202 # uncommon architectures, and as far as I can tell) might be 

1203 # if 'tp' were a function type, but that is not possible here. 

1204 # (If 'tp' is a function _pointer_ type, then casts from "fn_t 

1205 # **" to "void *" are again no-ops, as far as I can tell.) 

1206 decl = '*_cffi_var_%s(void)' % (name,) 

1207 prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) 

1208 prnt('{') 

1209 prnt(' return %s(%s);' % (ampersand, name)) 

1210 prnt('}') 

1211 prnt() 

1212 

1213 def _generate_cpy_variable_ctx(self, tp, name): 

1214 tp = self._global_type(tp, name) 

1215 type_index = self._typesdict[tp] 

1216 if self.target_is_python: 

1217 op = OP_GLOBAL_VAR 

1218 else: 

1219 op = OP_GLOBAL_VAR_F 

1220 self._lsts["global"].append( 

1221 GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) 

1222 

1223 # ---------- 

1224 # extern "Python" 

1225 

1226 def _generate_cpy_extern_python_collecttype(self, tp, name): 

1227 assert isinstance(tp, model.FunctionPtrType) 

1228 self._do_collect_type(tp) 

1229 _generate_cpy_dllexport_python_collecttype = \ 

1230 _generate_cpy_extern_python_plus_c_collecttype = \ 

1231 _generate_cpy_extern_python_collecttype 

1232 

1233 def _extern_python_decl(self, tp, name, tag_and_space): 

1234 prnt = self._prnt 

1235 if isinstance(tp.result, model.VoidType): 

1236 size_of_result = '0' 

1237 else: 

1238 context = 'result of %s' % name 

1239 size_of_result = '(int)sizeof(%s)' % ( 

1240 tp.result.get_c_name('', context),) 

1241 prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) 

1242 prnt(' { "%s.%s", %s, 0, 0 };' % ( 

1243 self.module_name, name, size_of_result)) 

1244 prnt() 

1245 # 

1246 arguments = [] 

1247 context = 'argument of %s' % name 

1248 for i, type in enumerate(tp.args): 

1249 arg = type.get_c_name(' a%d' % i, context) 

1250 arguments.append(arg) 

1251 # 

1252 repr_arguments = ', '.join(arguments) 

1253 repr_arguments = repr_arguments or 'void' 

1254 name_and_arguments = '%s(%s)' % (name, repr_arguments) 

1255 if tp.abi == "__stdcall": 

1256 name_and_arguments = '_cffi_stdcall ' + name_and_arguments 

1257 # 

1258 def may_need_128_bits(tp): 

1259 return (isinstance(tp, model.PrimitiveType) and 

1260 tp.name == 'long double') 

1261 # 

1262 size_of_a = max(len(tp.args)*8, 8) 

1263 if may_need_128_bits(tp.result): 

1264 size_of_a = max(size_of_a, 16) 

1265 if isinstance(tp.result, model.StructOrUnion): 

1266 size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( 

1267 tp.result.get_c_name(''), size_of_a, 

1268 tp.result.get_c_name(''), size_of_a) 

1269 prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) 

1270 prnt('{') 

1271 prnt(' char a[%s];' % size_of_a) 

1272 prnt(' char *p = a;') 

1273 for i, type in enumerate(tp.args): 

1274 arg = 'a%d' % i 

1275 if (isinstance(type, model.StructOrUnion) or 

1276 may_need_128_bits(type)): 

1277 arg = '&' + arg 

1278 type = model.PointerType(type) 

1279 prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) 

1280 prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) 

1281 if not isinstance(tp.result, model.VoidType): 

1282 prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) 

1283 prnt('}') 

1284 prnt() 

1285 self._num_externpy += 1 

1286 

1287 def _generate_cpy_extern_python_decl(self, tp, name): 

1288 self._extern_python_decl(tp, name, 'static ') 

1289 

1290 def _generate_cpy_dllexport_python_decl(self, tp, name): 

1291 self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') 

1292 

1293 def _generate_cpy_extern_python_plus_c_decl(self, tp, name): 

1294 self._extern_python_decl(tp, name, '') 

1295 

1296 def _generate_cpy_extern_python_ctx(self, tp, name): 

1297 if self.target_is_python: 

1298 raise VerificationError( 

1299 "cannot use 'extern \"Python\"' in the ABI mode") 

1300 if tp.ellipsis: 

1301 raise NotImplementedError("a vararg function is extern \"Python\"") 

1302 type_index = self._typesdict[tp] 

1303 type_op = CffiOp(OP_EXTERN_PYTHON, type_index) 

1304 self._lsts["global"].append( 

1305 GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) 

1306 

1307 _generate_cpy_dllexport_python_ctx = \ 

1308 _generate_cpy_extern_python_plus_c_ctx = \ 

1309 _generate_cpy_extern_python_ctx 

1310 

1311 def _print_string_literal_in_array(self, s): 

1312 prnt = self._prnt 

1313 prnt('// # NB. this is not a string because of a size limit in MSVC') 

1314 if not isinstance(s, bytes): # unicode 

1315 s = s.encode('utf-8') # -> bytes 

1316 else: 

1317 s.decode('utf-8') # got bytes, check for valid utf-8 

1318 try: 

1319 s.decode('ascii') 

1320 except UnicodeDecodeError: 

1321 s = b'# -*- encoding: utf8 -*-\n' + s 

1322 for line in s.splitlines(True): 

1323 comment = line 

1324 if type('//') is bytes: # python2 

1325 line = map(ord, line) # make a list of integers 

1326 else: # python3 

1327 # type(line) is bytes, which enumerates like a list of integers 

1328 comment = ascii(comment)[1:-1] 

1329 prnt(('// ' + comment).rstrip()) 

1330 printed_line = '' 

1331 for c in line: 

1332 if len(printed_line) >= 76: 

1333 prnt(printed_line) 

1334 printed_line = '' 

1335 printed_line += '%d,' % (c,) 

1336 prnt(printed_line) 

1337 

1338 # ---------- 

1339 # emitting the opcodes for individual types 

1340 

1341 def _emit_bytecode_VoidType(self, tp, index): 

1342 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) 

1343 

1344 def _emit_bytecode_PrimitiveType(self, tp, index): 

1345 prim_index = PRIMITIVE_TO_INDEX[tp.name] 

1346 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) 

1347 

1348 def _emit_bytecode_UnknownIntegerType(self, tp, index): 

1349 s = ('_cffi_prim_int(sizeof(%s), (\n' 

1350 ' ((%s)-1) | 0 /* check that %s is an integer type */\n' 

1351 ' ) <= 0)' % (tp.name, tp.name, tp.name)) 

1352 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) 

1353 

1354 def _emit_bytecode_UnknownFloatType(self, tp, index): 

1355 s = ('_cffi_prim_float(sizeof(%s) *\n' 

1356 ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' 

1357 ' )' % (tp.name, tp.name)) 

1358 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) 

1359 

1360 def _emit_bytecode_RawFunctionType(self, tp, index): 

1361 self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) 

1362 index += 1 

1363 for tp1 in tp.args: 

1364 realindex = self._typesdict[tp1] 

1365 if index != realindex: 

1366 if isinstance(tp1, model.PrimitiveType): 

1367 self._emit_bytecode_PrimitiveType(tp1, index) 

1368 else: 

1369 self.cffi_types[index] = CffiOp(OP_NOOP, realindex) 

1370 index += 1 

1371 flags = int(tp.ellipsis) 

1372 if tp.abi is not None: 

1373 if tp.abi == '__stdcall': 

1374 flags |= 2 

1375 else: 

1376 raise NotImplementedError("abi=%r" % (tp.abi,)) 

1377 self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) 

1378 

1379 def _emit_bytecode_PointerType(self, tp, index): 

1380 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) 

1381 

1382 _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType 

1383 _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType 

1384 

1385 def _emit_bytecode_FunctionPtrType(self, tp, index): 

1386 raw = tp.as_raw_function() 

1387 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) 

1388 

1389 def _emit_bytecode_ArrayType(self, tp, index): 

1390 item_index = self._typesdict[tp.item] 

1391 if tp.length is None: 

1392 self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) 

1393 elif tp.length == '...': 

1394 raise VerificationError( 

1395 "type %s badly placed: the '...' array length can only be " 

1396 "used on global arrays or on fields of structures" % ( 

1397 str(tp).replace('/*...*/', '...'),)) 

1398 else: 

1399 assert self.cffi_types[index + 1] == 'LEN' 

1400 self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) 

1401 self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) 

1402 

1403 def _emit_bytecode_StructType(self, tp, index): 

1404 struct_index = self._struct_unions[tp] 

1405 self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) 

1406 _emit_bytecode_UnionType = _emit_bytecode_StructType 

1407 

1408 def _emit_bytecode_EnumType(self, tp, index): 

1409 enum_index = self._enums[tp] 

1410 self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) 

1411 

1412 

1413if sys.version_info >= (3,): 

1414 NativeIO = io.StringIO 

1415else: 

1416 class NativeIO(io.BytesIO): 

1417 def write(self, s): 

1418 if isinstance(s, unicode): 

1419 s = s.encode('ascii') 

1420 super(NativeIO, self).write(s) 

1421 

1422def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): 

1423 if verbose: 

1424 print("generating %s" % (target_file,)) 

1425 recompiler = Recompiler(ffi, module_name, 

1426 target_is_python=(preamble is None)) 

1427 recompiler.collect_type_table() 

1428 recompiler.collect_step_tables() 

1429 f = NativeIO() 

1430 recompiler.write_source_to_f(f, preamble) 

1431 output = f.getvalue() 

1432 try: 

1433 with open(target_file, 'r') as f1: 

1434 if f1.read(len(output) + 1) != output: 

1435 raise IOError 

1436 if verbose: 

1437 print("(already up-to-date)") 

1438 return False # already up-to-date 

1439 except IOError: 

1440 tmp_file = '%s.~%d' % (target_file, os.getpid()) 

1441 with open(tmp_file, 'w') as f1: 

1442 f1.write(output) 

1443 try: 

1444 os.rename(tmp_file, target_file) 

1445 except OSError: 

1446 os.unlink(target_file) 

1447 os.rename(tmp_file, target_file) 

1448 return True 

1449 

1450def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): 

1451 assert preamble is not None 

1452 return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, 

1453 verbose) 

1454 

1455def make_py_source(ffi, module_name, target_py_file, verbose=False): 

1456 return _make_c_or_py_source(ffi, module_name, None, target_py_file, 

1457 verbose) 

1458 

1459def _modname_to_file(outputdir, modname, extension): 

1460 parts = modname.split('.') 

1461 try: 

1462 os.makedirs(os.path.join(outputdir, *parts[:-1])) 

1463 except OSError: 

1464 pass 

1465 parts[-1] += extension 

1466 return os.path.join(outputdir, *parts), parts 

1467 

1468 

1469# Aaargh. Distutils is not tested at all for the purpose of compiling 

1470# DLLs that are not extension modules. Here are some hacks to work 

1471# around that, in the _patch_for_*() functions... 

1472 

1473def _patch_meth(patchlist, cls, name, new_meth): 

1474 old = getattr(cls, name) 

1475 patchlist.append((cls, name, old)) 

1476 setattr(cls, name, new_meth) 

1477 return old 

1478 

1479def _unpatch_meths(patchlist): 

1480 for cls, name, old_meth in reversed(patchlist): 

1481 setattr(cls, name, old_meth) 

1482 

1483def _patch_for_embedding(patchlist): 

1484 if sys.platform == 'win32': 

1485 # we must not remove the manifest when building for embedding! 

1486 from distutils.msvc9compiler import MSVCCompiler 

1487 _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', 

1488 lambda self, manifest_file: manifest_file) 

1489 

1490 if sys.platform == 'darwin': 

1491 # we must not make a '-bundle', but a '-dynamiclib' instead 

1492 from distutils.ccompiler import CCompiler 

1493 def my_link_shared_object(self, *args, **kwds): 

1494 if '-bundle' in self.linker_so: 

1495 self.linker_so = list(self.linker_so) 

1496 i = self.linker_so.index('-bundle') 

1497 self.linker_so[i] = '-dynamiclib' 

1498 return old_link_shared_object(self, *args, **kwds) 

1499 old_link_shared_object = _patch_meth(patchlist, CCompiler, 

1500 'link_shared_object', 

1501 my_link_shared_object) 

1502 

1503def _patch_for_target(patchlist, target): 

1504 from distutils.command.build_ext import build_ext 

1505 # if 'target' is different from '*', we need to patch some internal 

1506 # method to just return this 'target' value, instead of having it 

1507 # built from module_name 

1508 if target.endswith('.*'): 

1509 target = target[:-2] 

1510 if sys.platform == 'win32': 

1511 target += '.dll' 

1512 elif sys.platform == 'darwin': 

1513 target += '.dylib' 

1514 else: 

1515 target += '.so' 

1516 _patch_meth(patchlist, build_ext, 'get_ext_filename', 

1517 lambda self, ext_name: target) 

1518 

1519 

1520def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, 

1521 c_file=None, source_extension='.c', extradir=None, 

1522 compiler_verbose=1, target=None, debug=None, **kwds): 

1523 if not isinstance(module_name, str): 

1524 module_name = module_name.encode('ascii') 

1525 if ffi._windows_unicode: 

1526 ffi._apply_windows_unicode(kwds) 

1527 if preamble is not None: 

1528 embedding = (ffi._embedding is not None) 

1529 if embedding: 

1530 ffi._apply_embedding_fix(kwds) 

1531 if c_file is None: 

1532 c_file, parts = _modname_to_file(tmpdir, module_name, 

1533 source_extension) 

1534 if extradir: 

1535 parts = [extradir] + parts 

1536 ext_c_file = os.path.join(*parts) 

1537 else: 

1538 ext_c_file = c_file 

1539 # 

1540 if target is None: 

1541 if embedding: 

1542 target = '%s.*' % module_name 

1543 else: 

1544 target = '*' 

1545 # 

1546 ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) 

1547 updated = make_c_source(ffi, module_name, preamble, c_file, 

1548 verbose=compiler_verbose) 

1549 if call_c_compiler: 

1550 patchlist = [] 

1551 cwd = os.getcwd() 

1552 try: 

1553 if embedding: 

1554 _patch_for_embedding(patchlist) 

1555 if target != '*': 

1556 _patch_for_target(patchlist, target) 

1557 if compiler_verbose: 

1558 if tmpdir == '.': 

1559 msg = 'the current directory is' 

1560 else: 

1561 msg = 'setting the current directory to' 

1562 print('%s %r' % (msg, os.path.abspath(tmpdir))) 

1563 os.chdir(tmpdir) 

1564 outputfilename = ffiplatform.compile('.', ext, 

1565 compiler_verbose, debug) 

1566 finally: 

1567 os.chdir(cwd) 

1568 _unpatch_meths(patchlist) 

1569 return outputfilename 

1570 else: 

1571 return ext, updated 

1572 else: 

1573 if c_file is None: 

1574 c_file, _ = _modname_to_file(tmpdir, module_name, '.py') 

1575 updated = make_py_source(ffi, module_name, c_file, 

1576 verbose=compiler_verbose) 

1577 if call_c_compiler: 

1578 return c_file 

1579 else: 

1580 return None, updated 

1581