/lib_pypy/cffi/cparser.py

https://bitbucket.org/pypy/pypy/ · Python · 849 lines · 672 code · 38 blank · 139 comment · 227 complexity · 3dc801baeb1b09721e37b94ae1fa15c9 MD5 · raw file

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