You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

891 lines
38 KiB

4 years ago
  1. from . import model
  2. from .commontypes import COMMON_TYPES, resolve_common_type
  3. from .error import FFIError, CDefError
  4. try:
  5. from . import _pycparser as pycparser
  6. except ImportError:
  7. import pycparser
  8. import weakref, re, sys
  9. try:
  10. if sys.version_info < (3,):
  11. import thread as _thread
  12. else:
  13. import _thread
  14. lock = _thread.allocate_lock()
  15. except ImportError:
  16. lock = None
  17. CDEF_SOURCE_STRING = "<cdef source string>"
  18. _r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
  19. re.DOTALL | re.MULTILINE)
  20. _r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
  21. r"\b((?:[^\n\\]|\\.)*?)$",
  22. re.DOTALL | re.MULTILINE)
  23. _r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}")
  24. _r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
  25. _r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
  26. _r_words = re.compile(r"\w+|\S")
  27. _parser_cache = None
  28. _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
  29. _r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
  30. _r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
  31. _r_cdecl = re.compile(r"\b__cdecl\b")
  32. _r_extern_python = re.compile(r'\bextern\s*"'
  33. r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.')
  34. _r_star_const_space = re.compile( # matches "* const "
  35. r"[*]\s*((const|volatile|restrict)\b\s*)+")
  36. _r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+"
  37. r"\.\.\.")
  38. _r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.")
  39. def _get_parser():
  40. global _parser_cache
  41. if _parser_cache is None:
  42. _parser_cache = pycparser.CParser()
  43. return _parser_cache
  44. def _workaround_for_old_pycparser(csource):
  45. # Workaround for a pycparser issue (fixed between pycparser 2.10 and
  46. # 2.14): "char*const***" gives us a wrong syntax tree, the same as
  47. # for "char***(*const)". This means we can't tell the difference
  48. # afterwards. But "char(*const(***))" gives us the right syntax
  49. # tree. The issue only occurs if there are several stars in
  50. # sequence with no parenthesis inbetween, just possibly qualifiers.
  51. # Attempt to fix it by adding some parentheses in the source: each
  52. # time we see "* const" or "* const *", we add an opening
  53. # parenthesis before each star---the hard part is figuring out where
  54. # to close them.
  55. parts = []
  56. while True:
  57. match = _r_star_const_space.search(csource)
  58. if not match:
  59. break
  60. #print repr(''.join(parts)+csource), '=>',
  61. parts.append(csource[:match.start()])
  62. parts.append('('); closing = ')'
  63. parts.append(match.group()) # e.g. "* const "
  64. endpos = match.end()
  65. if csource.startswith('*', endpos):
  66. parts.append('('); closing += ')'
  67. level = 0
  68. i = endpos
  69. while i < len(csource):
  70. c = csource[i]
  71. if c == '(':
  72. level += 1
  73. elif c == ')':
  74. if level == 0:
  75. break
  76. level -= 1
  77. elif c in ',;=':
  78. if level == 0:
  79. break
  80. i += 1
  81. csource = csource[endpos:i] + closing + csource[i:]
  82. #print repr(''.join(parts)+csource)
  83. parts.append(csource)
  84. return ''.join(parts)
  85. def _preprocess_extern_python(csource):
  86. # input: `extern "Python" int foo(int);` or
  87. # `extern "Python" { int foo(int); }`
  88. # output:
  89. # void __cffi_extern_python_start;
  90. # int foo(int);
  91. # void __cffi_extern_python_stop;
  92. #
  93. # input: `extern "Python+C" int foo(int);`
  94. # output:
  95. # void __cffi_extern_python_plus_c_start;
  96. # int foo(int);
  97. # void __cffi_extern_python_stop;
  98. parts = []
  99. while True:
  100. match = _r_extern_python.search(csource)
  101. if not match:
  102. break
  103. endpos = match.end() - 1
  104. #print
  105. #print ''.join(parts)+csource
  106. #print '=>'
  107. parts.append(csource[:match.start()])
  108. if 'C' in match.group(1):
  109. parts.append('void __cffi_extern_python_plus_c_start; ')
  110. else:
  111. parts.append('void __cffi_extern_python_start; ')
  112. if csource[endpos] == '{':
  113. # grouping variant
  114. closing = csource.find('}', endpos)
  115. if closing < 0:
  116. raise CDefError("'extern \"Python\" {': no '}' found")
  117. if csource.find('{', endpos + 1, closing) >= 0:
  118. raise NotImplementedError("cannot use { } inside a block "
  119. "'extern \"Python\" { ... }'")
  120. parts.append(csource[endpos+1:closing])
  121. csource = csource[closing+1:]
  122. else:
  123. # non-grouping variant
  124. semicolon = csource.find(';', endpos)
  125. if semicolon < 0:
  126. raise CDefError("'extern \"Python\": no ';' found")
  127. parts.append(csource[endpos:semicolon+1])
  128. csource = csource[semicolon+1:]
  129. parts.append(' void __cffi_extern_python_stop;')
  130. #print ''.join(parts)+csource
  131. #print
  132. parts.append(csource)
  133. return ''.join(parts)
  134. def _preprocess(csource):
  135. # Remove comments. NOTE: this only work because the cdef() section
  136. # should not contain any string literal!
  137. csource = _r_comment.sub(' ', csource)
  138. # Remove the "#define FOO x" lines
  139. macros = {}
  140. for match in _r_define.finditer(csource):
  141. macroname, macrovalue = match.groups()
  142. macrovalue = macrovalue.replace('\\\n', '').strip()
  143. macros[macroname] = macrovalue
  144. csource = _r_define.sub('', csource)
  145. #
  146. if pycparser.__version__ < '2.14':
  147. csource = _workaround_for_old_pycparser(csource)
  148. #
  149. # BIG HACK: replace WINAPI or __stdcall with "volatile const".
  150. # It doesn't make sense for the return type of a function to be
  151. # "volatile volatile const", so we abuse it to detect __stdcall...
  152. # Hack number 2 is that "int(volatile *fptr)();" is not valid C
  153. # syntax, so we place the "volatile" before the opening parenthesis.
  154. csource = _r_stdcall2.sub(' volatile volatile const(', csource)
  155. csource = _r_stdcall1.sub(' volatile volatile const ', csource)
  156. csource = _r_cdecl.sub(' ', csource)
  157. #
  158. # Replace `extern "Python"` with start/end markers
  159. csource = _preprocess_extern_python(csource)
  160. #
  161. # Replace "[...]" with "[__dotdotdotarray__]"
  162. csource = _r_partial_array.sub('[__dotdotdotarray__]', csource)
  163. #
  164. # Replace "...}" with "__dotdotdotNUM__}". This construction should
  165. # occur only at the end of enums; at the end of structs we have "...;}"
  166. # and at the end of vararg functions "...);". Also replace "=...[,}]"
  167. # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when
  168. # giving an unknown value.
  169. matches = list(_r_partial_enum.finditer(csource))
  170. for number, match in enumerate(reversed(matches)):
  171. p = match.start()
  172. if csource[p] == '=':
  173. p2 = csource.find('...', p, match.end())
  174. assert p2 > p
  175. csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number,
  176. csource[p2+3:])
  177. else:
  178. assert csource[p:p+3] == '...'
  179. csource = '%s __dotdotdot%d__ %s' % (csource[:p], number,
  180. csource[p+3:])
  181. # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__"
  182. csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource)
  183. # Replace "float ..." or "double..." with "__dotdotdotfloat__"
  184. csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource)
  185. # Replace all remaining "..." with the same name, "__dotdotdot__",
  186. # which is declared with a typedef for the purpose of C parsing.
  187. return csource.replace('...', ' __dotdotdot__ '), macros
  188. def _common_type_names(csource):
  189. # Look in the source for what looks like usages of types from the
  190. # list of common types. A "usage" is approximated here as the
  191. # appearance of the word, minus a "definition" of the type, which
  192. # is the last word in a "typedef" statement. Approximative only
  193. # but should be fine for all the common types.
  194. look_for_words = set(COMMON_TYPES)
  195. look_for_words.add(';')
  196. look_for_words.add(',')
  197. look_for_words.add('(')
  198. look_for_words.add(')')
  199. look_for_words.add('typedef')
  200. words_used = set()
  201. is_typedef = False
  202. paren = 0
  203. previous_word = ''
  204. for word in _r_words.findall(csource):
  205. if word in look_for_words:
  206. if word == ';':
  207. if is_typedef:
  208. words_used.discard(previous_word)
  209. look_for_words.discard(previous_word)
  210. is_typedef = False
  211. elif word == 'typedef':
  212. is_typedef = True
  213. paren = 0
  214. elif word == '(':
  215. paren += 1
  216. elif word == ')':
  217. paren -= 1
  218. elif word == ',':
  219. if is_typedef and paren == 0:
  220. words_used.discard(previous_word)
  221. look_for_words.discard(previous_word)
  222. else: # word in COMMON_TYPES
  223. words_used.add(word)
  224. previous_word = word
  225. return words_used
  226. class Parser(object):
  227. def __init__(self):
  228. self._declarations = {}
  229. self._included_declarations = set()
  230. self._anonymous_counter = 0
  231. self._structnode2type = weakref.WeakKeyDictionary()
  232. self._options = {}
  233. self._int_constants = {}
  234. self._recomplete = []
  235. self._uses_new_feature = None
  236. def _parse(self, csource):
  237. csource, macros = _preprocess(csource)
  238. # XXX: for more efficiency we would need to poke into the
  239. # internals of CParser... the following registers the
  240. # typedefs, because their presence or absence influences the
  241. # parsing itself (but what they are typedef'ed to plays no role)
  242. ctn = _common_type_names(csource)
  243. typenames = []
  244. for name in sorted(self._declarations):
  245. if name.startswith('typedef '):
  246. name = name[8:]
  247. typenames.append(name)
  248. ctn.discard(name)
  249. typenames += sorted(ctn)
  250. #
  251. csourcelines = []
  252. csourcelines.append('# 1 "<cdef automatic initialization code>"')
  253. for typename in typenames:
  254. csourcelines.append('typedef int %s;' % typename)
  255. csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
  256. ' __dotdotdot__;')
  257. # this forces pycparser to consider the following in the file
  258. # called <cdef source string> from line 1
  259. csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
  260. csourcelines.append(csource)
  261. fullcsource = '\n'.join(csourcelines)
  262. if lock is not None:
  263. lock.acquire() # pycparser is not thread-safe...
  264. try:
  265. ast = _get_parser().parse(fullcsource)
  266. except pycparser.c_parser.ParseError as e:
  267. self.convert_pycparser_error(e, csource)
  268. finally:
  269. if lock is not None:
  270. lock.release()
  271. # csource will be used to find buggy source text
  272. return ast, macros, csource
  273. def _convert_pycparser_error(self, e, csource):
  274. # xxx look for "<cdef source string>:NUM:" at the start of str(e)
  275. # and interpret that as a line number. This will not work if
  276. # the user gives explicit ``# NUM "FILE"`` directives.
  277. line = None
  278. msg = str(e)
  279. match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
  280. if match:
  281. linenum = int(match.group(1), 10)
  282. csourcelines = csource.splitlines()
  283. if 1 <= linenum <= len(csourcelines):
  284. line = csourcelines[linenum-1]
  285. return line
  286. def convert_pycparser_error(self, e, csource):
  287. line = self._convert_pycparser_error(e, csource)
  288. msg = str(e)
  289. if line:
  290. msg = 'cannot parse "%s"\n%s' % (line.strip(), msg)
  291. else:
  292. msg = 'parse error\n%s' % (msg,)
  293. raise CDefError(msg)
  294. def parse(self, csource, override=False, packed=False, dllexport=False):
  295. prev_options = self._options
  296. try:
  297. self._options = {'override': override,
  298. 'packed': packed,
  299. 'dllexport': dllexport}
  300. self._internal_parse(csource)
  301. finally:
  302. self._options = prev_options
  303. def _internal_parse(self, csource):
  304. ast, macros, csource = self._parse(csource)
  305. # add the macros
  306. self._process_macros(macros)
  307. # find the first "__dotdotdot__" and use that as a separator
  308. # between the repeated typedefs and the real csource
  309. iterator = iter(ast.ext)
  310. for decl in iterator:
  311. if decl.name == '__dotdotdot__':
  312. break
  313. else:
  314. assert 0
  315. current_decl = None
  316. #
  317. try:
  318. self._inside_extern_python = '__cffi_extern_python_stop'
  319. for decl in iterator:
  320. current_decl = decl
  321. if isinstance(decl, pycparser.c_ast.Decl):
  322. self._parse_decl(decl)
  323. elif isinstance(decl, pycparser.c_ast.Typedef):
  324. if not decl.name:
  325. raise CDefError("typedef does not declare any name",
  326. decl)
  327. quals = 0
  328. if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and
  329. decl.type.type.names[-1].startswith('__dotdotdot')):
  330. realtype = self._get_unknown_type(decl)
  331. elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
  332. isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
  333. isinstance(decl.type.type.type,
  334. pycparser.c_ast.IdentifierType) and
  335. decl.type.type.type.names[-1].startswith('__dotdotdot')):
  336. realtype = self._get_unknown_ptr_type(decl)
  337. else:
  338. realtype, quals = self._get_type_and_quals(
  339. decl.type, name=decl.name, partial_length_ok=True)
  340. self._declare('typedef ' + decl.name, realtype, quals=quals)
  341. elif decl.__class__.__name__ == 'Pragma':
  342. pass # skip pragma, only in pycparser 2.15
  343. else:
  344. raise CDefError("unexpected <%s>: this construct is valid "
  345. "C but not valid in cdef()" %
  346. decl.__class__.__name__, decl)
  347. except CDefError as e:
  348. if len(e.args) == 1:
  349. e.args = e.args + (current_decl,)
  350. raise
  351. except FFIError as e:
  352. msg = self._convert_pycparser_error(e, csource)
  353. if msg:
  354. e.args = (e.args[0] + "\n *** Err: %s" % msg,)
  355. raise
  356. def _add_constants(self, key, val):
  357. if key in self._int_constants:
  358. if self._int_constants[key] == val:
  359. return # ignore identical double declarations
  360. raise FFIError(
  361. "multiple declarations of constant: %s" % (key,))
  362. self._int_constants[key] = val
  363. def _add_integer_constant(self, name, int_str):
  364. int_str = int_str.lower().rstrip("ul")
  365. neg = int_str.startswith('-')
  366. if neg:
  367. int_str = int_str[1:]
  368. # "010" is not valid oct in py3
  369. if (int_str.startswith("0") and int_str != '0'
  370. and not int_str.startswith("0x")):
  371. int_str = "0o" + int_str[1:]
  372. pyvalue = int(int_str, 0)
  373. if neg:
  374. pyvalue = -pyvalue
  375. self._add_constants(name, pyvalue)
  376. self._declare('macro ' + name, pyvalue)
  377. def _process_macros(self, macros):
  378. for key, value in macros.items():
  379. value = value.strip()
  380. if _r_int_literal.match(value):
  381. self._add_integer_constant(key, value)
  382. elif value == '...':
  383. self._declare('macro ' + key, value)
  384. else:
  385. raise CDefError(
  386. 'only supports one of the following syntax:\n'
  387. ' #define %s ... (literally dot-dot-dot)\n'
  388. ' #define %s NUMBER (with NUMBER an integer'
  389. ' constant, decimal/hex/octal)\n'
  390. 'got:\n'
  391. ' #define %s %s'
  392. % (key, key, key, value))
  393. def _declare_function(self, tp, quals, decl):
  394. tp = self._get_type_pointer(tp, quals)
  395. if self._options.get('dllexport'):
  396. tag = 'dllexport_python '
  397. elif self._inside_extern_python == '__cffi_extern_python_start':
  398. tag = 'extern_python '
  399. elif self._inside_extern_python == '__cffi_extern_python_plus_c_start':
  400. tag = 'extern_python_plus_c '
  401. else:
  402. tag = 'function '
  403. self._declare(tag + decl.name, tp)
  404. def _parse_decl(self, decl):
  405. node = decl.type
  406. if isinstance(node, pycparser.c_ast.FuncDecl):
  407. tp, quals = self._get_type_and_quals(node, name=decl.name)
  408. assert isinstance(tp, model.RawFunctionType)
  409. self._declare_function(tp, quals, decl)
  410. else:
  411. if isinstance(node, pycparser.c_ast.Struct):
  412. self._get_struct_union_enum_type('struct', node)
  413. elif isinstance(node, pycparser.c_ast.Union):
  414. self._get_struct_union_enum_type('union', node)
  415. elif isinstance(node, pycparser.c_ast.Enum):
  416. self._get_struct_union_enum_type('enum', node)
  417. elif not decl.name:
  418. raise CDefError("construct does not declare any variable",
  419. decl)
  420. #
  421. if decl.name:
  422. tp, quals = self._get_type_and_quals(node,
  423. partial_length_ok=True)
  424. if tp.is_raw_function:
  425. self._declare_function(tp, quals, decl)
  426. elif (tp.is_integer_type() and
  427. hasattr(decl, 'init') and
  428. hasattr(decl.init, 'value') and
  429. _r_int_literal.match(decl.init.value)):
  430. self._add_integer_constant(decl.name, decl.init.value)
  431. elif (tp.is_integer_type() and
  432. isinstance(decl.init, pycparser.c_ast.UnaryOp) and
  433. decl.init.op == '-' and
  434. hasattr(decl.init.expr, 'value') and
  435. _r_int_literal.match(decl.init.expr.value)):
  436. self._add_integer_constant(decl.name,
  437. '-' + decl.init.expr.value)
  438. elif (tp is model.void_type and
  439. decl.name.startswith('__cffi_extern_python_')):
  440. # hack: `extern "Python"` in the C source is replaced
  441. # with "void __cffi_extern_python_start;" and
  442. # "void __cffi_extern_python_stop;"
  443. self._inside_extern_python = decl.name
  444. else:
  445. if self._inside_extern_python !='__cffi_extern_python_stop':
  446. raise CDefError(
  447. "cannot declare constants or "
  448. "variables with 'extern \"Python\"'")
  449. if (quals & model.Q_CONST) and not tp.is_array_type:
  450. self._declare('constant ' + decl.name, tp, quals=quals)
  451. else:
  452. self._declare('variable ' + decl.name, tp, quals=quals)
  453. def parse_type(self, cdecl):
  454. return self.parse_type_and_quals(cdecl)[0]
  455. def parse_type_and_quals(self, cdecl):
  456. ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2]
  457. assert not macros
  458. exprnode = ast.ext[-1].type.args.params[0]
  459. if isinstance(exprnode, pycparser.c_ast.ID):
  460. raise CDefError("unknown identifier '%s'" % (exprnode.name,))
  461. return self._get_type_and_quals(exprnode.type)
  462. def _declare(self, name, obj, included=False, quals=0):
  463. if name in self._declarations:
  464. prevobj, prevquals = self._declarations[name]
  465. if prevobj is obj and prevquals == quals:
  466. return
  467. if not self._options.get('override'):
  468. raise FFIError(
  469. "multiple declarations of %s (for interactive usage, "
  470. "try cdef(xx, override=True))" % (name,))
  471. assert '__dotdotdot__' not in name.split()
  472. self._declarations[name] = (obj, quals)
  473. if included:
  474. self._included_declarations.add(obj)
  475. def _extract_quals(self, type):
  476. quals = 0
  477. if isinstance(type, (pycparser.c_ast.TypeDecl,
  478. pycparser.c_ast.PtrDecl)):
  479. if 'const' in type.quals:
  480. quals |= model.Q_CONST
  481. if 'volatile' in type.quals:
  482. quals |= model.Q_VOLATILE
  483. if 'restrict' in type.quals:
  484. quals |= model.Q_RESTRICT
  485. return quals
  486. def _get_type_pointer(self, type, quals, declname=None):
  487. if isinstance(type, model.RawFunctionType):
  488. return type.as_function_pointer()
  489. if (isinstance(type, model.StructOrUnionOrEnum) and
  490. type.name.startswith('$') and type.name[1:].isdigit() and
  491. type.forcename is None and declname is not None):
  492. return model.NamedPointerType(type, declname, quals)
  493. return model.PointerType(type, quals)
  494. def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False):
  495. # first, dereference typedefs, if we have it already parsed, we're good
  496. if (isinstance(typenode, pycparser.c_ast.TypeDecl) and
  497. isinstance(typenode.type, pycparser.c_ast.IdentifierType) and
  498. len(typenode.type.names) == 1 and
  499. ('typedef ' + typenode.type.names[0]) in self._declarations):
  500. tp, quals = self._declarations['typedef ' + typenode.type.names[0]]
  501. quals |= self._extract_quals(typenode)
  502. return tp, quals
  503. #
  504. if isinstance(typenode, pycparser.c_ast.ArrayDecl):
  505. # array type
  506. if typenode.dim is None:
  507. length = None
  508. else:
  509. length = self._parse_constant(
  510. typenode.dim, partial_length_ok=partial_length_ok)
  511. tp, quals = self._get_type_and_quals(typenode.type,
  512. partial_length_ok=partial_length_ok)
  513. return model.ArrayType(tp, length), quals
  514. #
  515. if isinstance(typenode, pycparser.c_ast.PtrDecl):
  516. # pointer type
  517. itemtype, itemquals = self._get_type_and_quals(typenode.type)
  518. tp = self._get_type_pointer(itemtype, itemquals, declname=name)
  519. quals = self._extract_quals(typenode)
  520. return tp, quals
  521. #
  522. if isinstance(typenode, pycparser.c_ast.TypeDecl):
  523. quals = self._extract_quals(typenode)
  524. type = typenode.type
  525. if isinstance(type, pycparser.c_ast.IdentifierType):
  526. # assume a primitive type. get it from .names, but reduce
  527. # synonyms to a single chosen combination
  528. names = list(type.names)
  529. if names != ['signed', 'char']: # keep this unmodified
  530. prefixes = {}
  531. while names:
  532. name = names[0]
  533. if name in ('short', 'long', 'signed', 'unsigned'):
  534. prefixes[name] = prefixes.get(name, 0) + 1
  535. del names[0]
  536. else:
  537. break
  538. # ignore the 'signed' prefix below, and reorder the others
  539. newnames = []
  540. for prefix in ('unsigned', 'short', 'long'):
  541. for i in range(prefixes.get(prefix, 0)):
  542. newnames.append(prefix)
  543. if not names:
  544. names = ['int'] # implicitly
  545. if names == ['int']: # but kill it if 'short' or 'long'
  546. if 'short' in prefixes or 'long' in prefixes:
  547. names = []
  548. names = newnames + names
  549. ident = ' '.join(names)
  550. if ident == 'void':
  551. return model.void_type, quals
  552. if ident == '__dotdotdot__':
  553. raise FFIError(':%d: bad usage of "..."' %
  554. typenode.coord.line)
  555. tp0, quals0 = resolve_common_type(self, ident)
  556. return tp0, (quals | quals0)
  557. #
  558. if isinstance(type, pycparser.c_ast.Struct):
  559. # 'struct foobar'
  560. tp = self._get_struct_union_enum_type('struct', type, name)
  561. return tp, quals
  562. #
  563. if isinstance(type, pycparser.c_ast.Union):
  564. # 'union foobar'
  565. tp = self._get_struct_union_enum_type('union', type, name)
  566. return tp, quals
  567. #
  568. if isinstance(type, pycparser.c_ast.Enum):
  569. # 'enum foobar'
  570. tp = self._get_struct_union_enum_type('enum', type, name)
  571. return tp, quals
  572. #
  573. if isinstance(typenode, pycparser.c_ast.FuncDecl):
  574. # a function type
  575. return self._parse_function_type(typenode, name), 0
  576. #
  577. # nested anonymous structs or unions end up here
  578. if isinstance(typenode, pycparser.c_ast.Struct):
  579. return self._get_struct_union_enum_type('struct', typenode, name,
  580. nested=True), 0
  581. if isinstance(typenode, pycparser.c_ast.Union):
  582. return self._get_struct_union_enum_type('union', typenode, name,
  583. nested=True), 0
  584. #
  585. raise FFIError(":%d: bad or unsupported type declaration" %
  586. typenode.coord.line)
  587. def _parse_function_type(self, typenode, funcname=None):
  588. params = list(getattr(typenode.args, 'params', []))
  589. for i, arg in enumerate(params):
  590. if not hasattr(arg, 'type'):
  591. raise CDefError("%s arg %d: unknown type '%s'"
  592. " (if you meant to use the old C syntax of giving"
  593. " untyped arguments, it is not supported)"
  594. % (funcname or 'in expression', i + 1,
  595. getattr(arg, 'name', '?')))
  596. ellipsis = (
  597. len(params) > 0 and
  598. isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and
  599. isinstance(params[-1].type.type,
  600. pycparser.c_ast.IdentifierType) and
  601. params[-1].type.type.names == ['__dotdotdot__'])
  602. if ellipsis:
  603. params.pop()
  604. if not params:
  605. raise CDefError(
  606. "%s: a function with only '(...)' as argument"
  607. " is not correct C" % (funcname or 'in expression'))
  608. args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type))
  609. for argdeclnode in params]
  610. if not ellipsis and args == [model.void_type]:
  611. args = []
  612. result, quals = self._get_type_and_quals(typenode.type)
  613. # the 'quals' on the result type are ignored. HACK: we absure them
  614. # to detect __stdcall functions: we textually replace "__stdcall"
  615. # with "volatile volatile const" above.
  616. abi = None
  617. if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway
  618. if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']:
  619. abi = '__stdcall'
  620. return model.RawFunctionType(tuple(args), result, ellipsis, abi)
  621. def _as_func_arg(self, type, quals):
  622. if isinstance(type, model.ArrayType):
  623. return model.PointerType(type.item, quals)
  624. elif isinstance(type, model.RawFunctionType):
  625. return type.as_function_pointer()
  626. else:
  627. return type
  628. def _get_struct_union_enum_type(self, kind, type, name=None, nested=False):
  629. # First, a level of caching on the exact 'type' node of the AST.
  630. # This is obscure, but needed because pycparser "unrolls" declarations
  631. # such as "typedef struct { } foo_t, *foo_p" and we end up with
  632. # an AST that is not a tree, but a DAG, with the "type" node of the
  633. # two branches foo_t and foo_p of the trees being the same node.
  634. # It's a bit silly but detecting "DAG-ness" in the AST tree seems
  635. # to be the only way to distinguish this case from two independent
  636. # structs. See test_struct_with_two_usages.
  637. try:
  638. return self._structnode2type[type]
  639. except KeyError:
  640. pass
  641. #
  642. # Note that this must handle parsing "struct foo" any number of
  643. # times and always return the same StructType object. Additionally,
  644. # one of these times (not necessarily the first), the fields of
  645. # the struct can be specified with "struct foo { ...fields... }".
  646. # If no name is given, then we have to create a new anonymous struct
  647. # with no caching; in this case, the fields are either specified
  648. # right now or never.
  649. #
  650. force_name = name
  651. name = type.name
  652. #
  653. # get the type or create it if needed
  654. if name is None:
  655. # 'force_name' is used to guess a more readable name for
  656. # anonymous structs, for the common case "typedef struct { } foo".
  657. if force_name is not None:
  658. explicit_name = '$%s' % force_name
  659. else:
  660. self._anonymous_counter += 1
  661. explicit_name = '$%d' % self._anonymous_counter
  662. tp = None
  663. else:
  664. explicit_name = name
  665. key = '%s %s' % (kind, name)
  666. tp, _ = self._declarations.get(key, (None, None))
  667. #
  668. if tp is None:
  669. if kind == 'struct':
  670. tp = model.StructType(explicit_name, None, None, None)
  671. elif kind == 'union':
  672. tp = model.UnionType(explicit_name, None, None, None)
  673. elif kind == 'enum':
  674. if explicit_name == '__dotdotdot__':
  675. raise CDefError("Enums cannot be declared with ...")
  676. tp = self._build_enum_type(explicit_name, type.values)
  677. else:
  678. raise AssertionError("kind = %r" % (kind,))
  679. if name is not None:
  680. self._declare(key, tp)
  681. else:
  682. if kind == 'enum' and type.values is not None:
  683. raise NotImplementedError(
  684. "enum %s: the '{}' declaration should appear on the first "
  685. "time the enum is mentioned, not later" % explicit_name)
  686. if not tp.forcename:
  687. tp.force_the_name(force_name)
  688. if tp.forcename and '$' in tp.name:
  689. self._declare('anonymous %s' % tp.forcename, tp)
  690. #
  691. self._structnode2type[type] = tp
  692. #
  693. # enums: done here
  694. if kind == 'enum':
  695. return tp
  696. #
  697. # is there a 'type.decls'? If yes, then this is the place in the
  698. # C sources that declare the fields. If no, then just return the
  699. # existing type, possibly still incomplete.
  700. if type.decls is None:
  701. return tp
  702. #
  703. if tp.fldnames is not None:
  704. raise CDefError("duplicate declaration of struct %s" % name)
  705. fldnames = []
  706. fldtypes = []
  707. fldbitsize = []
  708. fldquals = []
  709. for decl in type.decls:
  710. if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and
  711. ''.join(decl.type.names) == '__dotdotdot__'):
  712. # XXX pycparser is inconsistent: 'names' should be a list
  713. # of strings, but is sometimes just one string. Use
  714. # str.join() as a way to cope with both.
  715. self._make_partial(tp, nested)
  716. continue
  717. if decl.bitsize is None:
  718. bitsize = -1
  719. else:
  720. bitsize = self._parse_constant(decl.bitsize)
  721. self._partial_length = False
  722. type, fqual = self._get_type_and_quals(decl.type,
  723. partial_length_ok=True)
  724. if self._partial_length:
  725. self._make_partial(tp, nested)
  726. if isinstance(type, model.StructType) and type.partial:
  727. self._make_partial(tp, nested)
  728. fldnames.append(decl.name or '')
  729. fldtypes.append(type)
  730. fldbitsize.append(bitsize)
  731. fldquals.append(fqual)
  732. tp.fldnames = tuple(fldnames)
  733. tp.fldtypes = tuple(fldtypes)
  734. tp.fldbitsize = tuple(fldbitsize)
  735. tp.fldquals = tuple(fldquals)
  736. if fldbitsize != [-1] * len(fldbitsize):
  737. if isinstance(tp, model.StructType) and tp.partial:
  738. raise NotImplementedError("%s: using both bitfields and '...;'"
  739. % (tp,))
  740. tp.packed = self._options.get('packed')
  741. if tp.completed: # must be re-completed: it is not opaque any more
  742. tp.completed = 0
  743. self._recomplete.append(tp)
  744. return tp
  745. def _make_partial(self, tp, nested):
  746. if not isinstance(tp, model.StructOrUnion):
  747. raise CDefError("%s cannot be partial" % (tp,))
  748. if not tp.has_c_name() and not nested:
  749. raise NotImplementedError("%s is partial but has no C name" %(tp,))
  750. tp.partial = True
  751. def _parse_constant(self, exprnode, partial_length_ok=False):
  752. # for now, limited to expressions that are an immediate number
  753. # or positive/negative number
  754. if isinstance(exprnode, pycparser.c_ast.Constant):
  755. s = exprnode.value
  756. if s.startswith('0'):
  757. if s.startswith('0x') or s.startswith('0X'):
  758. return int(s, 16)
  759. return int(s, 8)
  760. elif '1' <= s[0] <= '9':
  761. return int(s, 10)
  762. elif s[0] == "'" and s[-1] == "'" and (
  763. len(s) == 3 or (len(s) == 4 and s[1] == "\\")):
  764. return ord(s[-2])
  765. else:
  766. raise CDefError("invalid constant %r" % (s,))
  767. #
  768. if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
  769. exprnode.op == '+'):
  770. return self._parse_constant(exprnode.expr)
  771. #
  772. if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
  773. exprnode.op == '-'):
  774. return -self._parse_constant(exprnode.expr)
  775. # load previously defined int constant
  776. if (isinstance(exprnode, pycparser.c_ast.ID) and
  777. exprnode.name in self._int_constants):
  778. return self._int_constants[exprnode.name]
  779. #
  780. if (isinstance(exprnode, pycparser.c_ast.ID) and
  781. exprnode.name == '__dotdotdotarray__'):
  782. if partial_length_ok:
  783. self._partial_length = True
  784. return '...'
  785. raise FFIError(":%d: unsupported '[...]' here, cannot derive "
  786. "the actual array length in this context"
  787. % exprnode.coord.line)
  788. #
  789. if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
  790. exprnode.op == '+'):
  791. return (self._parse_constant(exprnode.left) +
  792. self._parse_constant(exprnode.right))
  793. #
  794. if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
  795. exprnode.op == '-'):
  796. return (self._parse_constant(exprnode.left) -
  797. self._parse_constant(exprnode.right))
  798. #
  799. raise FFIError(":%d: unsupported expression: expected a "
  800. "simple numeric constant" % exprnode.coord.line)
  801. def _build_enum_type(self, explicit_name, decls):
  802. if decls is not None:
  803. partial = False
  804. enumerators = []
  805. enumvalues = []
  806. nextenumvalue = 0
  807. for enum in decls.enumerators:
  808. if _r_enum_dotdotdot.match(enum.name):
  809. partial = True
  810. continue
  811. if enum.value is not None:
  812. nextenumvalue = self._parse_constant(enum.value)
  813. enumerators.append(enum.name)
  814. enumvalues.append(nextenumvalue)
  815. self._add_constants(enum.name, nextenumvalue)
  816. nextenumvalue += 1
  817. enumerators = tuple(enumerators)
  818. enumvalues = tuple(enumvalues)
  819. tp = model.EnumType(explicit_name, enumerators, enumvalues)
  820. tp.partial = partial
  821. else: # opaque enum
  822. tp = model.EnumType(explicit_name, (), ())
  823. return tp
  824. def include(self, other):
  825. for name, (tp, quals) in other._declarations.items():
  826. if name.startswith('anonymous $enum_$'):
  827. continue # fix for test_anonymous_enum_include
  828. kind = name.split(' ', 1)[0]
  829. if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'):
  830. self._declare(name, tp, included=True, quals=quals)
  831. for k, v in other._int_constants.items():
  832. self._add_constants(k, v)
  833. def _get_unknown_type(self, decl):
  834. typenames = decl.type.type.names
  835. if typenames == ['__dotdotdot__']:
  836. return model.unknown_type(decl.name)
  837. if typenames == ['__dotdotdotint__']:
  838. if self._uses_new_feature is None:
  839. self._uses_new_feature = "'typedef int... %s'" % decl.name
  840. return model.UnknownIntegerType(decl.name)
  841. if typenames == ['__dotdotdotfloat__']:
  842. # note: not for 'long double' so far
  843. if self._uses_new_feature is None:
  844. self._uses_new_feature = "'typedef float... %s'" % decl.name
  845. return model.UnknownFloatType(decl.name)
  846. raise FFIError(':%d: unsupported usage of "..." in typedef'
  847. % decl.coord.line)
  848. def _get_unknown_ptr_type(self, decl):
  849. if decl.type.type.type.names == ['__dotdotdot__']:
  850. return model.unknown_ptr_type(decl.name)
  851. raise FFIError(':%d: unsupported usage of "..." in typedef'
  852. % decl.coord.line)