OLD | NEW |
(Empty) | |
| 1 # |
| 2 # Parse tree nodes |
| 3 # |
| 4 |
| 5 import cython |
| 6 cython.declare(sys=object, os=object, copy=object, |
| 7 Builtin=object, error=object, warning=object, Naming=object, Pyre
xTypes=object, |
| 8 py_object_type=object, ModuleScope=object, LocalScope=object, Clo
sureScope=object, |
| 9 StructOrUnionScope=object, PyClassScope=object, |
| 10 CppClassScope=object, UtilityCode=object, EncodedString=object, |
| 11 absolute_path_length=cython.Py_ssize_t) |
| 12 |
| 13 import sys, os, copy |
| 14 from itertools import chain |
| 15 |
| 16 import Builtin |
| 17 from Errors import error, warning, InternalError, CompileError |
| 18 import Naming |
| 19 import PyrexTypes |
| 20 import TypeSlots |
| 21 from PyrexTypes import py_object_type, error_type |
| 22 from Symtab import (ModuleScope, LocalScope, ClosureScope, |
| 23 StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope) |
| 24 from Code import UtilityCode |
| 25 from StringEncoding import EncodedString, escape_byte_string, split_string_liter
al |
| 26 import Options |
| 27 import DebugFlags |
| 28 from Cython.Utils import cached_function |
| 29 |
| 30 absolute_path_length = 0 |
| 31 |
| 32 def relative_position(pos): |
| 33 """ |
| 34 We embed the relative filename in the generated C file, since we |
| 35 don't want to have to regenerate and compile all the source code |
| 36 whenever the Python install directory moves (which could happen, |
| 37 e.g,. when distributing binaries.) |
| 38 |
| 39 INPUT: |
| 40 a position tuple -- (absolute filename, line number column position) |
| 41 |
| 42 OUTPUT: |
| 43 relative filename |
| 44 line number |
| 45 |
| 46 AUTHOR: William Stein |
| 47 """ |
| 48 global absolute_path_length |
| 49 if absolute_path_length==0: |
| 50 absolute_path_length = len(os.path.abspath(os.getcwd())) |
| 51 return (pos[0].get_filenametable_entry()[absolute_path_length+1:], pos[1]) |
| 52 |
| 53 def embed_position(pos, docstring): |
| 54 if not Options.embed_pos_in_docstring: |
| 55 return docstring |
| 56 pos_line = u'File: %s (starting at line %s)' % relative_position(pos) |
| 57 if docstring is None: |
| 58 # unicode string |
| 59 return EncodedString(pos_line) |
| 60 |
| 61 # make sure we can encode the filename in the docstring encoding |
| 62 # otherwise make the docstring a unicode string |
| 63 encoding = docstring.encoding |
| 64 if encoding is not None: |
| 65 try: |
| 66 pos_line.encode(encoding) |
| 67 except UnicodeEncodeError: |
| 68 encoding = None |
| 69 |
| 70 if not docstring: |
| 71 # reuse the string encoding of the original docstring |
| 72 doc = EncodedString(pos_line) |
| 73 else: |
| 74 doc = EncodedString(pos_line + u'\n' + docstring) |
| 75 doc.encoding = encoding |
| 76 return doc |
| 77 |
| 78 |
| 79 def write_func_call(func, codewriter_class): |
| 80 def f(*args, **kwds): |
| 81 if len(args) > 1 and isinstance(args[1], codewriter_class): |
| 82 # here we annotate the code with this function call |
| 83 # but only if new code is generated |
| 84 node, code = args[:2] |
| 85 marker = ' /* %s -> %s.%s %s */' % ( |
| 86 ' ' * code.call_level, |
| 87 node.__class__.__name__, |
| 88 func.__name__, |
| 89 node.pos[1:]) |
| 90 pristine = code.buffer.stream.tell() |
| 91 code.putln(marker) |
| 92 start = code.buffer.stream.tell() |
| 93 code.call_level += 4 |
| 94 res = func(*args, **kwds) |
| 95 code.call_level -= 4 |
| 96 if start == code.buffer.stream.tell(): |
| 97 code.buffer.stream.seek(pristine) |
| 98 else: |
| 99 marker = marker.replace('->', '<-') |
| 100 code.putln(marker) |
| 101 return res |
| 102 else: |
| 103 return func(*args, **kwds) |
| 104 return f |
| 105 |
| 106 class VerboseCodeWriter(type): |
| 107 # Set this as a metaclass to trace function calls in code. |
| 108 # This slows down code generation and makes much larger files. |
| 109 def __new__(cls, name, bases, attrs): |
| 110 from types import FunctionType |
| 111 from Code import CCodeWriter |
| 112 attrs = dict(attrs) |
| 113 for mname, m in attrs.items(): |
| 114 if isinstance(m, FunctionType): |
| 115 attrs[mname] = write_func_call(m, CCodeWriter) |
| 116 return super(VerboseCodeWriter, cls).__new__(cls, name, bases, attrs) |
| 117 |
| 118 |
| 119 class CheckAnalysers(type): |
| 120 """Metaclass to check that type analysis functions return a node. |
| 121 """ |
| 122 methods = set(['analyse_types', |
| 123 'analyse_expressions', |
| 124 'analyse_target_types']) |
| 125 |
| 126 def __new__(cls, name, bases, attrs): |
| 127 from types import FunctionType |
| 128 def check(name, func): |
| 129 def call(*args, **kwargs): |
| 130 retval = func(*args, **kwargs) |
| 131 if retval is None: |
| 132 print name, args, kwargs |
| 133 return retval |
| 134 return call |
| 135 |
| 136 attrs = dict(attrs) |
| 137 for mname, m in attrs.items(): |
| 138 if isinstance(m, FunctionType) and mname in cls.methods: |
| 139 attrs[mname] = check(mname, m) |
| 140 return super(CheckAnalysers, cls).__new__(cls, name, bases, attrs) |
| 141 |
| 142 |
| 143 class Node(object): |
| 144 # pos (string, int, int) Source file position |
| 145 # is_name boolean Is a NameNode |
| 146 # is_literal boolean Is a ConstNode |
| 147 |
| 148 #__metaclass__ = CheckAnalysers |
| 149 if DebugFlags.debug_trace_code_generation: |
| 150 __metaclass__ = VerboseCodeWriter |
| 151 |
| 152 is_name = 0 |
| 153 is_none = 0 |
| 154 is_nonecheck = 0 |
| 155 is_literal = 0 |
| 156 is_terminator = 0 |
| 157 temps = None |
| 158 |
| 159 # All descendants should set child_attrs to a list of the attributes |
| 160 # containing nodes considered "children" in the tree. Each such attribute |
| 161 # can either contain a single node or a list of nodes. See Visitor.py. |
| 162 child_attrs = None |
| 163 |
| 164 cf_state = None |
| 165 |
| 166 # This may be an additional (or 'actual') type that will be checked when |
| 167 # this node is coerced to another type. This could be useful to set when |
| 168 # the actual type to which it can coerce is known, but you want to leave |
| 169 # the type a py_object_type |
| 170 coercion_type = None |
| 171 |
| 172 def __init__(self, pos, **kw): |
| 173 self.pos = pos |
| 174 self.__dict__.update(kw) |
| 175 |
| 176 gil_message = "Operation" |
| 177 |
| 178 nogil_check = None |
| 179 |
| 180 def gil_error(self, env=None): |
| 181 error(self.pos, "%s not allowed without gil" % self.gil_message) |
| 182 |
| 183 cpp_message = "Operation" |
| 184 |
| 185 def cpp_check(self, env): |
| 186 if not env.is_cpp(): |
| 187 self.cpp_error() |
| 188 |
| 189 def cpp_error(self): |
| 190 error(self.pos, "%s only allowed in c++" % self.cpp_message) |
| 191 |
| 192 def clone_node(self): |
| 193 """Clone the node. This is defined as a shallow copy, except for member
lists |
| 194 amongst the child attributes (from get_child_accessors) which are als
o |
| 195 copied. Lists containing child nodes are thus seen as a way for the n
ode |
| 196 to hold multiple children directly; the list is not treated as a sepa
rate |
| 197 level in the tree.""" |
| 198 result = copy.copy(self) |
| 199 for attrname in result.child_attrs: |
| 200 value = getattr(result, attrname) |
| 201 if isinstance(value, list): |
| 202 setattr(result, attrname, [x for x in value]) |
| 203 return result |
| 204 |
| 205 |
| 206 # |
| 207 # There are 3 phases of parse tree processing, applied in order to |
| 208 # all the statements in a given scope-block: |
| 209 # |
| 210 # (0) analyse_declarations |
| 211 # Make symbol table entries for all declarations at the current |
| 212 # level, both explicit (def, cdef, etc.) and implicit (assignment |
| 213 # to an otherwise undeclared name). |
| 214 # |
| 215 # (1) analyse_expressions |
| 216 # Determine the result types of expressions and fill in the |
| 217 # 'type' attribute of each ExprNode. Insert coercion nodes into the |
| 218 # tree where needed to convert to and from Python objects. |
| 219 # Allocate temporary locals for intermediate results. Fill |
| 220 # in the 'result_code' attribute of each ExprNode with a C code |
| 221 # fragment. |
| 222 # |
| 223 # (2) generate_code |
| 224 # Emit C code for all declarations, statements and expressions. |
| 225 # Recursively applies the 3 processing phases to the bodies of |
| 226 # functions. |
| 227 # |
| 228 |
| 229 def analyse_declarations(self, env): |
| 230 pass |
| 231 |
| 232 def analyse_expressions(self, env): |
| 233 raise InternalError("analyse_expressions not implemented for %s" % \ |
| 234 self.__class__.__name__) |
| 235 |
| 236 def generate_code(self, code): |
| 237 raise InternalError("generate_code not implemented for %s" % \ |
| 238 self.__class__.__name__) |
| 239 |
| 240 def annotate(self, code): |
| 241 # mro does the wrong thing |
| 242 if isinstance(self, BlockNode): |
| 243 self.body.annotate(code) |
| 244 |
| 245 def end_pos(self): |
| 246 try: |
| 247 return self._end_pos |
| 248 except AttributeError: |
| 249 pos = self.pos |
| 250 if not self.child_attrs: |
| 251 self._end_pos = pos |
| 252 return pos |
| 253 for attr in self.child_attrs: |
| 254 child = getattr(self, attr) |
| 255 # Sometimes lists, sometimes nodes |
| 256 if child is None: |
| 257 pass |
| 258 elif isinstance(child, list): |
| 259 for c in child: |
| 260 pos = max(pos, c.end_pos()) |
| 261 else: |
| 262 pos = max(pos, child.end_pos()) |
| 263 self._end_pos = pos |
| 264 return pos |
| 265 |
| 266 def dump(self, level=0, filter_out=("pos",), cutoff=100, encountered=None): |
| 267 """Debug helper method that returns a recursive string representation of
this node. |
| 268 """ |
| 269 if cutoff == 0: |
| 270 return "<...nesting level cutoff...>" |
| 271 if encountered is None: |
| 272 encountered = set() |
| 273 if id(self) in encountered: |
| 274 return "<%s (0x%x) -- already output>" % (self.__class__.__name__, i
d(self)) |
| 275 encountered.add(id(self)) |
| 276 |
| 277 def dump_child(x, level): |
| 278 if isinstance(x, Node): |
| 279 return x.dump(level, filter_out, cutoff-1, encountered) |
| 280 elif isinstance(x, list): |
| 281 return "[%s]" % ", ".join([dump_child(item, level) for item in x
]) |
| 282 else: |
| 283 return repr(x) |
| 284 |
| 285 |
| 286 attrs = [(key, value) for key, value in self.__dict__.items() if key not
in filter_out] |
| 287 if len(attrs) == 0: |
| 288 return "<%s (0x%x)>" % (self.__class__.__name__, id(self)) |
| 289 else: |
| 290 indent = " " * level |
| 291 res = "<%s (0x%x)\n" % (self.__class__.__name__, id(self)) |
| 292 for key, value in attrs: |
| 293 res += "%s %s: %s\n" % (indent, key, dump_child(value, level +
1)) |
| 294 res += "%s>" % indent |
| 295 return res |
| 296 |
| 297 def dump_pos(self, mark_column=False, marker='(#)'): |
| 298 """Debug helper method that returns the source code context of this node
as a string. |
| 299 """ |
| 300 if not self.pos: |
| 301 return u'' |
| 302 source_desc, line, col = self.pos |
| 303 contents = source_desc.get_lines(encoding='ASCII', |
| 304 error_handling='ignore') |
| 305 # line numbers start at 1 |
| 306 lines = contents[max(0,line-3):line] |
| 307 current = lines[-1] |
| 308 if mark_column: |
| 309 current = current[:col] + marker + current[col:] |
| 310 lines[-1] = current.rstrip() + u' # <<<<<<<<<<<<<<\n' |
| 311 lines += contents[line:line+2] |
| 312 return u'"%s":%d:%d\n%s\n' % ( |
| 313 source_desc.get_escaped_description(), line, col, u''.join(lines)) |
| 314 |
| 315 class CompilerDirectivesNode(Node): |
| 316 """ |
| 317 Sets compiler directives for the children nodes |
| 318 """ |
| 319 # directives {string:value} A dictionary holding the right value for |
| 320 # *all* possible directives. |
| 321 # body Node |
| 322 child_attrs = ["body"] |
| 323 |
| 324 def analyse_declarations(self, env): |
| 325 old = env.directives |
| 326 env.directives = self.directives |
| 327 self.body.analyse_declarations(env) |
| 328 env.directives = old |
| 329 |
| 330 def analyse_expressions(self, env): |
| 331 old = env.directives |
| 332 env.directives = self.directives |
| 333 self.body = self.body.analyse_expressions(env) |
| 334 env.directives = old |
| 335 return self |
| 336 |
| 337 def generate_function_definitions(self, env, code): |
| 338 env_old = env.directives |
| 339 code_old = code.globalstate.directives |
| 340 code.globalstate.directives = self.directives |
| 341 self.body.generate_function_definitions(env, code) |
| 342 env.directives = env_old |
| 343 code.globalstate.directives = code_old |
| 344 |
| 345 def generate_execution_code(self, code): |
| 346 old = code.globalstate.directives |
| 347 code.globalstate.directives = self.directives |
| 348 self.body.generate_execution_code(code) |
| 349 code.globalstate.directives = old |
| 350 |
| 351 def annotate(self, code): |
| 352 old = code.globalstate.directives |
| 353 code.globalstate.directives = self.directives |
| 354 self.body.annotate(code) |
| 355 code.globalstate.directives = old |
| 356 |
| 357 class BlockNode(object): |
| 358 # Mixin class for nodes representing a declaration block. |
| 359 |
| 360 def generate_cached_builtins_decls(self, env, code): |
| 361 entries = env.global_scope().undeclared_cached_builtins |
| 362 for entry in entries: |
| 363 code.globalstate.add_cached_builtin_decl(entry) |
| 364 del entries[:] |
| 365 |
| 366 def generate_lambda_definitions(self, env, code): |
| 367 for node in env.lambda_defs: |
| 368 node.generate_function_definitions(env, code) |
| 369 |
| 370 class StatListNode(Node): |
| 371 # stats a list of StatNode |
| 372 |
| 373 child_attrs = ["stats"] |
| 374 |
| 375 def create_analysed(pos, env, *args, **kw): |
| 376 node = StatListNode(pos, *args, **kw) |
| 377 return node # No node-specific analysis necesarry |
| 378 create_analysed = staticmethod(create_analysed) |
| 379 |
| 380 def analyse_declarations(self, env): |
| 381 #print "StatListNode.analyse_declarations" ### |
| 382 for stat in self.stats: |
| 383 stat.analyse_declarations(env) |
| 384 |
| 385 def analyse_expressions(self, env): |
| 386 #print "StatListNode.analyse_expressions" ### |
| 387 self.stats = [ stat.analyse_expressions(env) |
| 388 for stat in self.stats ] |
| 389 return self |
| 390 |
| 391 def generate_function_definitions(self, env, code): |
| 392 #print "StatListNode.generate_function_definitions" ### |
| 393 for stat in self.stats: |
| 394 stat.generate_function_definitions(env, code) |
| 395 |
| 396 def generate_execution_code(self, code): |
| 397 #print "StatListNode.generate_execution_code" ### |
| 398 for stat in self.stats: |
| 399 code.mark_pos(stat.pos) |
| 400 stat.generate_execution_code(code) |
| 401 |
| 402 def annotate(self, code): |
| 403 for stat in self.stats: |
| 404 stat.annotate(code) |
| 405 |
| 406 |
| 407 class StatNode(Node): |
| 408 # |
| 409 # Code generation for statements is split into the following subphases: |
| 410 # |
| 411 # (1) generate_function_definitions |
| 412 # Emit C code for the definitions of any structs, |
| 413 # unions, enums and functions defined in the current |
| 414 # scope-block. |
| 415 # |
| 416 # (2) generate_execution_code |
| 417 # Emit C code for executable statements. |
| 418 # |
| 419 |
| 420 def generate_function_definitions(self, env, code): |
| 421 pass |
| 422 |
| 423 def generate_execution_code(self, code): |
| 424 raise InternalError("generate_execution_code not implemented for %s" % \ |
| 425 self.__class__.__name__) |
| 426 |
| 427 |
| 428 class CDefExternNode(StatNode): |
| 429 # include_file string or None |
| 430 # body StatNode |
| 431 |
| 432 child_attrs = ["body"] |
| 433 |
| 434 def analyse_declarations(self, env): |
| 435 if self.include_file: |
| 436 env.add_include_file(self.include_file) |
| 437 old_cinclude_flag = env.in_cinclude |
| 438 env.in_cinclude = 1 |
| 439 self.body.analyse_declarations(env) |
| 440 env.in_cinclude = old_cinclude_flag |
| 441 |
| 442 def analyse_expressions(self, env): |
| 443 return self |
| 444 |
| 445 def generate_execution_code(self, code): |
| 446 pass |
| 447 |
| 448 def annotate(self, code): |
| 449 self.body.annotate(code) |
| 450 |
| 451 |
| 452 class CDeclaratorNode(Node): |
| 453 # Part of a C declaration. |
| 454 # |
| 455 # Processing during analyse_declarations phase: |
| 456 # |
| 457 # analyse |
| 458 # Returns (name, type) pair where name is the |
| 459 # CNameDeclaratorNode of the name being declared |
| 460 # and type is the type it is being declared as. |
| 461 # |
| 462 # calling_convention string Calling convention of CFuncDeclaratorNode |
| 463 # for which this is a base |
| 464 |
| 465 child_attrs = [] |
| 466 |
| 467 calling_convention = "" |
| 468 |
| 469 def analyse_templates(self): |
| 470 # Only C++ functions have templates. |
| 471 return None |
| 472 |
| 473 class CNameDeclaratorNode(CDeclaratorNode): |
| 474 # name string The Cython name being declared |
| 475 # cname string or None C name, if specified |
| 476 # default ExprNode or None the value assigned on declaration |
| 477 |
| 478 child_attrs = ['default'] |
| 479 |
| 480 default = None |
| 481 |
| 482 def analyse(self, base_type, env, nonempty = 0): |
| 483 if nonempty and self.name == '': |
| 484 # May have mistaken the name for the type. |
| 485 if base_type.is_ptr or base_type.is_array or base_type.is_buffer: |
| 486 error(self.pos, "Missing argument name") |
| 487 elif base_type.is_void: |
| 488 error(self.pos, "Use spam() rather than spam(void) to declare a
function with no arguments.") |
| 489 else: |
| 490 self.name = base_type.declaration_code("", for_display=1, pyrex=
1) |
| 491 base_type = py_object_type |
| 492 |
| 493 if base_type.is_fused and env.fused_to_specific: |
| 494 base_type = base_type.specialize(env.fused_to_specific) |
| 495 |
| 496 self.type = base_type |
| 497 return self, base_type |
| 498 |
| 499 class CPtrDeclaratorNode(CDeclaratorNode): |
| 500 # base CDeclaratorNode |
| 501 |
| 502 child_attrs = ["base"] |
| 503 |
| 504 def analyse(self, base_type, env, nonempty = 0): |
| 505 if base_type.is_pyobject: |
| 506 error(self.pos, |
| 507 "Pointer base type cannot be a Python object") |
| 508 ptr_type = PyrexTypes.c_ptr_type(base_type) |
| 509 return self.base.analyse(ptr_type, env, nonempty = nonempty) |
| 510 |
| 511 class CReferenceDeclaratorNode(CDeclaratorNode): |
| 512 # base CDeclaratorNode |
| 513 |
| 514 child_attrs = ["base"] |
| 515 |
| 516 def analyse(self, base_type, env, nonempty = 0): |
| 517 if base_type.is_pyobject: |
| 518 error(self.pos, |
| 519 "Reference base type cannot be a Python object") |
| 520 ref_type = PyrexTypes.c_ref_type(base_type) |
| 521 return self.base.analyse(ref_type, env, nonempty = nonempty) |
| 522 |
| 523 class CArrayDeclaratorNode(CDeclaratorNode): |
| 524 # base CDeclaratorNode |
| 525 # dimension ExprNode |
| 526 |
| 527 child_attrs = ["base", "dimension"] |
| 528 |
| 529 def analyse(self, base_type, env, nonempty = 0): |
| 530 if base_type.is_cpp_class or base_type.is_cfunction: |
| 531 from ExprNodes import TupleNode |
| 532 if isinstance(self.dimension, TupleNode): |
| 533 args = self.dimension.args |
| 534 else: |
| 535 args = self.dimension, |
| 536 values = [v.analyse_as_type(env) for v in args] |
| 537 if None in values: |
| 538 ix = values.index(None) |
| 539 error(args[ix].pos, "Template parameter not a type") |
| 540 base_type = error_type |
| 541 else: |
| 542 base_type = base_type.specialize_here(self.pos, values) |
| 543 return self.base.analyse(base_type, env, nonempty = nonempty) |
| 544 if self.dimension: |
| 545 self.dimension = self.dimension.analyse_const_expression(env) |
| 546 if not self.dimension.type.is_int: |
| 547 error(self.dimension.pos, "Array dimension not integer") |
| 548 size = self.dimension.get_constant_c_result_code() |
| 549 if size is not None: |
| 550 try: |
| 551 size = int(size) |
| 552 except ValueError: |
| 553 # runtime constant? |
| 554 pass |
| 555 else: |
| 556 size = None |
| 557 if not base_type.is_complete(): |
| 558 error(self.pos, |
| 559 "Array element type '%s' is incomplete" % base_type) |
| 560 if base_type.is_pyobject: |
| 561 error(self.pos, |
| 562 "Array element cannot be a Python object") |
| 563 if base_type.is_cfunction: |
| 564 error(self.pos, |
| 565 "Array element cannot be a function") |
| 566 array_type = PyrexTypes.c_array_type(base_type, size) |
| 567 return self.base.analyse(array_type, env, nonempty = nonempty) |
| 568 |
| 569 |
| 570 class CFuncDeclaratorNode(CDeclaratorNode): |
| 571 # base CDeclaratorNode |
| 572 # args [CArgDeclNode] |
| 573 # templates [TemplatePlaceholderType] |
| 574 # has_varargs boolean |
| 575 # exception_value ConstNode |
| 576 # exception_check boolean True if PyErr_Occurred check needed |
| 577 # nogil boolean Can be called without gil |
| 578 # with_gil boolean Acquire gil around function body |
| 579 # is_const_method boolean Whether this is a const method |
| 580 |
| 581 child_attrs = ["base", "args", "exception_value"] |
| 582 |
| 583 overridable = 0 |
| 584 optional_arg_count = 0 |
| 585 is_const_method = 0 |
| 586 templates = None |
| 587 |
| 588 def analyse_templates(self): |
| 589 if isinstance(self.base, CArrayDeclaratorNode): |
| 590 from ExprNodes import TupleNode, NameNode |
| 591 template_node = self.base.dimension |
| 592 if isinstance(template_node, TupleNode): |
| 593 template_nodes = template_node.args |
| 594 elif isinstance(template_node, NameNode): |
| 595 template_nodes = [template_node] |
| 596 else: |
| 597 error(template_node.pos, "Template arguments must be a list of n
ames") |
| 598 return None |
| 599 self.templates = [] |
| 600 for template in template_nodes: |
| 601 if isinstance(template, NameNode): |
| 602 self.templates.append(PyrexTypes.TemplatePlaceholderType(tem
plate.name)) |
| 603 else: |
| 604 error(template.pos, "Template arguments must be a list of na
mes") |
| 605 self.base = self.base.base |
| 606 return self.templates |
| 607 else: |
| 608 return None |
| 609 |
| 610 def analyse(self, return_type, env, nonempty = 0, directive_locals = {}): |
| 611 if nonempty: |
| 612 nonempty -= 1 |
| 613 func_type_args = [] |
| 614 for i, arg_node in enumerate(self.args): |
| 615 name_declarator, type = arg_node.analyse(env, nonempty = nonempty, |
| 616 is_self_arg = (i == 0 and e
nv.is_c_class_scope)) |
| 617 name = name_declarator.name |
| 618 if name in directive_locals: |
| 619 type_node = directive_locals[name] |
| 620 other_type = type_node.analyse_as_type(env) |
| 621 if other_type is None: |
| 622 error(type_node.pos, "Not a type") |
| 623 elif (type is not PyrexTypes.py_object_type |
| 624 and not type.same_as(other_type)): |
| 625 error(self.base.pos, "Signature does not agree with previous
declaration") |
| 626 error(type_node.pos, "Previous declaration here") |
| 627 else: |
| 628 type = other_type |
| 629 if name_declarator.cname: |
| 630 error(self.pos, |
| 631 "Function argument cannot have C name specification") |
| 632 if i==0 and env.is_c_class_scope and type.is_unspecified: |
| 633 # fix the type of self |
| 634 type = env.parent_type |
| 635 # Turn *[] argument into ** |
| 636 if type.is_array: |
| 637 type = PyrexTypes.c_ptr_type(type.base_type) |
| 638 # Catch attempted C-style func(void) decl |
| 639 if type.is_void: |
| 640 error(arg_node.pos, "Use spam() rather than spam(void) to declar
e a function with no arguments.") |
| 641 func_type_args.append( |
| 642 PyrexTypes.CFuncTypeArg(name, type, arg_node.pos)) |
| 643 if arg_node.default: |
| 644 self.optional_arg_count += 1 |
| 645 elif self.optional_arg_count: |
| 646 error(self.pos, "Non-default argument follows default argument") |
| 647 |
| 648 exc_val = None |
| 649 exc_check = 0 |
| 650 if self.exception_check == '+': |
| 651 env.add_include_file('ios') # for std::ios_base::failure |
| 652 env.add_include_file('new') # for std::bad_alloc |
| 653 env.add_include_file('stdexcept') |
| 654 env.add_include_file('typeinfo') # for std::bad_cast |
| 655 if (return_type.is_pyobject |
| 656 and (self.exception_value or self.exception_check) |
| 657 and self.exception_check != '+'): |
| 658 error(self.pos, |
| 659 "Exception clause not allowed for function returning Python obje
ct") |
| 660 else: |
| 661 if self.exception_value: |
| 662 self.exception_value = self.exception_value.analyse_const_expres
sion(env) |
| 663 if self.exception_check == '+': |
| 664 exc_val_type = self.exception_value.type |
| 665 if (not exc_val_type.is_error |
| 666 and not exc_val_type.is_pyobject |
| 667 and not (exc_val_type.is_cfunction |
| 668 and not exc_val_type.return_type.is_pyobjec
t |
| 669 and not exc_val_type.args)): |
| 670 error(self.exception_value.pos, |
| 671 "Exception value must be a Python exception or cde
f function with no arguments.") |
| 672 exc_val = self.exception_value |
| 673 else: |
| 674 self.exception_value = self.exception_value.coerce_to( |
| 675 return_type, env).analyse_const_expression(env) |
| 676 exc_val = self.exception_value.get_constant_c_result_code() |
| 677 if exc_val is None: |
| 678 raise InternalError( |
| 679 "get_constant_c_result_code not implemented for %s"
% |
| 680 self.exception_value.__class__.__name__) |
| 681 if not return_type.assignable_from(self.exception_value.type
): |
| 682 error(self.exception_value.pos, |
| 683 "Exception value incompatible with function return
type") |
| 684 exc_check = self.exception_check |
| 685 if return_type.is_cfunction: |
| 686 error(self.pos, |
| 687 "Function cannot return a function") |
| 688 func_type = PyrexTypes.CFuncType( |
| 689 return_type, func_type_args, self.has_varargs, |
| 690 optional_arg_count = self.optional_arg_count, |
| 691 exception_value = exc_val, exception_check = exc_check, |
| 692 calling_convention = self.base.calling_convention, |
| 693 nogil = self.nogil, with_gil = self.with_gil, is_overridable = self.
overridable, |
| 694 is_const_method = self.is_const_method, |
| 695 templates = self.templates) |
| 696 |
| 697 if self.optional_arg_count: |
| 698 if func_type.is_fused: |
| 699 # This is a bit of a hack... When we need to create specialized
CFuncTypes |
| 700 # on the fly because the cdef is defined in a pxd, we need to de
clare the specialized optional arg |
| 701 # struct |
| 702 def declare_opt_arg_struct(func_type, fused_cname): |
| 703 self.declare_optional_arg_struct(func_type, env, fused_cname
) |
| 704 |
| 705 func_type.declare_opt_arg_struct = declare_opt_arg_struct |
| 706 else: |
| 707 self.declare_optional_arg_struct(func_type, env) |
| 708 |
| 709 callspec = env.directives['callspec'] |
| 710 if callspec: |
| 711 current = func_type.calling_convention |
| 712 if current and current != callspec: |
| 713 error(self.pos, "cannot have both '%s' and '%s' " |
| 714 "calling conventions" % (current, callspec)) |
| 715 func_type.calling_convention = callspec |
| 716 return self.base.analyse(func_type, env) |
| 717 |
| 718 def declare_optional_arg_struct(self, func_type, env, fused_cname=None): |
| 719 """ |
| 720 Declares the optional argument struct (the struct used to hold the |
| 721 values for optional arguments). For fused cdef functions, this is |
| 722 deferred as analyse_declarations is called only once (on the fused |
| 723 cdef function). |
| 724 """ |
| 725 scope = StructOrUnionScope() |
| 726 arg_count_member = '%sn' % Naming.pyrex_prefix |
| 727 scope.declare_var(arg_count_member, PyrexTypes.c_int_type, self.pos) |
| 728 |
| 729 for arg in func_type.args[len(func_type.args)-self.optional_arg_count:]: |
| 730 scope.declare_var(arg.name, arg.type, arg.pos, allow_pyobject = 1) |
| 731 |
| 732 struct_cname = env.mangle(Naming.opt_arg_prefix, self.base.name) |
| 733 |
| 734 if fused_cname is not None: |
| 735 struct_cname = PyrexTypes.get_fused_cname(fused_cname, struct_cname) |
| 736 |
| 737 op_args_struct = env.global_scope().declare_struct_or_union( |
| 738 name = struct_cname, |
| 739 kind = 'struct', |
| 740 scope = scope, |
| 741 typedef_flag = 0, |
| 742 pos = self.pos, |
| 743 cname = struct_cname) |
| 744 |
| 745 op_args_struct.defined_in_pxd = 1 |
| 746 op_args_struct.used = 1 |
| 747 |
| 748 func_type.op_arg_struct = PyrexTypes.c_ptr_type(op_args_struct.type) |
| 749 |
| 750 |
| 751 class CConstDeclaratorNode(CDeclaratorNode): |
| 752 # base CDeclaratorNode |
| 753 |
| 754 child_attrs = ["base"] |
| 755 |
| 756 def analyse(self, base_type, env, nonempty = 0): |
| 757 if base_type.is_pyobject: |
| 758 error(self.pos, |
| 759 "Const base type cannot be a Python object") |
| 760 const = PyrexTypes.c_const_type(base_type) |
| 761 return self.base.analyse(const, env, nonempty = nonempty) |
| 762 |
| 763 |
| 764 class CArgDeclNode(Node): |
| 765 # Item in a function declaration argument list. |
| 766 # |
| 767 # base_type CBaseTypeNode |
| 768 # declarator CDeclaratorNode |
| 769 # not_none boolean Tagged with 'not None' |
| 770 # or_none boolean Tagged with 'or None' |
| 771 # accept_none boolean Resolved boolean for not_none/or_none |
| 772 # default ExprNode or None |
| 773 # default_value PyObjectConst constant for default value |
| 774 # annotation ExprNode or None Py3 function arg annotation |
| 775 # is_self_arg boolean Is the "self" arg of an extension type m
ethod |
| 776 # is_type_arg boolean Is the "class" arg of an extension type
classmethod |
| 777 # is_kw_only boolean Is a keyword-only argument |
| 778 # is_dynamic boolean Non-literal arg stored inside CyFunction |
| 779 |
| 780 child_attrs = ["base_type", "declarator", "default", "annotation"] |
| 781 |
| 782 is_self_arg = 0 |
| 783 is_type_arg = 0 |
| 784 is_generic = 1 |
| 785 kw_only = 0 |
| 786 not_none = 0 |
| 787 or_none = 0 |
| 788 type = None |
| 789 name_declarator = None |
| 790 default_value = None |
| 791 annotation = None |
| 792 is_dynamic = 0 |
| 793 |
| 794 def analyse(self, env, nonempty = 0, is_self_arg = False): |
| 795 if is_self_arg: |
| 796 self.base_type.is_self_arg = self.is_self_arg = True |
| 797 if self.type is None: |
| 798 # The parser may misinterpret names as types. We fix that here. |
| 799 if isinstance(self.declarator, CNameDeclaratorNode) and self.declara
tor.name == '': |
| 800 if nonempty: |
| 801 if self.base_type.is_basic_c_type: |
| 802 # char, short, long called "int" |
| 803 type = self.base_type.analyse(env, could_be_name = True) |
| 804 arg_name = type.declaration_code("") |
| 805 else: |
| 806 arg_name = self.base_type.name |
| 807 self.declarator.name = EncodedString(arg_name) |
| 808 self.base_type.name = None |
| 809 self.base_type.is_basic_c_type = False |
| 810 could_be_name = True |
| 811 else: |
| 812 could_be_name = False |
| 813 self.base_type.is_arg = True |
| 814 base_type = self.base_type.analyse(env, could_be_name = could_be_nam
e) |
| 815 if hasattr(self.base_type, 'arg_name') and self.base_type.arg_name: |
| 816 self.declarator.name = self.base_type.arg_name |
| 817 # The parser is unable to resolve the ambiguity of [] as part of the |
| 818 # type (e.g. in buffers) or empty declarator (as with arrays). |
| 819 # This is only arises for empty multi-dimensional arrays. |
| 820 if (base_type.is_array |
| 821 and isinstance(self.base_type, TemplatedTypeNode) |
| 822 and isinstance(self.declarator, CArrayDeclaratorNode)): |
| 823 declarator = self.declarator |
| 824 while isinstance(declarator.base, CArrayDeclaratorNode): |
| 825 declarator = declarator.base |
| 826 declarator.base = self.base_type.array_declarator |
| 827 base_type = base_type.base_type |
| 828 return self.declarator.analyse(base_type, env, nonempty = nonempty) |
| 829 else: |
| 830 return self.name_declarator, self.type |
| 831 |
| 832 def calculate_default_value_code(self, code): |
| 833 if self.default_value is None: |
| 834 if self.default: |
| 835 if self.default.is_literal: |
| 836 # will not output any code, just assign the result_code |
| 837 self.default.generate_evaluation_code(code) |
| 838 return self.type.cast_code(self.default.result()) |
| 839 self.default_value = code.get_argument_default_const(self.type) |
| 840 return self.default_value |
| 841 |
| 842 def annotate(self, code): |
| 843 if self.default: |
| 844 self.default.annotate(code) |
| 845 |
| 846 def generate_assignment_code(self, code, target=None): |
| 847 default = self.default |
| 848 if default is None or default.is_literal: |
| 849 return |
| 850 if target is None: |
| 851 target = self.calculate_default_value_code(code) |
| 852 default.generate_evaluation_code(code) |
| 853 default.make_owned_reference(code) |
| 854 result = default.result_as(self.type) |
| 855 code.putln("%s = %s;" % (target, result)) |
| 856 if self.type.is_pyobject: |
| 857 code.put_giveref(default.result()) |
| 858 default.generate_post_assignment_code(code) |
| 859 default.free_temps(code) |
| 860 |
| 861 |
| 862 class CBaseTypeNode(Node): |
| 863 # Abstract base class for C base type nodes. |
| 864 # |
| 865 # Processing during analyse_declarations phase: |
| 866 # |
| 867 # analyse |
| 868 # Returns the type. |
| 869 |
| 870 pass |
| 871 |
| 872 def analyse_as_type(self, env): |
| 873 return self.analyse(env) |
| 874 |
| 875 class CAnalysedBaseTypeNode(Node): |
| 876 # type type |
| 877 |
| 878 child_attrs = [] |
| 879 |
| 880 def analyse(self, env, could_be_name = False): |
| 881 return self.type |
| 882 |
| 883 class CSimpleBaseTypeNode(CBaseTypeNode): |
| 884 # name string |
| 885 # module_path [string] Qualifying name components |
| 886 # is_basic_c_type boolean |
| 887 # signed boolean |
| 888 # longness integer |
| 889 # complex boolean |
| 890 # is_self_arg boolean Is self argument of C method |
| 891 # ##is_type_arg boolean Is type argument of class method |
| 892 |
| 893 child_attrs = [] |
| 894 arg_name = None # in case the argument name was interpreted as a type |
| 895 module_path = [] |
| 896 is_basic_c_type = False |
| 897 complex = False |
| 898 |
| 899 def analyse(self, env, could_be_name = False): |
| 900 # Return type descriptor. |
| 901 #print "CSimpleBaseTypeNode.analyse: is_self_arg =", self.is_self_arg ##
# |
| 902 type = None |
| 903 if self.is_basic_c_type: |
| 904 type = PyrexTypes.simple_c_type(self.signed, self.longness, self.nam
e) |
| 905 if not type: |
| 906 error(self.pos, "Unrecognised type modifier combination") |
| 907 elif self.name == "object" and not self.module_path: |
| 908 type = py_object_type |
| 909 elif self.name is None: |
| 910 if self.is_self_arg and env.is_c_class_scope: |
| 911 #print "CSimpleBaseTypeNode.analyse: defaulting to parent type"
### |
| 912 type = env.parent_type |
| 913 ## elif self.is_type_arg and env.is_c_class_scope: |
| 914 ## type = Builtin.type_type |
| 915 else: |
| 916 type = py_object_type |
| 917 else: |
| 918 if self.module_path: |
| 919 # Maybe it's a nested C++ class. |
| 920 scope = env |
| 921 for item in self.module_path: |
| 922 entry = scope.lookup(item) |
| 923 if entry is not None and entry.is_cpp_class: |
| 924 scope = entry.type.scope |
| 925 else: |
| 926 scope = None |
| 927 break |
| 928 |
| 929 if scope is None: |
| 930 # Maybe it's a cimport. |
| 931 scope = env.find_imported_module(self.module_path, self.pos) |
| 932 if scope: |
| 933 scope.fused_to_specific = env.fused_to_specific |
| 934 else: |
| 935 scope = env |
| 936 |
| 937 if scope: |
| 938 if scope.is_c_class_scope: |
| 939 scope = scope.global_scope() |
| 940 |
| 941 type = scope.lookup_type(self.name) |
| 942 if type is not None: |
| 943 pass |
| 944 elif could_be_name: |
| 945 if self.is_self_arg and env.is_c_class_scope: |
| 946 type = env.parent_type |
| 947 ## elif self.is_type_arg and env.is_c_class_scope: |
| 948 ## type = Builtin.type_type |
| 949 else: |
| 950 type = py_object_type |
| 951 self.arg_name = EncodedString(self.name) |
| 952 else: |
| 953 if self.templates: |
| 954 if not self.name in self.templates: |
| 955 error(self.pos, "'%s' is not a type identifier" % se
lf.name) |
| 956 type = PyrexTypes.TemplatePlaceholderType(self.name) |
| 957 else: |
| 958 error(self.pos, "'%s' is not a type identifier" % self.n
ame) |
| 959 if self.complex: |
| 960 if not type.is_numeric or type.is_complex: |
| 961 error(self.pos, "can only complexify c numeric types") |
| 962 type = PyrexTypes.CComplexType(type) |
| 963 type.create_declaration_utility_code(env) |
| 964 elif type is Builtin.complex_type: |
| 965 # Special case: optimise builtin complex type into C's |
| 966 # double complex. The parser cannot do this (as for the |
| 967 # normal scalar types) as the user may have redeclared the |
| 968 # 'complex' type. Testing for the exact type here works. |
| 969 type = PyrexTypes.c_double_complex_type |
| 970 type.create_declaration_utility_code(env) |
| 971 self.complex = True |
| 972 if type: |
| 973 return type |
| 974 else: |
| 975 return PyrexTypes.error_type |
| 976 |
| 977 class MemoryViewSliceTypeNode(CBaseTypeNode): |
| 978 |
| 979 name = 'memoryview' |
| 980 child_attrs = ['base_type_node', 'axes'] |
| 981 |
| 982 def analyse(self, env, could_be_name = False): |
| 983 |
| 984 base_type = self.base_type_node.analyse(env) |
| 985 if base_type.is_error: return base_type |
| 986 |
| 987 import MemoryView |
| 988 |
| 989 try: |
| 990 axes_specs = MemoryView.get_axes_specs(env, self.axes) |
| 991 except CompileError, e: |
| 992 error(e.position, e.message_only) |
| 993 self.type = PyrexTypes.ErrorType() |
| 994 return self.type |
| 995 |
| 996 if not MemoryView.validate_axes(self.pos, axes_specs): |
| 997 self.type = error_type |
| 998 else: |
| 999 MemoryView.validate_memslice_dtype(self.pos, base_type) |
| 1000 self.type = PyrexTypes.MemoryViewSliceType(base_type, axes_specs) |
| 1001 self.use_memview_utilities(env) |
| 1002 |
| 1003 return self.type |
| 1004 |
| 1005 def use_memview_utilities(self, env): |
| 1006 import MemoryView |
| 1007 env.use_utility_code(MemoryView.view_utility_code) |
| 1008 |
| 1009 |
| 1010 class CNestedBaseTypeNode(CBaseTypeNode): |
| 1011 # For C++ classes that live inside other C++ classes. |
| 1012 |
| 1013 # name string |
| 1014 # base_type CBaseTypeNode |
| 1015 |
| 1016 child_attrs = ['base_type'] |
| 1017 |
| 1018 def analyse(self, env, could_be_name = None): |
| 1019 base_type = self.base_type.analyse(env) |
| 1020 if base_type is PyrexTypes.error_type: |
| 1021 return PyrexTypes.error_type |
| 1022 if not base_type.is_cpp_class: |
| 1023 error(self.pos, "'%s' is not a valid type scope" % base_type) |
| 1024 return PyrexTypes.error_type |
| 1025 type_entry = base_type.scope.lookup_here(self.name) |
| 1026 if not type_entry or not type_entry.is_type: |
| 1027 error(self.pos, "'%s.%s' is not a type identifier" % (base_type, sel
f.name)) |
| 1028 return PyrexTypes.error_type |
| 1029 return type_entry.type |
| 1030 |
| 1031 |
| 1032 class TemplatedTypeNode(CBaseTypeNode): |
| 1033 # After parsing: |
| 1034 # positional_args [ExprNode] List of positional arguments |
| 1035 # keyword_args DictNode Keyword arguments |
| 1036 # base_type_node CBaseTypeNode |
| 1037 |
| 1038 # After analysis: |
| 1039 # type PyrexTypes.BufferType or PyrexTypes.CppClassType ...con
taining the right options |
| 1040 |
| 1041 child_attrs = ["base_type_node", "positional_args", |
| 1042 "keyword_args", "dtype_node"] |
| 1043 |
| 1044 dtype_node = None |
| 1045 |
| 1046 name = None |
| 1047 |
| 1048 def analyse(self, env, could_be_name = False, base_type = None): |
| 1049 if base_type is None: |
| 1050 base_type = self.base_type_node.analyse(env) |
| 1051 if base_type.is_error: return base_type |
| 1052 |
| 1053 if base_type.is_cpp_class: |
| 1054 # Templated class |
| 1055 if self.keyword_args and self.keyword_args.key_value_pairs: |
| 1056 error(self.pos, "c++ templates cannot take keyword arguments") |
| 1057 self.type = PyrexTypes.error_type |
| 1058 else: |
| 1059 template_types = [] |
| 1060 for template_node in self.positional_args: |
| 1061 type = template_node.analyse_as_type(env) |
| 1062 if type is None: |
| 1063 error(template_node.pos, "unknown type in template argum
ent") |
| 1064 return error_type |
| 1065 template_types.append(type) |
| 1066 self.type = base_type.specialize_here(self.pos, template_types) |
| 1067 |
| 1068 elif base_type.is_pyobject: |
| 1069 # Buffer |
| 1070 import Buffer |
| 1071 |
| 1072 options = Buffer.analyse_buffer_options( |
| 1073 self.pos, |
| 1074 env, |
| 1075 self.positional_args, |
| 1076 self.keyword_args, |
| 1077 base_type.buffer_defaults) |
| 1078 |
| 1079 if sys.version_info[0] < 3: |
| 1080 # Py 2.x enforces byte strings as keyword arguments ... |
| 1081 options = dict([ (name.encode('ASCII'), value) |
| 1082 for name, value in options.items() ]) |
| 1083 |
| 1084 self.type = PyrexTypes.BufferType(base_type, **options) |
| 1085 |
| 1086 else: |
| 1087 # Array |
| 1088 empty_declarator = CNameDeclaratorNode(self.pos, name="", cname=None
) |
| 1089 if len(self.positional_args) > 1 or self.keyword_args.key_value_pair
s: |
| 1090 error(self.pos, "invalid array declaration") |
| 1091 self.type = PyrexTypes.error_type |
| 1092 else: |
| 1093 # It would be nice to merge this class with CArrayDeclaratorNode
, |
| 1094 # but arrays are part of the declaration, not the type... |
| 1095 if not self.positional_args: |
| 1096 dimension = None |
| 1097 else: |
| 1098 dimension = self.positional_args[0] |
| 1099 self.array_declarator = CArrayDeclaratorNode(self.pos, |
| 1100 base = empty_declarator, |
| 1101 dimension = dimension) |
| 1102 self.type = self.array_declarator.analyse(base_type, env)[1] |
| 1103 |
| 1104 if self.type.is_fused and env.fused_to_specific: |
| 1105 self.type = self.type.specialize(env.fused_to_specific) |
| 1106 |
| 1107 return self.type |
| 1108 |
| 1109 class CComplexBaseTypeNode(CBaseTypeNode): |
| 1110 # base_type CBaseTypeNode |
| 1111 # declarator CDeclaratorNode |
| 1112 |
| 1113 child_attrs = ["base_type", "declarator"] |
| 1114 |
| 1115 def analyse(self, env, could_be_name = False): |
| 1116 base = self.base_type.analyse(env, could_be_name) |
| 1117 _, type = self.declarator.analyse(base, env) |
| 1118 return type |
| 1119 |
| 1120 |
| 1121 class FusedTypeNode(CBaseTypeNode): |
| 1122 """ |
| 1123 Represents a fused type in a ctypedef statement: |
| 1124 |
| 1125 ctypedef cython.fused_type(int, long, long long) integral |
| 1126 |
| 1127 name str name of this fused type |
| 1128 types [CSimpleBaseTypeNode] is the list of types to be fused |
| 1129 """ |
| 1130 |
| 1131 child_attrs = [] |
| 1132 |
| 1133 def analyse_declarations(self, env): |
| 1134 type = self.analyse(env) |
| 1135 entry = env.declare_typedef(self.name, type, self.pos) |
| 1136 |
| 1137 # Omit the typedef declaration that self.declarator would produce |
| 1138 entry.in_cinclude = True |
| 1139 |
| 1140 def analyse(self, env): |
| 1141 types = [] |
| 1142 for type_node in self.types: |
| 1143 type = type_node.analyse_as_type(env) |
| 1144 |
| 1145 if not type: |
| 1146 error(type_node.pos, "Not a type") |
| 1147 continue |
| 1148 |
| 1149 if type in types: |
| 1150 error(type_node.pos, "Type specified multiple times") |
| 1151 elif type.is_fused: |
| 1152 error(type_node.pos, "Cannot fuse a fused type") |
| 1153 else: |
| 1154 types.append(type) |
| 1155 |
| 1156 # if len(self.types) == 1: |
| 1157 # return types[0] |
| 1158 |
| 1159 return PyrexTypes.FusedType(types, name=self.name) |
| 1160 |
| 1161 |
| 1162 class CConstTypeNode(CBaseTypeNode): |
| 1163 # base_type CBaseTypeNode |
| 1164 |
| 1165 child_attrs = ["base_type"] |
| 1166 |
| 1167 def analyse(self, env, could_be_name = False): |
| 1168 base = self.base_type.analyse(env, could_be_name) |
| 1169 if base.is_pyobject: |
| 1170 error(self.pos, |
| 1171 "Const base type cannot be a Python object") |
| 1172 return PyrexTypes.c_const_type(base) |
| 1173 |
| 1174 |
| 1175 class CVarDefNode(StatNode): |
| 1176 # C variable definition or forward/extern function declaration. |
| 1177 # |
| 1178 # visibility 'private' or 'public' or 'extern' |
| 1179 # base_type CBaseTypeNode |
| 1180 # declarators [CDeclaratorNode] |
| 1181 # in_pxd boolean |
| 1182 # api boolean |
| 1183 # overridable boolean whether it is a cpdef |
| 1184 # modifiers ['inline'] |
| 1185 |
| 1186 # decorators [cython.locals(...)] or None |
| 1187 # directive_locals { string : NameNode } locals defined by cython.locals(..
.) |
| 1188 |
| 1189 child_attrs = ["base_type", "declarators"] |
| 1190 |
| 1191 decorators = None |
| 1192 directive_locals = None |
| 1193 |
| 1194 def analyse_declarations(self, env, dest_scope = None): |
| 1195 if self.directive_locals is None: |
| 1196 self.directive_locals = {} |
| 1197 if not dest_scope: |
| 1198 dest_scope = env |
| 1199 self.dest_scope = dest_scope |
| 1200 |
| 1201 if self.declarators: |
| 1202 templates = self.declarators[0].analyse_templates() |
| 1203 else: |
| 1204 templates = None |
| 1205 if templates is not None: |
| 1206 if self.visibility != 'extern': |
| 1207 error(self.pos, "Only extern functions allowed") |
| 1208 if len(self.declarators) > 1: |
| 1209 error(self.declarators[1].pos, "Can't multiply declare template
types") |
| 1210 env = TemplateScope('func_template', env) |
| 1211 env.directives = env.outer_scope.directives |
| 1212 for template_param in templates: |
| 1213 env.declare_type(template_param.name, template_param, self.pos) |
| 1214 |
| 1215 base_type = self.base_type.analyse(env) |
| 1216 |
| 1217 if base_type.is_fused and not self.in_pxd and (env.is_c_class_scope or |
| 1218 env.is_module_scope): |
| 1219 error(self.pos, "Fused types not allowed here") |
| 1220 return error_type |
| 1221 |
| 1222 self.entry = None |
| 1223 visibility = self.visibility |
| 1224 |
| 1225 for declarator in self.declarators: |
| 1226 |
| 1227 if (len(self.declarators) > 1 |
| 1228 and not isinstance(declarator, CNameDeclaratorNode) |
| 1229 and env.directives['warn.multiple_declarators']): |
| 1230 warning(declarator.pos, |
| 1231 "Non-trivial type declarators in shared declaration (e.g. mi
x of pointers and values). " + |
| 1232 "Each pointer declaration should be on its own line.", 1) |
| 1233 |
| 1234 if isinstance(declarator, CFuncDeclaratorNode): |
| 1235 name_declarator, type = declarator.analyse(base_type, env, direc
tive_locals=self.directive_locals) |
| 1236 else: |
| 1237 name_declarator, type = declarator.analyse(base_type, env) |
| 1238 if not type.is_complete(): |
| 1239 if not (self.visibility == 'extern' and type.is_array or type.is
_memoryviewslice): |
| 1240 error(declarator.pos, |
| 1241 "Variable type '%s' is incomplete" % type) |
| 1242 if self.visibility == 'extern' and type.is_pyobject: |
| 1243 error(declarator.pos, |
| 1244 "Python object cannot be declared extern") |
| 1245 name = name_declarator.name |
| 1246 cname = name_declarator.cname |
| 1247 if name == '': |
| 1248 error(declarator.pos, "Missing name in declaration.") |
| 1249 return |
| 1250 if type.is_cfunction: |
| 1251 self.entry = dest_scope.declare_cfunction(name, type, declarator
.pos, |
| 1252 cname = cname, visibility = self.visibility, in_pxd = self.i
n_pxd, |
| 1253 api = self.api, modifiers = self.modifiers) |
| 1254 if self.entry is not None: |
| 1255 self.entry.is_overridable = self.overridable |
| 1256 self.entry.directive_locals = copy.copy(self.directive_local
s) |
| 1257 else: |
| 1258 if self.directive_locals: |
| 1259 error(self.pos, "Decorators can only be followed by function
s") |
| 1260 self.entry = dest_scope.declare_var(name, type, declarator.pos, |
| 1261 cname=cname, visibility=visibility, in_pxd=self.in_p
xd, |
| 1262 api=self.api, is_cdef=1) |
| 1263 if Options.docstrings: |
| 1264 self.entry.doc = embed_position(self.pos, self.doc) |
| 1265 |
| 1266 |
| 1267 class CStructOrUnionDefNode(StatNode): |
| 1268 # name string |
| 1269 # cname string or None |
| 1270 # kind "struct" or "union" |
| 1271 # typedef_flag boolean |
| 1272 # visibility "public" or "private" |
| 1273 # api boolean |
| 1274 # in_pxd boolean |
| 1275 # attributes [CVarDefNode] or None |
| 1276 # entry Entry |
| 1277 # packed boolean |
| 1278 |
| 1279 child_attrs = ["attributes"] |
| 1280 |
| 1281 def declare(self, env, scope=None): |
| 1282 if self.visibility == 'extern' and self.packed and not scope: |
| 1283 error(self.pos, "Cannot declare extern struct as 'packed'") |
| 1284 self.entry = env.declare_struct_or_union( |
| 1285 self.name, self.kind, scope, self.typedef_flag, self.pos, |
| 1286 self.cname, visibility = self.visibility, api = self.api, |
| 1287 packed = self.packed) |
| 1288 |
| 1289 def analyse_declarations(self, env): |
| 1290 scope = None |
| 1291 if self.attributes is not None: |
| 1292 scope = StructOrUnionScope(self.name) |
| 1293 self.declare(env, scope) |
| 1294 if self.attributes is not None: |
| 1295 if self.in_pxd and not env.in_cinclude: |
| 1296 self.entry.defined_in_pxd = 1 |
| 1297 for attr in self.attributes: |
| 1298 attr.analyse_declarations(env, scope) |
| 1299 if self.visibility != 'extern': |
| 1300 for attr in scope.var_entries: |
| 1301 type = attr.type |
| 1302 while type.is_array: |
| 1303 type = type.base_type |
| 1304 if type == self.entry.type: |
| 1305 error(attr.pos, "Struct cannot contain itself as a membe
r.") |
| 1306 |
| 1307 def analyse_expressions(self, env): |
| 1308 return self |
| 1309 |
| 1310 def generate_execution_code(self, code): |
| 1311 pass |
| 1312 |
| 1313 |
| 1314 class CppClassNode(CStructOrUnionDefNode, BlockNode): |
| 1315 |
| 1316 # name string |
| 1317 # cname string or None |
| 1318 # visibility "extern" |
| 1319 # in_pxd boolean |
| 1320 # attributes [CVarDefNode] or None |
| 1321 # entry Entry |
| 1322 # base_classes [CBaseTypeNode] |
| 1323 # templates [string] or None |
| 1324 |
| 1325 def declare(self, env): |
| 1326 if self.templates is None: |
| 1327 template_types = None |
| 1328 else: |
| 1329 template_types = [PyrexTypes.TemplatePlaceholderType(template_name)
for template_name in self.templates] |
| 1330 self.entry = env.declare_cpp_class( |
| 1331 self.name, None, self.pos, |
| 1332 self.cname, base_classes = [], visibility = self.visibility, templat
es = template_types) |
| 1333 |
| 1334 def analyse_declarations(self, env): |
| 1335 scope = None |
| 1336 if self.attributes is not None: |
| 1337 scope = CppClassScope(self.name, env, templates = self.templates) |
| 1338 def base_ok(base_class): |
| 1339 if base_class.is_cpp_class or base_class.is_struct: |
| 1340 return True |
| 1341 else: |
| 1342 error(self.pos, "Base class '%s' not a struct or class." % base_
class) |
| 1343 base_class_types = filter(base_ok, [b.analyse(scope or env) for b in sel
f.base_classes]) |
| 1344 if self.templates is None: |
| 1345 template_types = None |
| 1346 else: |
| 1347 template_types = [PyrexTypes.TemplatePlaceholderType(template_name)
for template_name in self.templates] |
| 1348 self.entry = env.declare_cpp_class( |
| 1349 self.name, scope, self.pos, |
| 1350 self.cname, base_class_types, visibility = self.visibility, template
s = template_types) |
| 1351 if self.entry is None: |
| 1352 return |
| 1353 self.entry.is_cpp_class = 1 |
| 1354 if scope is not None: |
| 1355 scope.type = self.entry.type |
| 1356 defined_funcs = [] |
| 1357 if self.attributes is not None: |
| 1358 if self.in_pxd and not env.in_cinclude: |
| 1359 self.entry.defined_in_pxd = 1 |
| 1360 for attr in self.attributes: |
| 1361 attr.analyse_declarations(scope) |
| 1362 if isinstance(attr, CFuncDefNode): |
| 1363 defined_funcs.append(attr) |
| 1364 if self.templates is not None: |
| 1365 attr.template_declaration = "template <typename %s>" % "
, typename ".join(self.templates) |
| 1366 self.body = StatListNode(self.pos, stats=defined_funcs) |
| 1367 self.scope = scope |
| 1368 |
| 1369 def analyse_expressions(self, env): |
| 1370 self.body = self.body.analyse_expressions(self.entry.type.scope) |
| 1371 return self |
| 1372 |
| 1373 def generate_function_definitions(self, env, code): |
| 1374 self.body.generate_function_definitions(self.entry.type.scope, code) |
| 1375 |
| 1376 def generate_execution_code(self, code): |
| 1377 self.body.generate_execution_code(code) |
| 1378 |
| 1379 def annotate(self, code): |
| 1380 self.body.annotate(code) |
| 1381 |
| 1382 |
| 1383 class CEnumDefNode(StatNode): |
| 1384 # name string or None |
| 1385 # cname string or None |
| 1386 # items [CEnumDefItemNode] |
| 1387 # typedef_flag boolean |
| 1388 # visibility "public" or "private" |
| 1389 # api boolean |
| 1390 # in_pxd boolean |
| 1391 # entry Entry |
| 1392 |
| 1393 child_attrs = ["items"] |
| 1394 |
| 1395 def declare(self, env): |
| 1396 self.entry = env.declare_enum(self.name, self.pos, |
| 1397 cname = self.cname, typedef_flag = self.typedef_flag, |
| 1398 visibility = self.visibility, api = self.api) |
| 1399 |
| 1400 def analyse_declarations(self, env): |
| 1401 if self.items is not None: |
| 1402 if self.in_pxd and not env.in_cinclude: |
| 1403 self.entry.defined_in_pxd = 1 |
| 1404 for item in self.items: |
| 1405 item.analyse_declarations(env, self.entry) |
| 1406 |
| 1407 def analyse_expressions(self, env): |
| 1408 return self |
| 1409 |
| 1410 def generate_execution_code(self, code): |
| 1411 if self.visibility == 'public' or self.api: |
| 1412 temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manag
e_ref=True) |
| 1413 for item in self.entry.enum_values: |
| 1414 code.putln("%s = PyInt_FromLong(%s); %s" % ( |
| 1415 temp, |
| 1416 item.cname, |
| 1417 code.error_goto_if_null(temp, item.pos))) |
| 1418 code.put_gotref(temp) |
| 1419 code.putln('if (PyDict_SetItemString(%s, "%s", %s) < 0) %s' % ( |
| 1420 Naming.moddict_cname, |
| 1421 item.name, |
| 1422 temp, |
| 1423 code.error_goto(item.pos))) |
| 1424 code.put_decref_clear(temp, PyrexTypes.py_object_type) |
| 1425 code.funcstate.release_temp(temp) |
| 1426 |
| 1427 |
| 1428 class CEnumDefItemNode(StatNode): |
| 1429 # name string |
| 1430 # cname string or None |
| 1431 # value ExprNode or None |
| 1432 |
| 1433 child_attrs = ["value"] |
| 1434 |
| 1435 def analyse_declarations(self, env, enum_entry): |
| 1436 if self.value: |
| 1437 self.value = self.value.analyse_const_expression(env) |
| 1438 if not self.value.type.is_int: |
| 1439 self.value = self.value.coerce_to(PyrexTypes.c_int_type, env) |
| 1440 self.value = self.value.analyse_const_expression(env) |
| 1441 entry = env.declare_const(self.name, enum_entry.type, |
| 1442 self.value, self.pos, cname = self.cname, |
| 1443 visibility = enum_entry.visibility, api = enum_entry.api) |
| 1444 enum_entry.enum_values.append(entry) |
| 1445 |
| 1446 |
| 1447 class CTypeDefNode(StatNode): |
| 1448 # base_type CBaseTypeNode |
| 1449 # declarator CDeclaratorNode |
| 1450 # visibility "public" or "private" |
| 1451 # api boolean |
| 1452 # in_pxd boolean |
| 1453 |
| 1454 child_attrs = ["base_type", "declarator"] |
| 1455 |
| 1456 def analyse_declarations(self, env): |
| 1457 base = self.base_type.analyse(env) |
| 1458 name_declarator, type = self.declarator.analyse(base, env) |
| 1459 name = name_declarator.name |
| 1460 cname = name_declarator.cname |
| 1461 |
| 1462 entry = env.declare_typedef(name, type, self.pos, |
| 1463 cname = cname, visibility = self.visibility, api = self.api) |
| 1464 |
| 1465 if type.is_fused: |
| 1466 entry.in_cinclude = True |
| 1467 |
| 1468 if self.in_pxd and not env.in_cinclude: |
| 1469 entry.defined_in_pxd = 1 |
| 1470 |
| 1471 def analyse_expressions(self, env): |
| 1472 return self |
| 1473 |
| 1474 def generate_execution_code(self, code): |
| 1475 pass |
| 1476 |
| 1477 |
| 1478 class FuncDefNode(StatNode, BlockNode): |
| 1479 # Base class for function definition nodes. |
| 1480 # |
| 1481 # return_type PyrexType |
| 1482 # #filename string C name of filename string const |
| 1483 # entry Symtab.Entry |
| 1484 # needs_closure boolean Whether or not this function has inner fun
ctions/classes/yield |
| 1485 # needs_outer_scope boolean Whether or not this function requires oute
r scope |
| 1486 # pymethdef_required boolean Force Python method struct generation |
| 1487 # directive_locals { string : ExprNode } locals defined by cython.locals(..
.) |
| 1488 # directive_returns [ExprNode] type defined by cython.returns(...) |
| 1489 # star_arg PyArgDeclNode or None * argument |
| 1490 # starstar_arg PyArgDeclNode or None ** argument |
| 1491 |
| 1492 # has_fused_arguments boolean |
| 1493 # Whether this cdef function has fused parameters. This is needed |
| 1494 # by AnalyseDeclarationsTransform, so it can replace CFuncDefNodes |
| 1495 # with fused argument types with a FusedCFuncDefNode |
| 1496 |
| 1497 py_func = None |
| 1498 needs_closure = False |
| 1499 needs_outer_scope = False |
| 1500 pymethdef_required = False |
| 1501 is_generator = False |
| 1502 is_generator_body = False |
| 1503 modifiers = [] |
| 1504 has_fused_arguments = False |
| 1505 star_arg = None |
| 1506 starstar_arg = None |
| 1507 is_cyfunction = False |
| 1508 |
| 1509 def analyse_default_values(self, env): |
| 1510 default_seen = 0 |
| 1511 for arg in self.args: |
| 1512 if arg.default: |
| 1513 default_seen = 1 |
| 1514 if arg.is_generic: |
| 1515 arg.default = arg.default.analyse_types(env) |
| 1516 arg.default = arg.default.coerce_to(arg.type, env) |
| 1517 else: |
| 1518 error(arg.pos, |
| 1519 "This argument cannot have a default value") |
| 1520 arg.default = None |
| 1521 elif arg.kw_only: |
| 1522 default_seen = 1 |
| 1523 elif default_seen: |
| 1524 error(arg.pos, "Non-default argument following default argument"
) |
| 1525 |
| 1526 def align_argument_type(self, env, arg): |
| 1527 directive_locals = self.directive_locals |
| 1528 type = arg.type |
| 1529 if arg.name in directive_locals: |
| 1530 type_node = directive_locals[arg.name] |
| 1531 other_type = type_node.analyse_as_type(env) |
| 1532 if other_type is None: |
| 1533 error(type_node.pos, "Not a type") |
| 1534 elif (type is not PyrexTypes.py_object_type |
| 1535 and not type.same_as(other_type)): |
| 1536 error(arg.base_type.pos, "Signature does not agree with previous
declaration") |
| 1537 error(type_node.pos, "Previous declaration here") |
| 1538 else: |
| 1539 arg.type = other_type |
| 1540 return arg |
| 1541 |
| 1542 def need_gil_acquisition(self, lenv): |
| 1543 return 0 |
| 1544 |
| 1545 def create_local_scope(self, env): |
| 1546 genv = env |
| 1547 while genv.is_py_class_scope or genv.is_c_class_scope: |
| 1548 genv = genv.outer_scope |
| 1549 if self.needs_closure: |
| 1550 lenv = ClosureScope(name=self.entry.name, |
| 1551 outer_scope = genv, |
| 1552 parent_scope = env, |
| 1553 scope_name=self.entry.cname) |
| 1554 else: |
| 1555 lenv = LocalScope(name=self.entry.name, |
| 1556 outer_scope=genv, |
| 1557 parent_scope=env) |
| 1558 lenv.return_type = self.return_type |
| 1559 type = self.entry.type |
| 1560 if type.is_cfunction: |
| 1561 lenv.nogil = type.nogil and not type.with_gil |
| 1562 self.local_scope = lenv |
| 1563 lenv.directives = env.directives |
| 1564 return lenv |
| 1565 |
| 1566 def generate_function_body(self, env, code): |
| 1567 self.body.generate_execution_code(code) |
| 1568 |
| 1569 def generate_function_definitions(self, env, code): |
| 1570 import Buffer |
| 1571 if self.return_type.is_memoryviewslice: |
| 1572 import MemoryView |
| 1573 |
| 1574 lenv = self.local_scope |
| 1575 if lenv.is_closure_scope and not lenv.is_passthrough: |
| 1576 outer_scope_cname = "%s->%s" % (Naming.cur_scope_cname, |
| 1577 Naming.outer_scope_cname) |
| 1578 else: |
| 1579 outer_scope_cname = Naming.outer_scope_cname |
| 1580 lenv.mangle_closure_cnames(outer_scope_cname) |
| 1581 # Generate closure function definitions |
| 1582 self.body.generate_function_definitions(lenv, code) |
| 1583 # generate lambda function definitions |
| 1584 self.generate_lambda_definitions(lenv, code) |
| 1585 |
| 1586 is_getbuffer_slot = (self.entry.name == "__getbuffer__" and |
| 1587 self.entry.scope.is_c_class_scope) |
| 1588 is_releasebuffer_slot = (self.entry.name == "__releasebuffer__" and |
| 1589 self.entry.scope.is_c_class_scope) |
| 1590 is_buffer_slot = is_getbuffer_slot or is_releasebuffer_slot |
| 1591 if is_buffer_slot: |
| 1592 if 'cython_unused' not in self.modifiers: |
| 1593 self.modifiers = self.modifiers + ['cython_unused'] |
| 1594 |
| 1595 preprocessor_guard = self.get_preprocessor_guard() |
| 1596 |
| 1597 profile = code.globalstate.directives['profile'] |
| 1598 linetrace = code.globalstate.directives['linetrace'] |
| 1599 if (linetrace or profile) and lenv.nogil: |
| 1600 warning(self.pos, "Cannot profile nogil function.", 1) |
| 1601 profile = linetrace = False |
| 1602 if profile or linetrace: |
| 1603 code.globalstate.use_utility_code( |
| 1604 UtilityCode.load_cached("Profile", "Profile.c")) |
| 1605 |
| 1606 # Generate C code for header and body of function |
| 1607 code.enter_cfunc_scope() |
| 1608 code.return_from_error_cleanup_label = code.new_label() |
| 1609 |
| 1610 # ----- Top-level constants used by this function |
| 1611 code.mark_pos(self.pos) |
| 1612 self.generate_cached_builtins_decls(lenv, code) |
| 1613 # ----- Function header |
| 1614 code.putln("") |
| 1615 |
| 1616 if preprocessor_guard: |
| 1617 code.putln(preprocessor_guard) |
| 1618 |
| 1619 with_pymethdef = (self.needs_assignment_synthesis(env, code) or |
| 1620 self.pymethdef_required) |
| 1621 if self.py_func: |
| 1622 self.py_func.generate_function_header(code, |
| 1623 with_pymethdef = with_pymethdef, |
| 1624 proto_only=True) |
| 1625 self.generate_function_header(code, |
| 1626 with_pymethdef = with_pymethdef) |
| 1627 # ----- Local variable declarations |
| 1628 # Find function scope |
| 1629 cenv = env |
| 1630 while cenv.is_py_class_scope or cenv.is_c_class_scope: |
| 1631 cenv = cenv.outer_scope |
| 1632 if self.needs_closure: |
| 1633 code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cna
me)) |
| 1634 code.putln(";") |
| 1635 elif self.needs_outer_scope: |
| 1636 if lenv.is_passthrough: |
| 1637 code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope
_cname)) |
| 1638 code.putln(";") |
| 1639 code.put(cenv.scope_class.type.declaration_code(Naming.outer_scope_c
name)) |
| 1640 code.putln(";") |
| 1641 self.generate_argument_declarations(lenv, code) |
| 1642 |
| 1643 for entry in lenv.var_entries: |
| 1644 if not (entry.in_closure or entry.is_arg): |
| 1645 code.put_var_declaration(entry) |
| 1646 |
| 1647 # Initialize the return variable __pyx_r |
| 1648 init = "" |
| 1649 if not self.return_type.is_void: |
| 1650 if self.return_type.is_pyobject: |
| 1651 init = " = NULL" |
| 1652 elif self.return_type.is_memoryviewslice: |
| 1653 init = ' = ' + MemoryView.memslice_entry_init |
| 1654 |
| 1655 code.putln( |
| 1656 "%s%s;" % |
| 1657 (self.return_type.declaration_code(Naming.retval_cname), |
| 1658 init)) |
| 1659 |
| 1660 tempvardecl_code = code.insertion_point() |
| 1661 self.generate_keyword_list(code) |
| 1662 |
| 1663 if profile or linetrace: |
| 1664 code.put_trace_declarations() |
| 1665 |
| 1666 # ----- Extern library function declarations |
| 1667 lenv.generate_library_function_declarations(code) |
| 1668 |
| 1669 # ----- GIL acquisition |
| 1670 acquire_gil = self.acquire_gil |
| 1671 |
| 1672 # See if we need to acquire the GIL for variable declarations, or for |
| 1673 # refnanny only |
| 1674 |
| 1675 # Profiling or closures are not currently possible for cdef nogil |
| 1676 # functions, but check them anyway |
| 1677 have_object_args = (self.needs_closure or self.needs_outer_scope or |
| 1678 profile or linetrace) |
| 1679 for arg in lenv.arg_entries: |
| 1680 if arg.type.is_pyobject: |
| 1681 have_object_args = True |
| 1682 break |
| 1683 |
| 1684 acquire_gil_for_var_decls_only = ( |
| 1685 lenv.nogil and lenv.has_with_gil_block and |
| 1686 (have_object_args or lenv.buffer_entries)) |
| 1687 |
| 1688 acquire_gil_for_refnanny_only = ( |
| 1689 lenv.nogil and lenv.has_with_gil_block and not |
| 1690 acquire_gil_for_var_decls_only) |
| 1691 |
| 1692 use_refnanny = not lenv.nogil or lenv.has_with_gil_block |
| 1693 |
| 1694 if acquire_gil or acquire_gil_for_var_decls_only: |
| 1695 code.put_ensure_gil() |
| 1696 elif lenv.nogil and lenv.has_with_gil_block: |
| 1697 code.declare_gilstate() |
| 1698 |
| 1699 # ----- set up refnanny |
| 1700 if use_refnanny: |
| 1701 tempvardecl_code.put_declare_refcount_context() |
| 1702 code.put_setup_refcount_context( |
| 1703 self.entry.name, acquire_gil=acquire_gil_for_refnanny_only) |
| 1704 |
| 1705 # ----- Automatic lead-ins for certain special functions |
| 1706 if is_getbuffer_slot: |
| 1707 self.getbuffer_init(code) |
| 1708 # ----- Create closure scope object |
| 1709 if self.needs_closure: |
| 1710 tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__') |
| 1711 slot_func_cname = TypeSlots.get_slot_function(lenv.scope_class.type.
scope, tp_slot) |
| 1712 if not slot_func_cname: |
| 1713 slot_func_cname = '%s->tp_new' % lenv.scope_class.type.typeptr_c
name |
| 1714 code.putln("%s = (%s)%s(%s, %s, NULL);" % ( |
| 1715 Naming.cur_scope_cname, |
| 1716 lenv.scope_class.type.declaration_code(''), |
| 1717 slot_func_cname, |
| 1718 lenv.scope_class.type.typeptr_cname, |
| 1719 Naming.empty_tuple)) |
| 1720 code.putln("if (unlikely(!%s)) {" % Naming.cur_scope_cname) |
| 1721 if is_getbuffer_slot: |
| 1722 self.getbuffer_error_cleanup(code) |
| 1723 |
| 1724 if use_refnanny: |
| 1725 code.put_finish_refcount_context() |
| 1726 if acquire_gil or acquire_gil_for_var_decls_only: |
| 1727 code.put_release_ensured_gil() |
| 1728 |
| 1729 # FIXME: what if the error return value is a Python value? |
| 1730 code.putln("return %s;" % self.error_value()) |
| 1731 code.putln("}") |
| 1732 code.put_gotref(Naming.cur_scope_cname) |
| 1733 # Note that it is unsafe to decref the scope at this point. |
| 1734 if self.needs_outer_scope: |
| 1735 if self.is_cyfunction: |
| 1736 code.putln("%s = (%s) __Pyx_CyFunction_GetClosure(%s);" % ( |
| 1737 outer_scope_cname, |
| 1738 cenv.scope_class.type.declaration_code(''), |
| 1739 Naming.self_cname)) |
| 1740 else: |
| 1741 code.putln("%s = (%s) %s;" % ( |
| 1742 outer_scope_cname, |
| 1743 cenv.scope_class.type.declaration_code(''), |
| 1744 Naming.self_cname)) |
| 1745 if lenv.is_passthrough: |
| 1746 code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cna
me)) |
| 1747 elif self.needs_closure: |
| 1748 # inner closures own a reference to their outer parent |
| 1749 code.put_incref(outer_scope_cname, cenv.scope_class.type) |
| 1750 code.put_giveref(outer_scope_cname) |
| 1751 # ----- Trace function call |
| 1752 if profile or linetrace: |
| 1753 # this looks a bit late, but if we don't get here due to a |
| 1754 # fatal error before hand, it's not really worth tracing |
| 1755 code.put_trace_call(self.entry.name, self.pos) |
| 1756 code.funcstate.can_trace = True |
| 1757 # ----- Fetch arguments |
| 1758 self.generate_argument_parsing_code(env, code) |
| 1759 # If an argument is assigned to in the body, we must |
| 1760 # incref it to properly keep track of refcounts. |
| 1761 is_cdef = isinstance(self, CFuncDefNode) |
| 1762 for entry in lenv.arg_entries: |
| 1763 if entry.type.is_pyobject: |
| 1764 if ((acquire_gil or len(entry.cf_assignments) > 1) and |
| 1765 not entry.in_closure): |
| 1766 code.put_var_incref(entry) |
| 1767 |
| 1768 # Note: defaults are always incref-ed. For def functions, we |
| 1769 # we aquire arguments from object converstion, so we have |
| 1770 # new references. If we are a cdef function, we need to |
| 1771 # incref our arguments |
| 1772 elif (is_cdef and entry.type.is_memoryviewslice and |
| 1773 len(entry.cf_assignments) > 1): |
| 1774 code.put_incref_memoryviewslice(entry.cname, |
| 1775 have_gil=not lenv.nogil) |
| 1776 for entry in lenv.var_entries: |
| 1777 if entry.is_arg and len(entry.cf_assignments) > 1: |
| 1778 code.put_var_incref(entry) |
| 1779 |
| 1780 # ----- Initialise local buffer auxiliary variables |
| 1781 for entry in lenv.var_entries + lenv.arg_entries: |
| 1782 if entry.type.is_buffer and entry.buffer_aux.buflocal_nd_var.used: |
| 1783 Buffer.put_init_vars(entry, code) |
| 1784 |
| 1785 # ----- Check and convert arguments |
| 1786 self.generate_argument_type_tests(code) |
| 1787 # ----- Acquire buffer arguments |
| 1788 for entry in lenv.arg_entries: |
| 1789 if entry.type.is_buffer: |
| 1790 Buffer.put_acquire_arg_buffer(entry, code, self.pos) |
| 1791 |
| 1792 if acquire_gil_for_var_decls_only: |
| 1793 code.put_release_ensured_gil() |
| 1794 |
| 1795 # ------------------------- |
| 1796 # ----- Function body ----- |
| 1797 # ------------------------- |
| 1798 self.generate_function_body(env, code) |
| 1799 |
| 1800 code.mark_pos(self.pos) |
| 1801 code.putln("") |
| 1802 code.putln("/* function exit code */") |
| 1803 |
| 1804 # ----- Default return value |
| 1805 if not self.body.is_terminator: |
| 1806 if self.return_type.is_pyobject: |
| 1807 #if self.return_type.is_extension_type: |
| 1808 # lhs = "(PyObject *)%s" % Naming.retval_cname |
| 1809 #else: |
| 1810 lhs = Naming.retval_cname |
| 1811 code.put_init_to_py_none(lhs, self.return_type) |
| 1812 else: |
| 1813 val = self.return_type.default_value |
| 1814 if val: |
| 1815 code.putln("%s = %s;" % (Naming.retval_cname, val)) |
| 1816 # ----- Error cleanup |
| 1817 if code.error_label in code.labels_used: |
| 1818 if not self.body.is_terminator: |
| 1819 code.put_goto(code.return_label) |
| 1820 code.put_label(code.error_label) |
| 1821 for cname, type in code.funcstate.all_managed_temps(): |
| 1822 code.put_xdecref(cname, type, have_gil=not lenv.nogil) |
| 1823 |
| 1824 # Clean up buffers -- this calls a Python function |
| 1825 # so need to save and restore error state |
| 1826 buffers_present = len(lenv.buffer_entries) > 0 |
| 1827 memslice_entries = [e for e in lenv.entries.itervalues() |
| 1828 if e.type.is_memoryviewslice] |
| 1829 if buffers_present: |
| 1830 code.globalstate.use_utility_code(restore_exception_utility_code
) |
| 1831 code.putln("{ PyObject *__pyx_type, *__pyx_value, *__pyx_tb;") |
| 1832 code.putln("__Pyx_ErrFetch(&__pyx_type, &__pyx_value, &__pyx_tb)
;") |
| 1833 for entry in lenv.buffer_entries: |
| 1834 Buffer.put_release_buffer_code(code, entry) |
| 1835 #code.putln("%s = 0;" % entry.cname) |
| 1836 code.putln("__Pyx_ErrRestore(__pyx_type, __pyx_value, __pyx_tb);
}") |
| 1837 |
| 1838 if self.return_type.is_memoryviewslice: |
| 1839 MemoryView.put_init_entry(Naming.retval_cname, code) |
| 1840 err_val = Naming.retval_cname |
| 1841 else: |
| 1842 err_val = self.error_value() |
| 1843 |
| 1844 exc_check = self.caller_will_check_exceptions() |
| 1845 if err_val is not None or exc_check: |
| 1846 # TODO: Fix exception tracing (though currently unused by cProfi
le). |
| 1847 # code.globalstate.use_utility_code(get_exception_tuple_utility_
code) |
| 1848 # code.put_trace_exception() |
| 1849 |
| 1850 if lenv.nogil and not lenv.has_with_gil_block: |
| 1851 code.putln("{") |
| 1852 code.put_ensure_gil() |
| 1853 |
| 1854 code.put_add_traceback(self.entry.qualified_name) |
| 1855 |
| 1856 if lenv.nogil and not lenv.has_with_gil_block: |
| 1857 code.put_release_ensured_gil() |
| 1858 code.putln("}") |
| 1859 else: |
| 1860 warning(self.entry.pos, |
| 1861 "Unraisable exception in function '%s'." % |
| 1862 self.entry.qualified_name, 0) |
| 1863 code.put_unraisable(self.entry.qualified_name) |
| 1864 default_retval = self.return_type.default_value |
| 1865 if err_val is None and default_retval: |
| 1866 err_val = default_retval |
| 1867 if err_val is not None: |
| 1868 code.putln("%s = %s;" % (Naming.retval_cname, err_val)) |
| 1869 |
| 1870 if is_getbuffer_slot: |
| 1871 self.getbuffer_error_cleanup(code) |
| 1872 |
| 1873 # If we are using the non-error cleanup section we should |
| 1874 # jump past it if we have an error. The if-test below determine |
| 1875 # whether this section is used. |
| 1876 if buffers_present or is_getbuffer_slot or self.return_type.is_memor
yviewslice: |
| 1877 code.put_goto(code.return_from_error_cleanup_label) |
| 1878 |
| 1879 # ----- Non-error return cleanup |
| 1880 code.put_label(code.return_label) |
| 1881 for entry in lenv.buffer_entries: |
| 1882 if entry.used: |
| 1883 Buffer.put_release_buffer_code(code, entry) |
| 1884 if is_getbuffer_slot: |
| 1885 self.getbuffer_normal_cleanup(code) |
| 1886 |
| 1887 if self.return_type.is_memoryviewslice: |
| 1888 # See if our return value is uninitialized on non-error return |
| 1889 # import MemoryView |
| 1890 # MemoryView.err_if_nogil_initialized_check(self.pos, env) |
| 1891 cond = code.unlikely(self.return_type.error_condition( |
| 1892 Naming.retval_cname)) |
| 1893 code.putln( |
| 1894 'if (%s) {' % cond) |
| 1895 if env.nogil: |
| 1896 code.put_ensure_gil() |
| 1897 code.putln( |
| 1898 'PyErr_SetString(' |
| 1899 'PyExc_TypeError,' |
| 1900 '"Memoryview return value is not initialized");') |
| 1901 if env.nogil: |
| 1902 code.put_release_ensured_gil() |
| 1903 code.putln( |
| 1904 '}') |
| 1905 |
| 1906 # ----- Return cleanup for both error and no-error return |
| 1907 code.put_label(code.return_from_error_cleanup_label) |
| 1908 |
| 1909 for entry in lenv.var_entries: |
| 1910 if not entry.used or entry.in_closure: |
| 1911 continue |
| 1912 |
| 1913 if entry.type.is_memoryviewslice: |
| 1914 code.put_xdecref_memoryviewslice(entry.cname, |
| 1915 have_gil=not lenv.nogil) |
| 1916 elif entry.type.is_pyobject: |
| 1917 if not entry.is_arg or len(entry.cf_assignments) > 1: |
| 1918 code.put_var_decref(entry) |
| 1919 |
| 1920 # Decref any increfed args |
| 1921 for entry in lenv.arg_entries: |
| 1922 if entry.type.is_pyobject: |
| 1923 if ((acquire_gil or len(entry.cf_assignments) > 1) and |
| 1924 not entry.in_closure): |
| 1925 code.put_var_decref(entry) |
| 1926 elif (entry.type.is_memoryviewslice and |
| 1927 (not is_cdef or len(entry.cf_assignments) > 1)): |
| 1928 # decref slices of def functions and acquired slices from cdef |
| 1929 # functions, but not borrowed slices from cdef functions. |
| 1930 code.put_xdecref_memoryviewslice(entry.cname, |
| 1931 have_gil=not lenv.nogil) |
| 1932 if self.needs_closure: |
| 1933 code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type) |
| 1934 |
| 1935 # ----- Return |
| 1936 # This code is duplicated in ModuleNode.generate_module_init_func |
| 1937 if not lenv.nogil: |
| 1938 default_retval = self.return_type.default_value |
| 1939 err_val = self.error_value() |
| 1940 if err_val is None and default_retval: |
| 1941 err_val = default_retval # FIXME: why is err_val not used? |
| 1942 if self.return_type.is_pyobject: |
| 1943 code.put_xgiveref(self.return_type.as_pyobject(Naming.retval_cna
me)) |
| 1944 |
| 1945 if self.entry.is_special and self.entry.name == "__hash__": |
| 1946 # Returning -1 for __hash__ is supposed to signal an error |
| 1947 # We do as Python instances and coerce -1 into -2. |
| 1948 code.putln("if (unlikely(%s == -1) && !PyErr_Occurred()) %s = -2;" %
( |
| 1949 Naming.retval_cname, Naming.retval_cname)) |
| 1950 |
| 1951 if profile or linetrace: |
| 1952 code.funcstate.can_trace = False |
| 1953 if self.return_type.is_pyobject: |
| 1954 code.put_trace_return(Naming.retval_cname) |
| 1955 else: |
| 1956 code.put_trace_return("Py_None") |
| 1957 |
| 1958 if not lenv.nogil: |
| 1959 # GIL holding function |
| 1960 code.put_finish_refcount_context() |
| 1961 |
| 1962 if acquire_gil or (lenv.nogil and lenv.has_with_gil_block): |
| 1963 # release the GIL (note that with-gil blocks acquire it on exit in t
heir EnsureGILNode) |
| 1964 code.put_release_ensured_gil() |
| 1965 |
| 1966 if not self.return_type.is_void: |
| 1967 code.putln("return %s;" % Naming.retval_cname) |
| 1968 |
| 1969 code.putln("}") |
| 1970 |
| 1971 if preprocessor_guard: |
| 1972 code.putln("#endif /*!(%s)*/" % preprocessor_guard) |
| 1973 |
| 1974 # ----- Go back and insert temp variable declarations |
| 1975 tempvardecl_code.put_temp_declarations(code.funcstate) |
| 1976 |
| 1977 # ----- Python version |
| 1978 code.exit_cfunc_scope() |
| 1979 if self.py_func: |
| 1980 self.py_func.generate_function_definitions(env, code) |
| 1981 self.generate_wrapper_functions(code) |
| 1982 |
| 1983 def declare_argument(self, env, arg): |
| 1984 if arg.type.is_void: |
| 1985 error(arg.pos, "Invalid use of 'void'") |
| 1986 elif not arg.type.is_complete() and not (arg.type.is_array or arg.type.i
s_memoryviewslice): |
| 1987 error(arg.pos, |
| 1988 "Argument type '%s' is incomplete" % arg.type) |
| 1989 return env.declare_arg(arg.name, arg.type, arg.pos) |
| 1990 |
| 1991 def generate_arg_type_test(self, arg, code): |
| 1992 # Generate type test for one argument. |
| 1993 if arg.type.typeobj_is_available(): |
| 1994 code.globalstate.use_utility_code( |
| 1995 UtilityCode.load_cached("ArgTypeTest", "FunctionArguments.c")) |
| 1996 typeptr_cname = arg.type.typeptr_cname |
| 1997 arg_code = "((PyObject *)%s)" % arg.entry.cname |
| 1998 code.putln( |
| 1999 'if (unlikely(!__Pyx_ArgTypeTest(%s, %s, %d, "%s", %s))) %s' % ( |
| 2000 arg_code, |
| 2001 typeptr_cname, |
| 2002 arg.accept_none, |
| 2003 arg.name, |
| 2004 arg.type.is_builtin_type, |
| 2005 code.error_goto(arg.pos))) |
| 2006 else: |
| 2007 error(arg.pos, "Cannot test type of extern C class " |
| 2008 "without type object name specification") |
| 2009 |
| 2010 def generate_arg_none_check(self, arg, code): |
| 2011 # Generate None check for one argument. |
| 2012 if arg.type.is_memoryviewslice: |
| 2013 cname = "%s.memview" % arg.entry.cname |
| 2014 else: |
| 2015 cname = arg.entry.cname |
| 2016 |
| 2017 code.putln('if (unlikely(((PyObject *)%s) == Py_None)) {' % cname) |
| 2018 code.putln('''PyErr_Format(PyExc_TypeError, "Argument '%%.%ds' must not
be None", "%s"); %s''' % ( |
| 2019 max(200, len(arg.name)), arg.name, |
| 2020 code.error_goto(arg.pos))) |
| 2021 code.putln('}') |
| 2022 |
| 2023 def generate_wrapper_functions(self, code): |
| 2024 pass |
| 2025 |
| 2026 def generate_execution_code(self, code): |
| 2027 # Evaluate and store argument default values |
| 2028 for arg in self.args: |
| 2029 if not arg.is_dynamic: |
| 2030 arg.generate_assignment_code(code) |
| 2031 |
| 2032 # |
| 2033 # Special code for the __getbuffer__ function |
| 2034 # |
| 2035 def getbuffer_init(self, code): |
| 2036 info = self.local_scope.arg_entries[1].cname |
| 2037 # Python 3.0 betas have a bug in memoryview which makes it call |
| 2038 # getbuffer with a NULL parameter. For now we work around this; |
| 2039 # the following block should be removed when this bug is fixed. |
| 2040 code.putln("if (%s != NULL) {" % info) |
| 2041 code.putln("%s->obj = Py_None; __Pyx_INCREF(Py_None);" % info) |
| 2042 code.put_giveref("%s->obj" % info) # Do not refnanny object within struc
ts |
| 2043 code.putln("}") |
| 2044 |
| 2045 def getbuffer_error_cleanup(self, code): |
| 2046 info = self.local_scope.arg_entries[1].cname |
| 2047 code.putln("if (%s != NULL && %s->obj != NULL) {" |
| 2048 % (info, info)) |
| 2049 code.put_gotref("%s->obj" % info) |
| 2050 code.putln("__Pyx_DECREF(%s->obj); %s->obj = NULL;" |
| 2051 % (info, info)) |
| 2052 code.putln("}") |
| 2053 |
| 2054 def getbuffer_normal_cleanup(self, code): |
| 2055 info = self.local_scope.arg_entries[1].cname |
| 2056 code.putln("if (%s != NULL && %s->obj == Py_None) {" % (info, info)) |
| 2057 code.put_gotref("Py_None") |
| 2058 code.putln("__Pyx_DECREF(Py_None); %s->obj = NULL;" % info) |
| 2059 code.putln("}") |
| 2060 |
| 2061 def get_preprocessor_guard(self): |
| 2062 if not self.entry.is_special: |
| 2063 return None |
| 2064 name = self.entry.name |
| 2065 slot = TypeSlots.method_name_to_slot.get(name) |
| 2066 if not slot: |
| 2067 return None |
| 2068 if name == '__long__' and not self.entry.scope.lookup_here('__int__'): |
| 2069 return None |
| 2070 if name in ("__getbuffer__", "__releasebuffer__") and self.entry.scope.i
s_c_class_scope: |
| 2071 return None |
| 2072 return slot.preprocessor_guard_code() |
| 2073 |
| 2074 |
| 2075 class CFuncDefNode(FuncDefNode): |
| 2076 # C function definition. |
| 2077 # |
| 2078 # modifiers ['inline'] |
| 2079 # visibility 'private' or 'public' or 'extern' |
| 2080 # base_type CBaseTypeNode |
| 2081 # declarator CDeclaratorNode |
| 2082 # cfunc_declarator the CFuncDeclarator of this function |
| 2083 # (this is also available through declarator or a |
| 2084 # base thereof) |
| 2085 # body StatListNode |
| 2086 # api boolean |
| 2087 # decorators [DecoratorNode] list of decorators |
| 2088 # |
| 2089 # with_gil boolean Acquire GIL around body |
| 2090 # type CFuncType |
| 2091 # py_func wrapper for calling from Python |
| 2092 # overridable whether or not this is a cpdef function |
| 2093 # inline_in_pxd whether this is an inline function in a pxd file |
| 2094 # template_declaration String or None Used for c++ class methods |
| 2095 # is_const_method whether this is a const method |
| 2096 |
| 2097 child_attrs = ["base_type", "declarator", "body", "py_func"] |
| 2098 |
| 2099 inline_in_pxd = False |
| 2100 decorators = None |
| 2101 directive_locals = None |
| 2102 directive_returns = None |
| 2103 override = None |
| 2104 template_declaration = None |
| 2105 is_const_method = False |
| 2106 |
| 2107 def unqualified_name(self): |
| 2108 return self.entry.name |
| 2109 |
| 2110 def analyse_declarations(self, env): |
| 2111 if self.directive_locals is None: |
| 2112 self.directive_locals = {} |
| 2113 self.directive_locals.update(env.directives['locals']) |
| 2114 if self.directive_returns is not None: |
| 2115 base_type = self.directive_returns.analyse_as_type(env) |
| 2116 if base_type is None: |
| 2117 error(self.directive_returns.pos, "Not a type") |
| 2118 base_type = PyrexTypes.error_type |
| 2119 else: |
| 2120 base_type = self.base_type.analyse(env) |
| 2121 # The 2 here is because we need both function and argument names. |
| 2122 if isinstance(self.declarator, CFuncDeclaratorNode): |
| 2123 name_declarator, type = self.declarator.analyse(base_type, env, |
| 2124 nonempty = 2 * (self
.body is not None), |
| 2125 directive_locals = s
elf.directive_locals) |
| 2126 else: |
| 2127 name_declarator, type = self.declarator.analyse(base_type, env, none
mpty = 2 * (self.body is not None)) |
| 2128 if not type.is_cfunction: |
| 2129 error(self.pos, |
| 2130 "Suite attached to non-function declaration") |
| 2131 # Remember the actual type according to the function header |
| 2132 # written here, because the type in the symbol table entry |
| 2133 # may be different if we're overriding a C method inherited |
| 2134 # from the base type of an extension type. |
| 2135 self.type = type |
| 2136 type.is_overridable = self.overridable |
| 2137 declarator = self.declarator |
| 2138 while not hasattr(declarator, 'args'): |
| 2139 declarator = declarator.base |
| 2140 |
| 2141 self.cfunc_declarator = declarator |
| 2142 self.args = declarator.args |
| 2143 |
| 2144 opt_arg_count = self.cfunc_declarator.optional_arg_count |
| 2145 if (self.visibility == 'public' or self.api) and opt_arg_count: |
| 2146 error(self.cfunc_declarator.pos, |
| 2147 "Function with optional arguments may not be declared " |
| 2148 "public or api") |
| 2149 |
| 2150 if (type.exception_check == '+' and self.visibility != 'extern'): |
| 2151 warning(self.cfunc_declarator.pos, |
| 2152 "Only extern functions can throw C++ exceptions.") |
| 2153 |
| 2154 for formal_arg, type_arg in zip(self.args, type.args): |
| 2155 self.align_argument_type(env, type_arg) |
| 2156 formal_arg.type = type_arg.type |
| 2157 formal_arg.name = type_arg.name |
| 2158 formal_arg.cname = type_arg.cname |
| 2159 |
| 2160 self._validate_type_visibility(type_arg.type, type_arg.pos, env) |
| 2161 |
| 2162 if type_arg.type.is_fused: |
| 2163 self.has_fused_arguments = True |
| 2164 |
| 2165 if type_arg.type.is_buffer and 'inline' in self.modifiers: |
| 2166 warning(formal_arg.pos, "Buffer unpacking not optimized away.",
1) |
| 2167 |
| 2168 if type_arg.type.is_buffer: |
| 2169 if self.type.nogil: |
| 2170 error(formal_arg.pos, |
| 2171 "Buffer may not be acquired without the GIL. " |
| 2172 "Consider using memoryview slices instead.") |
| 2173 elif 'inline' in self.modifiers: |
| 2174 warning(formal_arg.pos, "Buffer unpacking not optimized away
.", 1) |
| 2175 |
| 2176 self._validate_type_visibility(type.return_type, self.pos, env) |
| 2177 |
| 2178 name = name_declarator.name |
| 2179 cname = name_declarator.cname |
| 2180 |
| 2181 type.is_const_method = self.is_const_method |
| 2182 self.entry = env.declare_cfunction( |
| 2183 name, type, self.pos, |
| 2184 cname = cname, visibility = self.visibility, api = self.api, |
| 2185 defining = self.body is not None, modifiers = self.modifiers) |
| 2186 self.entry.inline_func_in_pxd = self.inline_in_pxd |
| 2187 self.return_type = type.return_type |
| 2188 if self.return_type.is_array and self.visibility != 'extern': |
| 2189 error(self.pos, |
| 2190 "Function cannot return an array") |
| 2191 if self.return_type.is_cpp_class: |
| 2192 self.return_type.check_nullary_constructor(self.pos, "used as a retu
rn value") |
| 2193 |
| 2194 if self.overridable and not env.is_module_scope: |
| 2195 if len(self.args) < 1 or not self.args[0].type.is_pyobject: |
| 2196 # An error will be produced in the cdef function |
| 2197 self.overridable = False |
| 2198 |
| 2199 self.declare_cpdef_wrapper(env) |
| 2200 self.create_local_scope(env) |
| 2201 |
| 2202 def declare_cpdef_wrapper(self, env): |
| 2203 if self.overridable: |
| 2204 name = self.entry.name |
| 2205 py_func_body = self.call_self_node(is_module_scope = env.is_module_s
cope) |
| 2206 self.py_func = DefNode(pos = self.pos, |
| 2207 name = self.entry.name, |
| 2208 args = self.args, |
| 2209 star_arg = None, |
| 2210 starstar_arg = None, |
| 2211 doc = self.doc, |
| 2212 body = py_func_body, |
| 2213 is_wrapper = 1) |
| 2214 self.py_func.is_module_scope = env.is_module_scope |
| 2215 self.py_func.analyse_declarations(env) |
| 2216 self.entry.as_variable = self.py_func.entry |
| 2217 self.entry.used = self.entry.as_variable.used = True |
| 2218 # Reset scope entry the above cfunction |
| 2219 env.entries[name] = self.entry |
| 2220 if (not self.entry.is_final_cmethod and |
| 2221 (not env.is_module_scope or Options.lookup_module_cpdef)): |
| 2222 self.override = OverrideCheckNode(self.pos, py_func = self.py_fu
nc) |
| 2223 self.body = StatListNode(self.pos, stats=[self.override, self.bo
dy]) |
| 2224 |
| 2225 def _validate_type_visibility(self, type, pos, env): |
| 2226 """ |
| 2227 Ensure that types used in cdef functions are public or api, or |
| 2228 defined in a C header. |
| 2229 """ |
| 2230 public_or_api = (self.visibility == 'public' or self.api) |
| 2231 entry = getattr(type, 'entry', None) |
| 2232 if public_or_api and entry and env.is_module_scope: |
| 2233 if not (entry.visibility in ('public', 'extern') or |
| 2234 entry.api or entry.in_cinclude): |
| 2235 error(pos, "Function declared public or api may not have " |
| 2236 "private types") |
| 2237 |
| 2238 def call_self_node(self, omit_optional_args=0, is_module_scope=0): |
| 2239 import ExprNodes |
| 2240 args = self.type.args |
| 2241 if omit_optional_args: |
| 2242 args = args[:len(args) - self.type.optional_arg_count] |
| 2243 arg_names = [arg.name for arg in args] |
| 2244 if is_module_scope: |
| 2245 cfunc = ExprNodes.NameNode(self.pos, name=self.entry.name) |
| 2246 else: |
| 2247 self_arg = ExprNodes.NameNode(self.pos, name=arg_names[0]) |
| 2248 cfunc = ExprNodes.AttributeNode(self.pos, obj=self_arg, attribute=se
lf.entry.name) |
| 2249 skip_dispatch = not is_module_scope or Options.lookup_module_cpdef |
| 2250 c_call = ExprNodes.SimpleCallNode(self.pos, function=cfunc, args=[ExprNo
des.NameNode(self.pos, name=n) for n in arg_names[1-is_module_scope:]], wrapper_
call=skip_dispatch) |
| 2251 return ReturnStatNode(pos=self.pos, return_type=PyrexTypes.py_object_typ
e, value=c_call) |
| 2252 |
| 2253 def declare_arguments(self, env): |
| 2254 for arg in self.type.args: |
| 2255 if not arg.name: |
| 2256 error(arg.pos, "Missing argument name") |
| 2257 self.declare_argument(env, arg) |
| 2258 |
| 2259 def need_gil_acquisition(self, lenv): |
| 2260 return self.type.with_gil |
| 2261 |
| 2262 def nogil_check(self, env): |
| 2263 type = self.type |
| 2264 with_gil = type.with_gil |
| 2265 if type.nogil and not with_gil: |
| 2266 if type.return_type.is_pyobject: |
| 2267 error(self.pos, |
| 2268 "Function with Python return type cannot be declared nogil
") |
| 2269 for entry in self.local_scope.var_entries: |
| 2270 if entry.type.is_pyobject and not entry.in_with_gil_block: |
| 2271 error(self.pos, "Function declared nogil has Python locals o
r temporaries") |
| 2272 |
| 2273 def analyse_expressions(self, env): |
| 2274 self.local_scope.directives = env.directives |
| 2275 if self.py_func is not None: |
| 2276 # this will also analyse the default values |
| 2277 self.py_func = self.py_func.analyse_expressions(env) |
| 2278 else: |
| 2279 self.analyse_default_values(env) |
| 2280 self.acquire_gil = self.need_gil_acquisition(self.local_scope) |
| 2281 return self |
| 2282 |
| 2283 def needs_assignment_synthesis(self, env, code=None): |
| 2284 return False |
| 2285 |
| 2286 def generate_function_header(self, code, with_pymethdef, with_opt_args = 1,
with_dispatch = 1, cname = None): |
| 2287 scope = self.local_scope |
| 2288 arg_decls = [] |
| 2289 type = self.type |
| 2290 for arg in type.args[:len(type.args)-type.optional_arg_count]: |
| 2291 arg_decl = arg.declaration_code() |
| 2292 entry = scope.lookup(arg.name) |
| 2293 if not entry.cf_used: |
| 2294 arg_decl = 'CYTHON_UNUSED %s' % arg_decl |
| 2295 arg_decls.append(arg_decl) |
| 2296 if with_dispatch and self.overridable: |
| 2297 dispatch_arg = PyrexTypes.c_int_type.declaration_code( |
| 2298 Naming.skip_dispatch_cname) |
| 2299 if self.override: |
| 2300 arg_decls.append(dispatch_arg) |
| 2301 else: |
| 2302 arg_decls.append('CYTHON_UNUSED %s' % dispatch_arg) |
| 2303 if type.optional_arg_count and with_opt_args: |
| 2304 arg_decls.append(type.op_arg_struct.declaration_code(Naming.optional
_args_cname)) |
| 2305 if type.has_varargs: |
| 2306 arg_decls.append("...") |
| 2307 if not arg_decls: |
| 2308 arg_decls = ["void"] |
| 2309 if cname is None: |
| 2310 cname = self.entry.func_cname |
| 2311 entity = type.function_header_code(cname, ', '.join(arg_decls)) |
| 2312 if self.entry.visibility == 'private' and '::' not in cname: |
| 2313 storage_class = "static " |
| 2314 else: |
| 2315 storage_class = "" |
| 2316 dll_linkage = None |
| 2317 modifiers = code.build_function_modifiers(self.entry.func_modifiers) |
| 2318 |
| 2319 header = self.return_type.declaration_code(entity, dll_linkage=dll_linka
ge) |
| 2320 #print (storage_class, modifiers, header) |
| 2321 if self.template_declaration: |
| 2322 code.putln(self.template_declaration) |
| 2323 code.putln("%s%s%s {" % (storage_class, modifiers, header)) |
| 2324 |
| 2325 def generate_argument_declarations(self, env, code): |
| 2326 scope = self.local_scope |
| 2327 for arg in self.args: |
| 2328 if arg.default: |
| 2329 entry = scope.lookup(arg.name) |
| 2330 if self.override or entry.cf_used: |
| 2331 result = arg.calculate_default_value_code(code) |
| 2332 code.putln('%s = %s;' % ( |
| 2333 arg.type.declaration_code(arg.cname), result)) |
| 2334 |
| 2335 def generate_keyword_list(self, code): |
| 2336 pass |
| 2337 |
| 2338 def generate_argument_parsing_code(self, env, code): |
| 2339 i = 0 |
| 2340 used = 0 |
| 2341 if self.type.optional_arg_count: |
| 2342 scope = self.local_scope |
| 2343 code.putln('if (%s) {' % Naming.optional_args_cname) |
| 2344 for arg in self.args: |
| 2345 if arg.default: |
| 2346 entry = scope.lookup(arg.name) |
| 2347 if self.override or entry.cf_used: |
| 2348 code.putln('if (%s->%sn > %s) {' % |
| 2349 (Naming.optional_args_cname, |
| 2350 Naming.pyrex_prefix, i)) |
| 2351 declarator = arg.declarator |
| 2352 while not hasattr(declarator, 'name'): |
| 2353 declarator = declarator.base |
| 2354 code.putln('%s = %s->%s;' % |
| 2355 (arg.cname, Naming.optional_args_cname, |
| 2356 self.type.opt_arg_cname(declarator.name))) |
| 2357 used += 1 |
| 2358 i += 1 |
| 2359 for _ in range(used): |
| 2360 code.putln('}') |
| 2361 code.putln('}') |
| 2362 |
| 2363 def generate_argument_conversion_code(self, code): |
| 2364 pass |
| 2365 |
| 2366 def generate_argument_type_tests(self, code): |
| 2367 # Generate type tests for args whose type in a parent |
| 2368 # class is a supertype of the declared type. |
| 2369 for arg in self.type.args: |
| 2370 if arg.needs_type_test: |
| 2371 self.generate_arg_type_test(arg, code) |
| 2372 elif arg.type.is_pyobject and not arg.accept_none: |
| 2373 self.generate_arg_none_check(arg, code) |
| 2374 |
| 2375 def error_value(self): |
| 2376 if self.return_type.is_pyobject: |
| 2377 return "0" |
| 2378 else: |
| 2379 #return None |
| 2380 return self.entry.type.exception_value |
| 2381 |
| 2382 def caller_will_check_exceptions(self): |
| 2383 return self.entry.type.exception_check |
| 2384 |
| 2385 def generate_wrapper_functions(self, code): |
| 2386 # If the C signature of a function has changed, we need to generate |
| 2387 # wrappers to put in the slots here. |
| 2388 k = 0 |
| 2389 entry = self.entry |
| 2390 func_type = entry.type |
| 2391 while entry.prev_entry is not None: |
| 2392 k += 1 |
| 2393 entry = entry.prev_entry |
| 2394 entry.func_cname = "%s%swrap_%s" % (self.entry.func_cname, Naming.py
rex_prefix, k) |
| 2395 code.putln() |
| 2396 self.generate_function_header(code, |
| 2397 0, |
| 2398 with_dispatch = entry.type.is_overrida
ble, |
| 2399 with_opt_args = entry.type.optional_ar
g_count, |
| 2400 cname = entry.func_cname) |
| 2401 if not self.return_type.is_void: |
| 2402 code.put('return ') |
| 2403 args = self.type.args |
| 2404 arglist = [arg.cname for arg in args[:len(args)-self.type.optional_a
rg_count]] |
| 2405 if entry.type.is_overridable: |
| 2406 arglist.append(Naming.skip_dispatch_cname) |
| 2407 elif func_type.is_overridable: |
| 2408 arglist.append('0') |
| 2409 if entry.type.optional_arg_count: |
| 2410 arglist.append(Naming.optional_args_cname) |
| 2411 elif func_type.optional_arg_count: |
| 2412 arglist.append('NULL') |
| 2413 code.putln('%s(%s);' % (self.entry.func_cname, ', '.join(arglist))) |
| 2414 code.putln('}') |
| 2415 |
| 2416 |
| 2417 class PyArgDeclNode(Node): |
| 2418 # Argument which must be a Python object (used |
| 2419 # for * and ** arguments). |
| 2420 # |
| 2421 # name string |
| 2422 # entry Symtab.Entry |
| 2423 # annotation ExprNode or None Py3 argument annotation |
| 2424 child_attrs = [] |
| 2425 is_self_arg = False |
| 2426 is_type_arg = False |
| 2427 |
| 2428 def generate_function_definitions(self, env, code): |
| 2429 self.entry.generate_function_definitions(env, code) |
| 2430 |
| 2431 class DecoratorNode(Node): |
| 2432 # A decorator |
| 2433 # |
| 2434 # decorator NameNode or CallNode or AttributeNode |
| 2435 child_attrs = ['decorator'] |
| 2436 |
| 2437 |
| 2438 class DefNode(FuncDefNode): |
| 2439 # A Python function definition. |
| 2440 # |
| 2441 # name string the Python name of the function |
| 2442 # lambda_name string the internal name of a lambda 'functi
on' |
| 2443 # decorators [DecoratorNode] list of decorators |
| 2444 # args [CArgDeclNode] formal arguments |
| 2445 # doc EncodedString or None |
| 2446 # body StatListNode |
| 2447 # return_type_annotation |
| 2448 # ExprNode or None the Py3 return type annotation |
| 2449 # |
| 2450 # The following subnode is constructed internally |
| 2451 # when the def statement is inside a Python class definition. |
| 2452 # |
| 2453 # fused_py_func DefNode The original fused cpdef DefNode |
| 2454 # (in case this is a specialization) |
| 2455 # specialized_cpdefs [DefNode] list of specialized cpdef DefNodes |
| 2456 # py_cfunc_node PyCFunctionNode/InnerFunctionNode The PyCFunction to cre
ate and assign |
| 2457 # |
| 2458 # decorator_indirection IndirectionNode Used to remove __Pyx_Method_ClassMet
hod for fused functions |
| 2459 |
| 2460 child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"] |
| 2461 |
| 2462 lambda_name = None |
| 2463 reqd_kw_flags_cname = "0" |
| 2464 is_wrapper = 0 |
| 2465 no_assignment_synthesis = 0 |
| 2466 decorators = None |
| 2467 return_type_annotation = None |
| 2468 entry = None |
| 2469 acquire_gil = 0 |
| 2470 self_in_stararg = 0 |
| 2471 py_cfunc_node = None |
| 2472 requires_classobj = False |
| 2473 defaults_struct = None # Dynamic kwrds structure name |
| 2474 doc = None |
| 2475 |
| 2476 fused_py_func = False |
| 2477 specialized_cpdefs = None |
| 2478 py_wrapper = None |
| 2479 py_wrapper_required = True |
| 2480 func_cname = None |
| 2481 |
| 2482 defaults_getter = None |
| 2483 |
| 2484 def __init__(self, pos, **kwds): |
| 2485 FuncDefNode.__init__(self, pos, **kwds) |
| 2486 k = rk = r = 0 |
| 2487 for arg in self.args: |
| 2488 if arg.kw_only: |
| 2489 k += 1 |
| 2490 if not arg.default: |
| 2491 rk += 1 |
| 2492 if not arg.default: |
| 2493 r += 1 |
| 2494 self.num_kwonly_args = k |
| 2495 self.num_required_kw_args = rk |
| 2496 self.num_required_args = r |
| 2497 |
| 2498 def as_cfunction(self, cfunc=None, scope=None, overridable=True, returns=Non
e): |
| 2499 if self.star_arg: |
| 2500 error(self.star_arg.pos, "cdef function cannot have star argument") |
| 2501 if self.starstar_arg: |
| 2502 error(self.starstar_arg.pos, "cdef function cannot have starstar arg
ument") |
| 2503 if cfunc is None: |
| 2504 cfunc_args = [] |
| 2505 for formal_arg in self.args: |
| 2506 name_declarator, type = formal_arg.analyse(scope, nonempty=1) |
| 2507 cfunc_args.append(PyrexTypes.CFuncTypeArg(name = name_declarator
.name, |
| 2508 cname = None, |
| 2509 type = py_object_type, |
| 2510 pos = formal_arg.pos)) |
| 2511 cfunc_type = PyrexTypes.CFuncType(return_type = py_object_type, |
| 2512 args = cfunc_args, |
| 2513 has_varargs = False, |
| 2514 exception_value = None, |
| 2515 exception_check = False, |
| 2516 nogil = False, |
| 2517 with_gil = False, |
| 2518 is_overridable = overridable) |
| 2519 cfunc = CVarDefNode(self.pos, type=cfunc_type) |
| 2520 else: |
| 2521 if scope is None: |
| 2522 scope = cfunc.scope |
| 2523 cfunc_type = cfunc.type |
| 2524 if len(self.args) != len(cfunc_type.args) or cfunc_type.has_varargs: |
| 2525 error(self.pos, "wrong number of arguments") |
| 2526 error(cfunc.pos, "previous declaration here") |
| 2527 for i, (formal_arg, type_arg) in enumerate(zip(self.args, cfunc_type
.args)): |
| 2528 name_declarator, type = formal_arg.analyse(scope, nonempty=1, |
| 2529 is_self_arg = (i == 0
and scope.is_c_class_scope)) |
| 2530 if type is None or type is PyrexTypes.py_object_type: |
| 2531 formal_arg.type = type_arg.type |
| 2532 formal_arg.name_declarator = name_declarator |
| 2533 import ExprNodes |
| 2534 if cfunc_type.exception_value is None: |
| 2535 exception_value = None |
| 2536 else: |
| 2537 exception_value = ExprNodes.ConstNode(self.pos, value=cfunc_type.exc
eption_value, type=cfunc_type.return_type) |
| 2538 declarator = CFuncDeclaratorNode(self.pos, |
| 2539 base = CNameDeclaratorNode(self.pos, na
me=self.name, cname=None), |
| 2540 args = self.args, |
| 2541 has_varargs = False, |
| 2542 exception_check = cfunc_type.exception_
check, |
| 2543 exception_value = exception_value, |
| 2544 with_gil = cfunc_type.with_gil, |
| 2545 nogil = cfunc_type.nogil) |
| 2546 return CFuncDefNode(self.pos, |
| 2547 modifiers = [], |
| 2548 base_type = CAnalysedBaseTypeNode(self.pos, type=cfu
nc_type.return_type), |
| 2549 declarator = declarator, |
| 2550 body = self.body, |
| 2551 doc = self.doc, |
| 2552 overridable = cfunc_type.is_overridable, |
| 2553 type = cfunc_type, |
| 2554 with_gil = cfunc_type.with_gil, |
| 2555 nogil = cfunc_type.nogil, |
| 2556 visibility = 'private', |
| 2557 api = False, |
| 2558 directive_locals = getattr(cfunc, 'directive_locals'
, {}), |
| 2559 directive_returns = returns) |
| 2560 |
| 2561 def is_cdef_func_compatible(self): |
| 2562 """Determines if the function's signature is compatible with a |
| 2563 cdef function. This can be used before calling |
| 2564 .as_cfunction() to see if that will be successful. |
| 2565 """ |
| 2566 if self.needs_closure: |
| 2567 return False |
| 2568 if self.star_arg or self.starstar_arg: |
| 2569 return False |
| 2570 return True |
| 2571 |
| 2572 def analyse_declarations(self, env): |
| 2573 self.is_classmethod = self.is_staticmethod = False |
| 2574 if self.decorators: |
| 2575 for decorator in self.decorators: |
| 2576 func = decorator.decorator |
| 2577 if func.is_name: |
| 2578 self.is_classmethod |= func.name == 'classmethod' |
| 2579 self.is_staticmethod |= func.name == 'staticmethod' |
| 2580 |
| 2581 if self.is_classmethod and env.lookup_here('classmethod'): |
| 2582 # classmethod() was overridden - not much we can do here ... |
| 2583 self.is_classmethod = False |
| 2584 if self.is_staticmethod and env.lookup_here('staticmethod'): |
| 2585 # staticmethod() was overridden - not much we can do here ... |
| 2586 self.is_staticmethod = False |
| 2587 |
| 2588 if self.name == '__new__' and env.is_py_class_scope: |
| 2589 self.is_staticmethod = 1 |
| 2590 |
| 2591 self.analyse_argument_types(env) |
| 2592 if self.name == '<lambda>': |
| 2593 self.declare_lambda_function(env) |
| 2594 else: |
| 2595 self.declare_pyfunction(env) |
| 2596 |
| 2597 self.analyse_signature(env) |
| 2598 self.return_type = self.entry.signature.return_type() |
| 2599 self.create_local_scope(env) |
| 2600 |
| 2601 self.py_wrapper = DefNodeWrapper( |
| 2602 self.pos, |
| 2603 target=self, |
| 2604 name=self.entry.name, |
| 2605 args=self.args, |
| 2606 star_arg=self.star_arg, |
| 2607 starstar_arg=self.starstar_arg, |
| 2608 return_type=self.return_type) |
| 2609 self.py_wrapper.analyse_declarations(env) |
| 2610 |
| 2611 def analyse_argument_types(self, env): |
| 2612 self.directive_locals = env.directives['locals'] |
| 2613 allow_none_for_extension_args = env.directives['allow_none_for_extension
_args'] |
| 2614 |
| 2615 f2s = env.fused_to_specific |
| 2616 env.fused_to_specific = None |
| 2617 |
| 2618 for arg in self.args: |
| 2619 if hasattr(arg, 'name'): |
| 2620 name_declarator = None |
| 2621 else: |
| 2622 base_type = arg.base_type.analyse(env) |
| 2623 name_declarator, type = \ |
| 2624 arg.declarator.analyse(base_type, env) |
| 2625 arg.name = name_declarator.name |
| 2626 arg.type = type |
| 2627 |
| 2628 if type.is_fused: |
| 2629 self.has_fused_arguments = True |
| 2630 |
| 2631 self.align_argument_type(env, arg) |
| 2632 if name_declarator and name_declarator.cname: |
| 2633 error(self.pos, |
| 2634 "Python function argument cannot have C name specification") |
| 2635 arg.type = arg.type.as_argument_type() |
| 2636 arg.hdr_type = None |
| 2637 arg.needs_conversion = 0 |
| 2638 arg.needs_type_test = 0 |
| 2639 arg.is_generic = 1 |
| 2640 if arg.type.is_pyobject or arg.type.is_buffer or arg.type.is_memoryv
iewslice: |
| 2641 if arg.or_none: |
| 2642 arg.accept_none = True |
| 2643 elif arg.not_none: |
| 2644 arg.accept_none = False |
| 2645 elif (arg.type.is_extension_type or arg.type.is_builtin_type |
| 2646 or arg.type.is_buffer or arg.type.is_memoryviewslice): |
| 2647 if arg.default and arg.default.constant_result is None: |
| 2648 # special case: def func(MyType obj = None) |
| 2649 arg.accept_none = True |
| 2650 else: |
| 2651 # default depends on compiler directive |
| 2652 arg.accept_none = allow_none_for_extension_args |
| 2653 else: |
| 2654 # probably just a plain 'object' |
| 2655 arg.accept_none = True |
| 2656 else: |
| 2657 arg.accept_none = True # won't be used, but must be there |
| 2658 if arg.not_none: |
| 2659 error(arg.pos, "Only Python type arguments can have 'not Non
e'") |
| 2660 if arg.or_none: |
| 2661 error(arg.pos, "Only Python type arguments can have 'or None
'") |
| 2662 |
| 2663 env.fused_to_specific = f2s |
| 2664 |
| 2665 def analyse_signature(self, env): |
| 2666 if self.entry.is_special: |
| 2667 if self.decorators: |
| 2668 error(self.pos, "special functions of cdef classes cannot have d
ecorators") |
| 2669 self.entry.trivial_signature = len(self.args) == 1 and not (self.sta
r_arg or self.starstar_arg) |
| 2670 elif not env.directives['always_allow_keywords'] and not (self.star_arg
or self.starstar_arg): |
| 2671 # Use the simpler calling signature for zero- and one-argument funct
ions. |
| 2672 if self.entry.signature is TypeSlots.pyfunction_signature: |
| 2673 if len(self.args) == 0: |
| 2674 self.entry.signature = TypeSlots.pyfunction_noargs |
| 2675 elif len(self.args) == 1: |
| 2676 if self.args[0].default is None and not self.args[0].kw_only
: |
| 2677 self.entry.signature = TypeSlots.pyfunction_onearg |
| 2678 elif self.entry.signature is TypeSlots.pymethod_signature: |
| 2679 if len(self.args) == 1: |
| 2680 self.entry.signature = TypeSlots.unaryfunc |
| 2681 elif len(self.args) == 2: |
| 2682 if self.args[1].default is None and not self.args[1].kw_only
: |
| 2683 self.entry.signature = TypeSlots.ibinaryfunc |
| 2684 |
| 2685 sig = self.entry.signature |
| 2686 nfixed = sig.num_fixed_args() |
| 2687 if sig is TypeSlots.pymethod_signature and nfixed == 1 \ |
| 2688 and len(self.args) == 0 and self.star_arg: |
| 2689 # this is the only case where a diverging number of |
| 2690 # arguments is not an error - when we have no explicit |
| 2691 # 'self' parameter as in method(*args) |
| 2692 sig = self.entry.signature = TypeSlots.pyfunction_signature # self i
s not 'really' used |
| 2693 self.self_in_stararg = 1 |
| 2694 nfixed = 0 |
| 2695 |
| 2696 if self.is_staticmethod and env.is_c_class_scope: |
| 2697 nfixed = 0 |
| 2698 self.self_in_stararg = True # FIXME: why for staticmethods? |
| 2699 |
| 2700 self.entry.signature = sig = copy.copy(sig) |
| 2701 sig.fixed_arg_format = "*" |
| 2702 sig.is_staticmethod = True |
| 2703 sig.has_generic_args = True |
| 2704 |
| 2705 if ((self.is_classmethod or self.is_staticmethod) and |
| 2706 self.has_fused_arguments and env.is_c_class_scope): |
| 2707 del self.decorator_indirection.stats[:] |
| 2708 |
| 2709 for i in range(min(nfixed, len(self.args))): |
| 2710 arg = self.args[i] |
| 2711 arg.is_generic = 0 |
| 2712 if sig.is_self_arg(i) and not self.is_staticmethod: |
| 2713 if self.is_classmethod: |
| 2714 arg.is_type_arg = 1 |
| 2715 arg.hdr_type = arg.type = Builtin.type_type |
| 2716 else: |
| 2717 arg.is_self_arg = 1 |
| 2718 arg.hdr_type = arg.type = env.parent_type |
| 2719 arg.needs_conversion = 0 |
| 2720 else: |
| 2721 arg.hdr_type = sig.fixed_arg_type(i) |
| 2722 if not arg.type.same_as(arg.hdr_type): |
| 2723 if arg.hdr_type.is_pyobject and arg.type.is_pyobject: |
| 2724 arg.needs_type_test = 1 |
| 2725 else: |
| 2726 arg.needs_conversion = 1 |
| 2727 if arg.needs_conversion: |
| 2728 arg.hdr_cname = Naming.arg_prefix + arg.name |
| 2729 else: |
| 2730 arg.hdr_cname = Naming.var_prefix + arg.name |
| 2731 |
| 2732 if nfixed > len(self.args): |
| 2733 self.bad_signature() |
| 2734 return |
| 2735 elif nfixed < len(self.args): |
| 2736 if not sig.has_generic_args: |
| 2737 self.bad_signature() |
| 2738 for arg in self.args: |
| 2739 if arg.is_generic and \ |
| 2740 (arg.type.is_extension_type or arg.type.is_builtin_type)
: |
| 2741 arg.needs_type_test = 1 |
| 2742 |
| 2743 def bad_signature(self): |
| 2744 sig = self.entry.signature |
| 2745 expected_str = "%d" % sig.num_fixed_args() |
| 2746 if sig.has_generic_args: |
| 2747 expected_str += " or more" |
| 2748 name = self.name |
| 2749 if name.startswith("__") and name.endswith("__"): |
| 2750 desc = "Special method" |
| 2751 else: |
| 2752 desc = "Method" |
| 2753 error(self.pos, |
| 2754 "%s %s has wrong number of arguments " |
| 2755 "(%d declared, %s expected)" % ( |
| 2756 desc, self.name, len(self.args), expected_str)) |
| 2757 |
| 2758 def declare_pyfunction(self, env): |
| 2759 #print "DefNode.declare_pyfunction:", self.name, "in", env ### |
| 2760 name = self.name |
| 2761 entry = env.lookup_here(name) |
| 2762 if entry: |
| 2763 if entry.is_final_cmethod and not env.parent_type.is_final_type: |
| 2764 error(self.pos, "Only final types can have final Python (def/cpd
ef) methods") |
| 2765 if (entry.type.is_cfunction and not entry.is_builtin_cmethod |
| 2766 and not self.is_wrapper): |
| 2767 warning(self.pos, "Overriding cdef method with def method.", 5) |
| 2768 entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.i
s_wrapper) |
| 2769 self.entry = entry |
| 2770 prefix = env.next_id(env.scope_prefix) |
| 2771 self.entry.pyfunc_cname = Naming.pyfunc_prefix + prefix + name |
| 2772 if Options.docstrings: |
| 2773 entry.doc = embed_position(self.pos, self.doc) |
| 2774 entry.doc_cname = Naming.funcdoc_prefix + prefix + name |
| 2775 if entry.is_special: |
| 2776 if entry.name in TypeSlots.invisible or not entry.doc or (entry.
name in '__getattr__' and env.directives['fast_getattr']): |
| 2777 entry.wrapperbase_cname = None |
| 2778 else: |
| 2779 entry.wrapperbase_cname = Naming.wrapperbase_prefix + prefix
+ name |
| 2780 else: |
| 2781 entry.doc = None |
| 2782 |
| 2783 def declare_lambda_function(self, env): |
| 2784 entry = env.declare_lambda_function(self.lambda_name, self.pos) |
| 2785 entry.doc = None |
| 2786 self.entry = entry |
| 2787 self.entry.pyfunc_cname = entry.cname |
| 2788 |
| 2789 def declare_arguments(self, env): |
| 2790 for arg in self.args: |
| 2791 if not arg.name: |
| 2792 error(arg.pos, "Missing argument name") |
| 2793 if arg.needs_conversion: |
| 2794 arg.entry = env.declare_var(arg.name, arg.type, arg.pos) |
| 2795 if arg.type.is_pyobject: |
| 2796 arg.entry.init = "0" |
| 2797 else: |
| 2798 arg.entry = self.declare_argument(env, arg) |
| 2799 arg.entry.is_arg = 1 |
| 2800 arg.entry.used = 1 |
| 2801 arg.entry.is_self_arg = arg.is_self_arg |
| 2802 self.declare_python_arg(env, self.star_arg) |
| 2803 self.declare_python_arg(env, self.starstar_arg) |
| 2804 |
| 2805 def declare_python_arg(self, env, arg): |
| 2806 if arg: |
| 2807 if env.directives['infer_types'] != False: |
| 2808 type = PyrexTypes.unspecified_type |
| 2809 else: |
| 2810 type = py_object_type |
| 2811 entry = env.declare_var(arg.name, type, arg.pos) |
| 2812 entry.is_arg = 1 |
| 2813 entry.used = 1 |
| 2814 entry.init = "0" |
| 2815 entry.xdecref_cleanup = 1 |
| 2816 arg.entry = entry |
| 2817 |
| 2818 def analyse_expressions(self, env): |
| 2819 self.local_scope.directives = env.directives |
| 2820 self.analyse_default_values(env) |
| 2821 |
| 2822 if not self.needs_assignment_synthesis(env) and self.decorators: |
| 2823 for decorator in self.decorators[::-1]: |
| 2824 decorator.decorator = decorator.decorator.analyse_expressions(en
v) |
| 2825 |
| 2826 self.py_wrapper.prepare_argument_coercion(env) |
| 2827 return self |
| 2828 |
| 2829 def needs_assignment_synthesis(self, env, code=None): |
| 2830 if self.is_wrapper or self.specialized_cpdefs or self.entry.is_fused_spe
cialized: |
| 2831 return False |
| 2832 if self.is_staticmethod: |
| 2833 return True |
| 2834 if self.no_assignment_synthesis: |
| 2835 return False |
| 2836 # Should enable for module level as well, that will require more testing
... |
| 2837 if self.entry.is_anonymous: |
| 2838 return True |
| 2839 if env.is_module_scope: |
| 2840 if code is None: |
| 2841 return env.directives['binding'] |
| 2842 else: |
| 2843 return code.globalstate.directives['binding'] |
| 2844 return env.is_py_class_scope or env.is_closure_scope |
| 2845 |
| 2846 def error_value(self): |
| 2847 return self.entry.signature.error_value |
| 2848 |
| 2849 def caller_will_check_exceptions(self): |
| 2850 return self.entry.signature.exception_check |
| 2851 |
| 2852 def generate_function_definitions(self, env, code): |
| 2853 if self.defaults_getter: |
| 2854 self.defaults_getter.generate_function_definitions(env, code) |
| 2855 |
| 2856 # Before closure cnames are mangled |
| 2857 if self.py_wrapper_required: |
| 2858 # func_cname might be modified by @cname |
| 2859 self.py_wrapper.func_cname = self.entry.func_cname |
| 2860 self.py_wrapper.generate_function_definitions(env, code) |
| 2861 FuncDefNode.generate_function_definitions(self, env, code) |
| 2862 |
| 2863 def generate_function_header(self, code, with_pymethdef, proto_only=0): |
| 2864 if proto_only: |
| 2865 if self.py_wrapper_required: |
| 2866 self.py_wrapper.generate_function_header( |
| 2867 code, with_pymethdef, True) |
| 2868 return |
| 2869 arg_code_list = [] |
| 2870 if self.entry.signature.has_dummy_arg: |
| 2871 self_arg = 'PyObject *%s' % Naming.self_cname |
| 2872 if not self.needs_outer_scope: |
| 2873 self_arg = 'CYTHON_UNUSED ' + self_arg |
| 2874 arg_code_list.append(self_arg) |
| 2875 |
| 2876 def arg_decl_code(arg): |
| 2877 entry = arg.entry |
| 2878 if entry.in_closure: |
| 2879 cname = entry.original_cname |
| 2880 else: |
| 2881 cname = entry.cname |
| 2882 decl = entry.type.declaration_code(cname) |
| 2883 if not entry.cf_used: |
| 2884 decl = 'CYTHON_UNUSED ' + decl |
| 2885 return decl |
| 2886 |
| 2887 for arg in self.args: |
| 2888 arg_code_list.append(arg_decl_code(arg)) |
| 2889 if self.star_arg: |
| 2890 arg_code_list.append(arg_decl_code(self.star_arg)) |
| 2891 if self.starstar_arg: |
| 2892 arg_code_list.append(arg_decl_code(self.starstar_arg)) |
| 2893 arg_code = ', '.join(arg_code_list) |
| 2894 dc = self.return_type.declaration_code(self.entry.pyfunc_cname) |
| 2895 |
| 2896 decls_code = code.globalstate['decls'] |
| 2897 preprocessor_guard = self.get_preprocessor_guard() |
| 2898 if preprocessor_guard: |
| 2899 decls_code.putln(preprocessor_guard) |
| 2900 decls_code.putln( |
| 2901 "static %s(%s); /* proto */" % (dc, arg_code)) |
| 2902 if preprocessor_guard: |
| 2903 decls_code.putln("#endif") |
| 2904 code.putln("static %s(%s) {" % (dc, arg_code)) |
| 2905 |
| 2906 def generate_argument_declarations(self, env, code): |
| 2907 pass |
| 2908 |
| 2909 def generate_keyword_list(self, code): |
| 2910 pass |
| 2911 |
| 2912 def generate_argument_parsing_code(self, env, code): |
| 2913 # Move arguments into closure if required |
| 2914 def put_into_closure(entry): |
| 2915 if entry.in_closure: |
| 2916 code.putln('%s = %s;' % (entry.cname, entry.original_cname)) |
| 2917 code.put_var_incref(entry) |
| 2918 code.put_var_giveref(entry) |
| 2919 for arg in self.args: |
| 2920 put_into_closure(arg.entry) |
| 2921 for arg in self.star_arg, self.starstar_arg: |
| 2922 if arg: |
| 2923 put_into_closure(arg.entry) |
| 2924 |
| 2925 def generate_argument_type_tests(self, code): |
| 2926 pass |
| 2927 |
| 2928 |
| 2929 class DefNodeWrapper(FuncDefNode): |
| 2930 # DefNode python wrapper code generator |
| 2931 |
| 2932 defnode = None |
| 2933 target = None # Target DefNode |
| 2934 |
| 2935 def __init__(self, *args, **kwargs): |
| 2936 FuncDefNode.__init__(self, *args, **kwargs) |
| 2937 self.num_kwonly_args = self.target.num_kwonly_args |
| 2938 self.num_required_kw_args = self.target.num_required_kw_args |
| 2939 self.num_required_args = self.target.num_required_args |
| 2940 self.self_in_stararg = self.target.self_in_stararg |
| 2941 self.signature = None |
| 2942 |
| 2943 def analyse_declarations(self, env): |
| 2944 target_entry = self.target.entry |
| 2945 name = self.name |
| 2946 prefix = env.next_id(env.scope_prefix) |
| 2947 target_entry.func_cname = Naming.pywrap_prefix + prefix + name |
| 2948 target_entry.pymethdef_cname = Naming.pymethdef_prefix + prefix + name |
| 2949 |
| 2950 self.signature = target_entry.signature |
| 2951 |
| 2952 def prepare_argument_coercion(self, env): |
| 2953 # This is only really required for Cython utility code at this time, |
| 2954 # everything else can be done during code generation. But we expand |
| 2955 # all utility code here, simply because we cannot easily distinguish |
| 2956 # different code types. |
| 2957 for arg in self.args: |
| 2958 if not arg.type.is_pyobject: |
| 2959 if not arg.type.create_from_py_utility_code(env): |
| 2960 pass # will fail later |
| 2961 elif arg.hdr_type and not arg.hdr_type.is_pyobject: |
| 2962 if not arg.hdr_type.create_to_py_utility_code(env): |
| 2963 pass # will fail later |
| 2964 |
| 2965 def signature_has_nongeneric_args(self): |
| 2966 argcount = len(self.args) |
| 2967 if argcount == 0 or ( |
| 2968 argcount == 1 and (self.args[0].is_self_arg or |
| 2969 self.args[0].is_type_arg)): |
| 2970 return 0 |
| 2971 return 1 |
| 2972 |
| 2973 def signature_has_generic_args(self): |
| 2974 return self.signature.has_generic_args |
| 2975 |
| 2976 def generate_function_body(self, code): |
| 2977 args = [] |
| 2978 if self.signature.has_dummy_arg: |
| 2979 args.append(Naming.self_cname) |
| 2980 for arg in self.args: |
| 2981 if arg.hdr_type and not (arg.type.is_memoryviewslice or |
| 2982 arg.type.is_struct or |
| 2983 arg.type.is_complex): |
| 2984 args.append(arg.type.cast_code(arg.entry.cname)) |
| 2985 else: |
| 2986 args.append(arg.entry.cname) |
| 2987 if self.star_arg: |
| 2988 args.append(self.star_arg.entry.cname) |
| 2989 if self.starstar_arg: |
| 2990 args.append(self.starstar_arg.entry.cname) |
| 2991 args = ', '.join(args) |
| 2992 if not self.return_type.is_void: |
| 2993 code.put('%s = ' % Naming.retval_cname) |
| 2994 code.putln('%s(%s);' % ( |
| 2995 self.target.entry.pyfunc_cname, args)) |
| 2996 |
| 2997 def generate_function_definitions(self, env, code): |
| 2998 lenv = self.target.local_scope |
| 2999 # Generate C code for header and body of function |
| 3000 code.mark_pos(self.pos) |
| 3001 code.putln("") |
| 3002 code.putln("/* Python wrapper */") |
| 3003 preprocessor_guard = self.target.get_preprocessor_guard() |
| 3004 if preprocessor_guard: |
| 3005 code.putln(preprocessor_guard) |
| 3006 |
| 3007 code.enter_cfunc_scope() |
| 3008 code.return_from_error_cleanup_label = code.new_label() |
| 3009 |
| 3010 with_pymethdef = (self.target.needs_assignment_synthesis(env, code) or |
| 3011 self.target.pymethdef_required) |
| 3012 self.generate_function_header(code, with_pymethdef) |
| 3013 self.generate_argument_declarations(lenv, code) |
| 3014 tempvardecl_code = code.insertion_point() |
| 3015 |
| 3016 if self.return_type.is_pyobject: |
| 3017 retval_init = ' = 0' |
| 3018 else: |
| 3019 retval_init = '' |
| 3020 if not self.return_type.is_void: |
| 3021 code.putln('%s%s;' % ( |
| 3022 self.return_type.declaration_code(Naming.retval_cname), |
| 3023 retval_init)) |
| 3024 code.put_declare_refcount_context() |
| 3025 code.put_setup_refcount_context('%s (wrapper)' % self.name) |
| 3026 |
| 3027 self.generate_argument_parsing_code(lenv, code) |
| 3028 self.generate_argument_type_tests(code) |
| 3029 self.generate_function_body(code) |
| 3030 |
| 3031 # ----- Go back and insert temp variable declarations |
| 3032 tempvardecl_code.put_temp_declarations(code.funcstate) |
| 3033 |
| 3034 code.mark_pos(self.pos) |
| 3035 code.putln("") |
| 3036 code.putln("/* function exit code */") |
| 3037 |
| 3038 # ----- Error cleanup |
| 3039 if code.error_label in code.labels_used: |
| 3040 code.put_goto(code.return_label) |
| 3041 code.put_label(code.error_label) |
| 3042 for cname, type in code.funcstate.all_managed_temps(): |
| 3043 code.put_xdecref(cname, type) |
| 3044 err_val = self.error_value() |
| 3045 if err_val is not None: |
| 3046 code.putln("%s = %s;" % (Naming.retval_cname, err_val)) |
| 3047 |
| 3048 # ----- Non-error return cleanup |
| 3049 code.put_label(code.return_label) |
| 3050 for entry in lenv.var_entries: |
| 3051 if entry.is_arg and entry.type.is_pyobject: |
| 3052 code.put_var_decref(entry) |
| 3053 |
| 3054 code.put_finish_refcount_context() |
| 3055 if not self.return_type.is_void: |
| 3056 code.putln("return %s;" % Naming.retval_cname) |
| 3057 code.putln('}') |
| 3058 code.exit_cfunc_scope() |
| 3059 if preprocessor_guard: |
| 3060 code.putln("#endif /*!(%s)*/" % preprocessor_guard) |
| 3061 |
| 3062 def generate_function_header(self, code, with_pymethdef, proto_only=0): |
| 3063 arg_code_list = [] |
| 3064 sig = self.signature |
| 3065 |
| 3066 if sig.has_dummy_arg or self.self_in_stararg: |
| 3067 arg_code = "PyObject *%s" % Naming.self_cname |
| 3068 if not sig.has_dummy_arg: |
| 3069 arg_code = 'CYTHON_UNUSED ' + arg_code |
| 3070 arg_code_list.append(arg_code) |
| 3071 |
| 3072 for arg in self.args: |
| 3073 if not arg.is_generic: |
| 3074 if arg.is_self_arg or arg.is_type_arg: |
| 3075 arg_code_list.append("PyObject *%s" % arg.hdr_cname) |
| 3076 else: |
| 3077 arg_code_list.append( |
| 3078 arg.hdr_type.declaration_code(arg.hdr_cname)) |
| 3079 entry = self.target.entry |
| 3080 if not entry.is_special and sig.method_flags() == [TypeSlots.method_noar
gs]: |
| 3081 arg_code_list.append("CYTHON_UNUSED PyObject *unused") |
| 3082 if entry.scope.is_c_class_scope and entry.name == "__ipow__": |
| 3083 arg_code_list.append("CYTHON_UNUSED PyObject *unused") |
| 3084 if sig.has_generic_args: |
| 3085 arg_code_list.append( |
| 3086 "PyObject *%s, PyObject *%s" |
| 3087 % (Naming.args_cname, Naming.kwds_cname)) |
| 3088 arg_code = ", ".join(arg_code_list) |
| 3089 |
| 3090 # Prevent warning: unused function '__pyx_pw_5numpy_7ndarray_1__getbuffe
r__' |
| 3091 mf = "" |
| 3092 if (entry.name in ("__getbuffer__", "__releasebuffer__") |
| 3093 and entry.scope.is_c_class_scope): |
| 3094 mf = "CYTHON_UNUSED " |
| 3095 with_pymethdef = False |
| 3096 |
| 3097 dc = self.return_type.declaration_code(entry.func_cname) |
| 3098 header = "static %s%s(%s)" % (mf, dc, arg_code) |
| 3099 code.putln("%s; /*proto*/" % header) |
| 3100 |
| 3101 if proto_only: |
| 3102 if self.target.fused_py_func: |
| 3103 # If we are the specialized version of the cpdef, we still |
| 3104 # want the prototype for the "fused cpdef", in case we're |
| 3105 # checking to see if our method was overridden in Python |
| 3106 self.target.fused_py_func.generate_function_header( |
| 3107 code, with_pymethdef, proto_only=True) |
| 3108 return |
| 3109 |
| 3110 if (Options.docstrings and entry.doc and |
| 3111 not self.target.fused_py_func and |
| 3112 not entry.scope.is_property_scope and |
| 3113 (not entry.is_special or entry.wrapperbase_cname)): |
| 3114 # h_code = code.globalstate['h_code'] |
| 3115 docstr = entry.doc |
| 3116 |
| 3117 if docstr.is_unicode: |
| 3118 docstr = docstr.utf8encode() |
| 3119 |
| 3120 code.putln( |
| 3121 'static char %s[] = "%s";' % ( |
| 3122 entry.doc_cname, |
| 3123 split_string_literal(escape_byte_string(docstr)))) |
| 3124 |
| 3125 if entry.is_special: |
| 3126 code.putln('#if CYTHON_COMPILING_IN_CPYTHON') |
| 3127 code.putln( |
| 3128 "struct wrapperbase %s;" % entry.wrapperbase_cname) |
| 3129 code.putln('#endif') |
| 3130 |
| 3131 if with_pymethdef or self.target.fused_py_func: |
| 3132 code.put( |
| 3133 "static PyMethodDef %s = " % |
| 3134 entry.pymethdef_cname) |
| 3135 code.put_pymethoddef(self.target.entry, ";", allow_skip=False) |
| 3136 code.putln("%s {" % header) |
| 3137 |
| 3138 def generate_argument_declarations(self, env, code): |
| 3139 for arg in self.args: |
| 3140 if arg.is_generic: |
| 3141 if arg.needs_conversion: |
| 3142 code.putln("PyObject *%s = 0;" % arg.hdr_cname) |
| 3143 else: |
| 3144 code.put_var_declaration(arg.entry) |
| 3145 for entry in env.var_entries: |
| 3146 if entry.is_arg: |
| 3147 code.put_var_declaration(entry) |
| 3148 |
| 3149 def generate_argument_parsing_code(self, env, code): |
| 3150 # Generate fast equivalent of PyArg_ParseTuple call for |
| 3151 # generic arguments, if any, including args/kwargs |
| 3152 old_error_label = code.new_error_label() |
| 3153 our_error_label = code.error_label |
| 3154 end_label = code.new_label("argument_unpacking_done") |
| 3155 |
| 3156 has_kwonly_args = self.num_kwonly_args > 0 |
| 3157 has_star_or_kw_args = self.star_arg is not None \ |
| 3158 or self.starstar_arg is not None or has_kwonly_args |
| 3159 |
| 3160 for arg in self.args: |
| 3161 if not arg.type.is_pyobject: |
| 3162 if not arg.type.create_from_py_utility_code(env): |
| 3163 pass # will fail later |
| 3164 |
| 3165 if not self.signature_has_generic_args(): |
| 3166 if has_star_or_kw_args: |
| 3167 error(self.pos, "This method cannot have * or keyword arguments"
) |
| 3168 self.generate_argument_conversion_code(code) |
| 3169 |
| 3170 elif not self.signature_has_nongeneric_args(): |
| 3171 # func(*args) or func(**kw) or func(*args, **kw) |
| 3172 self.generate_stararg_copy_code(code) |
| 3173 |
| 3174 else: |
| 3175 self.generate_tuple_and_keyword_parsing_code(self.args, end_label, c
ode) |
| 3176 |
| 3177 code.error_label = old_error_label |
| 3178 if code.label_used(our_error_label): |
| 3179 if not code.label_used(end_label): |
| 3180 code.put_goto(end_label) |
| 3181 code.put_label(our_error_label) |
| 3182 if has_star_or_kw_args: |
| 3183 self.generate_arg_decref(self.star_arg, code) |
| 3184 if self.starstar_arg: |
| 3185 if self.starstar_arg.entry.xdecref_cleanup: |
| 3186 code.put_var_xdecref_clear(self.starstar_arg.entry) |
| 3187 else: |
| 3188 code.put_var_decref_clear(self.starstar_arg.entry) |
| 3189 code.put_add_traceback(self.target.entry.qualified_name) |
| 3190 code.put_finish_refcount_context() |
| 3191 code.putln("return %s;" % self.error_value()) |
| 3192 if code.label_used(end_label): |
| 3193 code.put_label(end_label) |
| 3194 |
| 3195 def generate_arg_xdecref(self, arg, code): |
| 3196 if arg: |
| 3197 code.put_var_xdecref_clear(arg.entry) |
| 3198 |
| 3199 def generate_arg_decref(self, arg, code): |
| 3200 if arg: |
| 3201 code.put_var_decref_clear(arg.entry) |
| 3202 |
| 3203 def generate_stararg_copy_code(self, code): |
| 3204 if not self.star_arg: |
| 3205 code.globalstate.use_utility_code( |
| 3206 UtilityCode.load_cached("RaiseArgTupleInvalid", "FunctionArgumen
ts.c")) |
| 3207 code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > 0)) {" % |
| 3208 Naming.args_cname) |
| 3209 code.put('__Pyx_RaiseArgtupleInvalid("%s", 1, 0, 0, PyTuple_GET_SIZE
(%s)); return %s;' % ( |
| 3210 self.name, Naming.args_cname, self.error_value())) |
| 3211 code.putln("}") |
| 3212 |
| 3213 if self.starstar_arg: |
| 3214 if self.star_arg: |
| 3215 kwarg_check = "unlikely(%s)" % Naming.kwds_cname |
| 3216 else: |
| 3217 kwarg_check = "%s" % Naming.kwds_cname |
| 3218 else: |
| 3219 kwarg_check = "unlikely(%s) && unlikely(PyDict_Size(%s) > 0)" % ( |
| 3220 Naming.kwds_cname, Naming.kwds_cname) |
| 3221 code.globalstate.use_utility_code( |
| 3222 UtilityCode.load_cached("KeywordStringCheck", "FunctionArguments.c")
) |
| 3223 code.putln( |
| 3224 "if (%s && unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) ret
urn %s;" % ( |
| 3225 kwarg_check, Naming.kwds_cname, self.name, |
| 3226 bool(self.starstar_arg), self.error_value())) |
| 3227 |
| 3228 if self.starstar_arg: |
| 3229 code.putln("%s = (%s) ? PyDict_Copy(%s) : PyDict_New();" % ( |
| 3230 self.starstar_arg.entry.cname, |
| 3231 Naming.kwds_cname, |
| 3232 Naming.kwds_cname)) |
| 3233 code.putln("if (unlikely(!%s)) return %s;" % ( |
| 3234 self.starstar_arg.entry.cname, self.error_value())) |
| 3235 self.starstar_arg.entry.xdecref_cleanup = 0 |
| 3236 code.put_gotref(self.starstar_arg.entry.cname) |
| 3237 |
| 3238 if self.self_in_stararg and not self.target.is_staticmethod: |
| 3239 # need to create a new tuple with 'self' inserted as first item |
| 3240 code.put("%s = PyTuple_New(PyTuple_GET_SIZE(%s)+1); if (unlikely(!%s
)) " % ( |
| 3241 self.star_arg.entry.cname, |
| 3242 Naming.args_cname, |
| 3243 self.star_arg.entry.cname)) |
| 3244 if self.starstar_arg: |
| 3245 code.putln("{") |
| 3246 code.put_decref_clear(self.starstar_arg.entry.cname, py_object_t
ype) |
| 3247 code.putln("return %s;" % self.error_value()) |
| 3248 code.putln("}") |
| 3249 else: |
| 3250 code.putln("return %s;" % self.error_value()) |
| 3251 code.put_gotref(self.star_arg.entry.cname) |
| 3252 code.put_incref(Naming.self_cname, py_object_type) |
| 3253 code.put_giveref(Naming.self_cname) |
| 3254 code.putln("PyTuple_SET_ITEM(%s, 0, %s);" % ( |
| 3255 self.star_arg.entry.cname, Naming.self_cname)) |
| 3256 temp = code.funcstate.allocate_temp(PyrexTypes.c_py_ssize_t_type, ma
nage_ref=False) |
| 3257 code.putln("for (%s=0; %s < PyTuple_GET_SIZE(%s); %s++) {" % ( |
| 3258 temp, temp, Naming.args_cname, temp)) |
| 3259 code.putln("PyObject* item = PyTuple_GET_ITEM(%s, %s);" % ( |
| 3260 Naming.args_cname, temp)) |
| 3261 code.put_incref("item", py_object_type) |
| 3262 code.put_giveref("item") |
| 3263 code.putln("PyTuple_SET_ITEM(%s, %s+1, item);" % ( |
| 3264 self.star_arg.entry.cname, temp)) |
| 3265 code.putln("}") |
| 3266 code.funcstate.release_temp(temp) |
| 3267 self.star_arg.entry.xdecref_cleanup = 0 |
| 3268 elif self.star_arg: |
| 3269 code.put_incref(Naming.args_cname, py_object_type) |
| 3270 code.putln("%s = %s;" % ( |
| 3271 self.star_arg.entry.cname, |
| 3272 Naming.args_cname)) |
| 3273 self.star_arg.entry.xdecref_cleanup = 0 |
| 3274 |
| 3275 def generate_tuple_and_keyword_parsing_code(self, args, success_label, code)
: |
| 3276 argtuple_error_label = code.new_label("argtuple_error") |
| 3277 |
| 3278 positional_args = [] |
| 3279 required_kw_only_args = [] |
| 3280 optional_kw_only_args = [] |
| 3281 for arg in args: |
| 3282 if arg.is_generic: |
| 3283 if arg.default: |
| 3284 if not arg.is_self_arg and not arg.is_type_arg: |
| 3285 if arg.kw_only: |
| 3286 optional_kw_only_args.append(arg) |
| 3287 else: |
| 3288 positional_args.append(arg) |
| 3289 elif arg.kw_only: |
| 3290 required_kw_only_args.append(arg) |
| 3291 elif not arg.is_self_arg and not arg.is_type_arg: |
| 3292 positional_args.append(arg) |
| 3293 |
| 3294 # sort required kw-only args before optional ones to avoid special |
| 3295 # cases in the unpacking code |
| 3296 kw_only_args = required_kw_only_args + optional_kw_only_args |
| 3297 |
| 3298 min_positional_args = self.num_required_args - self.num_required_kw_args |
| 3299 if len(args) > 0 and (args[0].is_self_arg or args[0].is_type_arg): |
| 3300 min_positional_args -= 1 |
| 3301 max_positional_args = len(positional_args) |
| 3302 has_fixed_positional_count = not self.star_arg and \ |
| 3303 min_positional_args == max_positional_args |
| 3304 has_kw_only_args = bool(kw_only_args) |
| 3305 |
| 3306 if self.num_required_kw_args: |
| 3307 code.globalstate.use_utility_code( |
| 3308 UtilityCode.load_cached("RaiseKeywordRequired", "FunctionArgumen
ts.c")) |
| 3309 |
| 3310 if self.starstar_arg or self.star_arg: |
| 3311 self.generate_stararg_init_code(max_positional_args, code) |
| 3312 |
| 3313 code.putln('{') |
| 3314 all_args = tuple(positional_args) + tuple(kw_only_args) |
| 3315 code.putln("static PyObject **%s[] = {%s,0};" % ( |
| 3316 Naming.pykwdlist_cname, |
| 3317 ','.join([ '&%s' % code.intern_identifier(arg.name) |
| 3318 for arg in all_args ]))) |
| 3319 |
| 3320 # Before being converted and assigned to the target variables, |
| 3321 # borrowed references to all unpacked argument values are |
| 3322 # collected into a local PyObject* array called "values", |
| 3323 # regardless if they were taken from default arguments, |
| 3324 # positional arguments or keyword arguments. Note that |
| 3325 # C-typed default arguments are handled at conversion time, |
| 3326 # so their array value is NULL in the end if no argument |
| 3327 # was passed for them. |
| 3328 self.generate_argument_values_setup_code(all_args, code) |
| 3329 |
| 3330 # --- optimised code when we receive keyword arguments |
| 3331 code.putln("if (%s(%s)) {" % ( |
| 3332 (self.num_required_kw_args > 0) and "likely" or "unlikely", |
| 3333 Naming.kwds_cname)) |
| 3334 self.generate_keyword_unpacking_code( |
| 3335 min_positional_args, max_positional_args, |
| 3336 has_fixed_positional_count, has_kw_only_args, |
| 3337 all_args, argtuple_error_label, code) |
| 3338 |
| 3339 # --- optimised code when we do not receive any keyword arguments |
| 3340 if (self.num_required_kw_args and min_positional_args > 0) or min_positi
onal_args == max_positional_args: |
| 3341 # Python raises arg tuple related errors first, so we must |
| 3342 # check the length here |
| 3343 if min_positional_args == max_positional_args and not self.star_arg: |
| 3344 compare = '!=' |
| 3345 else: |
| 3346 compare = '<' |
| 3347 code.putln('} else if (PyTuple_GET_SIZE(%s) %s %d) {' % ( |
| 3348 Naming.args_cname, compare, min_positional_args)) |
| 3349 code.put_goto(argtuple_error_label) |
| 3350 |
| 3351 if self.num_required_kw_args: |
| 3352 # pure error case: keywords required but not passed |
| 3353 if max_positional_args > min_positional_args and not self.star_arg: |
| 3354 code.putln('} else if (PyTuple_GET_SIZE(%s) > %d) {' % ( |
| 3355 Naming.args_cname, max_positional_args)) |
| 3356 code.put_goto(argtuple_error_label) |
| 3357 code.putln('} else {') |
| 3358 for i, arg in enumerate(kw_only_args): |
| 3359 if not arg.default: |
| 3360 pystring_cname = code.intern_identifier(arg.name) |
| 3361 # required keyword-only argument missing |
| 3362 code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' % ( |
| 3363 self.name, |
| 3364 pystring_cname)) |
| 3365 code.putln(code.error_goto(self.pos)) |
| 3366 break |
| 3367 |
| 3368 else: |
| 3369 # optimised tuple unpacking code |
| 3370 code.putln('} else {') |
| 3371 if min_positional_args == max_positional_args: |
| 3372 # parse the exact number of positional arguments from |
| 3373 # the args tuple |
| 3374 for i, arg in enumerate(positional_args): |
| 3375 code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % (i, Na
ming.args_cname, i)) |
| 3376 else: |
| 3377 # parse the positional arguments from the variable length |
| 3378 # args tuple and reject illegal argument tuple sizes |
| 3379 code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname
) |
| 3380 if self.star_arg: |
| 3381 code.putln('default:') |
| 3382 reversed_args = list(enumerate(positional_args))[::-1] |
| 3383 for i, arg in reversed_args: |
| 3384 if i >= min_positional_args-1: |
| 3385 code.put('case %2d: ' % (i+1)) |
| 3386 code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % (i, Na
ming.args_cname, i)) |
| 3387 if min_positional_args == 0: |
| 3388 code.put('case 0: ') |
| 3389 code.putln('break;') |
| 3390 if self.star_arg: |
| 3391 if min_positional_args: |
| 3392 for i in range(min_positional_args-1, -1, -1): |
| 3393 code.putln('case %2d:' % i) |
| 3394 code.put_goto(argtuple_error_label) |
| 3395 else: |
| 3396 code.put('default: ') |
| 3397 code.put_goto(argtuple_error_label) |
| 3398 code.putln('}') |
| 3399 |
| 3400 code.putln('}') # end of the conditional unpacking blocks |
| 3401 |
| 3402 # Convert arg values to their final type and assign them. |
| 3403 # Also inject non-Python default arguments, which do cannot |
| 3404 # live in the values[] array. |
| 3405 for i, arg in enumerate(all_args): |
| 3406 self.generate_arg_assignment(arg, "values[%d]" % i, code) |
| 3407 |
| 3408 code.putln('}') # end of the whole argument unpacking block |
| 3409 |
| 3410 if code.label_used(argtuple_error_label): |
| 3411 code.put_goto(success_label) |
| 3412 code.put_label(argtuple_error_label) |
| 3413 code.globalstate.use_utility_code( |
| 3414 UtilityCode.load_cached("RaiseArgTupleInvalid", "FunctionArgumen
ts.c")) |
| 3415 code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, PyTuple_GET_S
IZE(%s)); ' % ( |
| 3416 self.name, has_fixed_positional_count, |
| 3417 min_positional_args, max_positional_args, |
| 3418 Naming.args_cname)) |
| 3419 code.putln(code.error_goto(self.pos)) |
| 3420 |
| 3421 def generate_arg_assignment(self, arg, item, code): |
| 3422 if arg.type.is_pyobject: |
| 3423 # Python default arguments were already stored in 'item' at the very
beginning |
| 3424 if arg.is_generic: |
| 3425 item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type,
item) |
| 3426 entry = arg.entry |
| 3427 code.putln("%s = %s;" % (entry.cname, item)) |
| 3428 else: |
| 3429 func = arg.type.from_py_function |
| 3430 if func: |
| 3431 if arg.default: |
| 3432 # C-typed default arguments must be handled here |
| 3433 code.putln('if (%s) {' % item) |
| 3434 rhs = "%s(%s)" % (func, item) |
| 3435 if arg.type.is_enum: |
| 3436 rhs = arg.type.cast_code(rhs) |
| 3437 code.putln("%s = %s; %s" % ( |
| 3438 arg.entry.cname, |
| 3439 rhs, |
| 3440 code.error_goto_if(arg.type.error_condition(arg.entry.cname)
, arg.pos))) |
| 3441 if arg.default: |
| 3442 code.putln('} else {') |
| 3443 code.putln( |
| 3444 "%s = %s;" % ( |
| 3445 arg.entry.cname, |
| 3446 arg.calculate_default_value_code(code))) |
| 3447 if arg.type.is_memoryviewslice: |
| 3448 code.put_incref_memoryviewslice(arg.entry.cname, |
| 3449 have_gil=True) |
| 3450 code.putln('}') |
| 3451 else: |
| 3452 error(arg.pos, "Cannot convert Python object argument to type '%
s'" % arg.type) |
| 3453 |
| 3454 def generate_stararg_init_code(self, max_positional_args, code): |
| 3455 if self.starstar_arg: |
| 3456 self.starstar_arg.entry.xdecref_cleanup = 0 |
| 3457 code.putln('%s = PyDict_New(); if (unlikely(!%s)) return %s;' % ( |
| 3458 self.starstar_arg.entry.cname, |
| 3459 self.starstar_arg.entry.cname, |
| 3460 self.error_value())) |
| 3461 code.put_gotref(self.starstar_arg.entry.cname) |
| 3462 if self.star_arg: |
| 3463 self.star_arg.entry.xdecref_cleanup = 0 |
| 3464 code.putln('if (PyTuple_GET_SIZE(%s) > %d) {' % ( |
| 3465 Naming.args_cname, |
| 3466 max_positional_args)) |
| 3467 code.putln('%s = PyTuple_GetSlice(%s, %d, PyTuple_GET_SIZE(%s));' %
( |
| 3468 self.star_arg.entry.cname, Naming.args_cname, |
| 3469 max_positional_args, Naming.args_cname)) |
| 3470 code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname) |
| 3471 if self.starstar_arg: |
| 3472 code.put_decref_clear(self.starstar_arg.entry.cname, py_object_t
ype) |
| 3473 code.put_finish_refcount_context() |
| 3474 code.putln('return %s;' % self.error_value()) |
| 3475 code.putln('}') |
| 3476 code.put_gotref(self.star_arg.entry.cname) |
| 3477 code.putln('} else {') |
| 3478 code.put("%s = %s; " % (self.star_arg.entry.cname, Naming.empty_tupl
e)) |
| 3479 code.put_incref(Naming.empty_tuple, py_object_type) |
| 3480 code.putln('}') |
| 3481 |
| 3482 def generate_argument_values_setup_code(self, args, code): |
| 3483 max_args = len(args) |
| 3484 # the 'values' array collects borrowed references to arguments |
| 3485 # before doing any type coercion etc. |
| 3486 code.putln("PyObject* values[%d] = {%s};" % ( |
| 3487 max_args, ','.join('0'*max_args))) |
| 3488 |
| 3489 if self.target.defaults_struct: |
| 3490 code.putln('%s *%s = __Pyx_CyFunction_Defaults(%s, %s);' % ( |
| 3491 self.target.defaults_struct, Naming.dynamic_args_cname, |
| 3492 self.target.defaults_struct, Naming.self_cname)) |
| 3493 |
| 3494 # assign borrowed Python default values to the values array, |
| 3495 # so that they can be overwritten by received arguments below |
| 3496 for i, arg in enumerate(args): |
| 3497 if arg.default and arg.type.is_pyobject: |
| 3498 default_value = arg.calculate_default_value_code(code) |
| 3499 code.putln('values[%d] = %s;' % (i, arg.type.as_pyobject(default
_value))) |
| 3500 |
| 3501 def generate_keyword_unpacking_code(self, min_positional_args, max_positiona
l_args, |
| 3502 has_fixed_positional_count, has_kw_only_
args, |
| 3503 all_args, argtuple_error_label, code): |
| 3504 code.putln('Py_ssize_t kw_args;') |
| 3505 code.putln('const Py_ssize_t pos_args = PyTuple_GET_SIZE(%s);' % Naming.
args_cname) |
| 3506 # copy the values from the args tuple and check that it's not too long |
| 3507 code.putln('switch (pos_args) {') |
| 3508 if self.star_arg: |
| 3509 code.putln('default:') |
| 3510 for i in range(max_positional_args-1, -1, -1): |
| 3511 code.put('case %2d: ' % (i+1)) |
| 3512 code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % ( |
| 3513 i, Naming.args_cname, i)) |
| 3514 code.putln('case 0: break;') |
| 3515 if not self.star_arg: |
| 3516 code.put('default: ') # more arguments than allowed |
| 3517 code.put_goto(argtuple_error_label) |
| 3518 code.putln('}') |
| 3519 |
| 3520 # The code above is very often (but not always) the same as |
| 3521 # the optimised non-kwargs tuple unpacking code, so we keep |
| 3522 # the code block above at the very top, before the following |
| 3523 # 'external' PyDict_Size() call, to make it easy for the C |
| 3524 # compiler to merge the two separate tuple unpacking |
| 3525 # implementations into one when they turn out to be identical. |
| 3526 |
| 3527 # If we received kwargs, fill up the positional/required |
| 3528 # arguments with values from the kw dict |
| 3529 code.putln('kw_args = PyDict_Size(%s);' % Naming.kwds_cname) |
| 3530 if self.num_required_args or max_positional_args > 0: |
| 3531 last_required_arg = -1 |
| 3532 for i, arg in enumerate(all_args): |
| 3533 if not arg.default: |
| 3534 last_required_arg = i |
| 3535 if last_required_arg < max_positional_args: |
| 3536 last_required_arg = max_positional_args-1 |
| 3537 if max_positional_args > 0: |
| 3538 code.putln('switch (pos_args) {') |
| 3539 for i, arg in enumerate(all_args[:last_required_arg+1]): |
| 3540 if max_positional_args > 0 and i <= max_positional_args: |
| 3541 if self.star_arg and i == max_positional_args: |
| 3542 code.putln('default:') |
| 3543 else: |
| 3544 code.putln('case %2d:' % i) |
| 3545 pystring_cname = code.intern_identifier(arg.name) |
| 3546 if arg.default: |
| 3547 if arg.kw_only: |
| 3548 # optional kw-only args are handled separately below |
| 3549 continue |
| 3550 code.putln('if (kw_args > 0) {') |
| 3551 # don't overwrite default argument |
| 3552 code.putln('PyObject* value = PyDict_GetItem(%s, %s);' % ( |
| 3553 Naming.kwds_cname, pystring_cname)) |
| 3554 code.putln('if (value) { values[%d] = value; kw_args--; }' %
i) |
| 3555 code.putln('}') |
| 3556 else: |
| 3557 code.putln('if (likely((values[%d] = PyDict_GetItem(%s, %s))
!= 0)) kw_args--;' % ( |
| 3558 i, Naming.kwds_cname, pystring_cname)) |
| 3559 if i < min_positional_args: |
| 3560 if i == 0: |
| 3561 # special case: we know arg 0 is missing |
| 3562 code.put('else ') |
| 3563 code.put_goto(argtuple_error_label) |
| 3564 else: |
| 3565 # print the correct number of values (args or |
| 3566 # kwargs) that were passed into positional |
| 3567 # arguments up to this point |
| 3568 code.putln('else {') |
| 3569 code.globalstate.use_utility_code( |
| 3570 UtilityCode.load_cached("RaiseArgTupleInvalid",
"FunctionArguments.c")) |
| 3571 code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %
d, %d); ' % ( |
| 3572 self.name, has_fixed_positional_count, |
| 3573 min_positional_args, max_positional_args, i)
) |
| 3574 code.putln(code.error_goto(self.pos)) |
| 3575 code.putln('}') |
| 3576 elif arg.kw_only: |
| 3577 code.putln('else {') |
| 3578 code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' %( |
| 3579 self.name, pystring_cname)) |
| 3580 code.putln(code.error_goto(self.pos)) |
| 3581 code.putln('}') |
| 3582 if max_positional_args > 0: |
| 3583 code.putln('}') |
| 3584 |
| 3585 if has_kw_only_args: |
| 3586 # unpack optional keyword-only arguments separately because |
| 3587 # checking for interned strings in a dict is faster than iterating |
| 3588 self.generate_optional_kwonly_args_unpacking_code(all_args, code) |
| 3589 |
| 3590 code.putln('if (unlikely(kw_args > 0)) {') |
| 3591 # non-positional/-required kw args left in dict: default args, |
| 3592 # kw-only args, **kwargs or error |
| 3593 # |
| 3594 # This is sort of a catch-all: except for checking required |
| 3595 # arguments, this will always do the right thing for unpacking |
| 3596 # keyword arguments, so that we can concentrate on optimising |
| 3597 # common cases above. |
| 3598 if max_positional_args == 0: |
| 3599 pos_arg_count = "0" |
| 3600 elif self.star_arg: |
| 3601 code.putln("const Py_ssize_t used_pos_args = (pos_args < %d) ? pos_a
rgs : %d;" % ( |
| 3602 max_positional_args, max_positional_args)) |
| 3603 pos_arg_count = "used_pos_args" |
| 3604 else: |
| 3605 pos_arg_count = "pos_args" |
| 3606 code.globalstate.use_utility_code( |
| 3607 UtilityCode.load_cached("ParseKeywords", "FunctionArguments.c")) |
| 3608 code.putln( |
| 3609 'if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, values, %s, "%
s") < 0)) %s' % ( |
| 3610 Naming.kwds_cname, |
| 3611 Naming.pykwdlist_cname, |
| 3612 self.starstar_arg and self.starstar_arg.entry.cname or '0', |
| 3613 pos_arg_count, |
| 3614 self.name, |
| 3615 code.error_goto(self.pos))) |
| 3616 code.putln('}') |
| 3617 |
| 3618 def generate_optional_kwonly_args_unpacking_code(self, all_args, code): |
| 3619 optional_args = [] |
| 3620 first_optional_arg = -1 |
| 3621 for i, arg in enumerate(all_args): |
| 3622 if not arg.kw_only or not arg.default: |
| 3623 continue |
| 3624 if not optional_args: |
| 3625 first_optional_arg = i |
| 3626 optional_args.append(arg.name) |
| 3627 if optional_args: |
| 3628 if len(optional_args) > 1: |
| 3629 # if we receive more than the named kwargs, we either have **kwa
rgs |
| 3630 # (in which case we must iterate anyway) or it's an error (which
we |
| 3631 # also handle during iteration) => skip this part if there are m
ore |
| 3632 code.putln('if (kw_args > 0 && %s(kw_args <= %d)) {' % ( |
| 3633 not self.starstar_arg and 'likely' or '', |
| 3634 len(optional_args))) |
| 3635 code.putln('Py_ssize_t index;') |
| 3636 # not unrolling the loop here reduces the C code overhead |
| 3637 code.putln('for (index = %d; index < %d && kw_args > 0; index++)
{' % ( |
| 3638 first_optional_arg, first_optional_arg + len(optional_args))
) |
| 3639 else: |
| 3640 code.putln('if (kw_args == 1) {') |
| 3641 code.putln('const Py_ssize_t index = %d;' % first_optional_arg) |
| 3642 code.putln('PyObject* value = PyDict_GetItem(%s, *%s[index]);' % ( |
| 3643 Naming.kwds_cname, Naming.pykwdlist_cname)) |
| 3644 code.putln('if (value) { values[index] = value; kw_args--; }') |
| 3645 if len(optional_args) > 1: |
| 3646 code.putln('}') |
| 3647 code.putln('}') |
| 3648 |
| 3649 def generate_argument_conversion_code(self, code): |
| 3650 # Generate code to convert arguments from signature type to |
| 3651 # declared type, if needed. Also copies signature arguments |
| 3652 # into closure fields. |
| 3653 for arg in self.args: |
| 3654 if arg.needs_conversion: |
| 3655 self.generate_arg_conversion(arg, code) |
| 3656 |
| 3657 def generate_arg_conversion(self, arg, code): |
| 3658 # Generate conversion code for one argument. |
| 3659 old_type = arg.hdr_type |
| 3660 new_type = arg.type |
| 3661 if old_type.is_pyobject: |
| 3662 if arg.default: |
| 3663 code.putln("if (%s) {" % arg.hdr_cname) |
| 3664 else: |
| 3665 code.putln("assert(%s); {" % arg.hdr_cname) |
| 3666 self.generate_arg_conversion_from_pyobject(arg, code) |
| 3667 code.putln("}") |
| 3668 elif new_type.is_pyobject: |
| 3669 self.generate_arg_conversion_to_pyobject(arg, code) |
| 3670 else: |
| 3671 if new_type.assignable_from(old_type): |
| 3672 code.putln( |
| 3673 "%s = %s;" % (arg.entry.cname, arg.hdr_cname)) |
| 3674 else: |
| 3675 error(arg.pos, |
| 3676 "Cannot convert 1 argument from '%s' to '%s'" % |
| 3677 (old_type, new_type)) |
| 3678 |
| 3679 def generate_arg_conversion_from_pyobject(self, arg, code): |
| 3680 new_type = arg.type |
| 3681 func = new_type.from_py_function |
| 3682 # copied from CoerceFromPyTypeNode |
| 3683 if func: |
| 3684 lhs = arg.entry.cname |
| 3685 rhs = "%s(%s)" % (func, arg.hdr_cname) |
| 3686 if new_type.is_enum: |
| 3687 rhs = PyrexTypes.typecast(new_type, PyrexTypes.c_long_type, rhs) |
| 3688 code.putln("%s = %s; %s" % ( |
| 3689 lhs, |
| 3690 rhs, |
| 3691 code.error_goto_if(new_type.error_condition(arg.entry.cname), ar
g.pos))) |
| 3692 else: |
| 3693 error(arg.pos, |
| 3694 "Cannot convert Python object argument to type '%s'" |
| 3695 % new_type) |
| 3696 |
| 3697 def generate_arg_conversion_to_pyobject(self, arg, code): |
| 3698 old_type = arg.hdr_type |
| 3699 func = old_type.to_py_function |
| 3700 if func: |
| 3701 code.putln("%s = %s(%s); %s" % ( |
| 3702 arg.entry.cname, |
| 3703 func, |
| 3704 arg.hdr_cname, |
| 3705 code.error_goto_if_null(arg.entry.cname, arg.pos))) |
| 3706 code.put_var_gotref(arg.entry) |
| 3707 else: |
| 3708 error(arg.pos, |
| 3709 "Cannot convert argument of type '%s' to Python object" |
| 3710 % old_type) |
| 3711 |
| 3712 def generate_argument_type_tests(self, code): |
| 3713 # Generate type tests for args whose signature |
| 3714 # type is PyObject * and whose declared type is |
| 3715 # a subtype thereof. |
| 3716 for arg in self.args: |
| 3717 if arg.needs_type_test: |
| 3718 self.generate_arg_type_test(arg, code) |
| 3719 elif not arg.accept_none and (arg.type.is_pyobject or |
| 3720 arg.type.is_buffer or |
| 3721 arg.type.is_memoryviewslice): |
| 3722 self.generate_arg_none_check(arg, code) |
| 3723 |
| 3724 def error_value(self): |
| 3725 return self.signature.error_value |
| 3726 |
| 3727 |
| 3728 class GeneratorDefNode(DefNode): |
| 3729 # Generator function node that creates a new generator instance when called. |
| 3730 # |
| 3731 # gbody GeneratorBodyDefNode the function implementing the genera
tor |
| 3732 # |
| 3733 |
| 3734 is_generator = True |
| 3735 needs_closure = True |
| 3736 |
| 3737 child_attrs = DefNode.child_attrs + ["gbody"] |
| 3738 |
| 3739 def __init__(self, **kwargs): |
| 3740 # XXX: don't actually needs a body |
| 3741 kwargs['body'] = StatListNode(kwargs['pos'], stats=[]) |
| 3742 super(GeneratorDefNode, self).__init__(**kwargs) |
| 3743 |
| 3744 def analyse_declarations(self, env): |
| 3745 super(GeneratorDefNode, self).analyse_declarations(env) |
| 3746 self.gbody.local_scope = self.local_scope |
| 3747 self.gbody.analyse_declarations(env) |
| 3748 |
| 3749 def generate_function_body(self, env, code): |
| 3750 body_cname = self.gbody.entry.func_cname |
| 3751 |
| 3752 code.putln('{') |
| 3753 code.putln('__pyx_GeneratorObject *gen = __Pyx_Generator_New(' |
| 3754 '(__pyx_generator_body_t) %s, (PyObject *) %s); %s' % ( |
| 3755 body_cname, Naming.cur_scope_cname, |
| 3756 code.error_goto_if_null('gen', self.pos))) |
| 3757 code.put_decref(Naming.cur_scope_cname, py_object_type) |
| 3758 if self.requires_classobj: |
| 3759 classobj_cname = 'gen->classobj' |
| 3760 code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % ( |
| 3761 classobj_cname, Naming.self_cname)) |
| 3762 code.put_incref(classobj_cname, py_object_type) |
| 3763 code.put_giveref(classobj_cname) |
| 3764 code.put_finish_refcount_context() |
| 3765 code.putln('return (PyObject *) gen;') |
| 3766 code.putln('}') |
| 3767 |
| 3768 def generate_function_definitions(self, env, code): |
| 3769 env.use_utility_code(UtilityCode.load_cached("Generator", "Generator.c")
) |
| 3770 |
| 3771 self.gbody.generate_function_header(code, proto=True) |
| 3772 super(GeneratorDefNode, self).generate_function_definitions(env, code) |
| 3773 self.gbody.generate_function_definitions(env, code) |
| 3774 |
| 3775 |
| 3776 class GeneratorBodyDefNode(DefNode): |
| 3777 # Main code body of a generator implemented as a DefNode. |
| 3778 # |
| 3779 |
| 3780 is_generator_body = True |
| 3781 |
| 3782 def __init__(self, pos=None, name=None, body=None): |
| 3783 super(GeneratorBodyDefNode, self).__init__( |
| 3784 pos=pos, body=body, name=name, doc=None, |
| 3785 args=[], star_arg=None, starstar_arg=None) |
| 3786 |
| 3787 def declare_generator_body(self, env): |
| 3788 prefix = env.next_id(env.scope_prefix) |
| 3789 name = env.next_id('generator') |
| 3790 cname = Naming.genbody_prefix + prefix + name |
| 3791 entry = env.declare_var(None, py_object_type, self.pos, |
| 3792 cname=cname, visibility='private') |
| 3793 entry.func_cname = cname |
| 3794 entry.qualified_name = EncodedString(self.name) |
| 3795 self.entry = entry |
| 3796 |
| 3797 def analyse_declarations(self, env): |
| 3798 self.analyse_argument_types(env) |
| 3799 self.declare_generator_body(env) |
| 3800 |
| 3801 def generate_function_header(self, code, proto=False): |
| 3802 header = "static PyObject *%s(__pyx_GeneratorObject *%s, PyObject *%s)"
% ( |
| 3803 self.entry.func_cname, |
| 3804 Naming.generator_cname, |
| 3805 Naming.sent_value_cname) |
| 3806 if proto: |
| 3807 code.putln('%s; /* proto */' % header) |
| 3808 else: |
| 3809 code.putln('%s /* generator body */\n{' % header) |
| 3810 |
| 3811 def generate_function_definitions(self, env, code): |
| 3812 lenv = self.local_scope |
| 3813 |
| 3814 # Generate closure function definitions |
| 3815 self.body.generate_function_definitions(lenv, code) |
| 3816 |
| 3817 # Generate C code for header and body of function |
| 3818 code.enter_cfunc_scope() |
| 3819 code.return_from_error_cleanup_label = code.new_label() |
| 3820 |
| 3821 # ----- Top-level constants used by this function |
| 3822 code.mark_pos(self.pos) |
| 3823 self.generate_cached_builtins_decls(lenv, code) |
| 3824 # ----- Function header |
| 3825 code.putln("") |
| 3826 self.generate_function_header(code) |
| 3827 closure_init_code = code.insertion_point() |
| 3828 # ----- Local variables |
| 3829 code.putln("PyObject *%s = NULL;" % Naming.retval_cname) |
| 3830 tempvardecl_code = code.insertion_point() |
| 3831 code.put_declare_refcount_context() |
| 3832 code.put_setup_refcount_context(self.entry.name) |
| 3833 |
| 3834 # ----- Resume switch point. |
| 3835 code.funcstate.init_closure_temps(lenv.scope_class.type.scope) |
| 3836 resume_code = code.insertion_point() |
| 3837 first_run_label = code.new_label('first_run') |
| 3838 code.use_label(first_run_label) |
| 3839 code.put_label(first_run_label) |
| 3840 code.putln('%s' % |
| 3841 (code.error_goto_if_null(Naming.sent_value_cname, self.pos))) |
| 3842 |
| 3843 # ----- Function body |
| 3844 self.generate_function_body(env, code) |
| 3845 # ----- Closure initialization |
| 3846 if lenv.scope_class.type.scope.entries: |
| 3847 closure_init_code.putln('%s = %s;' % ( |
| 3848 lenv.scope_class.type.declaration_code(Naming.cur_scope_cname), |
| 3849 lenv.scope_class.type.cast_code('%s->closure' % |
| 3850 Naming.generator_cname))) |
| 3851 |
| 3852 code.mark_pos(self.pos) |
| 3853 code.putln("") |
| 3854 code.putln("/* function exit code */") |
| 3855 |
| 3856 # on normal generator termination, we do not take the exception propagat
ion |
| 3857 # path: no traceback info is required and not creating it is much faster |
| 3858 if not self.body.is_terminator: |
| 3859 code.putln('PyErr_SetNone(PyExc_StopIteration);') |
| 3860 # ----- Error cleanup |
| 3861 if code.error_label in code.labels_used: |
| 3862 if not self.body.is_terminator: |
| 3863 code.put_goto(code.return_label) |
| 3864 code.put_label(code.error_label) |
| 3865 for cname, type in code.funcstate.all_managed_temps(): |
| 3866 code.put_xdecref(cname, type) |
| 3867 code.put_add_traceback(self.entry.qualified_name) |
| 3868 |
| 3869 # ----- Non-error return cleanup |
| 3870 code.put_label(code.return_label) |
| 3871 code.put_xdecref(Naming.retval_cname, py_object_type) |
| 3872 code.putln('%s->resume_label = -1;' % Naming.generator_cname) |
| 3873 # clean up as early as possible to help breaking any reference cycles |
| 3874 code.putln('__Pyx_Generator_clear((PyObject*)%s);' % Naming.generator_cn
ame) |
| 3875 code.put_finish_refcount_context() |
| 3876 code.putln('return NULL;') |
| 3877 code.putln("}") |
| 3878 |
| 3879 # ----- Go back and insert temp variable declarations |
| 3880 tempvardecl_code.put_temp_declarations(code.funcstate) |
| 3881 # ----- Generator resume code |
| 3882 resume_code.putln("switch (%s->resume_label) {" % ( |
| 3883 Naming.generator_cname)) |
| 3884 resume_code.putln("case 0: goto %s;" % first_run_label) |
| 3885 |
| 3886 for i, label in code.yield_labels: |
| 3887 resume_code.putln("case %d: goto %s;" % (i, label)) |
| 3888 resume_code.putln("default: /* CPython raises the right error here */") |
| 3889 resume_code.put_finish_refcount_context() |
| 3890 resume_code.putln("return NULL;") |
| 3891 resume_code.putln("}") |
| 3892 |
| 3893 code.exit_cfunc_scope() |
| 3894 |
| 3895 |
| 3896 class OverrideCheckNode(StatNode): |
| 3897 # A Node for dispatching to the def method if it |
| 3898 # is overriden. |
| 3899 # |
| 3900 # py_func |
| 3901 # |
| 3902 # args |
| 3903 # func_temp |
| 3904 # body |
| 3905 |
| 3906 child_attrs = ['body'] |
| 3907 |
| 3908 body = None |
| 3909 |
| 3910 def analyse_expressions(self, env): |
| 3911 self.args = env.arg_entries |
| 3912 if self.py_func.is_module_scope: |
| 3913 first_arg = 0 |
| 3914 else: |
| 3915 first_arg = 1 |
| 3916 import ExprNodes |
| 3917 self.func_node = ExprNodes.RawCNameExprNode(self.pos, py_object_type) |
| 3918 call_node = ExprNodes.SimpleCallNode( |
| 3919 self.pos, function=self.func_node, |
| 3920 args=[ ExprNodes.NameNode(self.pos, name=arg.name) |
| 3921 for arg in self.args[first_arg:] ]) |
| 3922 self.body = ReturnStatNode(self.pos, value=call_node) |
| 3923 self.body = self.body.analyse_expressions(env) |
| 3924 return self |
| 3925 |
| 3926 def generate_execution_code(self, code): |
| 3927 interned_attr_cname = code.intern_identifier(self.py_func.entry.name) |
| 3928 # Check to see if we are an extension type |
| 3929 if self.py_func.is_module_scope: |
| 3930 self_arg = "((PyObject *)%s)" % Naming.module_cname |
| 3931 else: |
| 3932 self_arg = "((PyObject *)%s)" % self.args[0].cname |
| 3933 code.putln("/* Check if called by wrapper */") |
| 3934 code.putln("if (unlikely(%s)) ;" % Naming.skip_dispatch_cname) |
| 3935 code.putln("/* Check if overridden in Python */") |
| 3936 if self.py_func.is_module_scope: |
| 3937 code.putln("else {") |
| 3938 else: |
| 3939 code.putln("else if (unlikely(Py_TYPE(%s)->tp_dictoffset != 0)) {" %
self_arg) |
| 3940 func_node_temp = code.funcstate.allocate_temp(py_object_type, manage_ref
=True) |
| 3941 self.func_node.set_cname(func_node_temp) |
| 3942 # need to get attribute manually--scope would return cdef method |
| 3943 code.globalstate.use_utility_code( |
| 3944 UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c")) |
| 3945 err = code.error_goto_if_null(func_node_temp, self.pos) |
| 3946 code.putln("%s = __Pyx_PyObject_GetAttrStr(%s, %s); %s" % ( |
| 3947 func_node_temp, self_arg, interned_attr_cname, err)) |
| 3948 code.put_gotref(func_node_temp) |
| 3949 is_builtin_function_or_method = "PyCFunction_Check(%s)" % func_node_temp |
| 3950 is_overridden = "(PyCFunction_GET_FUNCTION(%s) != (PyCFunction)%s)" % ( |
| 3951 func_node_temp, self.py_func.entry.func_cname) |
| 3952 code.putln("if (!%s || %s) {" % (is_builtin_function_or_method, is_overr
idden)) |
| 3953 self.body.generate_execution_code(code) |
| 3954 code.putln("}") |
| 3955 code.put_decref_clear(func_node_temp, PyrexTypes.py_object_type) |
| 3956 code.funcstate.release_temp(func_node_temp) |
| 3957 code.putln("}") |
| 3958 |
| 3959 class ClassDefNode(StatNode, BlockNode): |
| 3960 pass |
| 3961 |
| 3962 class PyClassDefNode(ClassDefNode): |
| 3963 # A Python class definition. |
| 3964 # |
| 3965 # name EncodedString Name of the class |
| 3966 # doc string or None |
| 3967 # body StatNode Attribute definition code |
| 3968 # entry Symtab.Entry |
| 3969 # scope PyClassScope |
| 3970 # decorators [DecoratorNode] list of decorators or None |
| 3971 # |
| 3972 # The following subnodes are constructed internally: |
| 3973 # |
| 3974 # dict DictNode Class dictionary or Py3 namespace |
| 3975 # classobj ClassNode Class object |
| 3976 # target NameNode Variable to assign class object to |
| 3977 |
| 3978 child_attrs = ["body", "dict", "metaclass", "mkw", "bases", "class_result", |
| 3979 "target", "class_cell", "decorators"] |
| 3980 decorators = None |
| 3981 class_result = None |
| 3982 is_py3_style_class = False # Python3 style class (kwargs) |
| 3983 metaclass = None |
| 3984 mkw = None |
| 3985 |
| 3986 def __init__(self, pos, name, bases, doc, body, decorators=None, |
| 3987 keyword_args=None, starstar_arg=None, force_py3_semantics=False
): |
| 3988 StatNode.__init__(self, pos) |
| 3989 self.name = name |
| 3990 self.doc = doc |
| 3991 self.body = body |
| 3992 self.decorators = decorators |
| 3993 self.bases = bases |
| 3994 import ExprNodes |
| 3995 if self.doc and Options.docstrings: |
| 3996 doc = embed_position(self.pos, self.doc) |
| 3997 doc_node = ExprNodes.StringNode(pos, value=doc) |
| 3998 else: |
| 3999 doc_node = None |
| 4000 |
| 4001 allow_py2_metaclass = not force_py3_semantics |
| 4002 if keyword_args or starstar_arg: |
| 4003 allow_py2_metaclass = False |
| 4004 self.is_py3_style_class = True |
| 4005 if keyword_args and not starstar_arg: |
| 4006 for i, item in list(enumerate(keyword_args.key_value_pairs))[::-
1]: |
| 4007 if item.key.value == 'metaclass': |
| 4008 if self.metaclass is not None: |
| 4009 error(item.pos, "keyword argument 'metaclass' passed
multiple times") |
| 4010 # special case: we already know the metaclass, |
| 4011 # so we don't need to do the "build kwargs, |
| 4012 # find metaclass" dance at runtime |
| 4013 self.metaclass = item.value |
| 4014 del keyword_args.key_value_pairs[i] |
| 4015 if starstar_arg: |
| 4016 self.mkw = ExprNodes.KeywordArgsNode( |
| 4017 pos, keyword_args=keyword_args and keyword_args.key_value_pa
irs or [], |
| 4018 starstar_arg=starstar_arg) |
| 4019 elif keyword_args.key_value_pairs: |
| 4020 self.mkw = keyword_args |
| 4021 else: |
| 4022 assert self.metaclass is not None |
| 4023 |
| 4024 if force_py3_semantics or self.bases or self.mkw or self.metaclass: |
| 4025 if self.metaclass is None: |
| 4026 if starstar_arg: |
| 4027 # **kwargs may contain 'metaclass' arg |
| 4028 mkdict = self.mkw |
| 4029 else: |
| 4030 mkdict = None |
| 4031 if (not mkdict and |
| 4032 self.bases.is_sequence_constructor and |
| 4033 not self.bases.args): |
| 4034 pass # no base classes => no inherited metaclass |
| 4035 else: |
| 4036 self.metaclass = ExprNodes.PyClassMetaclassNode( |
| 4037 pos, mkw=mkdict, bases=self.bases) |
| 4038 needs_metaclass_calculation = False |
| 4039 else: |
| 4040 needs_metaclass_calculation = True |
| 4041 |
| 4042 self.dict = ExprNodes.PyClassNamespaceNode( |
| 4043 pos, name=name, doc=doc_node, |
| 4044 metaclass=self.metaclass, bases=self.bases, mkw=self.mkw) |
| 4045 self.classobj = ExprNodes.Py3ClassNode( |
| 4046 pos, name=name, |
| 4047 bases=self.bases, dict=self.dict, doc=doc_node, |
| 4048 metaclass=self.metaclass, mkw=self.mkw, |
| 4049 calculate_metaclass=needs_metaclass_calculation, |
| 4050 allow_py2_metaclass=allow_py2_metaclass) |
| 4051 else: |
| 4052 # no bases, no metaclass => old style class creation |
| 4053 self.dict = ExprNodes.DictNode(pos, key_value_pairs=[]) |
| 4054 self.classobj = ExprNodes.ClassNode( |
| 4055 pos, name=name, |
| 4056 bases=bases, dict=self.dict, doc=doc_node) |
| 4057 |
| 4058 self.target = ExprNodes.NameNode(pos, name=name) |
| 4059 self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos) |
| 4060 |
| 4061 def as_cclass(self): |
| 4062 """ |
| 4063 Return this node as if it were declared as an extension class |
| 4064 """ |
| 4065 if self.is_py3_style_class: |
| 4066 error(self.classobj.pos, "Python3 style class could not be represent
ed as C class") |
| 4067 return |
| 4068 bases = self.classobj.bases.args |
| 4069 if len(bases) == 0: |
| 4070 base_class_name = None |
| 4071 base_class_module = None |
| 4072 elif len(bases) == 1: |
| 4073 base = bases[0] |
| 4074 path = [] |
| 4075 from ExprNodes import AttributeNode, NameNode |
| 4076 while isinstance(base, AttributeNode): |
| 4077 path.insert(0, base.attribute) |
| 4078 base = base.obj |
| 4079 if isinstance(base, NameNode): |
| 4080 path.insert(0, base.name) |
| 4081 base_class_name = path[-1] |
| 4082 if len(path) > 1: |
| 4083 base_class_module = u'.'.join(path[:-1]) |
| 4084 else: |
| 4085 base_class_module = None |
| 4086 else: |
| 4087 error(self.classobj.bases.args.pos, "Invalid base class") |
| 4088 else: |
| 4089 error(self.classobj.bases.args.pos, "C class may only have one base
class") |
| 4090 return None |
| 4091 |
| 4092 return CClassDefNode(self.pos, |
| 4093 visibility = 'private', |
| 4094 module_name = None, |
| 4095 class_name = self.name, |
| 4096 base_class_module = base_class_module, |
| 4097 base_class_name = base_class_name, |
| 4098 decorators = self.decorators, |
| 4099 body = self.body, |
| 4100 in_pxd = False, |
| 4101 doc = self.doc) |
| 4102 |
| 4103 def create_scope(self, env): |
| 4104 genv = env |
| 4105 while genv.is_py_class_scope or genv.is_c_class_scope: |
| 4106 genv = genv.outer_scope |
| 4107 cenv = self.scope = PyClassScope(name = self.name, outer_scope = genv) |
| 4108 return cenv |
| 4109 |
| 4110 def analyse_declarations(self, env): |
| 4111 class_result = self.classobj |
| 4112 if self.decorators: |
| 4113 from ExprNodes import SimpleCallNode |
| 4114 for decorator in self.decorators[::-1]: |
| 4115 class_result = SimpleCallNode( |
| 4116 decorator.pos, |
| 4117 function = decorator.decorator, |
| 4118 args = [class_result]) |
| 4119 self.decorators = None |
| 4120 self.class_result = class_result |
| 4121 self.class_result.analyse_declarations(env) |
| 4122 self.target.analyse_target_declaration(env) |
| 4123 cenv = self.create_scope(env) |
| 4124 cenv.directives = env.directives |
| 4125 cenv.class_obj_cname = self.target.entry.cname |
| 4126 self.body.analyse_declarations(cenv) |
| 4127 |
| 4128 def analyse_expressions(self, env): |
| 4129 if self.bases: |
| 4130 self.bases = self.bases.analyse_expressions(env) |
| 4131 if self.metaclass: |
| 4132 self.metaclass = self.metaclass.analyse_expressions(env) |
| 4133 if self.mkw: |
| 4134 self.mkw = self.mkw.analyse_expressions(env) |
| 4135 self.dict = self.dict.analyse_expressions(env) |
| 4136 self.class_result = self.class_result.analyse_expressions(env) |
| 4137 genv = env.global_scope() |
| 4138 cenv = self.scope |
| 4139 self.body = self.body.analyse_expressions(cenv) |
| 4140 self.target.analyse_target_expression(env, self.classobj) |
| 4141 self.class_cell = self.class_cell.analyse_expressions(cenv) |
| 4142 return self |
| 4143 |
| 4144 def generate_function_definitions(self, env, code): |
| 4145 self.generate_lambda_definitions(self.scope, code) |
| 4146 self.body.generate_function_definitions(self.scope, code) |
| 4147 |
| 4148 def generate_execution_code(self, code): |
| 4149 code.pyclass_stack.append(self) |
| 4150 cenv = self.scope |
| 4151 if self.bases: |
| 4152 self.bases.generate_evaluation_code(code) |
| 4153 if self.mkw: |
| 4154 self.mkw.generate_evaluation_code(code) |
| 4155 if self.metaclass: |
| 4156 self.metaclass.generate_evaluation_code(code) |
| 4157 self.dict.generate_evaluation_code(code) |
| 4158 cenv.namespace_cname = cenv.class_obj_cname = self.dict.result() |
| 4159 self.class_cell.generate_evaluation_code(code) |
| 4160 self.body.generate_execution_code(code) |
| 4161 self.class_result.generate_evaluation_code(code) |
| 4162 self.class_cell.generate_injection_code( |
| 4163 code, self.class_result.result()) |
| 4164 self.class_cell.generate_disposal_code(code) |
| 4165 cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result() |
| 4166 self.target.generate_assignment_code(self.class_result, code) |
| 4167 self.dict.generate_disposal_code(code) |
| 4168 self.dict.free_temps(code) |
| 4169 if self.metaclass: |
| 4170 self.metaclass.generate_disposal_code(code) |
| 4171 self.metaclass.free_temps(code) |
| 4172 if self.mkw: |
| 4173 self.mkw.generate_disposal_code(code) |
| 4174 self.mkw.free_temps(code) |
| 4175 if self.bases: |
| 4176 self.bases.generate_disposal_code(code) |
| 4177 self.bases.free_temps(code) |
| 4178 code.pyclass_stack.pop() |
| 4179 |
| 4180 class CClassDefNode(ClassDefNode): |
| 4181 # An extension type definition. |
| 4182 # |
| 4183 # visibility 'private' or 'public' or 'extern' |
| 4184 # typedef_flag boolean |
| 4185 # api boolean |
| 4186 # module_name string or None For import of extern type objects |
| 4187 # class_name string Unqualified name of class |
| 4188 # as_name string or None Name to declare as in this scope |
| 4189 # base_class_module string or None Module containing the base class |
| 4190 # base_class_name string or None Name of the base class |
| 4191 # objstruct_name string or None Specified C name of object struct |
| 4192 # typeobj_name string or None Specified C name of type object |
| 4193 # in_pxd boolean Is in a .pxd file |
| 4194 # decorators [DecoratorNode] list of decorators or None |
| 4195 # doc string or None |
| 4196 # body StatNode or None |
| 4197 # entry Symtab.Entry |
| 4198 # base_type PyExtensionType or None |
| 4199 # buffer_defaults_node DictNode or None Declares defaults for a buffer |
| 4200 # buffer_defaults_pos |
| 4201 |
| 4202 child_attrs = ["body"] |
| 4203 buffer_defaults_node = None |
| 4204 buffer_defaults_pos = None |
| 4205 typedef_flag = False |
| 4206 api = False |
| 4207 objstruct_name = None |
| 4208 typeobj_name = None |
| 4209 decorators = None |
| 4210 shadow = False |
| 4211 |
| 4212 def buffer_defaults(self, env): |
| 4213 if not hasattr(self, '_buffer_defaults'): |
| 4214 import Buffer |
| 4215 if self.buffer_defaults_node: |
| 4216 self._buffer_defaults = Buffer.analyse_buffer_options( |
| 4217 self.buffer_defaults_pos, |
| 4218 env, [], self.buffer_defaults_node, |
| 4219 need_complete=False) |
| 4220 else: |
| 4221 self._buffer_defaults = None |
| 4222 return self._buffer_defaults |
| 4223 |
| 4224 def declare(self, env): |
| 4225 if self.module_name and self.visibility != 'extern': |
| 4226 module_path = self.module_name.split(".") |
| 4227 home_scope = env.find_imported_module(module_path, self.pos) |
| 4228 if not home_scope: |
| 4229 return None |
| 4230 else: |
| 4231 home_scope = env |
| 4232 |
| 4233 self.entry = home_scope.declare_c_class( |
| 4234 name = self.class_name, |
| 4235 pos = self.pos, |
| 4236 defining = 0, |
| 4237 implementing = 0, |
| 4238 module_name = self.module_name, |
| 4239 base_type = None, |
| 4240 objstruct_cname = self.objstruct_name, |
| 4241 typeobj_cname = self.typeobj_name, |
| 4242 visibility = self.visibility, |
| 4243 typedef_flag = self.typedef_flag, |
| 4244 api = self.api, |
| 4245 buffer_defaults = self.buffer_defaults(env), |
| 4246 shadow = self.shadow) |
| 4247 |
| 4248 def analyse_declarations(self, env): |
| 4249 #print "CClassDefNode.analyse_declarations:", self.class_name |
| 4250 #print "...visibility =", self.visibility |
| 4251 #print "...module_name =", self.module_name |
| 4252 |
| 4253 if env.in_cinclude and not self.objstruct_name: |
| 4254 error(self.pos, "Object struct name specification required for " |
| 4255 "C class defined in 'extern from' block") |
| 4256 if self.decorators: |
| 4257 error(self.pos, |
| 4258 "Decorators not allowed on cdef classes (used on type '%s')" %
self.class_name) |
| 4259 self.base_type = None |
| 4260 # Now that module imports are cached, we need to |
| 4261 # import the modules for extern classes. |
| 4262 if self.module_name: |
| 4263 self.module = None |
| 4264 for module in env.cimported_modules: |
| 4265 if module.name == self.module_name: |
| 4266 self.module = module |
| 4267 if self.module is None: |
| 4268 self.module = ModuleScope(self.module_name, None, env.context) |
| 4269 self.module.has_extern_class = 1 |
| 4270 env.add_imported_module(self.module) |
| 4271 |
| 4272 if self.base_class_name: |
| 4273 if self.base_class_module: |
| 4274 base_class_scope = env.find_module(self.base_class_module, self.
pos) |
| 4275 else: |
| 4276 base_class_scope = env |
| 4277 if self.base_class_name == 'object': |
| 4278 # extension classes are special and don't need to inherit from o
bject |
| 4279 if base_class_scope is None or base_class_scope.lookup('object')
is None: |
| 4280 self.base_class_name = None |
| 4281 self.base_class_module = None |
| 4282 base_class_scope = None |
| 4283 if base_class_scope: |
| 4284 base_class_entry = base_class_scope.find(self.base_class_name, s
elf.pos) |
| 4285 if base_class_entry: |
| 4286 if not base_class_entry.is_type: |
| 4287 error(self.pos, "'%s' is not a type name" % self.base_cl
ass_name) |
| 4288 elif not base_class_entry.type.is_extension_type and \ |
| 4289 not (base_class_entry.type.is_builtin_type and |
| 4290 base_class_entry.type.objstruct_cname): |
| 4291 error(self.pos, "'%s' is not an extension type" % self.b
ase_class_name) |
| 4292 elif not base_class_entry.type.is_complete(): |
| 4293 error(self.pos, "Base class '%s' of type '%s' is incompl
ete" % ( |
| 4294 self.base_class_name, self.class_name)) |
| 4295 elif base_class_entry.type.scope and base_class_entry.type.s
cope.directives and \ |
| 4296 base_class_entry.type.is_final_type: |
| 4297 error(self.pos, "Base class '%s' of type '%s' is final"
% ( |
| 4298 self.base_class_name, self.class_name)) |
| 4299 elif base_class_entry.type.is_builtin_type and \ |
| 4300 base_class_entry.type.name in ('tuple', 'str', 'byt
es'): |
| 4301 error(self.pos, "inheritance from PyVarObject types like
'%s' is not currently supported" |
| 4302 % base_class_entry.type.name) |
| 4303 else: |
| 4304 self.base_type = base_class_entry.type |
| 4305 if env.directives.get('freelist', 0) > 0: |
| 4306 warning(self.pos, "freelists cannot be used on subtypes, onl
y the base class can manage them", 1) |
| 4307 |
| 4308 has_body = self.body is not None |
| 4309 if has_body and self.base_type and not self.base_type.scope: |
| 4310 # To properly initialize inherited attributes, the base type must |
| 4311 # be analysed before this type. |
| 4312 self.base_type.defered_declarations.append(lambda : self.analyse_dec
larations(env)) |
| 4313 return |
| 4314 |
| 4315 if self.module_name and self.visibility != 'extern': |
| 4316 module_path = self.module_name.split(".") |
| 4317 home_scope = env.find_imported_module(module_path, self.pos) |
| 4318 if not home_scope: |
| 4319 return |
| 4320 else: |
| 4321 home_scope = env |
| 4322 |
| 4323 if self.visibility == 'extern': |
| 4324 if (self.module_name == '__builtin__' and |
| 4325 self.class_name in Builtin.builtin_types and |
| 4326 env.qualified_name[:8] != 'cpython.'): # allow overloaded names
for cimporting from cpython |
| 4327 warning(self.pos, "%s already a builtin Cython type" % self.clas
s_name, 1) |
| 4328 |
| 4329 self.entry = home_scope.declare_c_class( |
| 4330 name = self.class_name, |
| 4331 pos = self.pos, |
| 4332 defining = has_body and self.in_pxd, |
| 4333 implementing = has_body and not self.in_pxd, |
| 4334 module_name = self.module_name, |
| 4335 base_type = self.base_type, |
| 4336 objstruct_cname = self.objstruct_name, |
| 4337 typeobj_cname = self.typeobj_name, |
| 4338 visibility = self.visibility, |
| 4339 typedef_flag = self.typedef_flag, |
| 4340 api = self.api, |
| 4341 buffer_defaults = self.buffer_defaults(env), |
| 4342 shadow = self.shadow) |
| 4343 |
| 4344 if self.shadow: |
| 4345 home_scope.lookup(self.class_name).as_variable = self.entry |
| 4346 if home_scope is not env and self.visibility == 'extern': |
| 4347 env.add_imported_entry(self.class_name, self.entry, self.pos) |
| 4348 self.scope = scope = self.entry.type.scope |
| 4349 if scope is not None: |
| 4350 scope.directives = env.directives |
| 4351 |
| 4352 if self.doc and Options.docstrings: |
| 4353 scope.doc = embed_position(self.pos, self.doc) |
| 4354 |
| 4355 if has_body: |
| 4356 self.body.analyse_declarations(scope) |
| 4357 if self.in_pxd: |
| 4358 scope.defined = 1 |
| 4359 else: |
| 4360 scope.implemented = 1 |
| 4361 env.allocate_vtable_names(self.entry) |
| 4362 |
| 4363 for thunk in self.entry.type.defered_declarations: |
| 4364 thunk() |
| 4365 |
| 4366 def analyse_expressions(self, env): |
| 4367 if self.body: |
| 4368 scope = self.entry.type.scope |
| 4369 self.body = self.body.analyse_expressions(scope) |
| 4370 return self |
| 4371 |
| 4372 def generate_function_definitions(self, env, code): |
| 4373 if self.body: |
| 4374 self.generate_lambda_definitions(self.scope, code) |
| 4375 self.body.generate_function_definitions(self.scope, code) |
| 4376 |
| 4377 def generate_execution_code(self, code): |
| 4378 # This is needed to generate evaluation code for |
| 4379 # default values of method arguments. |
| 4380 if self.body: |
| 4381 self.body.generate_execution_code(code) |
| 4382 |
| 4383 def annotate(self, code): |
| 4384 if self.body: |
| 4385 self.body.annotate(code) |
| 4386 |
| 4387 |
| 4388 class PropertyNode(StatNode): |
| 4389 # Definition of a property in an extension type. |
| 4390 # |
| 4391 # name string |
| 4392 # doc EncodedString or None Doc string |
| 4393 # entry Symtab.Entry |
| 4394 # body StatListNode |
| 4395 |
| 4396 child_attrs = ["body"] |
| 4397 |
| 4398 def analyse_declarations(self, env): |
| 4399 self.entry = env.declare_property(self.name, self.doc, self.pos) |
| 4400 self.entry.scope.directives = env.directives |
| 4401 self.body.analyse_declarations(self.entry.scope) |
| 4402 |
| 4403 def analyse_expressions(self, env): |
| 4404 self.body = self.body.analyse_expressions(env) |
| 4405 return self |
| 4406 |
| 4407 def generate_function_definitions(self, env, code): |
| 4408 self.body.generate_function_definitions(env, code) |
| 4409 |
| 4410 def generate_execution_code(self, code): |
| 4411 pass |
| 4412 |
| 4413 def annotate(self, code): |
| 4414 self.body.annotate(code) |
| 4415 |
| 4416 |
| 4417 class GlobalNode(StatNode): |
| 4418 # Global variable declaration. |
| 4419 # |
| 4420 # names [string] |
| 4421 |
| 4422 child_attrs = [] |
| 4423 |
| 4424 def analyse_declarations(self, env): |
| 4425 for name in self.names: |
| 4426 env.declare_global(name, self.pos) |
| 4427 |
| 4428 def analyse_expressions(self, env): |
| 4429 return self |
| 4430 |
| 4431 def generate_execution_code(self, code): |
| 4432 pass |
| 4433 |
| 4434 |
| 4435 class NonlocalNode(StatNode): |
| 4436 # Nonlocal variable declaration via the 'nonlocal' keyword. |
| 4437 # |
| 4438 # names [string] |
| 4439 |
| 4440 child_attrs = [] |
| 4441 |
| 4442 def analyse_declarations(self, env): |
| 4443 for name in self.names: |
| 4444 env.declare_nonlocal(name, self.pos) |
| 4445 |
| 4446 def analyse_expressions(self, env): |
| 4447 return self |
| 4448 |
| 4449 def generate_execution_code(self, code): |
| 4450 pass |
| 4451 |
| 4452 |
| 4453 class ExprStatNode(StatNode): |
| 4454 # Expression used as a statement. |
| 4455 # |
| 4456 # expr ExprNode |
| 4457 |
| 4458 child_attrs = ["expr"] |
| 4459 |
| 4460 def analyse_declarations(self, env): |
| 4461 import ExprNodes |
| 4462 if isinstance(self.expr, ExprNodes.GeneralCallNode): |
| 4463 func = self.expr.function.as_cython_attribute() |
| 4464 if func == u'declare': |
| 4465 args, kwds = self.expr.explicit_args_kwds() |
| 4466 if len(args): |
| 4467 error(self.expr.pos, "Variable names must be specified.") |
| 4468 for var, type_node in kwds.key_value_pairs: |
| 4469 type = type_node.analyse_as_type(env) |
| 4470 if type is None: |
| 4471 error(type_node.pos, "Unknown type") |
| 4472 else: |
| 4473 env.declare_var(var.value, type, var.pos, is_cdef = True
) |
| 4474 self.__class__ = PassStatNode |
| 4475 |
| 4476 def analyse_expressions(self, env): |
| 4477 self.expr.result_is_used = False # hint that .result() may safely be lef
t empty |
| 4478 self.expr = self.expr.analyse_expressions(env) |
| 4479 return self |
| 4480 |
| 4481 def nogil_check(self, env): |
| 4482 if self.expr.type.is_pyobject and self.expr.is_temp: |
| 4483 self.gil_error() |
| 4484 |
| 4485 gil_message = "Discarding owned Python object" |
| 4486 |
| 4487 def generate_execution_code(self, code): |
| 4488 self.expr.generate_evaluation_code(code) |
| 4489 if not self.expr.is_temp and self.expr.result(): |
| 4490 code.putln("%s;" % self.expr.result()) |
| 4491 self.expr.generate_disposal_code(code) |
| 4492 self.expr.free_temps(code) |
| 4493 |
| 4494 def generate_function_definitions(self, env, code): |
| 4495 self.expr.generate_function_definitions(env, code) |
| 4496 |
| 4497 def annotate(self, code): |
| 4498 self.expr.annotate(code) |
| 4499 |
| 4500 |
| 4501 class AssignmentNode(StatNode): |
| 4502 # Abstract base class for assignment nodes. |
| 4503 # |
| 4504 # The analyse_expressions and generate_execution_code |
| 4505 # phases of assignments are split into two sub-phases |
| 4506 # each, to enable all the right hand sides of a |
| 4507 # parallel assignment to be evaluated before assigning |
| 4508 # to any of the left hand sides. |
| 4509 |
| 4510 def analyse_expressions(self, env): |
| 4511 return self.analyse_types(env) |
| 4512 |
| 4513 # def analyse_expressions(self, env): |
| 4514 # self.analyse_expressions_1(env) |
| 4515 # self.analyse_expressions_2(env) |
| 4516 |
| 4517 def generate_execution_code(self, code): |
| 4518 self.generate_rhs_evaluation_code(code) |
| 4519 self.generate_assignment_code(code) |
| 4520 |
| 4521 |
| 4522 class SingleAssignmentNode(AssignmentNode): |
| 4523 # The simplest case: |
| 4524 # |
| 4525 # a = b |
| 4526 # |
| 4527 # lhs ExprNode Left hand side |
| 4528 # rhs ExprNode Right hand side |
| 4529 # first bool Is this guaranteed the first assignment to lhs? |
| 4530 |
| 4531 child_attrs = ["lhs", "rhs"] |
| 4532 first = False |
| 4533 declaration_only = False |
| 4534 |
| 4535 def analyse_declarations(self, env): |
| 4536 import ExprNodes |
| 4537 |
| 4538 # handle declarations of the form x = cython.foo() |
| 4539 if isinstance(self.rhs, ExprNodes.CallNode): |
| 4540 func_name = self.rhs.function.as_cython_attribute() |
| 4541 if func_name: |
| 4542 args, kwds = self.rhs.explicit_args_kwds() |
| 4543 |
| 4544 if func_name in ['declare', 'typedef']: |
| 4545 if len(args) > 2 or kwds is not None: |
| 4546 error(self.rhs.pos, "Can only declare one type at a time
.") |
| 4547 return |
| 4548 |
| 4549 type = args[0].analyse_as_type(env) |
| 4550 if type is None: |
| 4551 error(args[0].pos, "Unknown type") |
| 4552 return |
| 4553 lhs = self.lhs |
| 4554 if func_name == 'declare': |
| 4555 if isinstance(lhs, ExprNodes.NameNode): |
| 4556 vars = [(lhs.name, lhs.pos)] |
| 4557 elif isinstance(lhs, ExprNodes.TupleNode): |
| 4558 vars = [(var.name, var.pos) for var in lhs.args] |
| 4559 else: |
| 4560 error(lhs.pos, "Invalid declaration") |
| 4561 return |
| 4562 for var, pos in vars: |
| 4563 env.declare_var(var, type, pos, is_cdef = True) |
| 4564 if len(args) == 2: |
| 4565 # we have a value |
| 4566 self.rhs = args[1] |
| 4567 else: |
| 4568 self.declaration_only = True |
| 4569 else: |
| 4570 self.declaration_only = True |
| 4571 if not isinstance(lhs, ExprNodes.NameNode): |
| 4572 error(lhs.pos, "Invalid declaration.") |
| 4573 env.declare_typedef(lhs.name, type, self.pos, visibility
='private') |
| 4574 |
| 4575 elif func_name in ['struct', 'union']: |
| 4576 self.declaration_only = True |
| 4577 if len(args) > 0 or kwds is None: |
| 4578 error(self.rhs.pos, "Struct or union members must be giv
en by name.") |
| 4579 return |
| 4580 members = [] |
| 4581 for member, type_node in kwds.key_value_pairs: |
| 4582 type = type_node.analyse_as_type(env) |
| 4583 if type is None: |
| 4584 error(type_node.pos, "Unknown type") |
| 4585 else: |
| 4586 members.append((member.value, type, member.pos)) |
| 4587 if len(members) < len(kwds.key_value_pairs): |
| 4588 return |
| 4589 if not isinstance(self.lhs, ExprNodes.NameNode): |
| 4590 error(self.lhs.pos, "Invalid declaration.") |
| 4591 name = self.lhs.name |
| 4592 scope = StructOrUnionScope(name) |
| 4593 env.declare_struct_or_union(name, func_name, scope, False, s
elf.rhs.pos) |
| 4594 for member, type, pos in members: |
| 4595 scope.declare_var(member, type, pos) |
| 4596 |
| 4597 elif func_name == 'fused_type': |
| 4598 # dtype = cython.fused_type(...) |
| 4599 self.declaration_only = True |
| 4600 if kwds: |
| 4601 error(self.rhs.function.pos, |
| 4602 "fused_type does not take keyword arguments") |
| 4603 |
| 4604 fusednode = FusedTypeNode(self.rhs.pos, |
| 4605 name = self.lhs.name, types=args) |
| 4606 fusednode.analyse_declarations(env) |
| 4607 |
| 4608 if self.declaration_only: |
| 4609 return |
| 4610 else: |
| 4611 self.lhs.analyse_target_declaration(env) |
| 4612 |
| 4613 def analyse_types(self, env, use_temp = 0): |
| 4614 import ExprNodes |
| 4615 |
| 4616 self.rhs = self.rhs.analyse_types(env) |
| 4617 self.lhs = self.lhs.analyse_target_types(env) |
| 4618 self.lhs.gil_assignment_check(env) |
| 4619 |
| 4620 if self.lhs.memslice_broadcast or self.rhs.memslice_broadcast: |
| 4621 self.lhs.memslice_broadcast = True |
| 4622 self.rhs.memslice_broadcast = True |
| 4623 |
| 4624 is_index_node = isinstance(self.lhs, ExprNodes.IndexNode) |
| 4625 if (is_index_node and not self.rhs.type.is_memoryviewslice and |
| 4626 (self.lhs.memslice_slice or self.lhs.is_memslice_copy) and |
| 4627 (self.lhs.type.dtype.assignable_from(self.rhs.type) or |
| 4628 self.rhs.type.is_pyobject)): |
| 4629 # scalar slice assignment |
| 4630 self.lhs.is_memslice_scalar_assignment = True |
| 4631 dtype = self.lhs.type.dtype |
| 4632 else: |
| 4633 dtype = self.lhs.type |
| 4634 |
| 4635 rhs = self.rhs.coerce_to(dtype, env) |
| 4636 if use_temp or rhs.is_attribute or ( |
| 4637 not rhs.is_name and not rhs.is_literal and |
| 4638 rhs.type.is_pyobject): |
| 4639 # things like (cdef) attribute access are not safe (traverses pointe
rs) |
| 4640 rhs = rhs.coerce_to_temp(env) |
| 4641 elif rhs.type.is_pyobject: |
| 4642 rhs = rhs.coerce_to_simple(env) |
| 4643 self.rhs = rhs |
| 4644 return self |
| 4645 |
| 4646 def generate_rhs_evaluation_code(self, code): |
| 4647 self.rhs.generate_evaluation_code(code) |
| 4648 |
| 4649 def generate_assignment_code(self, code): |
| 4650 self.lhs.generate_assignment_code(self.rhs, code) |
| 4651 |
| 4652 def generate_function_definitions(self, env, code): |
| 4653 self.rhs.generate_function_definitions(env, code) |
| 4654 |
| 4655 def annotate(self, code): |
| 4656 self.lhs.annotate(code) |
| 4657 self.rhs.annotate(code) |
| 4658 |
| 4659 |
| 4660 class CascadedAssignmentNode(AssignmentNode): |
| 4661 # An assignment with multiple left hand sides: |
| 4662 # |
| 4663 # a = b = c |
| 4664 # |
| 4665 # lhs_list [ExprNode] Left hand sides |
| 4666 # rhs ExprNode Right hand sides |
| 4667 # |
| 4668 # Used internally: |
| 4669 # |
| 4670 # coerced_rhs_list [ExprNode] RHS coerced to type of each LHS |
| 4671 |
| 4672 child_attrs = ["lhs_list", "rhs", "coerced_rhs_list"] |
| 4673 coerced_rhs_list = None |
| 4674 |
| 4675 def analyse_declarations(self, env): |
| 4676 for lhs in self.lhs_list: |
| 4677 lhs.analyse_target_declaration(env) |
| 4678 |
| 4679 def analyse_types(self, env, use_temp = 0): |
| 4680 from ExprNodes import CloneNode, ProxyNode |
| 4681 |
| 4682 rhs = self.rhs.analyse_types(env) |
| 4683 if use_temp or rhs.is_attribute or ( |
| 4684 not rhs.is_name and not rhs.is_literal and |
| 4685 rhs.type.is_pyobject): |
| 4686 rhs = rhs.coerce_to_temp(env) |
| 4687 else: |
| 4688 rhs = rhs.coerce_to_simple(env) |
| 4689 self.rhs = ProxyNode(rhs) |
| 4690 |
| 4691 self.coerced_rhs_list = [] |
| 4692 for lhs in self.lhs_list: |
| 4693 lhs.analyse_target_types(env) |
| 4694 lhs.gil_assignment_check(env) |
| 4695 rhs = CloneNode(self.rhs) |
| 4696 rhs = rhs.coerce_to(lhs.type, env) |
| 4697 self.coerced_rhs_list.append(rhs) |
| 4698 return self |
| 4699 |
| 4700 def generate_rhs_evaluation_code(self, code): |
| 4701 self.rhs.generate_evaluation_code(code) |
| 4702 |
| 4703 def generate_assignment_code(self, code): |
| 4704 for i in range(len(self.lhs_list)): |
| 4705 lhs = self.lhs_list[i] |
| 4706 rhs = self.coerced_rhs_list[i] |
| 4707 rhs.generate_evaluation_code(code) |
| 4708 lhs.generate_assignment_code(rhs, code) |
| 4709 # Assignment has disposed of the cloned RHS |
| 4710 self.rhs.generate_disposal_code(code) |
| 4711 self.rhs.free_temps(code) |
| 4712 |
| 4713 def generate_function_definitions(self, env, code): |
| 4714 self.rhs.generate_function_definitions(env, code) |
| 4715 |
| 4716 def annotate(self, code): |
| 4717 for i in range(len(self.lhs_list)): |
| 4718 self.lhs_list[i].annotate(code) |
| 4719 self.coerced_rhs_list[i].annotate(code) |
| 4720 self.rhs.annotate(code) |
| 4721 |
| 4722 |
| 4723 class ParallelAssignmentNode(AssignmentNode): |
| 4724 # A combined packing/unpacking assignment: |
| 4725 # |
| 4726 # a, b, c = d, e, f |
| 4727 # |
| 4728 # This has been rearranged by the parser into |
| 4729 # |
| 4730 # a = d ; b = e ; c = f |
| 4731 # |
| 4732 # but we must evaluate all the right hand sides |
| 4733 # before assigning to any of the left hand sides. |
| 4734 # |
| 4735 # stats [AssignmentNode] The constituent assignments |
| 4736 |
| 4737 child_attrs = ["stats"] |
| 4738 |
| 4739 def analyse_declarations(self, env): |
| 4740 for stat in self.stats: |
| 4741 stat.analyse_declarations(env) |
| 4742 |
| 4743 def analyse_expressions(self, env): |
| 4744 self.stats = [ stat.analyse_types(env, use_temp = 1) |
| 4745 for stat in self.stats ] |
| 4746 return self |
| 4747 |
| 4748 # def analyse_expressions(self, env): |
| 4749 # for stat in self.stats: |
| 4750 # stat.analyse_expressions_1(env, use_temp = 1) |
| 4751 # for stat in self.stats: |
| 4752 # stat.analyse_expressions_2(env) |
| 4753 |
| 4754 def generate_execution_code(self, code): |
| 4755 for stat in self.stats: |
| 4756 stat.generate_rhs_evaluation_code(code) |
| 4757 for stat in self.stats: |
| 4758 stat.generate_assignment_code(code) |
| 4759 |
| 4760 def generate_function_definitions(self, env, code): |
| 4761 for stat in self.stats: |
| 4762 stat.generate_function_definitions(env, code) |
| 4763 |
| 4764 def annotate(self, code): |
| 4765 for stat in self.stats: |
| 4766 stat.annotate(code) |
| 4767 |
| 4768 |
| 4769 class InPlaceAssignmentNode(AssignmentNode): |
| 4770 # An in place arithmetic operand: |
| 4771 # |
| 4772 # a += b |
| 4773 # a -= b |
| 4774 # ... |
| 4775 # |
| 4776 # lhs ExprNode Left hand side |
| 4777 # rhs ExprNode Right hand side |
| 4778 # operator char one of "+-*/%^&|" |
| 4779 # |
| 4780 # This code is a bit tricky because in order to obey Python |
| 4781 # semantics the sub-expressions (e.g. indices) of the lhs must |
| 4782 # not be evaluated twice. So we must re-use the values calculated |
| 4783 # in evaluation phase for the assignment phase as well. |
| 4784 # Fortunately, the type of the lhs node is fairly constrained |
| 4785 # (it must be a NameNode, AttributeNode, or IndexNode). |
| 4786 |
| 4787 child_attrs = ["lhs", "rhs"] |
| 4788 |
| 4789 def analyse_declarations(self, env): |
| 4790 self.lhs.analyse_target_declaration(env) |
| 4791 |
| 4792 def analyse_types(self, env): |
| 4793 self.rhs = self.rhs.analyse_types(env) |
| 4794 self.lhs = self.lhs.analyse_target_types(env) |
| 4795 |
| 4796 # When assigning to a fully indexed buffer or memoryview, coerce the rhs |
| 4797 if (self.lhs.is_subscript and |
| 4798 (self.lhs.memslice_index or self.lhs.is_buffer_access)): |
| 4799 self.rhs = self.rhs.coerce_to(self.lhs.type, env) |
| 4800 elif self.lhs.type.is_string and self.operator in '+-': |
| 4801 # use pointer arithmetic for char* LHS instead of string concat |
| 4802 self.rhs = self.rhs.coerce_to(PyrexTypes.c_py_ssize_t_type, env) |
| 4803 return self |
| 4804 |
| 4805 def generate_execution_code(self, code): |
| 4806 self.rhs.generate_evaluation_code(code) |
| 4807 self.lhs.generate_subexpr_evaluation_code(code) |
| 4808 c_op = self.operator |
| 4809 if c_op == "//": |
| 4810 c_op = "/" |
| 4811 elif c_op == "**": |
| 4812 error(self.pos, "No C inplace power operator") |
| 4813 if self.lhs.is_subscript and self.lhs.is_buffer_access: |
| 4814 if self.lhs.type.is_pyobject: |
| 4815 error(self.pos, "In-place operators not allowed on object buffer
s in this release.") |
| 4816 if (c_op in ('/', '%') and self.lhs.type.is_int |
| 4817 and not code.globalstate.directives['cdivision']): |
| 4818 error(self.pos, "In-place non-c divide operators not allowed on
int buffers.") |
| 4819 self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op) |
| 4820 else: |
| 4821 # C++ |
| 4822 # TODO: make sure overload is declared |
| 4823 code.putln("%s %s= %s;" % (self.lhs.result(), c_op, self.rhs.result(
))) |
| 4824 self.lhs.generate_subexpr_disposal_code(code) |
| 4825 self.lhs.free_subexpr_temps(code) |
| 4826 self.rhs.generate_disposal_code(code) |
| 4827 self.rhs.free_temps(code) |
| 4828 |
| 4829 def annotate(self, code): |
| 4830 self.lhs.annotate(code) |
| 4831 self.rhs.annotate(code) |
| 4832 |
| 4833 def create_binop_node(self): |
| 4834 import ExprNodes |
| 4835 return ExprNodes.binop_node(self.pos, self.operator, self.lhs, self.rhs) |
| 4836 |
| 4837 |
| 4838 class PrintStatNode(StatNode): |
| 4839 # print statement |
| 4840 # |
| 4841 # arg_tuple TupleNode |
| 4842 # stream ExprNode or None (stdout) |
| 4843 # append_newline boolean |
| 4844 |
| 4845 child_attrs = ["arg_tuple", "stream"] |
| 4846 |
| 4847 def analyse_expressions(self, env): |
| 4848 if self.stream: |
| 4849 stream = self.stream.analyse_expressions(env) |
| 4850 self.stream = stream.coerce_to_pyobject(env) |
| 4851 arg_tuple = self.arg_tuple.analyse_expressions(env) |
| 4852 self.arg_tuple = arg_tuple.coerce_to_pyobject(env) |
| 4853 env.use_utility_code(printing_utility_code) |
| 4854 if len(self.arg_tuple.args) == 1 and self.append_newline: |
| 4855 env.use_utility_code(printing_one_utility_code) |
| 4856 return self |
| 4857 |
| 4858 nogil_check = Node.gil_error |
| 4859 gil_message = "Python print statement" |
| 4860 |
| 4861 def generate_execution_code(self, code): |
| 4862 if self.stream: |
| 4863 self.stream.generate_evaluation_code(code) |
| 4864 stream_result = self.stream.py_result() |
| 4865 else: |
| 4866 stream_result = '0' |
| 4867 if len(self.arg_tuple.args) == 1 and self.append_newline: |
| 4868 arg = self.arg_tuple.args[0] |
| 4869 arg.generate_evaluation_code(code) |
| 4870 |
| 4871 code.putln( |
| 4872 "if (__Pyx_PrintOne(%s, %s) < 0) %s" % ( |
| 4873 stream_result, |
| 4874 arg.py_result(), |
| 4875 code.error_goto(self.pos))) |
| 4876 arg.generate_disposal_code(code) |
| 4877 arg.free_temps(code) |
| 4878 else: |
| 4879 self.arg_tuple.generate_evaluation_code(code) |
| 4880 code.putln( |
| 4881 "if (__Pyx_Print(%s, %s, %d) < 0) %s" % ( |
| 4882 stream_result, |
| 4883 self.arg_tuple.py_result(), |
| 4884 self.append_newline, |
| 4885 code.error_goto(self.pos))) |
| 4886 self.arg_tuple.generate_disposal_code(code) |
| 4887 self.arg_tuple.free_temps(code) |
| 4888 |
| 4889 if self.stream: |
| 4890 self.stream.generate_disposal_code(code) |
| 4891 self.stream.free_temps(code) |
| 4892 |
| 4893 def generate_function_definitions(self, env, code): |
| 4894 if self.stream: |
| 4895 self.stream.generate_function_definitions(env, code) |
| 4896 self.arg_tuple.generate_function_definitions(env, code) |
| 4897 |
| 4898 def annotate(self, code): |
| 4899 if self.stream: |
| 4900 self.stream.annotate(code) |
| 4901 self.arg_tuple.annotate(code) |
| 4902 |
| 4903 |
| 4904 class ExecStatNode(StatNode): |
| 4905 # exec statement |
| 4906 # |
| 4907 # args [ExprNode] |
| 4908 |
| 4909 child_attrs = ["args"] |
| 4910 |
| 4911 def analyse_expressions(self, env): |
| 4912 for i, arg in enumerate(self.args): |
| 4913 arg = arg.analyse_expressions(env) |
| 4914 arg = arg.coerce_to_pyobject(env) |
| 4915 self.args[i] = arg |
| 4916 env.use_utility_code(Builtin.pyexec_utility_code) |
| 4917 return self |
| 4918 |
| 4919 nogil_check = Node.gil_error |
| 4920 gil_message = "Python exec statement" |
| 4921 |
| 4922 def generate_execution_code(self, code): |
| 4923 args = [] |
| 4924 for arg in self.args: |
| 4925 arg.generate_evaluation_code(code) |
| 4926 args.append( arg.py_result() ) |
| 4927 args = tuple(args + ['0', '0'][:3-len(args)]) |
| 4928 temp_result = code.funcstate.allocate_temp(PyrexTypes.py_object_type, ma
nage_ref=True) |
| 4929 code.putln("%s = __Pyx_PyExec3(%s, %s, %s);" % ( |
| 4930 (temp_result,) + args)) |
| 4931 for arg in self.args: |
| 4932 arg.generate_disposal_code(code) |
| 4933 arg.free_temps(code) |
| 4934 code.putln( |
| 4935 code.error_goto_if_null(temp_result, self.pos)) |
| 4936 code.put_gotref(temp_result) |
| 4937 code.put_decref_clear(temp_result, py_object_type) |
| 4938 code.funcstate.release_temp(temp_result) |
| 4939 |
| 4940 def annotate(self, code): |
| 4941 for arg in self.args: |
| 4942 arg.annotate(code) |
| 4943 |
| 4944 |
| 4945 class DelStatNode(StatNode): |
| 4946 # del statement |
| 4947 # |
| 4948 # args [ExprNode] |
| 4949 |
| 4950 child_attrs = ["args"] |
| 4951 ignore_nonexisting = False |
| 4952 |
| 4953 def analyse_declarations(self, env): |
| 4954 for arg in self.args: |
| 4955 arg.analyse_target_declaration(env) |
| 4956 |
| 4957 def analyse_expressions(self, env): |
| 4958 for i, arg in enumerate(self.args): |
| 4959 arg = self.args[i] = arg.analyse_target_expression(env, None) |
| 4960 if arg.type.is_pyobject or (arg.is_name and |
| 4961 arg.type.is_memoryviewslice): |
| 4962 if arg.is_name and arg.entry.is_cglobal: |
| 4963 error(arg.pos, "Deletion of global C variable") |
| 4964 elif arg.type.is_ptr and arg.type.base_type.is_cpp_class: |
| 4965 self.cpp_check(env) |
| 4966 elif arg.type.is_cpp_class: |
| 4967 error(arg.pos, "Deletion of non-heap C++ object") |
| 4968 elif arg.is_subscript and arg.base.type is Builtin.bytearray_type: |
| 4969 pass # del ba[i] |
| 4970 else: |
| 4971 error(arg.pos, "Deletion of non-Python, non-C++ object") |
| 4972 #arg.release_target_temp(env) |
| 4973 return self |
| 4974 |
| 4975 def nogil_check(self, env): |
| 4976 for arg in self.args: |
| 4977 if arg.type.is_pyobject: |
| 4978 self.gil_error() |
| 4979 |
| 4980 gil_message = "Deleting Python object" |
| 4981 |
| 4982 def generate_execution_code(self, code): |
| 4983 for arg in self.args: |
| 4984 if (arg.type.is_pyobject or |
| 4985 arg.type.is_memoryviewslice or |
| 4986 arg.is_subscript and arg.base.type is Builtin.bytearray_type
): |
| 4987 arg.generate_deletion_code( |
| 4988 code, ignore_nonexisting=self.ignore_nonexisting) |
| 4989 elif arg.type.is_ptr and arg.type.base_type.is_cpp_class: |
| 4990 arg.generate_result_code(code) |
| 4991 code.putln("delete %s;" % arg.result()) |
| 4992 # else error reported earlier |
| 4993 |
| 4994 def annotate(self, code): |
| 4995 for arg in self.args: |
| 4996 arg.annotate(code) |
| 4997 |
| 4998 |
| 4999 class PassStatNode(StatNode): |
| 5000 # pass statement |
| 5001 |
| 5002 child_attrs = [] |
| 5003 |
| 5004 def analyse_expressions(self, env): |
| 5005 return self |
| 5006 |
| 5007 def generate_execution_code(self, code): |
| 5008 pass |
| 5009 |
| 5010 |
| 5011 class IndirectionNode(StatListNode): |
| 5012 """ |
| 5013 This adds an indirection so that the node can be shared and a subtree can |
| 5014 be removed at any time by clearing self.stats. |
| 5015 """ |
| 5016 |
| 5017 def __init__(self, stats): |
| 5018 super(IndirectionNode, self).__init__(stats[0].pos, stats=stats) |
| 5019 |
| 5020 class BreakStatNode(StatNode): |
| 5021 |
| 5022 child_attrs = [] |
| 5023 is_terminator = True |
| 5024 |
| 5025 def analyse_expressions(self, env): |
| 5026 return self |
| 5027 |
| 5028 def generate_execution_code(self, code): |
| 5029 if not code.break_label: |
| 5030 error(self.pos, "break statement not inside loop") |
| 5031 else: |
| 5032 code.put_goto(code.break_label) |
| 5033 |
| 5034 |
| 5035 class ContinueStatNode(StatNode): |
| 5036 |
| 5037 child_attrs = [] |
| 5038 is_terminator = True |
| 5039 |
| 5040 def analyse_expressions(self, env): |
| 5041 return self |
| 5042 |
| 5043 def generate_execution_code(self, code): |
| 5044 if code.funcstate.in_try_finally: |
| 5045 error(self.pos, "continue statement inside try of try...finally") |
| 5046 elif not code.continue_label: |
| 5047 error(self.pos, "continue statement not inside loop") |
| 5048 else: |
| 5049 code.put_goto(code.continue_label) |
| 5050 |
| 5051 |
| 5052 class ReturnStatNode(StatNode): |
| 5053 # return statement |
| 5054 # |
| 5055 # value ExprNode or None |
| 5056 # return_type PyrexType |
| 5057 # in_generator return inside of generator => raise StopIteration |
| 5058 |
| 5059 child_attrs = ["value"] |
| 5060 is_terminator = True |
| 5061 in_generator = False |
| 5062 |
| 5063 # Whether we are in a parallel section |
| 5064 in_parallel = False |
| 5065 |
| 5066 def analyse_expressions(self, env): |
| 5067 return_type = env.return_type |
| 5068 self.return_type = return_type |
| 5069 if not return_type: |
| 5070 error(self.pos, "Return not inside a function body") |
| 5071 return self |
| 5072 if self.value: |
| 5073 self.value = self.value.analyse_types(env) |
| 5074 if return_type.is_void or return_type.is_returncode: |
| 5075 error(self.value.pos, |
| 5076 "Return with value in void function") |
| 5077 else: |
| 5078 self.value = self.value.coerce_to(env.return_type, env) |
| 5079 else: |
| 5080 if (not return_type.is_void |
| 5081 and not return_type.is_pyobject |
| 5082 and not return_type.is_returncode): |
| 5083 error(self.pos, "Return value required") |
| 5084 return self |
| 5085 |
| 5086 def nogil_check(self, env): |
| 5087 if self.return_type.is_pyobject: |
| 5088 self.gil_error() |
| 5089 |
| 5090 gil_message = "Returning Python object" |
| 5091 |
| 5092 def generate_execution_code(self, code): |
| 5093 code.mark_pos(self.pos) |
| 5094 if not self.return_type: |
| 5095 # error reported earlier |
| 5096 return |
| 5097 if self.return_type.is_pyobject: |
| 5098 code.put_xdecref(Naming.retval_cname, |
| 5099 self.return_type) |
| 5100 |
| 5101 if self.value: |
| 5102 self.value.generate_evaluation_code(code) |
| 5103 if self.return_type.is_memoryviewslice: |
| 5104 import MemoryView |
| 5105 MemoryView.put_acquire_memoryviewslice( |
| 5106 lhs_cname=Naming.retval_cname, |
| 5107 lhs_type=self.return_type, |
| 5108 lhs_pos=self.value.pos, |
| 5109 rhs=self.value, |
| 5110 code=code, |
| 5111 have_gil=self.in_nogil_context) |
| 5112 elif self.in_generator: |
| 5113 # return value == raise StopIteration(value), but uncatchable |
| 5114 code.putln( |
| 5115 "%s = NULL; PyErr_SetObject(PyExc_StopIteration, %s);" % ( |
| 5116 Naming.retval_cname, |
| 5117 self.value.result_as(self.return_type))) |
| 5118 self.value.generate_disposal_code(code) |
| 5119 else: |
| 5120 self.value.make_owned_reference(code) |
| 5121 code.putln( |
| 5122 "%s = %s;" % ( |
| 5123 Naming.retval_cname, |
| 5124 self.value.result_as(self.return_type))) |
| 5125 self.value.generate_post_assignment_code(code) |
| 5126 self.value.free_temps(code) |
| 5127 else: |
| 5128 if self.return_type.is_pyobject: |
| 5129 code.put_init_to_py_none(Naming.retval_cname, self.return_type) |
| 5130 elif self.return_type.is_returncode: |
| 5131 self.put_return(code, self.return_type.default_value) |
| 5132 |
| 5133 for cname, type in code.funcstate.temps_holding_reference(): |
| 5134 code.put_decref_clear(cname, type) |
| 5135 |
| 5136 code.put_goto(code.return_label) |
| 5137 |
| 5138 def put_return(self, code, value): |
| 5139 if self.in_parallel: |
| 5140 code.putln_openmp("#pragma omp critical(__pyx_returning)") |
| 5141 code.putln("%s = %s;" % (Naming.retval_cname, value)) |
| 5142 |
| 5143 def generate_function_definitions(self, env, code): |
| 5144 if self.value is not None: |
| 5145 self.value.generate_function_definitions(env, code) |
| 5146 |
| 5147 def annotate(self, code): |
| 5148 if self.value: |
| 5149 self.value.annotate(code) |
| 5150 |
| 5151 |
| 5152 class RaiseStatNode(StatNode): |
| 5153 # raise statement |
| 5154 # |
| 5155 # exc_type ExprNode or None |
| 5156 # exc_value ExprNode or None |
| 5157 # exc_tb ExprNode or None |
| 5158 # cause ExprNode or None |
| 5159 |
| 5160 child_attrs = ["exc_type", "exc_value", "exc_tb", "cause"] |
| 5161 is_terminator = True |
| 5162 |
| 5163 def analyse_expressions(self, env): |
| 5164 if self.exc_type: |
| 5165 exc_type = self.exc_type.analyse_types(env) |
| 5166 self.exc_type = exc_type.coerce_to_pyobject(env) |
| 5167 if self.exc_value: |
| 5168 exc_value = self.exc_value.analyse_types(env) |
| 5169 self.exc_value = exc_value.coerce_to_pyobject(env) |
| 5170 if self.exc_tb: |
| 5171 exc_tb = self.exc_tb.analyse_types(env) |
| 5172 self.exc_tb = exc_tb.coerce_to_pyobject(env) |
| 5173 if self.cause: |
| 5174 cause = self.cause.analyse_types(env) |
| 5175 self.cause = cause.coerce_to_pyobject(env) |
| 5176 # special cases for builtin exceptions |
| 5177 self.builtin_exc_name = None |
| 5178 if self.exc_type and not self.exc_value and not self.exc_tb: |
| 5179 exc = self.exc_type |
| 5180 import ExprNodes |
| 5181 if (isinstance(exc, ExprNodes.SimpleCallNode) and |
| 5182 not (exc.args or (exc.arg_tuple is not None and |
| 5183 exc.arg_tuple.args))): |
| 5184 exc = exc.function # extract the exception type |
| 5185 if exc.is_name and exc.entry.is_builtin: |
| 5186 self.builtin_exc_name = exc.name |
| 5187 if self.builtin_exc_name == 'MemoryError': |
| 5188 self.exc_type = None # has a separate implementation |
| 5189 return self |
| 5190 |
| 5191 nogil_check = Node.gil_error |
| 5192 gil_message = "Raising exception" |
| 5193 |
| 5194 def generate_execution_code(self, code): |
| 5195 if self.builtin_exc_name == 'MemoryError': |
| 5196 code.putln('PyErr_NoMemory(); %s' % code.error_goto(self.pos)) |
| 5197 return |
| 5198 |
| 5199 if self.exc_type: |
| 5200 self.exc_type.generate_evaluation_code(code) |
| 5201 type_code = self.exc_type.py_result() |
| 5202 else: |
| 5203 type_code = "0" |
| 5204 if self.exc_value: |
| 5205 self.exc_value.generate_evaluation_code(code) |
| 5206 value_code = self.exc_value.py_result() |
| 5207 else: |
| 5208 value_code = "0" |
| 5209 if self.exc_tb: |
| 5210 self.exc_tb.generate_evaluation_code(code) |
| 5211 tb_code = self.exc_tb.py_result() |
| 5212 else: |
| 5213 tb_code = "0" |
| 5214 if self.cause: |
| 5215 self.cause.generate_evaluation_code(code) |
| 5216 cause_code = self.cause.py_result() |
| 5217 else: |
| 5218 cause_code = "0" |
| 5219 code.globalstate.use_utility_code(raise_utility_code) |
| 5220 code.putln( |
| 5221 "__Pyx_Raise(%s, %s, %s, %s);" % ( |
| 5222 type_code, |
| 5223 value_code, |
| 5224 tb_code, |
| 5225 cause_code)) |
| 5226 for obj in (self.exc_type, self.exc_value, self.exc_tb, self.cause): |
| 5227 if obj: |
| 5228 obj.generate_disposal_code(code) |
| 5229 obj.free_temps(code) |
| 5230 code.putln( |
| 5231 code.error_goto(self.pos)) |
| 5232 |
| 5233 def generate_function_definitions(self, env, code): |
| 5234 if self.exc_type is not None: |
| 5235 self.exc_type.generate_function_definitions(env, code) |
| 5236 if self.exc_value is not None: |
| 5237 self.exc_value.generate_function_definitions(env, code) |
| 5238 if self.exc_tb is not None: |
| 5239 self.exc_tb.generate_function_definitions(env, code) |
| 5240 if self.cause is not None: |
| 5241 self.cause.generate_function_definitions(env, code) |
| 5242 |
| 5243 def annotate(self, code): |
| 5244 if self.exc_type: |
| 5245 self.exc_type.annotate(code) |
| 5246 if self.exc_value: |
| 5247 self.exc_value.annotate(code) |
| 5248 if self.exc_tb: |
| 5249 self.exc_tb.annotate(code) |
| 5250 if self.cause: |
| 5251 self.cause.annotate(code) |
| 5252 |
| 5253 |
| 5254 class ReraiseStatNode(StatNode): |
| 5255 |
| 5256 child_attrs = [] |
| 5257 is_terminator = True |
| 5258 |
| 5259 def analyse_expressions(self, env): |
| 5260 return self |
| 5261 |
| 5262 nogil_check = Node.gil_error |
| 5263 gil_message = "Raising exception" |
| 5264 |
| 5265 def generate_execution_code(self, code): |
| 5266 vars = code.funcstate.exc_vars |
| 5267 if vars: |
| 5268 code.globalstate.use_utility_code(restore_exception_utility_code) |
| 5269 code.put_giveref(vars[0]) |
| 5270 code.put_giveref(vars[1]) |
| 5271 # fresh exceptions may not have a traceback yet (-> finally!) |
| 5272 code.put_xgiveref(vars[2]) |
| 5273 code.putln("__Pyx_ErrRestore(%s, %s, %s);" % tuple(vars)) |
| 5274 for varname in vars: |
| 5275 code.put("%s = 0; " % varname) |
| 5276 code.putln() |
| 5277 code.putln(code.error_goto(self.pos)) |
| 5278 else: |
| 5279 code.globalstate.use_utility_code( |
| 5280 UtilityCode.load_cached("ReRaiseException", "Exceptions.c")) |
| 5281 code.putln("__Pyx_ReraiseException(); %s" % code.error_goto(self.pos
)) |
| 5282 |
| 5283 class AssertStatNode(StatNode): |
| 5284 # assert statement |
| 5285 # |
| 5286 # cond ExprNode |
| 5287 # value ExprNode or None |
| 5288 |
| 5289 child_attrs = ["cond", "value"] |
| 5290 |
| 5291 def analyse_expressions(self, env): |
| 5292 self.cond = self.cond.analyse_boolean_expression(env) |
| 5293 if self.value: |
| 5294 value = self.value.analyse_types(env) |
| 5295 if value.type is Builtin.tuple_type or not value.type.is_builtin_typ
e: |
| 5296 # prevent tuple values from being interpreted as argument value
tuples |
| 5297 from ExprNodes import TupleNode |
| 5298 value = TupleNode(value.pos, args=[value], slow=True) |
| 5299 self.value = value.analyse_types(env, skip_children=True) |
| 5300 else: |
| 5301 self.value = value.coerce_to_pyobject(env) |
| 5302 return self |
| 5303 |
| 5304 nogil_check = Node.gil_error |
| 5305 gil_message = "Raising exception" |
| 5306 |
| 5307 def generate_execution_code(self, code): |
| 5308 code.putln("#ifndef CYTHON_WITHOUT_ASSERTIONS") |
| 5309 code.putln("if (unlikely(!Py_OptimizeFlag)) {") |
| 5310 self.cond.generate_evaluation_code(code) |
| 5311 code.putln( |
| 5312 "if (unlikely(!%s)) {" % |
| 5313 self.cond.result()) |
| 5314 if self.value: |
| 5315 self.value.generate_evaluation_code(code) |
| 5316 code.putln( |
| 5317 "PyErr_SetObject(PyExc_AssertionError, %s);" % |
| 5318 self.value.py_result()) |
| 5319 self.value.generate_disposal_code(code) |
| 5320 self.value.free_temps(code) |
| 5321 else: |
| 5322 code.putln( |
| 5323 "PyErr_SetNone(PyExc_AssertionError);") |
| 5324 code.putln( |
| 5325 code.error_goto(self.pos)) |
| 5326 code.putln( |
| 5327 "}") |
| 5328 self.cond.generate_disposal_code(code) |
| 5329 self.cond.free_temps(code) |
| 5330 code.putln( |
| 5331 "}") |
| 5332 code.putln("#endif") |
| 5333 |
| 5334 def generate_function_definitions(self, env, code): |
| 5335 self.cond.generate_function_definitions(env, code) |
| 5336 if self.value is not None: |
| 5337 self.value.generate_function_definitions(env, code) |
| 5338 |
| 5339 def annotate(self, code): |
| 5340 self.cond.annotate(code) |
| 5341 if self.value: |
| 5342 self.value.annotate(code) |
| 5343 |
| 5344 |
| 5345 class IfStatNode(StatNode): |
| 5346 # if statement |
| 5347 # |
| 5348 # if_clauses [IfClauseNode] |
| 5349 # else_clause StatNode or None |
| 5350 |
| 5351 child_attrs = ["if_clauses", "else_clause"] |
| 5352 |
| 5353 def analyse_declarations(self, env): |
| 5354 for if_clause in self.if_clauses: |
| 5355 if_clause.analyse_declarations(env) |
| 5356 if self.else_clause: |
| 5357 self.else_clause.analyse_declarations(env) |
| 5358 |
| 5359 def analyse_expressions(self, env): |
| 5360 self.if_clauses = [ if_clause.analyse_expressions(env) |
| 5361 for if_clause in self.if_clauses ] |
| 5362 if self.else_clause: |
| 5363 self.else_clause = self.else_clause.analyse_expressions(env) |
| 5364 return self |
| 5365 |
| 5366 def generate_execution_code(self, code): |
| 5367 code.mark_pos(self.pos) |
| 5368 end_label = code.new_label() |
| 5369 for if_clause in self.if_clauses: |
| 5370 if_clause.generate_execution_code(code, end_label) |
| 5371 if self.else_clause: |
| 5372 code.putln("/*else*/ {") |
| 5373 self.else_clause.generate_execution_code(code) |
| 5374 code.putln("}") |
| 5375 code.put_label(end_label) |
| 5376 |
| 5377 def generate_function_definitions(self, env, code): |
| 5378 for clause in self.if_clauses: |
| 5379 clause.generate_function_definitions(env, code) |
| 5380 if self.else_clause is not None: |
| 5381 self.else_clause.generate_function_definitions(env, code) |
| 5382 |
| 5383 def annotate(self, code): |
| 5384 for if_clause in self.if_clauses: |
| 5385 if_clause.annotate(code) |
| 5386 if self.else_clause: |
| 5387 self.else_clause.annotate(code) |
| 5388 |
| 5389 |
| 5390 class IfClauseNode(Node): |
| 5391 # if or elif clause in an if statement |
| 5392 # |
| 5393 # condition ExprNode |
| 5394 # body StatNode |
| 5395 |
| 5396 child_attrs = ["condition", "body"] |
| 5397 |
| 5398 def analyse_declarations(self, env): |
| 5399 self.body.analyse_declarations(env) |
| 5400 |
| 5401 def analyse_expressions(self, env): |
| 5402 self.condition = \ |
| 5403 self.condition.analyse_temp_boolean_expression(env) |
| 5404 self.body = self.body.analyse_expressions(env) |
| 5405 return self |
| 5406 |
| 5407 def generate_execution_code(self, code, end_label): |
| 5408 self.condition.generate_evaluation_code(code) |
| 5409 code.putln( |
| 5410 "if (%s) {" % |
| 5411 self.condition.result()) |
| 5412 self.condition.generate_disposal_code(code) |
| 5413 self.condition.free_temps(code) |
| 5414 self.body.generate_execution_code(code) |
| 5415 if not self.body.is_terminator: |
| 5416 code.put_goto(end_label) |
| 5417 code.putln("}") |
| 5418 |
| 5419 def generate_function_definitions(self, env, code): |
| 5420 self.condition.generate_function_definitions(env, code) |
| 5421 self.body.generate_function_definitions(env, code) |
| 5422 |
| 5423 def annotate(self, code): |
| 5424 self.condition.annotate(code) |
| 5425 self.body.annotate(code) |
| 5426 |
| 5427 |
| 5428 class SwitchCaseNode(StatNode): |
| 5429 # Generated in the optimization of an if-elif-else node |
| 5430 # |
| 5431 # conditions [ExprNode] |
| 5432 # body StatNode |
| 5433 |
| 5434 child_attrs = ['conditions', 'body'] |
| 5435 |
| 5436 def generate_execution_code(self, code): |
| 5437 for cond in self.conditions: |
| 5438 code.mark_pos(cond.pos) |
| 5439 cond.generate_evaluation_code(code) |
| 5440 code.putln("case %s:" % cond.result()) |
| 5441 self.body.generate_execution_code(code) |
| 5442 code.putln("break;") |
| 5443 |
| 5444 def generate_function_definitions(self, env, code): |
| 5445 for cond in self.conditions: |
| 5446 cond.generate_function_definitions(env, code) |
| 5447 self.body.generate_function_definitions(env, code) |
| 5448 |
| 5449 def annotate(self, code): |
| 5450 for cond in self.conditions: |
| 5451 cond.annotate(code) |
| 5452 self.body.annotate(code) |
| 5453 |
| 5454 class SwitchStatNode(StatNode): |
| 5455 # Generated in the optimization of an if-elif-else node |
| 5456 # |
| 5457 # test ExprNode |
| 5458 # cases [SwitchCaseNode] |
| 5459 # else_clause StatNode or None |
| 5460 |
| 5461 child_attrs = ['test', 'cases', 'else_clause'] |
| 5462 |
| 5463 def generate_execution_code(self, code): |
| 5464 self.test.generate_evaluation_code(code) |
| 5465 code.putln("switch (%s) {" % self.test.result()) |
| 5466 for case in self.cases: |
| 5467 case.generate_execution_code(code) |
| 5468 if self.else_clause is not None: |
| 5469 code.putln("default:") |
| 5470 self.else_clause.generate_execution_code(code) |
| 5471 code.putln("break;") |
| 5472 else: |
| 5473 # Always generate a default clause to prevent C compiler warnings |
| 5474 # about unmatched enum values (it was not the user who decided to |
| 5475 # generate the switch statement, so shouldn't be bothered). |
| 5476 code.putln("default: break;") |
| 5477 code.putln("}") |
| 5478 |
| 5479 def generate_function_definitions(self, env, code): |
| 5480 self.test.generate_function_definitions(env, code) |
| 5481 for case in self.cases: |
| 5482 case.generate_function_definitions(env, code) |
| 5483 if self.else_clause is not None: |
| 5484 self.else_clause.generate_function_definitions(env, code) |
| 5485 |
| 5486 def annotate(self, code): |
| 5487 self.test.annotate(code) |
| 5488 for case in self.cases: |
| 5489 case.annotate(code) |
| 5490 if self.else_clause is not None: |
| 5491 self.else_clause.annotate(code) |
| 5492 |
| 5493 class LoopNode(object): |
| 5494 pass |
| 5495 |
| 5496 |
| 5497 class WhileStatNode(LoopNode, StatNode): |
| 5498 # while statement |
| 5499 # |
| 5500 # condition ExprNode |
| 5501 # body StatNode |
| 5502 # else_clause StatNode |
| 5503 |
| 5504 child_attrs = ["condition", "body", "else_clause"] |
| 5505 |
| 5506 def analyse_declarations(self, env): |
| 5507 self.body.analyse_declarations(env) |
| 5508 if self.else_clause: |
| 5509 self.else_clause.analyse_declarations(env) |
| 5510 |
| 5511 def analyse_expressions(self, env): |
| 5512 if self.condition: |
| 5513 self.condition = self.condition.analyse_temp_boolean_expression(env) |
| 5514 self.body = self.body.analyse_expressions(env) |
| 5515 if self.else_clause: |
| 5516 self.else_clause = self.else_clause.analyse_expressions(env) |
| 5517 return self |
| 5518 |
| 5519 def generate_execution_code(self, code): |
| 5520 old_loop_labels = code.new_loop_labels() |
| 5521 code.putln( |
| 5522 "while (1) {") |
| 5523 if self.condition: |
| 5524 self.condition.generate_evaluation_code(code) |
| 5525 self.condition.generate_disposal_code(code) |
| 5526 code.putln( |
| 5527 "if (!%s) break;" % |
| 5528 self.condition.result()) |
| 5529 self.condition.free_temps(code) |
| 5530 self.body.generate_execution_code(code) |
| 5531 code.put_label(code.continue_label) |
| 5532 code.putln("}") |
| 5533 break_label = code.break_label |
| 5534 code.set_loop_labels(old_loop_labels) |
| 5535 if self.else_clause: |
| 5536 code.mark_pos(self.else_clause.pos) |
| 5537 code.putln("/*else*/ {") |
| 5538 self.else_clause.generate_execution_code(code) |
| 5539 code.putln("}") |
| 5540 code.put_label(break_label) |
| 5541 |
| 5542 def generate_function_definitions(self, env, code): |
| 5543 if self.condition: |
| 5544 self.condition.generate_function_definitions(env, code) |
| 5545 self.body.generate_function_definitions(env, code) |
| 5546 if self.else_clause is not None: |
| 5547 self.else_clause.generate_function_definitions(env, code) |
| 5548 |
| 5549 def annotate(self, code): |
| 5550 if self.condition: |
| 5551 self.condition.annotate(code) |
| 5552 self.body.annotate(code) |
| 5553 if self.else_clause: |
| 5554 self.else_clause.annotate(code) |
| 5555 |
| 5556 |
| 5557 class DictIterationNextNode(Node): |
| 5558 # Helper node for calling PyDict_Next() inside of a WhileStatNode |
| 5559 # and checking the dictionary size for changes. Created in |
| 5560 # Optimize.py. |
| 5561 child_attrs = ['dict_obj', 'expected_size', 'pos_index_var', |
| 5562 'coerced_key_var', 'coerced_value_var', 'coerced_tuple_var', |
| 5563 'key_target', 'value_target', 'tuple_target', 'is_dict_flag'] |
| 5564 |
| 5565 coerced_key_var = key_ref = None |
| 5566 coerced_value_var = value_ref = None |
| 5567 coerced_tuple_var = tuple_ref = None |
| 5568 |
| 5569 def __init__(self, dict_obj, expected_size, pos_index_var, |
| 5570 key_target, value_target, tuple_target, is_dict_flag): |
| 5571 Node.__init__( |
| 5572 self, dict_obj.pos, |
| 5573 dict_obj = dict_obj, |
| 5574 expected_size = expected_size, |
| 5575 pos_index_var = pos_index_var, |
| 5576 key_target = key_target, |
| 5577 value_target = value_target, |
| 5578 tuple_target = tuple_target, |
| 5579 is_dict_flag = is_dict_flag, |
| 5580 is_temp = True, |
| 5581 type = PyrexTypes.c_bint_type) |
| 5582 |
| 5583 def analyse_expressions(self, env): |
| 5584 import ExprNodes |
| 5585 self.dict_obj = self.dict_obj.analyse_types(env) |
| 5586 self.expected_size = self.expected_size.analyse_types(env) |
| 5587 if self.pos_index_var: |
| 5588 self.pos_index_var = self.pos_index_var.analyse_types(env) |
| 5589 if self.key_target: |
| 5590 self.key_target = self.key_target.analyse_target_types(env) |
| 5591 self.key_ref = ExprNodes.TempNode(self.key_target.pos, PyrexTypes.py
_object_type) |
| 5592 self.coerced_key_var = self.key_ref.coerce_to(self.key_target.type,
env) |
| 5593 if self.value_target: |
| 5594 self.value_target = self.value_target.analyse_target_types(env) |
| 5595 self.value_ref = ExprNodes.TempNode(self.value_target.pos, type=Pyre
xTypes.py_object_type) |
| 5596 self.coerced_value_var = self.value_ref.coerce_to(self.value_target.
type, env) |
| 5597 if self.tuple_target: |
| 5598 self.tuple_target = self.tuple_target.analyse_target_types(env) |
| 5599 self.tuple_ref = ExprNodes.TempNode(self.tuple_target.pos, PyrexType
s.py_object_type) |
| 5600 self.coerced_tuple_var = self.tuple_ref.coerce_to(self.tuple_target.
type, env) |
| 5601 self.is_dict_flag = self.is_dict_flag.analyse_types(env) |
| 5602 return self |
| 5603 |
| 5604 def generate_function_definitions(self, env, code): |
| 5605 self.dict_obj.generate_function_definitions(env, code) |
| 5606 |
| 5607 def generate_execution_code(self, code): |
| 5608 code.globalstate.use_utility_code(UtilityCode.load_cached("dict_iter", "
Optimize.c")) |
| 5609 self.dict_obj.generate_evaluation_code(code) |
| 5610 |
| 5611 assignments = [] |
| 5612 temp_addresses = [] |
| 5613 for var, result, target in [(self.key_ref, self.coerced_key_var, self.ke
y_target), |
| 5614 (self.value_ref, self.coerced_value_var, sel
f.value_target), |
| 5615 (self.tuple_ref, self.coerced_tuple_var, sel
f.tuple_target)]: |
| 5616 if target is None: |
| 5617 addr = 'NULL' |
| 5618 else: |
| 5619 assignments.append((var, result, target)) |
| 5620 var.allocate(code) |
| 5621 addr = '&%s' % var.result() |
| 5622 temp_addresses.append(addr) |
| 5623 |
| 5624 result_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, False) |
| 5625 code.putln("%s = __Pyx_dict_iter_next(%s, %s, &%s, %s, %s, %s, %s);" % ( |
| 5626 result_temp, |
| 5627 self.dict_obj.py_result(), |
| 5628 self.expected_size.result(), |
| 5629 self.pos_index_var.result(), |
| 5630 temp_addresses[0], |
| 5631 temp_addresses[1], |
| 5632 temp_addresses[2], |
| 5633 self.is_dict_flag.result() |
| 5634 )) |
| 5635 code.putln("if (unlikely(%s == 0)) break;" % result_temp) |
| 5636 code.putln(code.error_goto_if("%s == -1" % result_temp, self.pos)) |
| 5637 code.funcstate.release_temp(result_temp) |
| 5638 |
| 5639 # evaluate all coercions before the assignments |
| 5640 for var, result, target in assignments: |
| 5641 code.put_gotref(var.result()) |
| 5642 for var, result, target in assignments: |
| 5643 result.generate_evaluation_code(code) |
| 5644 for var, result, target in assignments: |
| 5645 target.generate_assignment_code(result, code) |
| 5646 var.release(code) |
| 5647 |
| 5648 def ForStatNode(pos, **kw): |
| 5649 if 'iterator' in kw: |
| 5650 return ForInStatNode(pos, **kw) |
| 5651 else: |
| 5652 return ForFromStatNode(pos, **kw) |
| 5653 |
| 5654 class ForInStatNode(LoopNode, StatNode): |
| 5655 # for statement |
| 5656 # |
| 5657 # target ExprNode |
| 5658 # iterator IteratorNode |
| 5659 # body StatNode |
| 5660 # else_clause StatNode |
| 5661 # item NextNode used internally |
| 5662 |
| 5663 child_attrs = ["target", "iterator", "body", "else_clause"] |
| 5664 item = None |
| 5665 |
| 5666 def analyse_declarations(self, env): |
| 5667 import ExprNodes |
| 5668 self.target.analyse_target_declaration(env) |
| 5669 self.body.analyse_declarations(env) |
| 5670 if self.else_clause: |
| 5671 self.else_clause.analyse_declarations(env) |
| 5672 self.item = ExprNodes.NextNode(self.iterator) |
| 5673 |
| 5674 def analyse_expressions(self, env): |
| 5675 self.target = self.target.analyse_target_types(env) |
| 5676 self.iterator = self.iterator.analyse_expressions(env) |
| 5677 import ExprNodes |
| 5678 self.item = ExprNodes.NextNode(self.iterator) # must rewrap after analy
sis |
| 5679 self.item = self.item.analyse_expressions(env) |
| 5680 if (self.iterator.type.is_ptr or self.iterator.type.is_array) and \ |
| 5681 self.target.type.assignable_from(self.iterator.type): |
| 5682 # C array slice optimization. |
| 5683 pass |
| 5684 else: |
| 5685 self.item = self.item.coerce_to(self.target.type, env) |
| 5686 self.body = self.body.analyse_expressions(env) |
| 5687 if self.else_clause: |
| 5688 self.else_clause = self.else_clause.analyse_expressions(env) |
| 5689 return self |
| 5690 |
| 5691 def generate_execution_code(self, code): |
| 5692 old_loop_labels = code.new_loop_labels() |
| 5693 self.iterator.generate_evaluation_code(code) |
| 5694 code.putln("for (;;) {") |
| 5695 self.item.generate_evaluation_code(code) |
| 5696 self.target.generate_assignment_code(self.item, code) |
| 5697 self.body.generate_execution_code(code) |
| 5698 code.put_label(code.continue_label) |
| 5699 code.putln("}") |
| 5700 break_label = code.break_label |
| 5701 code.set_loop_labels(old_loop_labels) |
| 5702 |
| 5703 if self.else_clause: |
| 5704 # in nested loops, the 'else' block can contain a |
| 5705 # 'continue' statement for the outer loop, but we may need |
| 5706 # to generate cleanup code before taking that path, so we |
| 5707 # intercept it here |
| 5708 orig_continue_label = code.continue_label |
| 5709 code.continue_label = code.new_label('outer_continue') |
| 5710 |
| 5711 code.putln("/*else*/ {") |
| 5712 self.else_clause.generate_execution_code(code) |
| 5713 code.putln("}") |
| 5714 |
| 5715 if code.label_used(code.continue_label): |
| 5716 code.put_goto(break_label) |
| 5717 code.put_label(code.continue_label) |
| 5718 self.iterator.generate_disposal_code(code) |
| 5719 code.put_goto(orig_continue_label) |
| 5720 code.set_loop_labels(old_loop_labels) |
| 5721 |
| 5722 if code.label_used(break_label): |
| 5723 code.put_label(break_label) |
| 5724 self.iterator.generate_disposal_code(code) |
| 5725 self.iterator.free_temps(code) |
| 5726 |
| 5727 def generate_function_definitions(self, env, code): |
| 5728 self.target.generate_function_definitions(env, code) |
| 5729 self.iterator.generate_function_definitions(env, code) |
| 5730 self.body.generate_function_definitions(env, code) |
| 5731 if self.else_clause is not None: |
| 5732 self.else_clause.generate_function_definitions(env, code) |
| 5733 |
| 5734 def annotate(self, code): |
| 5735 self.target.annotate(code) |
| 5736 self.iterator.annotate(code) |
| 5737 self.body.annotate(code) |
| 5738 if self.else_clause: |
| 5739 self.else_clause.annotate(code) |
| 5740 self.item.annotate(code) |
| 5741 |
| 5742 |
| 5743 class ForFromStatNode(LoopNode, StatNode): |
| 5744 # for name from expr rel name rel expr |
| 5745 # |
| 5746 # target NameNode |
| 5747 # bound1 ExprNode |
| 5748 # relation1 string |
| 5749 # relation2 string |
| 5750 # bound2 ExprNode |
| 5751 # step ExprNode or None |
| 5752 # body StatNode |
| 5753 # else_clause StatNode or None |
| 5754 # |
| 5755 # Used internally: |
| 5756 # |
| 5757 # from_range bool |
| 5758 # is_py_target bool |
| 5759 # loopvar_node ExprNode (usually a NameNode or temp node) |
| 5760 # py_loopvar_node PyTempNode or None |
| 5761 child_attrs = ["target", "bound1", "bound2", "step", "body", "else_clause"] |
| 5762 |
| 5763 is_py_target = False |
| 5764 loopvar_node = None |
| 5765 py_loopvar_node = None |
| 5766 from_range = False |
| 5767 |
| 5768 gil_message = "For-loop using object bounds or target" |
| 5769 |
| 5770 def nogil_check(self, env): |
| 5771 for x in (self.target, self.bound1, self.bound2): |
| 5772 if x.type.is_pyobject: |
| 5773 self.gil_error() |
| 5774 |
| 5775 def analyse_declarations(self, env): |
| 5776 self.target.analyse_target_declaration(env) |
| 5777 self.body.analyse_declarations(env) |
| 5778 if self.else_clause: |
| 5779 self.else_clause.analyse_declarations(env) |
| 5780 |
| 5781 def analyse_expressions(self, env): |
| 5782 import ExprNodes |
| 5783 self.target = self.target.analyse_target_types(env) |
| 5784 self.bound1 = self.bound1.analyse_types(env) |
| 5785 self.bound2 = self.bound2.analyse_types(env) |
| 5786 if self.step is not None: |
| 5787 if isinstance(self.step, ExprNodes.UnaryMinusNode): |
| 5788 warning(self.step.pos, "Probable infinite loop in for-from-by st
atement. Consider switching the directions of the relations.", 2) |
| 5789 self.step = self.step.analyse_types(env) |
| 5790 |
| 5791 if self.target.type.is_numeric: |
| 5792 loop_type = self.target.type |
| 5793 else: |
| 5794 loop_type = PyrexTypes.c_int_type |
| 5795 if not self.bound1.type.is_pyobject: |
| 5796 loop_type = PyrexTypes.widest_numeric_type(loop_type, self.bound
1.type) |
| 5797 if not self.bound2.type.is_pyobject: |
| 5798 loop_type = PyrexTypes.widest_numeric_type(loop_type, self.bound
2.type) |
| 5799 if self.step is not None and not self.step.type.is_pyobject: |
| 5800 loop_type = PyrexTypes.widest_numeric_type(loop_type, self.step.
type) |
| 5801 self.bound1 = self.bound1.coerce_to(loop_type, env) |
| 5802 self.bound2 = self.bound2.coerce_to(loop_type, env) |
| 5803 if not self.bound2.is_literal: |
| 5804 self.bound2 = self.bound2.coerce_to_temp(env) |
| 5805 if self.step is not None: |
| 5806 self.step = self.step.coerce_to(loop_type, env) |
| 5807 if not self.step.is_literal: |
| 5808 self.step = self.step.coerce_to_temp(env) |
| 5809 |
| 5810 target_type = self.target.type |
| 5811 if not (target_type.is_pyobject or target_type.is_numeric): |
| 5812 error(self.target.pos, |
| 5813 "for-from loop variable must be c numeric type or Python object"
) |
| 5814 if target_type.is_numeric: |
| 5815 self.is_py_target = False |
| 5816 if isinstance(self.target, ExprNodes.IndexNode) and self.target.is_b
uffer_access: |
| 5817 raise error(self.pos, "Buffer indexing not allowed as for loop t
arget.") |
| 5818 self.loopvar_node = self.target |
| 5819 self.py_loopvar_node = None |
| 5820 else: |
| 5821 self.is_py_target = True |
| 5822 c_loopvar_node = ExprNodes.TempNode(self.pos, loop_type, env) |
| 5823 self.loopvar_node = c_loopvar_node |
| 5824 self.py_loopvar_node = \ |
| 5825 ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env) |
| 5826 self.body = self.body.analyse_expressions(env) |
| 5827 if self.else_clause: |
| 5828 self.else_clause = self.else_clause.analyse_expressions(env) |
| 5829 return self |
| 5830 |
| 5831 def generate_execution_code(self, code): |
| 5832 old_loop_labels = code.new_loop_labels() |
| 5833 from_range = self.from_range |
| 5834 self.bound1.generate_evaluation_code(code) |
| 5835 self.bound2.generate_evaluation_code(code) |
| 5836 offset, incop = self.relation_table[self.relation1] |
| 5837 if self.step is not None: |
| 5838 self.step.generate_evaluation_code(code) |
| 5839 step = self.step.result() |
| 5840 incop = "%s=%s" % (incop[0], step) |
| 5841 import ExprNodes |
| 5842 if isinstance(self.loopvar_node, ExprNodes.TempNode): |
| 5843 self.loopvar_node.allocate(code) |
| 5844 if isinstance(self.py_loopvar_node, ExprNodes.TempNode): |
| 5845 self.py_loopvar_node.allocate(code) |
| 5846 if from_range: |
| 5847 loopvar_name = code.funcstate.allocate_temp(self.target.type, False) |
| 5848 else: |
| 5849 loopvar_name = self.loopvar_node.result() |
| 5850 code.putln( |
| 5851 "for (%s = %s%s; %s %s %s; %s%s) {" % ( |
| 5852 loopvar_name, |
| 5853 self.bound1.result(), offset, |
| 5854 loopvar_name, self.relation2, self.bound2.result(), |
| 5855 loopvar_name, incop)) |
| 5856 if self.py_loopvar_node: |
| 5857 self.py_loopvar_node.generate_evaluation_code(code) |
| 5858 self.target.generate_assignment_code(self.py_loopvar_node, code) |
| 5859 elif from_range: |
| 5860 code.putln("%s = %s;" % ( |
| 5861 self.target.result(), loopvar_name)) |
| 5862 self.body.generate_execution_code(code) |
| 5863 code.put_label(code.continue_label) |
| 5864 if self.py_loopvar_node: |
| 5865 # This mess is to make for..from loops with python targets behave |
| 5866 # exactly like those with C targets with regards to re-assignment |
| 5867 # of the loop variable. |
| 5868 import ExprNodes |
| 5869 if self.target.entry.is_pyglobal: |
| 5870 # We know target is a NameNode, this is the only ugly case. |
| 5871 target_node = ExprNodes.PyTempNode(self.target.pos, None) |
| 5872 target_node.allocate(code) |
| 5873 interned_cname = code.intern_identifier(self.target.entry.name) |
| 5874 if self.target.entry.scope.is_module_scope: |
| 5875 code.globalstate.use_utility_code( |
| 5876 UtilityCode.load_cached("GetModuleGlobalName", "ObjectHa
ndling.c")) |
| 5877 lookup_func = '__Pyx_GetModuleGlobalName(%s)' |
| 5878 else: |
| 5879 code.globalstate.use_utility_code( |
| 5880 UtilityCode.load_cached("GetNameInClass", "ObjectHandlin
g.c")) |
| 5881 lookup_func = '__Pyx_GetNameInClass(%s, %%s)' % ( |
| 5882 self.target.entry.scope.namespace_cname) |
| 5883 code.putln("%s = %s; %s" % ( |
| 5884 target_node.result(), |
| 5885 lookup_func % interned_cname, |
| 5886 code.error_goto_if_null(target_node.result(), self.target.po
s))) |
| 5887 code.put_gotref(target_node.result()) |
| 5888 else: |
| 5889 target_node = self.target |
| 5890 from_py_node = ExprNodes.CoerceFromPyTypeNode( |
| 5891 self.loopvar_node.type, target_node, self.target.entry.scope) |
| 5892 from_py_node.temp_code = loopvar_name |
| 5893 from_py_node.generate_result_code(code) |
| 5894 if self.target.entry.is_pyglobal: |
| 5895 code.put_decref(target_node.result(), target_node.type) |
| 5896 target_node.release(code) |
| 5897 code.putln("}") |
| 5898 if self.py_loopvar_node: |
| 5899 # This is potentially wasteful, but we don't want the semantics to |
| 5900 # depend on whether or not the loop is a python type. |
| 5901 self.py_loopvar_node.generate_evaluation_code(code) |
| 5902 self.target.generate_assignment_code(self.py_loopvar_node, code) |
| 5903 if from_range: |
| 5904 code.funcstate.release_temp(loopvar_name) |
| 5905 break_label = code.break_label |
| 5906 code.set_loop_labels(old_loop_labels) |
| 5907 if self.else_clause: |
| 5908 code.putln("/*else*/ {") |
| 5909 self.else_clause.generate_execution_code(code) |
| 5910 code.putln("}") |
| 5911 code.put_label(break_label) |
| 5912 self.bound1.generate_disposal_code(code) |
| 5913 self.bound1.free_temps(code) |
| 5914 self.bound2.generate_disposal_code(code) |
| 5915 self.bound2.free_temps(code) |
| 5916 if isinstance(self.loopvar_node, ExprNodes.TempNode): |
| 5917 self.loopvar_node.release(code) |
| 5918 if isinstance(self.py_loopvar_node, ExprNodes.TempNode): |
| 5919 self.py_loopvar_node.release(code) |
| 5920 if self.step is not None: |
| 5921 self.step.generate_disposal_code(code) |
| 5922 self.step.free_temps(code) |
| 5923 |
| 5924 relation_table = { |
| 5925 # {relop : (initial offset, increment op)} |
| 5926 '<=': ("", "++"), |
| 5927 '<' : ("+1", "++"), |
| 5928 '>=': ("", "--"), |
| 5929 '>' : ("-1", "--") |
| 5930 } |
| 5931 |
| 5932 def generate_function_definitions(self, env, code): |
| 5933 self.target.generate_function_definitions(env, code) |
| 5934 self.bound1.generate_function_definitions(env, code) |
| 5935 self.bound2.generate_function_definitions(env, code) |
| 5936 if self.step is not None: |
| 5937 self.step.generate_function_definitions(env, code) |
| 5938 self.body.generate_function_definitions(env, code) |
| 5939 if self.else_clause is not None: |
| 5940 self.else_clause.generate_function_definitions(env, code) |
| 5941 |
| 5942 def annotate(self, code): |
| 5943 self.target.annotate(code) |
| 5944 self.bound1.annotate(code) |
| 5945 self.bound2.annotate(code) |
| 5946 if self.step: |
| 5947 self.step.annotate(code) |
| 5948 self.body.annotate(code) |
| 5949 if self.else_clause: |
| 5950 self.else_clause.annotate(code) |
| 5951 |
| 5952 |
| 5953 class WithStatNode(StatNode): |
| 5954 """ |
| 5955 Represents a Python with statement. |
| 5956 |
| 5957 Implemented by the WithTransform as follows: |
| 5958 |
| 5959 MGR = EXPR |
| 5960 EXIT = MGR.__exit__ |
| 5961 VALUE = MGR.__enter__() |
| 5962 EXC = True |
| 5963 try: |
| 5964 try: |
| 5965 TARGET = VALUE # optional |
| 5966 BODY |
| 5967 except: |
| 5968 EXC = False |
| 5969 if not EXIT(*EXCINFO): |
| 5970 raise |
| 5971 finally: |
| 5972 if EXC: |
| 5973 EXIT(None, None, None) |
| 5974 MGR = EXIT = VALUE = None |
| 5975 """ |
| 5976 # manager The with statement manager object |
| 5977 # target ExprNode the target lhs of the __enter__() call |
| 5978 # body StatNode |
| 5979 # enter_call ExprNode the call to the __enter__() method |
| 5980 # exit_var String the cname of the __exit__() method reference |
| 5981 |
| 5982 child_attrs = ["manager", "enter_call", "target", "body"] |
| 5983 |
| 5984 enter_call = None |
| 5985 |
| 5986 def analyse_declarations(self, env): |
| 5987 self.manager.analyse_declarations(env) |
| 5988 self.enter_call.analyse_declarations(env) |
| 5989 self.body.analyse_declarations(env) |
| 5990 |
| 5991 def analyse_expressions(self, env): |
| 5992 self.manager = self.manager.analyse_types(env) |
| 5993 self.enter_call = self.enter_call.analyse_types(env) |
| 5994 self.body = self.body.analyse_expressions(env) |
| 5995 return self |
| 5996 |
| 5997 def generate_function_definitions(self, env, code): |
| 5998 self.manager.generate_function_definitions(env, code) |
| 5999 self.enter_call.generate_function_definitions(env, code) |
| 6000 self.body.generate_function_definitions(env, code) |
| 6001 |
| 6002 def generate_execution_code(self, code): |
| 6003 code.putln("/*with:*/ {") |
| 6004 self.manager.generate_evaluation_code(code) |
| 6005 self.exit_var = code.funcstate.allocate_temp(py_object_type, manage_ref=
False) |
| 6006 code.globalstate.use_utility_code( |
| 6007 UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c")
) |
| 6008 code.putln("%s = __Pyx_PyObject_LookupSpecial(%s, %s); %s" % ( |
| 6009 self.exit_var, |
| 6010 self.manager.py_result(), |
| 6011 code.intern_identifier(EncodedString('__exit__')), |
| 6012 code.error_goto_if_null(self.exit_var, self.pos), |
| 6013 )) |
| 6014 code.put_gotref(self.exit_var) |
| 6015 |
| 6016 # need to free exit_var in the face of exceptions during setup |
| 6017 old_error_label = code.new_error_label() |
| 6018 intermediate_error_label = code.error_label |
| 6019 |
| 6020 self.enter_call.generate_evaluation_code(code) |
| 6021 if not self.target: |
| 6022 self.enter_call.generate_disposal_code(code) |
| 6023 self.enter_call.free_temps(code) |
| 6024 else: |
| 6025 # Otherwise, the node will be cleaned up by the |
| 6026 # WithTargetAssignmentStatNode after assigning its result |
| 6027 # to the target of the 'with' statement. |
| 6028 pass |
| 6029 self.manager.generate_disposal_code(code) |
| 6030 self.manager.free_temps(code) |
| 6031 |
| 6032 code.error_label = old_error_label |
| 6033 self.body.generate_execution_code(code) |
| 6034 |
| 6035 if code.label_used(intermediate_error_label): |
| 6036 step_over_label = code.new_label() |
| 6037 code.put_goto(step_over_label) |
| 6038 code.put_label(intermediate_error_label) |
| 6039 code.put_decref_clear(self.exit_var, py_object_type) |
| 6040 code.put_goto(old_error_label) |
| 6041 code.put_label(step_over_label) |
| 6042 |
| 6043 code.funcstate.release_temp(self.exit_var) |
| 6044 code.putln('}') |
| 6045 |
| 6046 class WithTargetAssignmentStatNode(AssignmentNode): |
| 6047 # The target assignment of the 'with' statement value (return |
| 6048 # value of the __enter__() call). |
| 6049 # |
| 6050 # This is a special cased assignment that steals the RHS reference |
| 6051 # and frees its temp. |
| 6052 # |
| 6053 # lhs ExprNode the assignment target |
| 6054 # rhs CloneNode a (coerced) CloneNode for the orig_rhs (not owned by
this node) |
| 6055 # orig_rhs ExprNode the original ExprNode of the rhs. this node will clea
n up the |
| 6056 # temps of the orig_rhs. basically, it takes ownership
of the node |
| 6057 # when the WithStatNode is done with it. |
| 6058 |
| 6059 child_attrs = ["lhs"] |
| 6060 |
| 6061 def analyse_declarations(self, env): |
| 6062 self.lhs.analyse_target_declaration(env) |
| 6063 |
| 6064 def analyse_expressions(self, env): |
| 6065 self.rhs = self.rhs.analyse_types(env) |
| 6066 self.lhs = self.lhs.analyse_target_types(env) |
| 6067 self.lhs.gil_assignment_check(env) |
| 6068 self.rhs = self.rhs.coerce_to(self.lhs.type, env) |
| 6069 return self |
| 6070 |
| 6071 def generate_execution_code(self, code): |
| 6072 if self.orig_rhs.type.is_pyobject: |
| 6073 # make sure rhs gets freed on errors, see below |
| 6074 old_error_label = code.new_error_label() |
| 6075 intermediate_error_label = code.error_label |
| 6076 |
| 6077 self.rhs.generate_evaluation_code(code) |
| 6078 self.lhs.generate_assignment_code(self.rhs, code) |
| 6079 |
| 6080 if self.orig_rhs.type.is_pyobject: |
| 6081 self.orig_rhs.generate_disposal_code(code) |
| 6082 code.error_label = old_error_label |
| 6083 if code.label_used(intermediate_error_label): |
| 6084 step_over_label = code.new_label() |
| 6085 code.put_goto(step_over_label) |
| 6086 code.put_label(intermediate_error_label) |
| 6087 self.orig_rhs.generate_disposal_code(code) |
| 6088 code.put_goto(old_error_label) |
| 6089 code.put_label(step_over_label) |
| 6090 |
| 6091 self.orig_rhs.free_temps(code) |
| 6092 |
| 6093 def annotate(self, code): |
| 6094 self.lhs.annotate(code) |
| 6095 self.rhs.annotate(code) |
| 6096 |
| 6097 |
| 6098 class TryExceptStatNode(StatNode): |
| 6099 # try .. except statement |
| 6100 # |
| 6101 # body StatNode |
| 6102 # except_clauses [ExceptClauseNode] |
| 6103 # else_clause StatNode or None |
| 6104 |
| 6105 child_attrs = ["body", "except_clauses", "else_clause"] |
| 6106 |
| 6107 def analyse_declarations(self, env): |
| 6108 self.body.analyse_declarations(env) |
| 6109 for except_clause in self.except_clauses: |
| 6110 except_clause.analyse_declarations(env) |
| 6111 if self.else_clause: |
| 6112 self.else_clause.analyse_declarations(env) |
| 6113 |
| 6114 def analyse_expressions(self, env): |
| 6115 self.body = self.body.analyse_expressions(env) |
| 6116 default_clause_seen = 0 |
| 6117 for i, except_clause in enumerate(self.except_clauses): |
| 6118 except_clause = self.except_clauses[i] = except_clause.analyse_expre
ssions(env) |
| 6119 if default_clause_seen: |
| 6120 error(except_clause.pos, "default 'except:' must be last") |
| 6121 if not except_clause.pattern: |
| 6122 default_clause_seen = 1 |
| 6123 self.has_default_clause = default_clause_seen |
| 6124 if self.else_clause: |
| 6125 self.else_clause = self.else_clause.analyse_expressions(env) |
| 6126 return self |
| 6127 |
| 6128 nogil_check = Node.gil_error |
| 6129 gil_message = "Try-except statement" |
| 6130 |
| 6131 def generate_execution_code(self, code): |
| 6132 old_return_label = code.return_label |
| 6133 old_break_label = code.break_label |
| 6134 old_continue_label = code.continue_label |
| 6135 old_error_label = code.new_error_label() |
| 6136 our_error_label = code.error_label |
| 6137 except_end_label = code.new_label('exception_handled') |
| 6138 except_error_label = code.new_label('except_error') |
| 6139 except_return_label = code.new_label('except_return') |
| 6140 try_return_label = code.new_label('try_return') |
| 6141 try_break_label = code.new_label('try_break') |
| 6142 try_continue_label = code.new_label('try_continue') |
| 6143 try_end_label = code.new_label('try_end') |
| 6144 |
| 6145 exc_save_vars = [code.funcstate.allocate_temp(py_object_type, False) |
| 6146 for _ in xrange(3)] |
| 6147 code.putln("{") |
| 6148 save_exc = code.insertion_point() |
| 6149 code.putln( |
| 6150 "/*try:*/ {") |
| 6151 code.return_label = try_return_label |
| 6152 code.break_label = try_break_label |
| 6153 code.continue_label = try_continue_label |
| 6154 self.body.generate_execution_code(code) |
| 6155 code.putln( |
| 6156 "}") |
| 6157 temps_to_clean_up = code.funcstate.all_free_managed_temps() |
| 6158 can_raise = code.label_used(our_error_label) |
| 6159 |
| 6160 if can_raise: |
| 6161 # inject code before the try block to save away the exception state |
| 6162 code.globalstate.use_utility_code(reset_exception_utility_code) |
| 6163 save_exc.putln("__Pyx_ExceptionSave(%s);" % |
| 6164 ', '.join(['&%s' % var for var in exc_save_vars])) |
| 6165 for var in exc_save_vars: |
| 6166 save_exc.put_xgotref(var) |
| 6167 |
| 6168 def restore_saved_exception(): |
| 6169 for name in exc_save_vars: |
| 6170 code.put_xgiveref(name) |
| 6171 code.putln("__Pyx_ExceptionReset(%s);" % |
| 6172 ', '.join(exc_save_vars)) |
| 6173 else: |
| 6174 # try block cannot raise exceptions, but we had to allocate the temp
s above, |
| 6175 # so just keep the C compiler from complaining about them being unus
ed |
| 6176 save_exc.putln("if (%s); else {/*mark used*/};" % '||'.join(exc_save
_vars)) |
| 6177 |
| 6178 def restore_saved_exception(): |
| 6179 pass |
| 6180 |
| 6181 code.error_label = except_error_label |
| 6182 code.return_label = except_return_label |
| 6183 if self.else_clause: |
| 6184 code.putln( |
| 6185 "/*else:*/ {") |
| 6186 self.else_clause.generate_execution_code(code) |
| 6187 code.putln( |
| 6188 "}") |
| 6189 |
| 6190 if can_raise: |
| 6191 for var in exc_save_vars: |
| 6192 code.put_xdecref_clear(var, py_object_type) |
| 6193 code.put_goto(try_end_label) |
| 6194 code.put_label(our_error_label) |
| 6195 for temp_name, temp_type in temps_to_clean_up: |
| 6196 code.put_xdecref_clear(temp_name, temp_type) |
| 6197 for except_clause in self.except_clauses: |
| 6198 except_clause.generate_handling_code(code, except_end_label) |
| 6199 if not self.has_default_clause: |
| 6200 code.put_goto(except_error_label) |
| 6201 |
| 6202 for exit_label, old_label in [(except_error_label, old_error_label), |
| 6203 (try_break_label, old_break_label), |
| 6204 (try_continue_label, old_continue_label), |
| 6205 (try_return_label, old_return_label), |
| 6206 (except_return_label, old_return_label)]: |
| 6207 if code.label_used(exit_label): |
| 6208 if not code.label_used(try_end_label): |
| 6209 code.put_goto(try_end_label) |
| 6210 code.put_label(exit_label) |
| 6211 restore_saved_exception() |
| 6212 code.put_goto(old_label) |
| 6213 |
| 6214 if code.label_used(except_end_label): |
| 6215 if not code.label_used(try_end_label): |
| 6216 code.put_goto(try_end_label) |
| 6217 code.put_label(except_end_label) |
| 6218 restore_saved_exception() |
| 6219 if code.label_used(try_end_label): |
| 6220 code.put_label(try_end_label) |
| 6221 code.putln("}") |
| 6222 |
| 6223 for cname in exc_save_vars: |
| 6224 code.funcstate.release_temp(cname) |
| 6225 |
| 6226 code.return_label = old_return_label |
| 6227 code.break_label = old_break_label |
| 6228 code.continue_label = old_continue_label |
| 6229 code.error_label = old_error_label |
| 6230 |
| 6231 def generate_function_definitions(self, env, code): |
| 6232 self.body.generate_function_definitions(env, code) |
| 6233 for except_clause in self.except_clauses: |
| 6234 except_clause.generate_function_definitions(env, code) |
| 6235 if self.else_clause is not None: |
| 6236 self.else_clause.generate_function_definitions(env, code) |
| 6237 |
| 6238 def annotate(self, code): |
| 6239 self.body.annotate(code) |
| 6240 for except_node in self.except_clauses: |
| 6241 except_node.annotate(code) |
| 6242 if self.else_clause: |
| 6243 self.else_clause.annotate(code) |
| 6244 |
| 6245 |
| 6246 class ExceptClauseNode(Node): |
| 6247 # Part of try ... except statement. |
| 6248 # |
| 6249 # pattern [ExprNode] |
| 6250 # target ExprNode or None |
| 6251 # body StatNode |
| 6252 # excinfo_target TupleNode(3*ResultRefNode) or None optional target for e
xception info (not owned here!) |
| 6253 # match_flag string result of exception match |
| 6254 # exc_value ExcValueNode used internally |
| 6255 # function_name string qualified name of enclosing function |
| 6256 # exc_vars (string * 3) local exception variables |
| 6257 # is_except_as bool Py3-style "except ... as xyz" |
| 6258 |
| 6259 # excinfo_target is never set by the parser, but can be set by a transform |
| 6260 # in order to extract more extensive information about the exception as a |
| 6261 # sys.exc_info()-style tuple into a target variable |
| 6262 |
| 6263 child_attrs = ["pattern", "target", "body", "exc_value"] |
| 6264 |
| 6265 exc_value = None |
| 6266 excinfo_target = None |
| 6267 is_except_as = False |
| 6268 |
| 6269 def analyse_declarations(self, env): |
| 6270 if self.target: |
| 6271 self.target.analyse_target_declaration(env) |
| 6272 self.body.analyse_declarations(env) |
| 6273 |
| 6274 def analyse_expressions(self, env): |
| 6275 self.function_name = env.qualified_name |
| 6276 if self.pattern: |
| 6277 # normalise/unpack self.pattern into a list |
| 6278 for i, pattern in enumerate(self.pattern): |
| 6279 pattern = pattern.analyse_expressions(env) |
| 6280 self.pattern[i] = pattern.coerce_to_pyobject(env) |
| 6281 |
| 6282 if self.target: |
| 6283 import ExprNodes |
| 6284 self.exc_value = ExprNodes.ExcValueNode(self.pos) |
| 6285 self.target = self.target.analyse_target_expression(env, self.exc_va
lue) |
| 6286 |
| 6287 self.body = self.body.analyse_expressions(env) |
| 6288 return self |
| 6289 |
| 6290 def generate_handling_code(self, code, end_label): |
| 6291 code.mark_pos(self.pos) |
| 6292 if self.pattern: |
| 6293 exc_tests = [] |
| 6294 for pattern in self.pattern: |
| 6295 pattern.generate_evaluation_code(code) |
| 6296 exc_tests.append("PyErr_ExceptionMatches(%s)" % pattern.py_resul
t()) |
| 6297 |
| 6298 match_flag = code.funcstate.allocate_temp(PyrexTypes.c_int_type, Fal
se) |
| 6299 code.putln( |
| 6300 "%s = %s;" % (match_flag, ' || '.join(exc_tests))) |
| 6301 for pattern in self.pattern: |
| 6302 pattern.generate_disposal_code(code) |
| 6303 pattern.free_temps(code) |
| 6304 code.putln( |
| 6305 "if (%s) {" % |
| 6306 match_flag) |
| 6307 code.funcstate.release_temp(match_flag) |
| 6308 else: |
| 6309 code.putln("/*except:*/ {") |
| 6310 |
| 6311 if (not getattr(self.body, 'stats', True) |
| 6312 and self.excinfo_target is None |
| 6313 and self.target is None): |
| 6314 # most simple case: no exception variable, empty body (pass) |
| 6315 # => reset the exception state, done |
| 6316 code.putln("PyErr_Restore(0,0,0);") |
| 6317 code.put_goto(end_label) |
| 6318 code.putln("}") |
| 6319 return |
| 6320 |
| 6321 exc_vars = [code.funcstate.allocate_temp(py_object_type, |
| 6322 manage_ref=True) |
| 6323 for _ in xrange(3)] |
| 6324 code.put_add_traceback(self.function_name) |
| 6325 # We always have to fetch the exception value even if |
| 6326 # there is no target, because this also normalises the |
| 6327 # exception and stores it in the thread state. |
| 6328 code.globalstate.use_utility_code(get_exception_utility_code) |
| 6329 exc_args = "&%s, &%s, &%s" % tuple(exc_vars) |
| 6330 code.putln("if (__Pyx_GetException(%s) < 0) %s" % (exc_args, |
| 6331 code.error_goto(self.pos))) |
| 6332 for x in exc_vars: |
| 6333 code.put_gotref(x) |
| 6334 if self.target: |
| 6335 self.exc_value.set_var(exc_vars[1]) |
| 6336 self.exc_value.generate_evaluation_code(code) |
| 6337 self.target.generate_assignment_code(self.exc_value, code) |
| 6338 if self.excinfo_target is not None: |
| 6339 for tempvar, node in zip(exc_vars, self.excinfo_target.args): |
| 6340 node.set_var(tempvar) |
| 6341 |
| 6342 old_break_label, old_continue_label = code.break_label, code.continue_la
bel |
| 6343 code.break_label = code.new_label('except_break') |
| 6344 code.continue_label = code.new_label('except_continue') |
| 6345 |
| 6346 old_exc_vars = code.funcstate.exc_vars |
| 6347 code.funcstate.exc_vars = exc_vars |
| 6348 self.body.generate_execution_code(code) |
| 6349 code.funcstate.exc_vars = old_exc_vars |
| 6350 for var in exc_vars: |
| 6351 code.put_decref_clear(var, py_object_type) |
| 6352 code.put_goto(end_label) |
| 6353 |
| 6354 for new_label, old_label in [(code.break_label, old_break_label), |
| 6355 (code.continue_label, old_continue_label)]: |
| 6356 if code.label_used(new_label): |
| 6357 code.put_label(new_label) |
| 6358 for var in exc_vars: |
| 6359 code.put_decref_clear(var, py_object_type) |
| 6360 code.put_goto(old_label) |
| 6361 code.break_label = old_break_label |
| 6362 code.continue_label = old_continue_label |
| 6363 |
| 6364 for temp in exc_vars: |
| 6365 code.funcstate.release_temp(temp) |
| 6366 |
| 6367 code.putln( |
| 6368 "}") |
| 6369 |
| 6370 def generate_function_definitions(self, env, code): |
| 6371 if self.target is not None: |
| 6372 self.target.generate_function_definitions(env, code) |
| 6373 self.body.generate_function_definitions(env, code) |
| 6374 |
| 6375 def annotate(self, code): |
| 6376 if self.pattern: |
| 6377 for pattern in self.pattern: |
| 6378 pattern.annotate(code) |
| 6379 if self.target: |
| 6380 self.target.annotate(code) |
| 6381 self.body.annotate(code) |
| 6382 |
| 6383 |
| 6384 class TryFinallyStatNode(StatNode): |
| 6385 # try ... finally statement |
| 6386 # |
| 6387 # body StatNode |
| 6388 # finally_clause StatNode |
| 6389 # |
| 6390 # The plan is that we funnel all continue, break |
| 6391 # return and error gotos into the beginning of the |
| 6392 # finally block, setting a variable to remember which |
| 6393 # one we're doing. At the end of the finally block, we |
| 6394 # switch on the variable to figure out where to go. |
| 6395 # In addition, if we're doing an error, we save the |
| 6396 # exception on entry to the finally block and restore |
| 6397 # it on exit. |
| 6398 |
| 6399 child_attrs = ["body", "finally_clause"] |
| 6400 |
| 6401 preserve_exception = 1 |
| 6402 |
| 6403 # handle exception case, in addition to return/break/continue |
| 6404 handle_error_case = True |
| 6405 func_return_type = None |
| 6406 |
| 6407 disallow_continue_in_try_finally = 0 |
| 6408 # There doesn't seem to be any point in disallowing |
| 6409 # continue in the try block, since we have no problem |
| 6410 # handling it. |
| 6411 |
| 6412 is_try_finally_in_nogil = False |
| 6413 |
| 6414 def create_analysed(pos, env, body, finally_clause): |
| 6415 node = TryFinallyStatNode(pos, body=body, finally_clause=finally_clause) |
| 6416 return node |
| 6417 create_analysed = staticmethod(create_analysed) |
| 6418 |
| 6419 def analyse_declarations(self, env): |
| 6420 self.body.analyse_declarations(env) |
| 6421 self.finally_clause.analyse_declarations(env) |
| 6422 |
| 6423 def analyse_expressions(self, env): |
| 6424 self.body = self.body.analyse_expressions(env) |
| 6425 self.finally_clause = self.finally_clause.analyse_expressions(env) |
| 6426 if env.return_type and not env.return_type.is_void: |
| 6427 self.func_return_type = env.return_type |
| 6428 return self |
| 6429 |
| 6430 nogil_check = Node.gil_error |
| 6431 gil_message = "Try-finally statement" |
| 6432 |
| 6433 def generate_execution_code(self, code): |
| 6434 old_error_label = code.error_label |
| 6435 old_labels = code.all_new_labels() |
| 6436 new_labels = code.get_all_labels() |
| 6437 new_error_label = code.error_label |
| 6438 if not self.handle_error_case: |
| 6439 code.error_label = old_error_label |
| 6440 catch_label = code.new_label() |
| 6441 |
| 6442 code.putln("/*try:*/ {") |
| 6443 |
| 6444 if self.disallow_continue_in_try_finally: |
| 6445 was_in_try_finally = code.funcstate.in_try_finally |
| 6446 code.funcstate.in_try_finally = 1 |
| 6447 |
| 6448 self.body.generate_execution_code(code) |
| 6449 |
| 6450 if self.disallow_continue_in_try_finally: |
| 6451 code.funcstate.in_try_finally = was_in_try_finally |
| 6452 |
| 6453 code.putln("}") |
| 6454 code.set_all_labels(old_labels) |
| 6455 |
| 6456 temps_to_clean_up = code.funcstate.all_free_managed_temps() |
| 6457 code.mark_pos(self.finally_clause.pos) |
| 6458 code.putln("/*finally:*/ {") |
| 6459 |
| 6460 def fresh_finally_clause(_next=[self.finally_clause]): |
| 6461 # generate the original subtree once and always keep a fresh copy |
| 6462 node = _next[0] |
| 6463 node_copy = copy.deepcopy(node) |
| 6464 if node is self.finally_clause: |
| 6465 _next[0] = node_copy |
| 6466 else: |
| 6467 node = node_copy |
| 6468 return node |
| 6469 |
| 6470 preserve_error = self.preserve_exception and code.label_used(new_error_l
abel) |
| 6471 needs_success_cleanup = not self.finally_clause.is_terminator |
| 6472 |
| 6473 if not self.body.is_terminator: |
| 6474 code.putln('/*normal exit:*/{') |
| 6475 fresh_finally_clause().generate_execution_code(code) |
| 6476 if not self.finally_clause.is_terminator: |
| 6477 code.put_goto(catch_label) |
| 6478 code.putln('}') |
| 6479 |
| 6480 if preserve_error: |
| 6481 code.putln('/*exception exit:*/{') |
| 6482 if self.is_try_finally_in_nogil: |
| 6483 code.declare_gilstate() |
| 6484 if needs_success_cleanup: |
| 6485 exc_lineno_cnames = tuple([ |
| 6486 code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_r
ef=False) |
| 6487 for _ in range(2)]) |
| 6488 exc_filename_cname = code.funcstate.allocate_temp( |
| 6489 PyrexTypes.CPtrType(PyrexTypes.c_const_type(PyrexTypes.c_cha
r_type)), |
| 6490 manage_ref=False) |
| 6491 else: |
| 6492 exc_lineno_cnames = exc_filename_cname = None |
| 6493 exc_vars = tuple([ |
| 6494 code.funcstate.allocate_temp(py_object_type, manage_ref=False) |
| 6495 for _ in range(6)]) |
| 6496 code.put_label(new_error_label) |
| 6497 self.put_error_catcher( |
| 6498 code, temps_to_clean_up, exc_vars, exc_lineno_cnames, exc_filena
me_cname) |
| 6499 finally_old_labels = code.all_new_labels() |
| 6500 |
| 6501 code.putln('{') |
| 6502 old_exc_vars = code.funcstate.exc_vars |
| 6503 code.funcstate.exc_vars = exc_vars[:3] |
| 6504 fresh_finally_clause().generate_execution_code(code) |
| 6505 code.funcstate.exc_vars = old_exc_vars |
| 6506 code.putln('}') |
| 6507 |
| 6508 if needs_success_cleanup: |
| 6509 self.put_error_uncatcher(code, exc_vars, exc_lineno_cnames, exc_
filename_cname) |
| 6510 if exc_lineno_cnames: |
| 6511 for cname in exc_lineno_cnames: |
| 6512 code.funcstate.release_temp(cname) |
| 6513 if exc_filename_cname: |
| 6514 code.funcstate.release_temp(exc_filename_cname) |
| 6515 code.put_goto(old_error_label) |
| 6516 |
| 6517 for new_label, old_label in zip(code.get_all_labels(), finally_old_l
abels): |
| 6518 if not code.label_used(new_label): |
| 6519 continue |
| 6520 code.put_label(new_label) |
| 6521 self.put_error_cleaner(code, exc_vars) |
| 6522 code.put_goto(old_label) |
| 6523 |
| 6524 for cname in exc_vars: |
| 6525 code.funcstate.release_temp(cname) |
| 6526 code.putln('}') |
| 6527 |
| 6528 code.set_all_labels(old_labels) |
| 6529 return_label = code.return_label |
| 6530 for i, (new_label, old_label) in enumerate(zip(new_labels, old_labels)): |
| 6531 if not code.label_used(new_label): |
| 6532 continue |
| 6533 if new_label == new_error_label and preserve_error: |
| 6534 continue # handled above |
| 6535 |
| 6536 code.put('%s: ' % new_label) |
| 6537 code.putln('{') |
| 6538 ret_temp = None |
| 6539 if old_label == return_label and not self.finally_clause.is_terminat
or: |
| 6540 # store away return value for later reuse |
| 6541 if (self.func_return_type and |
| 6542 not self.is_try_finally_in_nogil and |
| 6543 not isinstance(self.finally_clause, GILExitNode)): |
| 6544 ret_temp = code.funcstate.allocate_temp( |
| 6545 self.func_return_type, manage_ref=False) |
| 6546 code.putln("%s = %s;" % (ret_temp, Naming.retval_cname)) |
| 6547 if self.func_return_type.is_pyobject: |
| 6548 code.putln("%s = 0;" % Naming.retval_cname) |
| 6549 fresh_finally_clause().generate_execution_code(code) |
| 6550 if ret_temp: |
| 6551 code.putln("%s = %s;" % (Naming.retval_cname, ret_temp)) |
| 6552 if self.func_return_type.is_pyobject: |
| 6553 code.putln("%s = 0;" % ret_temp) |
| 6554 code.funcstate.release_temp(ret_temp) |
| 6555 ret_temp = None |
| 6556 if not self.finally_clause.is_terminator: |
| 6557 code.put_goto(old_label) |
| 6558 code.putln('}') |
| 6559 |
| 6560 # End finally |
| 6561 code.put_label(catch_label) |
| 6562 code.putln( |
| 6563 "}") |
| 6564 |
| 6565 def generate_function_definitions(self, env, code): |
| 6566 self.body.generate_function_definitions(env, code) |
| 6567 self.finally_clause.generate_function_definitions(env, code) |
| 6568 |
| 6569 def put_error_catcher(self, code, temps_to_clean_up, exc_vars, |
| 6570 exc_lineno_cnames, exc_filename_cname): |
| 6571 code.globalstate.use_utility_code(restore_exception_utility_code) |
| 6572 code.globalstate.use_utility_code(get_exception_utility_code) |
| 6573 code.globalstate.use_utility_code(swap_exception_utility_code) |
| 6574 |
| 6575 code.putln(' '.join(["%s = 0;"]*len(exc_vars)) % exc_vars) |
| 6576 if self.is_try_finally_in_nogil: |
| 6577 code.put_ensure_gil(declare_gilstate=False) |
| 6578 |
| 6579 for temp_name, type in temps_to_clean_up: |
| 6580 code.put_xdecref_clear(temp_name, type) |
| 6581 |
| 6582 # not using preprocessor here to avoid warnings about |
| 6583 # unused utility functions and/or temps |
| 6584 code.putln("if (PY_MAJOR_VERSION >= 3)" |
| 6585 " __Pyx_ExceptionSwap(&%s, &%s, &%s);" % exc_vars[3:]) |
| 6586 code.putln("if ((PY_MAJOR_VERSION < 3) ||" |
| 6587 # if __Pyx_GetException() fails in Py3, |
| 6588 # store the newly raised exception instead |
| 6589 " unlikely(__Pyx_GetException(&%s, &%s, &%s) < 0)) " |
| 6590 "__Pyx_ErrFetch(&%s, &%s, &%s);" % (exc_vars[:3] * 2)) |
| 6591 for var in exc_vars: |
| 6592 code.put_xgotref(var) |
| 6593 if exc_lineno_cnames: |
| 6594 code.putln("%s = %s; %s = %s; %s = %s;" % ( |
| 6595 exc_lineno_cnames[0], Naming.lineno_cname, |
| 6596 exc_lineno_cnames[1], Naming.clineno_cname, |
| 6597 exc_filename_cname, Naming.filename_cname)) |
| 6598 |
| 6599 if self.is_try_finally_in_nogil: |
| 6600 code.put_release_ensured_gil() |
| 6601 |
| 6602 def put_error_uncatcher(self, code, exc_vars, exc_lineno_cnames, exc_filenam
e_cname): |
| 6603 code.globalstate.use_utility_code(restore_exception_utility_code) |
| 6604 code.globalstate.use_utility_code(reset_exception_utility_code) |
| 6605 |
| 6606 if self.is_try_finally_in_nogil: |
| 6607 code.put_ensure_gil(declare_gilstate=False) |
| 6608 |
| 6609 # not using preprocessor here to avoid warnings about |
| 6610 # unused utility functions and/or temps |
| 6611 code.putln("if (PY_MAJOR_VERSION >= 3) {") |
| 6612 for var in exc_vars[3:]: |
| 6613 code.put_xgiveref(var) |
| 6614 code.putln("__Pyx_ExceptionReset(%s, %s, %s);" % exc_vars[3:]) |
| 6615 code.putln("}") |
| 6616 for var in exc_vars[:3]: |
| 6617 code.put_xgiveref(var) |
| 6618 code.putln("__Pyx_ErrRestore(%s, %s, %s);" % exc_vars[:3]) |
| 6619 |
| 6620 if self.is_try_finally_in_nogil: |
| 6621 code.put_release_ensured_gil() |
| 6622 |
| 6623 code.putln(' '.join(["%s = 0;"]*len(exc_vars)) % exc_vars) |
| 6624 if exc_lineno_cnames: |
| 6625 code.putln("%s = %s; %s = %s; %s = %s;" % ( |
| 6626 Naming.lineno_cname, exc_lineno_cnames[0], |
| 6627 Naming.clineno_cname, exc_lineno_cnames[1], |
| 6628 Naming.filename_cname, exc_filename_cname)) |
| 6629 |
| 6630 def put_error_cleaner(self, code, exc_vars): |
| 6631 code.globalstate.use_utility_code(reset_exception_utility_code) |
| 6632 if self.is_try_finally_in_nogil: |
| 6633 code.put_ensure_gil(declare_gilstate=False) |
| 6634 # not using preprocessor here to avoid warnings about |
| 6635 # unused utility functions and/or temps |
| 6636 code.putln("if (PY_MAJOR_VERSION >= 3) {") |
| 6637 for var in exc_vars[3:]: |
| 6638 code.put_xgiveref(var) |
| 6639 code.putln("__Pyx_ExceptionReset(%s, %s, %s);" % exc_vars[3:]) |
| 6640 code.putln("}") |
| 6641 for var in exc_vars[:3]: |
| 6642 code.put_xdecref_clear(var, py_object_type) |
| 6643 if self.is_try_finally_in_nogil: |
| 6644 code.put_release_ensured_gil() |
| 6645 code.putln(' '.join(["%s = 0;"]*3) % exc_vars[3:]) |
| 6646 |
| 6647 def annotate(self, code): |
| 6648 self.body.annotate(code) |
| 6649 self.finally_clause.annotate(code) |
| 6650 |
| 6651 |
| 6652 class NogilTryFinallyStatNode(TryFinallyStatNode): |
| 6653 """ |
| 6654 A try/finally statement that may be used in nogil code sections. |
| 6655 """ |
| 6656 |
| 6657 preserve_exception = False |
| 6658 nogil_check = None |
| 6659 |
| 6660 |
| 6661 class GILStatNode(NogilTryFinallyStatNode): |
| 6662 # 'with gil' or 'with nogil' statement |
| 6663 # |
| 6664 # state string 'gil' or 'nogil' |
| 6665 |
| 6666 state_temp = None |
| 6667 |
| 6668 def __init__(self, pos, state, body): |
| 6669 self.state = state |
| 6670 self.create_state_temp_if_needed(pos, state, body) |
| 6671 TryFinallyStatNode.__init__(self, pos, |
| 6672 body=body, |
| 6673 finally_clause=GILExitNode( |
| 6674 pos, state=state, state_temp=self.state_temp)) |
| 6675 |
| 6676 def create_state_temp_if_needed(self, pos, state, body): |
| 6677 from ParseTreeTransforms import YieldNodeCollector |
| 6678 collector = YieldNodeCollector() |
| 6679 collector.visitchildren(body) |
| 6680 if not collector.yields: |
| 6681 return |
| 6682 |
| 6683 if state == 'gil': |
| 6684 temp_type = PyrexTypes.c_gilstate_type |
| 6685 else: |
| 6686 temp_type = PyrexTypes.c_threadstate_ptr_type |
| 6687 import ExprNodes |
| 6688 self.state_temp = ExprNodes.TempNode(pos, temp_type) |
| 6689 |
| 6690 def analyse_declarations(self, env): |
| 6691 env._in_with_gil_block = (self.state == 'gil') |
| 6692 if self.state == 'gil': |
| 6693 env.has_with_gil_block = True |
| 6694 |
| 6695 return super(GILStatNode, self).analyse_declarations(env) |
| 6696 |
| 6697 def analyse_expressions(self, env): |
| 6698 env.use_utility_code( |
| 6699 UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c")) |
| 6700 was_nogil = env.nogil |
| 6701 env.nogil = self.state == 'nogil' |
| 6702 node = TryFinallyStatNode.analyse_expressions(self, env) |
| 6703 env.nogil = was_nogil |
| 6704 return node |
| 6705 |
| 6706 def generate_execution_code(self, code): |
| 6707 code.mark_pos(self.pos) |
| 6708 code.begin_block() |
| 6709 if self.state_temp: |
| 6710 self.state_temp.allocate(code) |
| 6711 variable = self.state_temp.result() |
| 6712 else: |
| 6713 variable = None |
| 6714 |
| 6715 old_trace_config = code.funcstate.can_trace |
| 6716 if self.state == 'gil': |
| 6717 code.put_ensure_gil(variable=variable) |
| 6718 # FIXME: not that easy, tracing may not be possible at all here |
| 6719 #code.funcstate.can_trace = True |
| 6720 else: |
| 6721 code.put_release_gil(variable=variable) |
| 6722 code.funcstate.can_trace = False |
| 6723 |
| 6724 TryFinallyStatNode.generate_execution_code(self, code) |
| 6725 |
| 6726 if self.state_temp: |
| 6727 self.state_temp.release(code) |
| 6728 |
| 6729 code.funcstate.can_trace = old_trace_config |
| 6730 code.end_block() |
| 6731 |
| 6732 |
| 6733 class GILExitNode(StatNode): |
| 6734 """ |
| 6735 Used as the 'finally' block in a GILStatNode |
| 6736 |
| 6737 state string 'gil' or 'nogil' |
| 6738 """ |
| 6739 |
| 6740 child_attrs = [] |
| 6741 state_temp = None |
| 6742 |
| 6743 def analyse_expressions(self, env): |
| 6744 return self |
| 6745 |
| 6746 def generate_execution_code(self, code): |
| 6747 if self.state_temp: |
| 6748 variable = self.state_temp.result() |
| 6749 else: |
| 6750 variable = None |
| 6751 |
| 6752 if self.state == 'gil': |
| 6753 code.put_release_ensured_gil(variable) |
| 6754 else: |
| 6755 code.put_acquire_gil(variable) |
| 6756 |
| 6757 |
| 6758 class EnsureGILNode(GILExitNode): |
| 6759 """ |
| 6760 Ensure the GIL in nogil functions for cleanup before returning. |
| 6761 """ |
| 6762 |
| 6763 def generate_execution_code(self, code): |
| 6764 code.put_ensure_gil(declare_gilstate=False) |
| 6765 |
| 6766 utility_code_for_cimports = { |
| 6767 # utility code (or inlining c) in a pxd (or pyx) file. |
| 6768 # TODO: Consider a generic user-level mechanism for importing |
| 6769 'cpython.array' : ("ArrayAPI", "arrayarray.h"), |
| 6770 'cpython.array.array' : ("ArrayAPI", "arrayarray.h"), |
| 6771 } |
| 6772 |
| 6773 class CImportStatNode(StatNode): |
| 6774 # cimport statement |
| 6775 # |
| 6776 # module_name string Qualified name of module being imported |
| 6777 # as_name string or None Name specified in "as" clause, if any |
| 6778 |
| 6779 child_attrs = [] |
| 6780 |
| 6781 def analyse_declarations(self, env): |
| 6782 if not env.is_module_scope: |
| 6783 error(self.pos, "cimport only allowed at module level") |
| 6784 return |
| 6785 module_scope = env.find_module(self.module_name, self.pos) |
| 6786 if "." in self.module_name: |
| 6787 names = [EncodedString(name) for name in self.module_name.split(".")
] |
| 6788 top_name = names[0] |
| 6789 top_module_scope = env.context.find_submodule(top_name) |
| 6790 module_scope = top_module_scope |
| 6791 for name in names[1:]: |
| 6792 submodule_scope = module_scope.find_submodule(name) |
| 6793 module_scope.declare_module(name, submodule_scope, self.pos) |
| 6794 module_scope = submodule_scope |
| 6795 if self.as_name: |
| 6796 env.declare_module(self.as_name, module_scope, self.pos) |
| 6797 else: |
| 6798 env.add_imported_module(module_scope) |
| 6799 env.declare_module(top_name, top_module_scope, self.pos) |
| 6800 else: |
| 6801 name = self.as_name or self.module_name |
| 6802 env.declare_module(name, module_scope, self.pos) |
| 6803 if self.module_name in utility_code_for_cimports: |
| 6804 env.use_utility_code(UtilityCode.load_cached( |
| 6805 *utility_code_for_cimports[self.module_name])) |
| 6806 |
| 6807 def analyse_expressions(self, env): |
| 6808 return self |
| 6809 |
| 6810 def generate_execution_code(self, code): |
| 6811 pass |
| 6812 |
| 6813 |
| 6814 class FromCImportStatNode(StatNode): |
| 6815 # from ... cimport statement |
| 6816 # |
| 6817 # module_name string Qualified name of module |
| 6818 # imported_names [(pos, name, as_name, kind)] Names to be imported |
| 6819 |
| 6820 child_attrs = [] |
| 6821 |
| 6822 def analyse_declarations(self, env): |
| 6823 if not env.is_module_scope: |
| 6824 error(self.pos, "cimport only allowed at module level") |
| 6825 return |
| 6826 module_scope = env.find_module(self.module_name, self.pos) |
| 6827 env.add_imported_module(module_scope) |
| 6828 for pos, name, as_name, kind in self.imported_names: |
| 6829 if name == "*": |
| 6830 for local_name, entry in module_scope.entries.items(): |
| 6831 env.add_imported_entry(local_name, entry, pos) |
| 6832 else: |
| 6833 entry = module_scope.lookup(name) |
| 6834 if entry: |
| 6835 if kind and not self.declaration_matches(entry, kind): |
| 6836 entry.redeclared(pos) |
| 6837 entry.used = 1 |
| 6838 else: |
| 6839 if kind == 'struct' or kind == 'union': |
| 6840 entry = module_scope.declare_struct_or_union(name, |
| 6841 kind = kind, scope = None, typedef_flag = 0, pos = p
os) |
| 6842 elif kind == 'class': |
| 6843 entry = module_scope.declare_c_class(name, pos = pos, |
| 6844 module_name = self.module_name) |
| 6845 else: |
| 6846 submodule_scope = env.context.find_module(name, relative
_to = module_scope, pos = self.pos) |
| 6847 if submodule_scope.parent_module is module_scope: |
| 6848 env.declare_module(as_name or name, submodule_scope,
self.pos) |
| 6849 else: |
| 6850 error(pos, "Name '%s' not declared in module '%s'" |
| 6851 % (name, self.module_name)) |
| 6852 |
| 6853 if entry: |
| 6854 local_name = as_name or name |
| 6855 env.add_imported_entry(local_name, entry, pos) |
| 6856 |
| 6857 if self.module_name.startswith('cpython'): # enough for now |
| 6858 if self.module_name in utility_code_for_cimports: |
| 6859 env.use_utility_code(UtilityCode.load_cached( |
| 6860 *utility_code_for_cimports[self.module_name])) |
| 6861 for _, name, _, _ in self.imported_names: |
| 6862 fqname = '%s.%s' % (self.module_name, name) |
| 6863 if fqname in utility_code_for_cimports: |
| 6864 env.use_utility_code(UtilityCode.load_cached( |
| 6865 *utility_code_for_cimports[fqname])) |
| 6866 |
| 6867 def declaration_matches(self, entry, kind): |
| 6868 if not entry.is_type: |
| 6869 return 0 |
| 6870 type = entry.type |
| 6871 if kind == 'class': |
| 6872 if not type.is_extension_type: |
| 6873 return 0 |
| 6874 else: |
| 6875 if not type.is_struct_or_union: |
| 6876 return 0 |
| 6877 if kind != type.kind: |
| 6878 return 0 |
| 6879 return 1 |
| 6880 |
| 6881 def analyse_expressions(self, env): |
| 6882 return self |
| 6883 |
| 6884 def generate_execution_code(self, code): |
| 6885 pass |
| 6886 |
| 6887 |
| 6888 class FromImportStatNode(StatNode): |
| 6889 # from ... import statement |
| 6890 # |
| 6891 # module ImportNode |
| 6892 # items [(string, NameNode)] |
| 6893 # interned_items [(string, NameNode, ExprNode)] |
| 6894 # item PyTempNode used internally |
| 6895 # import_star boolean used internally |
| 6896 |
| 6897 child_attrs = ["module"] |
| 6898 import_star = 0 |
| 6899 |
| 6900 def analyse_declarations(self, env): |
| 6901 for name, target in self.items: |
| 6902 if name == "*": |
| 6903 if not env.is_module_scope: |
| 6904 error(self.pos, "import * only allowed at module level") |
| 6905 return |
| 6906 env.has_import_star = 1 |
| 6907 self.import_star = 1 |
| 6908 else: |
| 6909 target.analyse_target_declaration(env) |
| 6910 |
| 6911 def analyse_expressions(self, env): |
| 6912 import ExprNodes |
| 6913 self.module = self.module.analyse_expressions(env) |
| 6914 self.item = ExprNodes.RawCNameExprNode(self.pos, py_object_type) |
| 6915 self.interned_items = [] |
| 6916 for name, target in self.items: |
| 6917 if name == '*': |
| 6918 for _, entry in env.entries.items(): |
| 6919 if not entry.is_type and entry.type.is_extension_type: |
| 6920 env.use_utility_code(UtilityCode.load_cached("ExtTypeTes
t", "ObjectHandling.c")) |
| 6921 break |
| 6922 else: |
| 6923 entry = env.lookup(target.name) |
| 6924 # check whether or not entry is already cimported |
| 6925 if (entry.is_type and entry.type.name == name |
| 6926 and hasattr(entry.type, 'module_name')): |
| 6927 if entry.type.module_name == self.module.module_name.value: |
| 6928 # cimported with absolute name |
| 6929 continue |
| 6930 try: |
| 6931 # cimported with relative name |
| 6932 module = env.find_module(self.module.module_name.value, |
| 6933 pos=None) |
| 6934 if entry.type.module_name == module.qualified_name: |
| 6935 continue |
| 6936 except AttributeError: |
| 6937 pass |
| 6938 target = target.analyse_target_expression(env, None) # FIXME? |
| 6939 if target.type is py_object_type: |
| 6940 coerced_item = None |
| 6941 else: |
| 6942 coerced_item = self.item.coerce_to(target.type, env) |
| 6943 self.interned_items.append((name, target, coerced_item)) |
| 6944 return self |
| 6945 |
| 6946 def generate_execution_code(self, code): |
| 6947 self.module.generate_evaluation_code(code) |
| 6948 if self.import_star: |
| 6949 code.putln( |
| 6950 'if (%s(%s) < 0) %s;' % ( |
| 6951 Naming.import_star, |
| 6952 self.module.py_result(), |
| 6953 code.error_goto(self.pos))) |
| 6954 item_temp = code.funcstate.allocate_temp(py_object_type, manage_ref=True
) |
| 6955 self.item.set_cname(item_temp) |
| 6956 if self.interned_items: |
| 6957 code.globalstate.use_utility_code( |
| 6958 UtilityCode.load_cached("ImportFrom", "ImportExport.c")) |
| 6959 for name, target, coerced_item in self.interned_items: |
| 6960 code.putln( |
| 6961 '%s = __Pyx_ImportFrom(%s, %s); %s' % ( |
| 6962 item_temp, |
| 6963 self.module.py_result(), |
| 6964 code.intern_identifier(name), |
| 6965 code.error_goto_if_null(item_temp, self.pos))) |
| 6966 code.put_gotref(item_temp) |
| 6967 if coerced_item is None: |
| 6968 target.generate_assignment_code(self.item, code) |
| 6969 else: |
| 6970 coerced_item.allocate_temp_result(code) |
| 6971 coerced_item.generate_result_code(code) |
| 6972 target.generate_assignment_code(coerced_item, code) |
| 6973 code.put_decref_clear(item_temp, py_object_type) |
| 6974 code.funcstate.release_temp(item_temp) |
| 6975 self.module.generate_disposal_code(code) |
| 6976 self.module.free_temps(code) |
| 6977 |
| 6978 |
| 6979 class ParallelNode(Node): |
| 6980 """ |
| 6981 Base class for cython.parallel constructs. |
| 6982 """ |
| 6983 |
| 6984 nogil_check = None |
| 6985 |
| 6986 |
| 6987 class ParallelStatNode(StatNode, ParallelNode): |
| 6988 """ |
| 6989 Base class for 'with cython.parallel.parallel():' and 'for i in prange():'. |
| 6990 |
| 6991 assignments { Entry(var) : (var.pos, inplace_operator_or_None) } |
| 6992 assignments to variables in this parallel section |
| 6993 |
| 6994 parent parent ParallelStatNode or None |
| 6995 is_parallel indicates whether this node is OpenMP parallel |
| 6996 (true for #pragma omp parallel for and |
| 6997 #pragma omp parallel) |
| 6998 |
| 6999 is_parallel is true for: |
| 7000 |
| 7001 #pragma omp parallel |
| 7002 #pragma omp parallel for |
| 7003 |
| 7004 sections, but NOT for |
| 7005 |
| 7006 #pragma omp for |
| 7007 |
| 7008 We need this to determine the sharing attributes. |
| 7009 |
| 7010 privatization_insertion_point a code insertion point used to make temps |
| 7011 private (esp. the "nsteps" temp) |
| 7012 |
| 7013 args tuple the arguments passed to the parallel construct |
| 7014 kwargs DictNode the keyword arguments passed to the parallel |
| 7015 construct (replaced by its compile time value) |
| 7016 """ |
| 7017 |
| 7018 child_attrs = ['body', 'num_threads'] |
| 7019 |
| 7020 body = None |
| 7021 |
| 7022 is_prange = False |
| 7023 is_nested_prange = False |
| 7024 |
| 7025 error_label_used = False |
| 7026 |
| 7027 num_threads = None |
| 7028 chunksize = None |
| 7029 |
| 7030 parallel_exc = ( |
| 7031 Naming.parallel_exc_type, |
| 7032 Naming.parallel_exc_value, |
| 7033 Naming.parallel_exc_tb, |
| 7034 ) |
| 7035 |
| 7036 parallel_pos_info = ( |
| 7037 Naming.parallel_filename, |
| 7038 Naming.parallel_lineno, |
| 7039 Naming.parallel_clineno, |
| 7040 ) |
| 7041 |
| 7042 pos_info = ( |
| 7043 Naming.filename_cname, |
| 7044 Naming.lineno_cname, |
| 7045 Naming.clineno_cname, |
| 7046 ) |
| 7047 |
| 7048 critical_section_counter = 0 |
| 7049 |
| 7050 def __init__(self, pos, **kwargs): |
| 7051 super(ParallelStatNode, self).__init__(pos, **kwargs) |
| 7052 |
| 7053 # All assignments in this scope |
| 7054 self.assignments = kwargs.get('assignments') or {} |
| 7055 |
| 7056 # All seen closure cnames and their temporary cnames |
| 7057 self.seen_closure_vars = set() |
| 7058 |
| 7059 # Dict of variables that should be declared (first|last|)private or |
| 7060 # reduction { Entry: (op, lastprivate) }. |
| 7061 # If op is not None, it's a reduction. |
| 7062 self.privates = {} |
| 7063 |
| 7064 # [NameNode] |
| 7065 self.assigned_nodes = [] |
| 7066 |
| 7067 def analyse_declarations(self, env): |
| 7068 self.body.analyse_declarations(env) |
| 7069 |
| 7070 self.num_threads = None |
| 7071 |
| 7072 if self.kwargs: |
| 7073 # Try to find num_threads and chunksize keyword arguments |
| 7074 pairs = [] |
| 7075 for dictitem in self.kwargs.key_value_pairs: |
| 7076 if dictitem.key.value == 'num_threads': |
| 7077 self.num_threads = dictitem.value |
| 7078 elif self.is_prange and dictitem.key.value == 'chunksize': |
| 7079 self.chunksize = dictitem.value |
| 7080 else: |
| 7081 pairs.append(dictitem) |
| 7082 |
| 7083 self.kwargs.key_value_pairs = pairs |
| 7084 |
| 7085 try: |
| 7086 self.kwargs = self.kwargs.compile_time_value(env) |
| 7087 except Exception, e: |
| 7088 error(self.kwargs.pos, "Only compile-time values may be " |
| 7089 "supplied as keyword arguments") |
| 7090 else: |
| 7091 self.kwargs = {} |
| 7092 |
| 7093 for kw, val in self.kwargs.iteritems(): |
| 7094 if kw not in self.valid_keyword_arguments: |
| 7095 error(self.pos, "Invalid keyword argument: %s" % kw) |
| 7096 else: |
| 7097 setattr(self, kw, val) |
| 7098 |
| 7099 def analyse_expressions(self, env): |
| 7100 if self.num_threads: |
| 7101 self.num_threads = self.num_threads.analyse_expressions(env) |
| 7102 |
| 7103 if self.chunksize: |
| 7104 self.chunksize = self.chunksize.analyse_expressions(env) |
| 7105 |
| 7106 self.body = self.body.analyse_expressions(env) |
| 7107 self.analyse_sharing_attributes(env) |
| 7108 |
| 7109 if self.num_threads is not None: |
| 7110 if (self.parent and self.parent.num_threads is not None and not |
| 7111 self.parent.is_prange): |
| 7112 error(self.pos, |
| 7113 "num_threads already declared in outer section") |
| 7114 elif self.parent and not self.parent.is_prange: |
| 7115 error(self.pos, |
| 7116 "num_threads must be declared in the parent parallel secti
on") |
| 7117 elif (self.num_threads.type.is_int and |
| 7118 self.num_threads.is_literal and |
| 7119 self.num_threads.compile_time_value(env) <= 0): |
| 7120 error(self.pos, |
| 7121 "argument to num_threads must be greater than 0") |
| 7122 |
| 7123 if not self.num_threads.is_simple(): |
| 7124 self.num_threads = self.num_threads.coerce_to( |
| 7125 PyrexTypes.c_int_type, env).coerce_to_temp(env) |
| 7126 return self |
| 7127 |
| 7128 def analyse_sharing_attributes(self, env): |
| 7129 """ |
| 7130 Analyse the privates for this block and set them in self.privates. |
| 7131 This should be called in a post-order fashion during the |
| 7132 analyse_expressions phase |
| 7133 """ |
| 7134 for entry, (pos, op) in self.assignments.iteritems(): |
| 7135 |
| 7136 if self.is_prange and not self.is_parallel: |
| 7137 # closely nested prange in a with parallel block, disallow |
| 7138 # assigning to privates in the with parallel block (we |
| 7139 # consider it too implicit and magicky for users) |
| 7140 if entry in self.parent.assignments: |
| 7141 error(pos, |
| 7142 "Cannot assign to private of outer parallel block") |
| 7143 continue |
| 7144 |
| 7145 if not self.is_prange and op: |
| 7146 # Again possible, but considered to magicky |
| 7147 error(pos, "Reductions not allowed for parallel blocks") |
| 7148 continue |
| 7149 |
| 7150 # By default all variables should have the same values as if |
| 7151 # executed sequentially |
| 7152 lastprivate = True |
| 7153 self.propagate_var_privatization(entry, pos, op, lastprivate) |
| 7154 |
| 7155 def propagate_var_privatization(self, entry, pos, op, lastprivate): |
| 7156 """ |
| 7157 Propagate the sharing attributes of a variable. If the privatization is |
| 7158 determined by a parent scope, done propagate further. |
| 7159 |
| 7160 If we are a prange, we propagate our sharing attributes outwards to |
| 7161 other pranges. If we are a prange in parallel block and the parallel |
| 7162 block does not determine the variable private, we propagate to the |
| 7163 parent of the parent. Recursion stops at parallel blocks, as they have |
| 7164 no concept of lastprivate or reduction. |
| 7165 |
| 7166 So the following cases propagate: |
| 7167 |
| 7168 sum is a reduction for all loops: |
| 7169 |
| 7170 for i in prange(n): |
| 7171 for j in prange(n): |
| 7172 for k in prange(n): |
| 7173 sum += i * j * k |
| 7174 |
| 7175 sum is a reduction for both loops, local_var is private to the |
| 7176 parallel with block: |
| 7177 |
| 7178 for i in prange(n): |
| 7179 with parallel: |
| 7180 local_var = ... # private to the parallel |
| 7181 for j in prange(n): |
| 7182 sum += i * j |
| 7183 |
| 7184 Nested with parallel blocks are disallowed, because they wouldn't |
| 7185 allow you to propagate lastprivates or reductions: |
| 7186 |
| 7187 #pragma omp parallel for lastprivate(i) |
| 7188 for i in prange(n): |
| 7189 |
| 7190 sum = 0 |
| 7191 |
| 7192 #pragma omp parallel private(j, sum) |
| 7193 with parallel: |
| 7194 |
| 7195 #pragma omp parallel |
| 7196 with parallel: |
| 7197 |
| 7198 #pragma omp for lastprivate(j) reduction(+:sum) |
| 7199 for j in prange(n): |
| 7200 sum += i |
| 7201 |
| 7202 # sum and j are well-defined here |
| 7203 |
| 7204 # sum and j are undefined here |
| 7205 |
| 7206 # sum and j are undefined here |
| 7207 """ |
| 7208 self.privates[entry] = (op, lastprivate) |
| 7209 |
| 7210 if entry.type.is_memoryviewslice: |
| 7211 error(pos, "Memoryview slices can only be shared in parallel section
s") |
| 7212 return |
| 7213 |
| 7214 if self.is_prange: |
| 7215 if not self.is_parallel and entry not in self.parent.assignments: |
| 7216 # Parent is a parallel with block |
| 7217 parent = self.parent.parent |
| 7218 else: |
| 7219 parent = self.parent |
| 7220 |
| 7221 # We don't need to propagate privates, only reductions and |
| 7222 # lastprivates |
| 7223 if parent and (op or lastprivate): |
| 7224 parent.propagate_var_privatization(entry, pos, op, lastprivate) |
| 7225 |
| 7226 def _allocate_closure_temp(self, code, entry): |
| 7227 """ |
| 7228 Helper function that allocate a temporary for a closure variable that |
| 7229 is assigned to. |
| 7230 """ |
| 7231 if self.parent: |
| 7232 return self.parent._allocate_closure_temp(code, entry) |
| 7233 |
| 7234 if entry.cname in self.seen_closure_vars: |
| 7235 return entry.cname |
| 7236 |
| 7237 cname = code.funcstate.allocate_temp(entry.type, True) |
| 7238 |
| 7239 # Add both the actual cname and the temp cname, as the actual cname |
| 7240 # will be replaced with the temp cname on the entry |
| 7241 self.seen_closure_vars.add(entry.cname) |
| 7242 self.seen_closure_vars.add(cname) |
| 7243 |
| 7244 self.modified_entries.append((entry, entry.cname)) |
| 7245 code.putln("%s = %s;" % (cname, entry.cname)) |
| 7246 entry.cname = cname |
| 7247 |
| 7248 def initialize_privates_to_nan(self, code, exclude=None): |
| 7249 first = True |
| 7250 |
| 7251 for entry, (op, lastprivate) in self.privates.iteritems(): |
| 7252 if not op and (not exclude or entry != exclude): |
| 7253 invalid_value = entry.type.invalid_value() |
| 7254 |
| 7255 if invalid_value: |
| 7256 if first: |
| 7257 code.putln("/* Initialize private variables to " |
| 7258 "invalid values */") |
| 7259 first = False |
| 7260 code.putln("%s = %s;" % (entry.cname, |
| 7261 entry.type.cast_code(invalid_value)
)) |
| 7262 |
| 7263 def evaluate_before_block(self, code, expr): |
| 7264 c = self.begin_of_parallel_control_block_point_after_decls |
| 7265 # we need to set the owner to ourselves temporarily, as |
| 7266 # allocate_temp may generate a comment in the middle of our pragma |
| 7267 # otherwise when DebugFlags.debug_temp_code_comments is in effect |
| 7268 owner = c.funcstate.owner |
| 7269 c.funcstate.owner = c |
| 7270 expr.generate_evaluation_code(c) |
| 7271 c.funcstate.owner = owner |
| 7272 |
| 7273 return expr.result() |
| 7274 |
| 7275 def put_num_threads(self, code): |
| 7276 """ |
| 7277 Write self.num_threads if set as the num_threads OpenMP directive |
| 7278 """ |
| 7279 if self.num_threads is not None: |
| 7280 code.put(" num_threads(%s)" % self.evaluate_before_block(code, |
| 7281 self.num_threads)) |
| 7282 |
| 7283 |
| 7284 def declare_closure_privates(self, code): |
| 7285 """ |
| 7286 If a variable is in a scope object, we need to allocate a temp and |
| 7287 assign the value from the temp to the variable in the scope object |
| 7288 after the parallel section. This kind of copying should be done only |
| 7289 in the outermost parallel section. |
| 7290 """ |
| 7291 self.modified_entries = [] |
| 7292 |
| 7293 for entry in self.assignments: |
| 7294 if entry.from_closure or entry.in_closure: |
| 7295 self._allocate_closure_temp(code, entry) |
| 7296 |
| 7297 def release_closure_privates(self, code): |
| 7298 """ |
| 7299 Release any temps used for variables in scope objects. As this is the |
| 7300 outermost parallel block, we don't need to delete the cnames from |
| 7301 self.seen_closure_vars. |
| 7302 """ |
| 7303 for entry, original_cname in self.modified_entries: |
| 7304 code.putln("%s = %s;" % (original_cname, entry.cname)) |
| 7305 code.funcstate.release_temp(entry.cname) |
| 7306 entry.cname = original_cname |
| 7307 |
| 7308 def privatize_temps(self, code, exclude_temps=()): |
| 7309 """ |
| 7310 Make any used temporaries private. Before the relevant code block |
| 7311 code.start_collecting_temps() should have been called. |
| 7312 """ |
| 7313 if self.is_parallel: |
| 7314 c = self.privatization_insertion_point |
| 7315 |
| 7316 self.temps = temps = code.funcstate.stop_collecting_temps() |
| 7317 privates, firstprivates = [], [] |
| 7318 for temp, type in temps: |
| 7319 if type.is_pyobject or type.is_memoryviewslice: |
| 7320 firstprivates.append(temp) |
| 7321 else: |
| 7322 privates.append(temp) |
| 7323 |
| 7324 if privates: |
| 7325 c.put(" private(%s)" % ", ".join(privates)) |
| 7326 if firstprivates: |
| 7327 c.put(" firstprivate(%s)" % ", ".join(firstprivates)) |
| 7328 |
| 7329 if self.breaking_label_used: |
| 7330 shared_vars = [Naming.parallel_why] |
| 7331 if self.error_label_used: |
| 7332 shared_vars.extend(self.parallel_exc) |
| 7333 c.put(" private(%s, %s, %s)" % self.pos_info) |
| 7334 |
| 7335 c.put(" shared(%s)" % ', '.join(shared_vars)) |
| 7336 |
| 7337 def cleanup_temps(self, code): |
| 7338 # Now clean up any memoryview slice and object temporaries |
| 7339 if self.is_parallel and not self.is_nested_prange: |
| 7340 code.putln("/* Clean up any temporaries */") |
| 7341 for temp, type in self.temps: |
| 7342 if type.is_memoryviewslice: |
| 7343 code.put_xdecref_memoryviewslice(temp, have_gil=False) |
| 7344 elif type.is_pyobject: |
| 7345 code.put_xdecref(temp, type) |
| 7346 code.putln("%s = NULL;" % temp) |
| 7347 |
| 7348 def setup_parallel_control_flow_block(self, code): |
| 7349 """ |
| 7350 Sets up a block that surrounds the parallel block to determine |
| 7351 how the parallel section was exited. Any kind of return is |
| 7352 trapped (break, continue, return, exceptions). This is the idea: |
| 7353 |
| 7354 { |
| 7355 int why = 0; |
| 7356 |
| 7357 #pragma omp parallel |
| 7358 { |
| 7359 return # -> goto new_return_label; |
| 7360 goto end_parallel; |
| 7361 |
| 7362 new_return_label: |
| 7363 why = 3; |
| 7364 goto end_parallel; |
| 7365 |
| 7366 end_parallel:; |
| 7367 #pragma omp flush(why) # we need to flush for every iteration |
| 7368 } |
| 7369 |
| 7370 if (why == 3) |
| 7371 goto old_return_label; |
| 7372 } |
| 7373 """ |
| 7374 self.old_loop_labels = code.new_loop_labels() |
| 7375 self.old_error_label = code.new_error_label() |
| 7376 self.old_return_label = code.return_label |
| 7377 code.return_label = code.new_label(name="return") |
| 7378 |
| 7379 code.begin_block() # parallel control flow block |
| 7380 self.begin_of_parallel_control_block_point = code.insertion_point() |
| 7381 self.begin_of_parallel_control_block_point_after_decls = code.insertion_
point() |
| 7382 |
| 7383 self.undef_builtin_expect_apple_gcc_bug(code) |
| 7384 |
| 7385 def begin_parallel_block(self, code): |
| 7386 """ |
| 7387 Each OpenMP thread in a parallel section that contains a with gil block |
| 7388 must have the thread-state initialized. The call to |
| 7389 PyGILState_Release() then deallocates our threadstate. If we wouldn't |
| 7390 do this, each with gil block would allocate and deallocate one, thereby |
| 7391 losing exception information before it can be saved before leaving the |
| 7392 parallel section. |
| 7393 """ |
| 7394 self.begin_of_parallel_block = code.insertion_point() |
| 7395 |
| 7396 def end_parallel_block(self, code): |
| 7397 """ |
| 7398 To ensure all OpenMP threads have thread states, we ensure the GIL |
| 7399 in each thread (which creates a thread state if it doesn't exist), |
| 7400 after which we release the GIL. |
| 7401 On exit, reacquire the GIL and release the thread state. |
| 7402 |
| 7403 If compiled without OpenMP support (at the C level), then we still have |
| 7404 to acquire the GIL to decref any object temporaries. |
| 7405 """ |
| 7406 if self.error_label_used: |
| 7407 begin_code = self.begin_of_parallel_block |
| 7408 end_code = code |
| 7409 |
| 7410 begin_code.putln("#ifdef _OPENMP") |
| 7411 begin_code.put_ensure_gil(declare_gilstate=True) |
| 7412 begin_code.putln("Py_BEGIN_ALLOW_THREADS") |
| 7413 begin_code.putln("#endif /* _OPENMP */") |
| 7414 |
| 7415 end_code.putln("#ifdef _OPENMP") |
| 7416 end_code.putln("Py_END_ALLOW_THREADS") |
| 7417 end_code.putln("#else") |
| 7418 end_code.put_safe("{\n") |
| 7419 end_code.put_ensure_gil() |
| 7420 end_code.putln("#endif /* _OPENMP */") |
| 7421 self.cleanup_temps(end_code) |
| 7422 end_code.put_release_ensured_gil() |
| 7423 end_code.putln("#ifndef _OPENMP") |
| 7424 end_code.put_safe("}\n") |
| 7425 end_code.putln("#endif /* _OPENMP */") |
| 7426 |
| 7427 def trap_parallel_exit(self, code, should_flush=False): |
| 7428 """ |
| 7429 Trap any kind of return inside a parallel construct. 'should_flush' |
| 7430 indicates whether the variable should be flushed, which is needed by |
| 7431 prange to skip the loop. It also indicates whether we need to register |
| 7432 a continue (we need this for parallel blocks, but not for prange |
| 7433 loops, as it is a direct jump there). |
| 7434 |
| 7435 It uses the same mechanism as try/finally: |
| 7436 1 continue |
| 7437 2 break |
| 7438 3 return |
| 7439 4 error |
| 7440 """ |
| 7441 save_lastprivates_label = code.new_label() |
| 7442 dont_return_label = code.new_label() |
| 7443 |
| 7444 self.any_label_used = False |
| 7445 self.breaking_label_used = False |
| 7446 self.error_label_used = False |
| 7447 |
| 7448 self.parallel_private_temps = [] |
| 7449 |
| 7450 all_labels = code.get_all_labels() |
| 7451 |
| 7452 # Figure this out before starting to generate any code |
| 7453 for label in all_labels: |
| 7454 if code.label_used(label): |
| 7455 self.breaking_label_used = (self.breaking_label_used or |
| 7456 label != code.continue_label) |
| 7457 self.any_label_used = True |
| 7458 |
| 7459 if self.any_label_used: |
| 7460 code.put_goto(dont_return_label) |
| 7461 |
| 7462 for i, label in enumerate(all_labels): |
| 7463 if not code.label_used(label): |
| 7464 continue |
| 7465 |
| 7466 is_continue_label = label == code.continue_label |
| 7467 |
| 7468 code.put_label(label) |
| 7469 |
| 7470 if not (should_flush and is_continue_label): |
| 7471 if label == code.error_label: |
| 7472 self.error_label_used = True |
| 7473 self.fetch_parallel_exception(code) |
| 7474 |
| 7475 code.putln("%s = %d;" % (Naming.parallel_why, i + 1)) |
| 7476 |
| 7477 if (self.breaking_label_used and self.is_prange and not |
| 7478 is_continue_label): |
| 7479 code.put_goto(save_lastprivates_label) |
| 7480 else: |
| 7481 code.put_goto(dont_return_label) |
| 7482 |
| 7483 if self.any_label_used: |
| 7484 if self.is_prange and self.breaking_label_used: |
| 7485 # Don't rely on lastprivate, save our lastprivates |
| 7486 code.put_label(save_lastprivates_label) |
| 7487 self.save_parallel_vars(code) |
| 7488 |
| 7489 code.put_label(dont_return_label) |
| 7490 |
| 7491 if should_flush and self.breaking_label_used: |
| 7492 code.putln_openmp("#pragma omp flush(%s)" % Naming.parallel_why) |
| 7493 |
| 7494 def save_parallel_vars(self, code): |
| 7495 """ |
| 7496 The following shenanigans are instated when we break, return or |
| 7497 propagate errors from a prange. In this case we cannot rely on |
| 7498 lastprivate() to do its job, as no iterations may have executed yet |
| 7499 in the last thread, leaving the values undefined. It is most likely |
| 7500 that the breaking thread has well-defined values of the lastprivate |
| 7501 variables, so we keep those values. |
| 7502 """ |
| 7503 section_name = ("__pyx_parallel_lastprivates%d" % |
| 7504 self.critical_section_counter) |
| 7505 code.putln_openmp("#pragma omp critical(%s)" % section_name) |
| 7506 ParallelStatNode.critical_section_counter += 1 |
| 7507 |
| 7508 code.begin_block() # begin critical section |
| 7509 |
| 7510 c = self.begin_of_parallel_control_block_point |
| 7511 |
| 7512 temp_count = 0 |
| 7513 for entry, (op, lastprivate) in self.privates.iteritems(): |
| 7514 if not lastprivate or entry.type.is_pyobject: |
| 7515 continue |
| 7516 |
| 7517 type_decl = entry.type.declaration_code("") |
| 7518 temp_cname = "__pyx_parallel_temp%d" % temp_count |
| 7519 private_cname = entry.cname |
| 7520 |
| 7521 temp_count += 1 |
| 7522 |
| 7523 invalid_value = entry.type.invalid_value() |
| 7524 if invalid_value: |
| 7525 init = ' = ' + invalid_value |
| 7526 else: |
| 7527 init = '' |
| 7528 # Declare the parallel private in the outer block |
| 7529 c.putln("%s %s%s;" % (type_decl, temp_cname, init)) |
| 7530 |
| 7531 # Initialize before escaping |
| 7532 code.putln("%s = %s;" % (temp_cname, private_cname)) |
| 7533 |
| 7534 self.parallel_private_temps.append((temp_cname, private_cname)) |
| 7535 |
| 7536 code.end_block() # end critical section |
| 7537 |
| 7538 def fetch_parallel_exception(self, code): |
| 7539 """ |
| 7540 As each OpenMP thread may raise an exception, we need to fetch that |
| 7541 exception from the threadstate and save it for after the parallel |
| 7542 section where it can be re-raised in the master thread. |
| 7543 |
| 7544 Although it would seem that __pyx_filename, __pyx_lineno and |
| 7545 __pyx_clineno are only assigned to under exception conditions (i.e., |
| 7546 when we have the GIL), and thus should be allowed to be shared without |
| 7547 any race condition, they are in fact subject to the same race |
| 7548 conditions that they were previously when they were global variables |
| 7549 and functions were allowed to release the GIL: |
| 7550 |
| 7551 thread A thread B |
| 7552 acquire |
| 7553 set lineno |
| 7554 release |
| 7555 acquire |
| 7556 set lineno |
| 7557 release |
| 7558 acquire |
| 7559 fetch exception |
| 7560 release |
| 7561 skip the fetch |
| 7562 |
| 7563 deallocate threadstate deallocate threadstate |
| 7564 """ |
| 7565 code.begin_block() |
| 7566 code.put_ensure_gil(declare_gilstate=True) |
| 7567 |
| 7568 code.putln_openmp("#pragma omp flush(%s)" % Naming.parallel_exc_type) |
| 7569 code.putln( |
| 7570 "if (!%s) {" % Naming.parallel_exc_type) |
| 7571 |
| 7572 code.putln("__Pyx_ErrFetch(&%s, &%s, &%s);" % self.parallel_exc) |
| 7573 pos_info = chain(*zip(self.parallel_pos_info, self.pos_info)) |
| 7574 code.funcstate.uses_error_indicator = True |
| 7575 code.putln("%s = %s; %s = %s; %s = %s;" % tuple(pos_info)) |
| 7576 code.put_gotref(Naming.parallel_exc_type) |
| 7577 |
| 7578 code.putln( |
| 7579 "}") |
| 7580 |
| 7581 code.put_release_ensured_gil() |
| 7582 code.end_block() |
| 7583 |
| 7584 def restore_parallel_exception(self, code): |
| 7585 "Re-raise a parallel exception" |
| 7586 code.begin_block() |
| 7587 code.put_ensure_gil(declare_gilstate=True) |
| 7588 |
| 7589 code.put_giveref(Naming.parallel_exc_type) |
| 7590 code.putln("__Pyx_ErrRestore(%s, %s, %s);" % self.parallel_exc) |
| 7591 pos_info = chain(*zip(self.pos_info, self.parallel_pos_info)) |
| 7592 code.putln("%s = %s; %s = %s; %s = %s;" % tuple(pos_info)) |
| 7593 |
| 7594 code.put_release_ensured_gil() |
| 7595 code.end_block() |
| 7596 |
| 7597 def restore_labels(self, code): |
| 7598 """ |
| 7599 Restore all old labels. Call this before the 'else' clause to for |
| 7600 loops and always before ending the parallel control flow block. |
| 7601 """ |
| 7602 code.set_all_labels(self.old_loop_labels + (self.old_return_label, |
| 7603 self.old_error_label)) |
| 7604 |
| 7605 def end_parallel_control_flow_block(self, code, |
| 7606 break_=False, continue_=False): |
| 7607 """ |
| 7608 This ends the parallel control flow block and based on how the parallel |
| 7609 section was exited, takes the corresponding action. The break_ and |
| 7610 continue_ parameters indicate whether these should be propagated |
| 7611 outwards: |
| 7612 |
| 7613 for i in prange(...): |
| 7614 with cython.parallel.parallel(): |
| 7615 continue |
| 7616 |
| 7617 Here break should be trapped in the parallel block, and propagated to |
| 7618 the for loop. |
| 7619 """ |
| 7620 c = self.begin_of_parallel_control_block_point |
| 7621 |
| 7622 # Firstly, always prefer errors over returning, continue or break |
| 7623 if self.error_label_used: |
| 7624 c.putln("const char *%s = NULL; int %s = 0, %s = 0;" % |
| 7625 self.parallel_pos_info) |
| 7626 |
| 7627 c.putln("PyObject *%s = NULL, *%s = NULL, *%s = NULL;" % |
| 7628 self.parallel_exc) |
| 7629 |
| 7630 code.putln( |
| 7631 "if (%s) {" % Naming.parallel_exc_type) |
| 7632 code.putln("/* This may have been overridden by a continue, " |
| 7633 "break or return in another thread. Prefer the error. */"
) |
| 7634 code.putln("%s = 4;" % Naming.parallel_why) |
| 7635 code.putln( |
| 7636 "}") |
| 7637 |
| 7638 if continue_: |
| 7639 any_label_used = self.any_label_used |
| 7640 else: |
| 7641 any_label_used = self.breaking_label_used |
| 7642 |
| 7643 if any_label_used: |
| 7644 # __pyx_parallel_why is used, declare and initialize |
| 7645 c.putln("int %s;" % Naming.parallel_why) |
| 7646 c.putln("%s = 0;" % Naming.parallel_why) |
| 7647 |
| 7648 code.putln( |
| 7649 "if (%s) {" % Naming.parallel_why) |
| 7650 |
| 7651 for temp_cname, private_cname in self.parallel_private_temps: |
| 7652 code.putln("%s = %s;" % (private_cname, temp_cname)) |
| 7653 |
| 7654 code.putln("switch (%s) {" % Naming.parallel_why) |
| 7655 if continue_: |
| 7656 code.put(" case 1: ") |
| 7657 code.put_goto(code.continue_label) |
| 7658 |
| 7659 if break_: |
| 7660 code.put(" case 2: ") |
| 7661 code.put_goto(code.break_label) |
| 7662 |
| 7663 code.put(" case 3: ") |
| 7664 code.put_goto(code.return_label) |
| 7665 |
| 7666 if self.error_label_used: |
| 7667 code.globalstate.use_utility_code(restore_exception_utility_code
) |
| 7668 code.putln(" case 4:") |
| 7669 self.restore_parallel_exception(code) |
| 7670 code.put_goto(code.error_label) |
| 7671 |
| 7672 code.putln("}") # end switch |
| 7673 code.putln( |
| 7674 "}") # end if |
| 7675 |
| 7676 code.end_block() # end parallel control flow block |
| 7677 self.redef_builtin_expect_apple_gcc_bug(code) |
| 7678 |
| 7679 # FIXME: improve with version number for OS X Lion |
| 7680 buggy_platform_macro_condition = "(defined(__APPLE__) || defined(__OSX__))" |
| 7681 have_expect_condition = "(defined(__GNUC__) && " \ |
| 7682 "(__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__
> 95))))" |
| 7683 redef_condition = "(%s && %s)" % (buggy_platform_macro_condition, have_expec
t_condition) |
| 7684 |
| 7685 def undef_builtin_expect_apple_gcc_bug(self, code): |
| 7686 """ |
| 7687 A bug on OS X Lion disallows __builtin_expect macros. This code avoids t
hem |
| 7688 """ |
| 7689 if not self.parent: |
| 7690 code.undef_builtin_expect(self.redef_condition) |
| 7691 |
| 7692 def redef_builtin_expect_apple_gcc_bug(self, code): |
| 7693 if not self.parent: |
| 7694 code.redef_builtin_expect(self.redef_condition) |
| 7695 |
| 7696 |
| 7697 class ParallelWithBlockNode(ParallelStatNode): |
| 7698 """ |
| 7699 This node represents a 'with cython.parallel.parallel():' block |
| 7700 """ |
| 7701 |
| 7702 valid_keyword_arguments = ['num_threads'] |
| 7703 |
| 7704 num_threads = None |
| 7705 |
| 7706 def analyse_declarations(self, env): |
| 7707 super(ParallelWithBlockNode, self).analyse_declarations(env) |
| 7708 if self.args: |
| 7709 error(self.pos, "cython.parallel.parallel() does not take " |
| 7710 "positional arguments") |
| 7711 |
| 7712 def generate_execution_code(self, code): |
| 7713 self.declare_closure_privates(code) |
| 7714 self.setup_parallel_control_flow_block(code) |
| 7715 |
| 7716 code.putln("#ifdef _OPENMP") |
| 7717 code.put("#pragma omp parallel ") |
| 7718 |
| 7719 if self.privates: |
| 7720 privates = [e.cname for e in self.privates |
| 7721 if not e.type.is_pyobject] |
| 7722 code.put('private(%s)' % ', '.join(privates)) |
| 7723 |
| 7724 self.privatization_insertion_point = code.insertion_point() |
| 7725 self.put_num_threads(code) |
| 7726 code.putln("") |
| 7727 |
| 7728 code.putln("#endif /* _OPENMP */") |
| 7729 |
| 7730 code.begin_block() # parallel block |
| 7731 self.begin_parallel_block(code) |
| 7732 self.initialize_privates_to_nan(code) |
| 7733 code.funcstate.start_collecting_temps() |
| 7734 self.body.generate_execution_code(code) |
| 7735 self.trap_parallel_exit(code) |
| 7736 self.privatize_temps(code) |
| 7737 self.end_parallel_block(code) |
| 7738 code.end_block() # end parallel block |
| 7739 |
| 7740 continue_ = code.label_used(code.continue_label) |
| 7741 break_ = code.label_used(code.break_label) |
| 7742 |
| 7743 self.restore_labels(code) |
| 7744 self.end_parallel_control_flow_block(code, break_=break_, |
| 7745 continue_=continue_) |
| 7746 self.release_closure_privates(code) |
| 7747 |
| 7748 |
| 7749 class ParallelRangeNode(ParallelStatNode): |
| 7750 """ |
| 7751 This node represents a 'for i in cython.parallel.prange():' construct. |
| 7752 |
| 7753 target NameNode the target iteration variable |
| 7754 else_clause Node or None the else clause of this loop |
| 7755 """ |
| 7756 |
| 7757 child_attrs = ['body', 'target', 'else_clause', 'args', 'num_threads', |
| 7758 'chunksize'] |
| 7759 |
| 7760 body = target = else_clause = args = None |
| 7761 |
| 7762 start = stop = step = None |
| 7763 |
| 7764 is_prange = True |
| 7765 |
| 7766 nogil = None |
| 7767 schedule = None |
| 7768 |
| 7769 valid_keyword_arguments = ['schedule', 'nogil', 'num_threads', 'chunksize'] |
| 7770 |
| 7771 def __init__(self, pos, **kwds): |
| 7772 super(ParallelRangeNode, self).__init__(pos, **kwds) |
| 7773 # Pretend to be a ForInStatNode for control flow analysis |
| 7774 self.iterator = PassStatNode(pos) |
| 7775 |
| 7776 def analyse_declarations(self, env): |
| 7777 super(ParallelRangeNode, self).analyse_declarations(env) |
| 7778 self.target.analyse_target_declaration(env) |
| 7779 if self.else_clause is not None: |
| 7780 self.else_clause.analyse_declarations(env) |
| 7781 |
| 7782 if not self.args or len(self.args) > 3: |
| 7783 error(self.pos, "Invalid number of positional arguments to prange") |
| 7784 return |
| 7785 |
| 7786 if len(self.args) == 1: |
| 7787 self.stop, = self.args |
| 7788 elif len(self.args) == 2: |
| 7789 self.start, self.stop = self.args |
| 7790 else: |
| 7791 self.start, self.stop, self.step = self.args |
| 7792 |
| 7793 if hasattr(self.schedule, 'decode'): |
| 7794 self.schedule = self.schedule.decode('ascii') |
| 7795 |
| 7796 if self.schedule not in (None, 'static', 'dynamic', 'guided', |
| 7797 'runtime'): |
| 7798 error(self.pos, "Invalid schedule argument to prange: %s" % |
| 7799 (self.schedule,)) |
| 7800 |
| 7801 def analyse_expressions(self, env): |
| 7802 was_nogil = env.nogil |
| 7803 if self.nogil: |
| 7804 env.nogil = True |
| 7805 |
| 7806 if self.target is None: |
| 7807 error(self.pos, "prange() can only be used as part of a for loop") |
| 7808 return self |
| 7809 |
| 7810 self.target = self.target.analyse_target_types(env) |
| 7811 |
| 7812 if not self.target.type.is_numeric: |
| 7813 # Not a valid type, assume one for now anyway |
| 7814 |
| 7815 if not self.target.type.is_pyobject: |
| 7816 # nogil_check will catch the is_pyobject case |
| 7817 error(self.target.pos, |
| 7818 "Must be of numeric type, not %s" % self.target.type) |
| 7819 |
| 7820 self.index_type = PyrexTypes.c_py_ssize_t_type |
| 7821 else: |
| 7822 self.index_type = self.target.type |
| 7823 if not self.index_type.signed: |
| 7824 warning(self.target.pos, |
| 7825 "Unsigned index type not allowed before OpenMP 3.0", |
| 7826 level=2) |
| 7827 |
| 7828 # Setup start, stop and step, allocating temps if needed |
| 7829 self.names = 'start', 'stop', 'step' |
| 7830 start_stop_step = self.start, self.stop, self.step |
| 7831 |
| 7832 for node, name in zip(start_stop_step, self.names): |
| 7833 if node is not None: |
| 7834 node.analyse_types(env) |
| 7835 if not node.type.is_numeric: |
| 7836 error(node.pos, "%s argument must be numeric" % name) |
| 7837 continue |
| 7838 |
| 7839 if not node.is_literal: |
| 7840 node = node.coerce_to_temp(env) |
| 7841 setattr(self, name, node) |
| 7842 |
| 7843 # As we range from 0 to nsteps, computing the index along the |
| 7844 # way, we need a fitting type for 'i' and 'nsteps' |
| 7845 self.index_type = PyrexTypes.widest_numeric_type( |
| 7846 self.index_type, node.type) |
| 7847 |
| 7848 if self.else_clause is not None: |
| 7849 self.else_clause = self.else_clause.analyse_expressions(env) |
| 7850 |
| 7851 # Although not actually an assignment in this scope, it should be |
| 7852 # treated as such to ensure it is unpacked if a closure temp, and to |
| 7853 # ensure lastprivate behaviour and propagation. If the target index is |
| 7854 # not a NameNode, it won't have an entry, and an error was issued by |
| 7855 # ParallelRangeTransform |
| 7856 if hasattr(self.target, 'entry'): |
| 7857 self.assignments[self.target.entry] = self.target.pos, None |
| 7858 |
| 7859 node = super(ParallelRangeNode, self).analyse_expressions(env) |
| 7860 |
| 7861 if node.chunksize: |
| 7862 if not node.schedule: |
| 7863 error(node.chunksize.pos, |
| 7864 "Must provide schedule with chunksize") |
| 7865 elif node.schedule == 'runtime': |
| 7866 error(node.chunksize.pos, |
| 7867 "Chunksize not valid for the schedule runtime") |
| 7868 elif (node.chunksize.type.is_int and |
| 7869 node.chunksize.is_literal and |
| 7870 node.chunksize.compile_time_value(env) <= 0): |
| 7871 error(node.chunksize.pos, "Chunksize must not be negative") |
| 7872 |
| 7873 node.chunksize = node.chunksize.coerce_to( |
| 7874 PyrexTypes.c_int_type, env).coerce_to_temp(env) |
| 7875 |
| 7876 if node.nogil: |
| 7877 env.nogil = was_nogil |
| 7878 |
| 7879 node.is_nested_prange = node.parent and node.parent.is_prange |
| 7880 if node.is_nested_prange: |
| 7881 parent = node |
| 7882 while parent.parent and parent.parent.is_prange: |
| 7883 parent = parent.parent |
| 7884 |
| 7885 parent.assignments.update(node.assignments) |
| 7886 parent.privates.update(node.privates) |
| 7887 parent.assigned_nodes.extend(node.assigned_nodes) |
| 7888 return node |
| 7889 |
| 7890 def nogil_check(self, env): |
| 7891 names = 'start', 'stop', 'step', 'target' |
| 7892 nodes = self.start, self.stop, self.step, self.target |
| 7893 for name, node in zip(names, nodes): |
| 7894 if node is not None and node.type.is_pyobject: |
| 7895 error(node.pos, "%s may not be a Python object " |
| 7896 "as we don't have the GIL" % name) |
| 7897 |
| 7898 def generate_execution_code(self, code): |
| 7899 """ |
| 7900 Generate code in the following steps |
| 7901 |
| 7902 1) copy any closure variables determined thread-private |
| 7903 into temporaries |
| 7904 |
| 7905 2) allocate temps for start, stop and step |
| 7906 |
| 7907 3) generate a loop that calculates the total number of steps, |
| 7908 which then computes the target iteration variable for every step
: |
| 7909 |
| 7910 for i in prange(start, stop, step): |
| 7911 ... |
| 7912 |
| 7913 becomes |
| 7914 |
| 7915 nsteps = (stop - start) / step; |
| 7916 i = start; |
| 7917 |
| 7918 #pragma omp parallel for lastprivate(i) |
| 7919 for (temp = 0; temp < nsteps; temp++) { |
| 7920 i = start + step * temp; |
| 7921 ... |
| 7922 } |
| 7923 |
| 7924 Note that accumulation of 'i' would have a data dependency |
| 7925 between iterations. |
| 7926 |
| 7927 Also, you can't do this |
| 7928 |
| 7929 for (i = start; i < stop; i += step) |
| 7930 ... |
| 7931 |
| 7932 as the '<' operator should become '>' for descending loops. |
| 7933 'for i from x < i < y:' does not suffer from this problem |
| 7934 as the relational operator is known at compile time! |
| 7935 |
| 7936 4) release our temps and write back any private closure variables |
| 7937 """ |
| 7938 self.declare_closure_privates(code) |
| 7939 |
| 7940 # This can only be a NameNode |
| 7941 target_index_cname = self.target.entry.cname |
| 7942 |
| 7943 # This will be used as the dict to format our code strings, holding |
| 7944 # the start, stop , step, temps and target cnames |
| 7945 fmt_dict = { |
| 7946 'target': target_index_cname, |
| 7947 } |
| 7948 |
| 7949 # Setup start, stop and step, allocating temps if needed |
| 7950 start_stop_step = self.start, self.stop, self.step |
| 7951 defaults = '0', '0', '1' |
| 7952 for node, name, default in zip(start_stop_step, self.names, defaults): |
| 7953 if node is None: |
| 7954 result = default |
| 7955 elif node.is_literal: |
| 7956 result = node.get_constant_c_result_code() |
| 7957 else: |
| 7958 node.generate_evaluation_code(code) |
| 7959 result = node.result() |
| 7960 |
| 7961 fmt_dict[name] = result |
| 7962 |
| 7963 fmt_dict['i'] = code.funcstate.allocate_temp(self.index_type, False) |
| 7964 fmt_dict['nsteps'] = code.funcstate.allocate_temp(self.index_type, False
) |
| 7965 |
| 7966 # TODO: check if the step is 0 and if so, raise an exception in a |
| 7967 # 'with gil' block. For now, just abort |
| 7968 code.putln("if (%(step)s == 0) abort();" % fmt_dict) |
| 7969 |
| 7970 self.setup_parallel_control_flow_block(code) # parallel control flow blo
ck |
| 7971 |
| 7972 self.control_flow_var_code_point = code.insertion_point() |
| 7973 |
| 7974 # Note: nsteps is private in an outer scope if present |
| 7975 code.putln("%(nsteps)s = (%(stop)s - %(start)s) / %(step)s;" % fmt_dict) |
| 7976 |
| 7977 # The target iteration variable might not be initialized, do it only if |
| 7978 # we are executing at least 1 iteration, otherwise we should leave the |
| 7979 # target unaffected. The target iteration variable is firstprivate to |
| 7980 # shut up compiler warnings caused by lastprivate, as the compiler |
| 7981 # erroneously believes that nsteps may be <= 0, leaving the private |
| 7982 # target index uninitialized |
| 7983 code.putln("if (%(nsteps)s > 0)" % fmt_dict) |
| 7984 code.begin_block() # if block |
| 7985 self.generate_loop(code, fmt_dict) |
| 7986 code.end_block() # end if block |
| 7987 |
| 7988 self.restore_labels(code) |
| 7989 |
| 7990 if self.else_clause: |
| 7991 if self.breaking_label_used: |
| 7992 code.put("if (%s < 2)" % Naming.parallel_why) |
| 7993 |
| 7994 code.begin_block() # else block |
| 7995 code.putln("/* else */") |
| 7996 self.else_clause.generate_execution_code(code) |
| 7997 code.end_block() # end else block |
| 7998 |
| 7999 # ------ cleanup ------ |
| 8000 self.end_parallel_control_flow_block(code) # end parallel control flow b
lock |
| 8001 |
| 8002 # And finally, release our privates and write back any closure |
| 8003 # variables |
| 8004 for temp in start_stop_step: |
| 8005 if temp is not None: |
| 8006 temp.generate_disposal_code(code) |
| 8007 temp.free_temps(code) |
| 8008 |
| 8009 code.funcstate.release_temp(fmt_dict['i']) |
| 8010 code.funcstate.release_temp(fmt_dict['nsteps']) |
| 8011 |
| 8012 self.release_closure_privates(code) |
| 8013 |
| 8014 def generate_loop(self, code, fmt_dict): |
| 8015 if self.is_nested_prange: |
| 8016 code.putln("#if 0") |
| 8017 else: |
| 8018 code.putln("#ifdef _OPENMP") |
| 8019 |
| 8020 if not self.is_parallel: |
| 8021 code.put("#pragma omp for") |
| 8022 self.privatization_insertion_point = code.insertion_point() |
| 8023 reduction_codepoint = self.parent.privatization_insertion_point |
| 8024 else: |
| 8025 code.put("#pragma omp parallel") |
| 8026 self.privatization_insertion_point = code.insertion_point() |
| 8027 reduction_codepoint = self.privatization_insertion_point |
| 8028 code.putln("") |
| 8029 code.putln("#endif /* _OPENMP */") |
| 8030 |
| 8031 code.begin_block() # pragma omp parallel begin block |
| 8032 |
| 8033 # Initialize the GIL if needed for this thread |
| 8034 self.begin_parallel_block(code) |
| 8035 |
| 8036 if self.is_nested_prange: |
| 8037 code.putln("#if 0") |
| 8038 else: |
| 8039 code.putln("#ifdef _OPENMP") |
| 8040 code.put("#pragma omp for") |
| 8041 |
| 8042 for entry, (op, lastprivate) in self.privates.iteritems(): |
| 8043 # Don't declare the index variable as a reduction |
| 8044 if op and op in "+*-&^|" and entry != self.target.entry: |
| 8045 if entry.type.is_pyobject: |
| 8046 error(self.pos, "Python objects cannot be reductions") |
| 8047 else: |
| 8048 #code.put(" reduction(%s:%s)" % (op, entry.cname)) |
| 8049 # This is the only way reductions + nesting works in gcc4.5 |
| 8050 reduction_codepoint.put( |
| 8051 " reduction(%s:%s)" % (op, entry.cname)) |
| 8052 else: |
| 8053 if entry == self.target.entry: |
| 8054 code.put(" firstprivate(%s)" % entry.cname) |
| 8055 code.put(" lastprivate(%s)" % entry.cname) |
| 8056 continue |
| 8057 |
| 8058 if not entry.type.is_pyobject: |
| 8059 if lastprivate: |
| 8060 private = 'lastprivate' |
| 8061 else: |
| 8062 private = 'private' |
| 8063 |
| 8064 code.put(" %s(%s)" % (private, entry.cname)) |
| 8065 |
| 8066 if self.schedule: |
| 8067 if self.chunksize: |
| 8068 chunksize = ", %s" % self.evaluate_before_block(code, |
| 8069 self.chunksize) |
| 8070 else: |
| 8071 chunksize = "" |
| 8072 |
| 8073 code.put(" schedule(%s%s)" % (self.schedule, chunksize)) |
| 8074 |
| 8075 self.put_num_threads(reduction_codepoint) |
| 8076 |
| 8077 code.putln("") |
| 8078 code.putln("#endif /* _OPENMP */") |
| 8079 |
| 8080 code.put("for (%(i)s = 0; %(i)s < %(nsteps)s; %(i)s++)" % fmt_dict) |
| 8081 code.begin_block() # for loop block |
| 8082 |
| 8083 guard_around_body_codepoint = code.insertion_point() |
| 8084 |
| 8085 # Start if guard block around the body. This may be unnecessary, but |
| 8086 # at least it doesn't spoil indentation |
| 8087 code.begin_block() |
| 8088 |
| 8089 code.putln("%(target)s = %(start)s + %(step)s * %(i)s;" % fmt_dict) |
| 8090 self.initialize_privates_to_nan(code, exclude=self.target.entry) |
| 8091 |
| 8092 if self.is_parallel: |
| 8093 code.funcstate.start_collecting_temps() |
| 8094 |
| 8095 self.body.generate_execution_code(code) |
| 8096 self.trap_parallel_exit(code, should_flush=True) |
| 8097 self.privatize_temps(code) |
| 8098 |
| 8099 if self.breaking_label_used: |
| 8100 # Put a guard around the loop body in case return, break or |
| 8101 # exceptions might be used |
| 8102 guard_around_body_codepoint.putln("if (%s < 2)" % Naming.parallel_wh
y) |
| 8103 |
| 8104 code.end_block() # end guard around loop body |
| 8105 code.end_block() # end for loop block |
| 8106 |
| 8107 if self.is_parallel: |
| 8108 # Release the GIL and deallocate the thread state |
| 8109 self.end_parallel_block(code) |
| 8110 code.end_block() # pragma omp parallel end block |
| 8111 |
| 8112 |
| 8113 class CnameDecoratorNode(StatNode): |
| 8114 """ |
| 8115 This node is for the cname decorator in CythonUtilityCode: |
| 8116 |
| 8117 @cname('the_cname') |
| 8118 cdef func(...): |
| 8119 ... |
| 8120 |
| 8121 In case of a cdef class the cname specifies the objstruct_cname. |
| 8122 |
| 8123 node the node to which the cname decorator is applied |
| 8124 cname the cname the node should get |
| 8125 """ |
| 8126 |
| 8127 child_attrs = ['node'] |
| 8128 |
| 8129 def analyse_declarations(self, env): |
| 8130 self.node.analyse_declarations(env) |
| 8131 |
| 8132 node = self.node |
| 8133 if isinstance(node, CompilerDirectivesNode): |
| 8134 node = node.body.stats[0] |
| 8135 |
| 8136 self.is_function = isinstance(node, FuncDefNode) |
| 8137 is_struct_or_enum = isinstance(node, (CStructOrUnionDefNode, |
| 8138 CEnumDefNode)) |
| 8139 e = node.entry |
| 8140 |
| 8141 if self.is_function: |
| 8142 e.cname = self.cname |
| 8143 e.func_cname = self.cname |
| 8144 e.used = True |
| 8145 if e.pyfunc_cname and '.' in e.pyfunc_cname: |
| 8146 e.pyfunc_cname = self.mangle(e.pyfunc_cname) |
| 8147 elif is_struct_or_enum: |
| 8148 e.cname = e.type.cname = self.cname |
| 8149 else: |
| 8150 scope = node.scope |
| 8151 |
| 8152 e.cname = self.cname |
| 8153 e.type.objstruct_cname = self.cname + '_obj' |
| 8154 e.type.typeobj_cname = Naming.typeobj_prefix + self.cname |
| 8155 e.type.typeptr_cname = self.cname + '_type' |
| 8156 e.type.scope.namespace_cname = e.type.typeptr_cname |
| 8157 |
| 8158 e.as_variable.cname = py_object_type.cast_code(e.type.typeptr_cname) |
| 8159 |
| 8160 scope.scope_prefix = self.cname + "_" |
| 8161 |
| 8162 for name, entry in scope.entries.iteritems(): |
| 8163 if entry.func_cname: |
| 8164 entry.func_cname = self.mangle(entry.cname) |
| 8165 if entry.pyfunc_cname: |
| 8166 entry.pyfunc_cname = self.mangle(entry.pyfunc_cname) |
| 8167 |
| 8168 def mangle(self, cname): |
| 8169 if '.' in cname: |
| 8170 # remove __pyx_base from func_cname |
| 8171 cname = cname.split('.')[-1] |
| 8172 return '%s_%s' % (self.cname, cname) |
| 8173 |
| 8174 def analyse_expressions(self, env): |
| 8175 self.node = self.node.analyse_expressions(env) |
| 8176 return self |
| 8177 |
| 8178 def generate_function_definitions(self, env, code): |
| 8179 "Ensure a prototype for every @cname method in the right place" |
| 8180 if self.is_function and env.is_c_class_scope: |
| 8181 # method in cdef class, generate a prototype in the header |
| 8182 h_code = code.globalstate['utility_code_proto'] |
| 8183 |
| 8184 if isinstance(self.node, DefNode): |
| 8185 self.node.generate_function_header( |
| 8186 h_code, with_pymethdef=False, proto_only=True) |
| 8187 else: |
| 8188 import ModuleNode |
| 8189 entry = self.node.entry |
| 8190 cname = entry.cname |
| 8191 entry.cname = entry.func_cname |
| 8192 |
| 8193 ModuleNode.generate_cfunction_declaration( |
| 8194 entry, |
| 8195 env.global_scope(), |
| 8196 h_code, |
| 8197 definition=True) |
| 8198 |
| 8199 entry.cname = cname |
| 8200 |
| 8201 self.node.generate_function_definitions(env, code) |
| 8202 |
| 8203 def generate_execution_code(self, code): |
| 8204 self.node.generate_execution_code(code) |
| 8205 |
| 8206 |
| 8207 #-------------------------------------------------------------------------------
----- |
| 8208 # |
| 8209 # Runtime support code |
| 8210 # |
| 8211 #-------------------------------------------------------------------------------
----- |
| 8212 |
| 8213 if Options.gcc_branch_hints: |
| 8214 branch_prediction_macros = """ |
| 8215 /* Test for GCC > 2.95 */ |
| 8216 #if defined(__GNUC__) \ |
| 8217 && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))) |
| 8218 #define likely(x) __builtin_expect(!!(x), 1) |
| 8219 #define unlikely(x) __builtin_expect(!!(x), 0) |
| 8220 #else /* !__GNUC__ or GCC < 2.95 */ |
| 8221 #define likely(x) (x) |
| 8222 #define unlikely(x) (x) |
| 8223 #endif /* __GNUC__ */ |
| 8224 """ |
| 8225 else: |
| 8226 branch_prediction_macros = """ |
| 8227 #define likely(x) (x) |
| 8228 #define unlikely(x) (x) |
| 8229 """ |
| 8230 |
| 8231 #-------------------------------------------------------------------------------
----- |
| 8232 |
| 8233 printing_utility_code = UtilityCode.load_cached("Print", "Printing.c") |
| 8234 printing_one_utility_code = UtilityCode.load_cached("PrintOne", "Printing.c") |
| 8235 |
| 8236 #-------------------------------------------------------------------------------
----- |
| 8237 |
| 8238 # Exception raising code |
| 8239 # |
| 8240 # Exceptions are raised by __Pyx_Raise() and stored as plain |
| 8241 # type/value/tb in PyThreadState->curexc_*. When being caught by an |
| 8242 # 'except' statement, curexc_* is moved over to exc_* by |
| 8243 # __Pyx_GetException() |
| 8244 |
| 8245 restore_exception_utility_code = UtilityCode.load_cached("PyErrFetchRestore", "E
xceptions.c") |
| 8246 raise_utility_code = UtilityCode.load_cached("RaiseException", "Exceptions.c") |
| 8247 get_exception_utility_code = UtilityCode.load_cached("GetException", "Exceptions
.c") |
| 8248 swap_exception_utility_code = UtilityCode.load_cached("SwapException", "Exceptio
ns.c") |
| 8249 reset_exception_utility_code = UtilityCode.load_cached("SaveResetException", "Ex
ceptions.c") |
| 8250 traceback_utility_code = UtilityCode.load_cached("AddTraceback", "Exceptions.c") |
| 8251 |
| 8252 #-------------------------------------------------------------------------------
----- |
| 8253 |
| 8254 get_exception_tuple_utility_code = UtilityCode(proto=""" |
| 8255 static PyObject *__Pyx_GetExceptionTuple(void); /*proto*/ |
| 8256 """, |
| 8257 # I doubt that calling __Pyx_GetException() here is correct as it moves |
| 8258 # the exception from tstate->curexc_* to tstate->exc_*, which prevents |
| 8259 # exception handlers later on from receiving it. |
| 8260 impl = """ |
| 8261 static PyObject *__Pyx_GetExceptionTuple(void) { |
| 8262 PyObject *type = NULL, *value = NULL, *tb = NULL; |
| 8263 if (__Pyx_GetException(&type, &value, &tb) == 0) { |
| 8264 PyObject* exc_info = PyTuple_New(3); |
| 8265 if (exc_info) { |
| 8266 Py_INCREF(type); |
| 8267 Py_INCREF(value); |
| 8268 Py_INCREF(tb); |
| 8269 PyTuple_SET_ITEM(exc_info, 0, type); |
| 8270 PyTuple_SET_ITEM(exc_info, 1, value); |
| 8271 PyTuple_SET_ITEM(exc_info, 2, tb); |
| 8272 return exc_info; |
| 8273 } |
| 8274 } |
| 8275 return NULL; |
| 8276 } |
| 8277 """, |
| 8278 requires=[get_exception_utility_code]) |
OLD | NEW |