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