# # Pyrex - Parse tree nodes for expressions # import operator from string import join from Errors import error, InternalError import Naming from Nodes import Node import PyrexTypes from PyrexTypes import py_object_type, c_long_type, typecast, error_type, \ CPtrType, CFuncType, COverloadedFuncType import Symtab import Options from Pyrex.Debugging import print_call_chain from DebugFlags import debug_disposal_code, debug_temp_alloc, \ debug_coercion class ExprNode(Node): # subexprs [string] Class var holding names of subexpr node attrs # type PyrexType Type of the result # result_code string Code fragment # result_ctype string C type of result_code if different from type # inplace_result string Temp var holding in-place operation result # is_temp boolean Result is in a temporary variable # is_sequence_constructor # boolean Is a list or tuple constructor expression # saved_subexpr_nodes # [ExprNode or [ExprNode or None] or None] # Cached result of subexpr_nodes() result_ctype = None # The Analyse Expressions phase for expressions is split # into two sub-phases: # # Analyse Types # Determines the result type of the expression based # on the types of its sub-expressions, and inserts # coercion nodes into the expression tree where needed. # Marks nodes which will need to have temporary variables # allocated. # # Allocate Temps # Allocates temporary variables where needed, and fills # in the result_code field of each node. # # ExprNode provides some convenience routines which # perform both of the above phases. These should only # be called from statement nodes, and only when no # coercion nodes need to be added around the expression # being analysed. If coercion is needed, the above two phases # should be invoked separately. # # Framework code in ExprNode provides much of the common # processing for the various phases. It makes use of the # 'subexprs' class attribute of ExprNodes, which should # contain a list of the names of attributes which can # hold sub-nodes or sequences of sub-nodes. # # The framework makes use of a number of abstract methods. # Their responsibilities are as follows. # # Declaration Analysis phase # # analyse_target_declaration # Called during the Analyse Declarations phase to analyse # the LHS of an assignment or argument of a del statement. # Nodes which cannot be the LHS of an assignment need not # implement it. # # Expression Analysis phase # # analyse_types # - Call analyse_types on all sub-expressions. # - Check operand types, and wrap coercion nodes around # sub-expressions where needed. # - Set the type of this node. # - If a temporary variable will be required for the # result, set the is_temp flag of this node. # # analyse_target_types # Called during the Analyse Types phase to analyse # the LHS of an assignment or argument of a del # statement. Similar responsibilities to analyse_types. # # allocate_temps # - Call allocate_temps for all sub-nodes. # - Call allocate_temp for this node. # - If a temporary was allocated, call release_temp on # all sub-expressions. # # allocate_target_temps # - Call allocate_temps on sub-nodes and allocate any other # temps used during assignment. # - Fill in result_code with a C lvalue if needed. # - If a rhs node is supplied, call release_temp on it. # - Call release_temp on sub-nodes and release any other # temps used during assignment. # # #calculate_result_code # # - Called during the Allocate Temps phase. Should return a # # C code fragment evaluating to the result. This is only # # called when the result is not a temporary. # # target_code # Called by the default implementation of allocate_target_temps. # Should return a C lvalue for assigning to the node. The default # implementation calls calculate_result_code. # # check_const # - Check that this node and its subnodes form a # legal constant expression. If so, do nothing, # otherwise call not_const. # # The default implementation of check_const # assumes that the expression is not constant. # # check_const_addr # - Same as check_const, except check that the # expression is a C lvalue whose address is # constant. Otherwise, call addr_not_const. # # The default implementation of calc_const_addr # assumes that the expression is not a constant # lvalue. # # Code Generation phase # # generate_evaluation_code # 1. Call generate_evaluation_code for sub-expressions. # 2. Generate any C statements necessary to calculate # the result of this node from the results of its # sub-expressions. If result is not in a temporary, record # any information that will be needed by this node's # implementation of calculate_result_code(). # 4. If result is in a temporary, call generate_disposal_code # on all sub-expressions. # # A default implementation of generate_evaluation_code # is provided which uses the folling abstract methods: # generate_result_code (for no. 2) # # generate_assignment_code # Called on the LHS of an assignment. # - Call generate_evaluation_code for sub-expressions. # - Generate code to perform the assignment. # - If the assignment absorbed a reference, call # generate_post_assignment_code on the RHS, # otherwise call generate_disposal_code on it. # # generate_deletion_code # Called on an argument of a del statement. # - Call generate_evaluation_code for sub-expressions. # - Generate code to perform the deletion. # - Call generate_disposal_code on all sub-expressions. # # calculate_result_code # Return a C code fragment representing the result of this node. # This is only called if the result is not in a temporary. # is_sequence_constructor = 0 is_attribute = 0 saved_subexpr_nodes = None is_temp = 0 def not_implemented(self, method_name): print_call_chain(method_name, "not implemented") ### raise InternalError( "%s.%s not implemented" % (self.__class__.__name__, method_name)) def is_lvalue(self): return 0 def is_inplace_lvalue(self): return 0 def is_ephemeral(self): # An ephemeral node is one whose result is in # a Python temporary and we suspect there are no # other references to it. Certain operations are # disallowed on such values, since they are # likely to result in a dangling pointer. return self.type.is_pyobject and self.is_temp def subexpr_nodes(self): # Extract a list of subexpression nodes based # on the contents of the subexprs class attribute. if self.saved_subexpr_nodes is None: nodes = [] for name in self.subexprs: item = getattr(self, name) if item: if isinstance(item, ExprNode): nodes.append(item) else: nodes.extend(item) self.saved_subexpr_nodes = nodes return self.saved_subexpr_nodes def result(self): # Return a C code fragment for the result of this node. if self.is_temp: result_code = self.result_code else: result_code = self.calculate_result_code() return result_code def result_as(self, type = None): # Return the result code cast to the specified C type. return typecast(type, self.ctype(), self.result()) def py_result(self): # Return the result code cast to PyObject *. return self.result_as(py_object_type) def ctype(self): # Return the native C type of the result. return self.result_ctype or self.type def compile_time_value(self, denv): # Return value of compile-time expression, or report error. error(self.pos, "Invalid compile-time expression") def compile_time_value_error(self, e): error(self.pos, "Error in compile-time expression: %s: %s" % ( e.__class__.__name__, e)) # ------------- Declaration Analysis ---------------- def analyse_target_declaration(self, env): error(self.pos, "Cannot assign to or delete this") # ------------- Expression Analysis ---------------- def analyse_const_expression(self, env): # Called during the analyse_declarations phase of a # constant expression. Analyses the expression's type, # checks whether it is a legal const expression, # and determines its value. self.analyse_types(env) self.allocate_temps(env) self.check_const() def analyse_expressions(self, env): # Convenience routine performing both the Type # Analysis and Temp Allocation phases for a whole # expression. self.analyse_types(env) self.allocate_temps(env) def analyse_target_expression(self, env, rhs): # Convenience routine performing both the Type # Analysis and Temp Allocation phases for the LHS of # an assignment. self.analyse_target_types(env) self.allocate_target_temps(env, rhs) def analyse_boolean_expression(self, env): # Analyse expression and coerce to a boolean. self.analyse_types(env) bool = self.coerce_to_boolean(env) bool.allocate_temps(env) return bool def analyse_temp_boolean_expression(self, env): # Analyse boolean expression and coerce result into # a temporary. This is used when a branch is to be # performed on the result and we won't have an # opportunity to ensure disposal code is executed # afterwards. By forcing the result into a temporary, # we ensure that all disposal has been done by the # time we get the result. self.analyse_types(env) bool = self.coerce_to_boolean(env) temp_bool = bool.coerce_to_temp(env) temp_bool.allocate_temps(env) return temp_bool # --------------- Type Analysis ------------------ def analyse_as_function(self, env): # Analyse types for an expression that is to be called. self.analyse_types(env) def analyse_as_module(self, env): # If this node can be interpreted as a reference to a # cimported module, return its scope, else None. return None def analyse_as_extension_type(self, env): # If this node can be interpreted as a reference to an # extension type, return its type, else None. return None def analyse_as_cimported_attribute(self, env, *args, **kwds): # If this node can be interpreted as a cimported name, # finish type analysis and return true, else return false. return 0 def analyse_types(self, env): self.not_implemented("analyse_types") def analyse_target_types(self, env): self.analyse_types(env) def analyse_inplace_types(self, env): if self.is_inplace_lvalue(): self.analyse_types(env) else: error(self.pos, "Invalid target for in-place operation") self.type = error_type def gil_assignment_check(self, env): if env.nogil and self.type.is_pyobject: error(self.pos, "Assignment of Python object not allowed without gil") def check_const(self): self.not_const() def not_const(self): error(self.pos, "Not allowed in a constant expression") def check_const_addr(self): self.addr_not_const() def addr_not_const(self): error(self.pos, "Address is not constant") def gil_check(self, env): if env.nogil and self.type.is_pyobject: self.gil_error() # ----------------- Result Allocation ----------------- def result_in_temp(self): # Return true if result is in a temporary owned by # this node or one of its subexpressions. Overridden # by certain nodes which can share the result of # a subnode. return self.is_temp def allocate_target_temps(self, env, rhs, inplace = 0): # Perform temp allocation for the LHS of an assignment. if debug_temp_alloc: print self, "Allocating target temps" self.allocate_subexpr_temps(env) #self.result_code = self.target_code() if rhs: rhs.release_temp(env) self.release_subexpr_temps(env) def allocate_inplace_target_temps(self, env, rhs): if debug_temp_alloc: print self, "Allocating inplace target temps" self.allocate_subexpr_temps(env) #self.result_code = self.target_code() py_inplace = self.type.is_pyobject if py_inplace: self.allocate_temp(env) self.inplace_result = env.allocate_temp(py_object_type) self.release_temp(env) rhs.release_temp(env) if py_inplace: env.release_temp(self.inplace_result) self.release_subexpr_temps(env) def allocate_temps(self, env, result = None): # Allocate temporary variables for this node and # all its sub-expressions. If a result is specified, # this must be a temp node and the specified variable # is used as the result instead of allocating a new # one. if debug_temp_alloc: print self, "Allocating temps" self.allocate_subexpr_temps(env) self.allocate_temp(env, result) if self.is_temp: self.release_subexpr_temps(env) def allocate_subexpr_temps(self, env): # Allocate temporary variables for all sub-expressions # of this node. if debug_temp_alloc: print self, "Allocating temps for:", self.subexprs for node in self.subexpr_nodes(): if node: if debug_temp_alloc: print self, "Allocating temps for", node node.allocate_temps(env) def allocate_temp(self, env, result = None): # If this node requires a temporary variable for its # result, allocate one. If a result is specified, # this must be a temp node and the specified variable # is used as the result instead of allocating a new # one. if debug_temp_alloc: print self, "Allocating temp" if result: if not self.is_temp: raise InternalError("Result forced on non-temp node") self.result_code = result elif self.is_temp: type = self.type if not type.is_void: if type.is_pyobject: type = PyrexTypes.py_object_type self.result_code = env.allocate_temp(type) else: self.result_code = None if debug_temp_alloc: print self, "Allocated result", self.result_code #else: # self.result_code = self.calculate_result_code() def target_code(self): # Return code fragment for use as LHS of a C assignment. return self.calculate_result_code() def calculate_result_code(self): self.not_implemented("calculate_result_code") def release_temp(self, env): # If this node owns a temporary result, release it, # otherwise release results of its sub-expressions. if self.is_temp: if debug_temp_alloc: print self, "Releasing result", self.result_code env.release_temp(self.result_code) else: self.release_subexpr_temps(env) def release_subexpr_temps(self, env): # Release the results of all sub-expressions of # this node. for node in self.subexpr_nodes(): if node: node.release_temp(env) # ---------------- Code Generation ----------------- def mark_vars_used(self): for node in self.subexpr_nodes(): node.mark_vars_used() def make_owned_reference(self, code): # If result is a pyobject, make sure we own # a reference to it. if self.type.is_pyobject and not self.result_in_temp(): code.put_incref(self.py_result()) def generate_evaluation_code(self, code): # Generate code to evaluate this node and # its sub-expressions, and dispose of any # temporary results of its sub-expressions. self.generate_subexpr_evaluation_code(code) self.generate_result_code(code) if self.is_temp: self.generate_subexpr_disposal_code(code) def generate_subexpr_evaluation_code(self, code): for node in self.subexpr_nodes(): node.generate_evaluation_code(code) def generate_result_code(self, code): self.not_implemented("generate_result_code") inplace_functions = { "+=": "PyNumber_InPlaceAdd", "-=": "PyNumber_InPlaceSubtract", "*=": "PyNumber_InPlaceMultiply", "/=": "PyNumber_InPlaceDivide", "%=": "PyNumber_InPlaceRemainder", "**=": "PyNumber_InPlacePower", "<<=": "PyNumber_InPlaceLshift", ">>=": "PyNumber_InPlaceRshift", "&=": "PyNumber_InPlaceAnd", "^=": "PyNumber_InPlaceXor", "|=": "PyNumber_InPlaceOr", } def generate_inplace_operation_code(self, operator, rhs, code): args = (self.py_result(), rhs.py_result()) if operator == "**=": arg_code = "%s, %s, Py_None" % args else: arg_code = "%s, %s" % args code.putln("%s = %s(%s); if (!%s) %s" % ( self.inplace_result, self.inplace_functions[operator], arg_code, self.inplace_result, code.error_goto(self.pos))) if self.is_temp: code.put_decref_clear(self.py_result()) rhs.generate_disposal_code(code) if self.type.is_extension_type: code.putln( "if (!__Pyx_TypeTest(%s, %s)) %s" % ( self.inplace_result, self.type.typeptr_cname, code.error_goto(self.pos))) def generate_disposal_code(self, code): # If necessary, generate code to dispose of # temporary Python reference. if self.is_temp: if self.type.is_pyobject: code.put_decref_clear(self.py_result(), self.ctype()) else: self.generate_subexpr_disposal_code(code) def generate_subexpr_disposal_code(self, code): # Generate code to dispose of temporary results # of all sub-expressions. for node in self.subexpr_nodes(): node.generate_disposal_code(code) def generate_post_assignment_code(self, code): # Same as generate_disposal_code except that # assignment will have absorbed a reference to # the result if it is a Python object. if self.is_temp: if self.type.is_pyobject: code.putln("%s = 0;" % self.result()) else: self.generate_subexpr_disposal_code(code) def generate_inplace_result_disposal_code(self, code): code.put_decref_clear(self.inplace_result, py_object_type) def generate_assignment_code(self, rhs, code): # Stub method for nodes which are not legal as # the LHS of an assignment. An error will have # been reported earlier. pass def generate_deletion_code(self, code): # Stub method for nodes that are not legal as # the argument of a del statement. An error # will have been reported earlier. pass # ----------------- Coercion ---------------------- def coerce_to(self, dst_type, env): # Coerce the result so that it can be assigned to # something of type dst_type. If processing is necessary, # wraps this node in a coercion node and returns that. # Otherwise, returns this node unchanged. # # This method is called during the analyse_expressions # phase of the src_node's processing. src = self src_type = self.type src_is_py_type = src_type.is_pyobject dst_is_py_type = dst_type.is_pyobject if dst_type.is_pyobject: if not src.type.is_pyobject: src = CoerceToPyTypeNode(src, env) if not src.type.subtype_of(dst_type): if not isinstance(src, NoneNode): src = PyTypeTestNode(src, dst_type, env) elif src.type.is_pyobject: src = CoerceFromPyTypeNode(dst_type, src, env) else: # neither src nor dst are py types if not dst_type.assignable_from(src_type): error(self.pos, "Cannot assign type '%s' to '%s'" % (src.type, dst_type)) return src def coerce_to_pyobject(self, env): return self.coerce_to(PyrexTypes.py_object_type, env) def coerce_to_boolean(self, env): # Coerce result to something acceptable as # a boolean value. type = self.type if type.is_pyobject or type.is_ptr or type.is_float: return CoerceToBooleanNode(self, env) else: if not type.is_int and not type.is_error: error(self.pos, "Type '%s' not acceptable as a boolean" % type) return self def coerce_to_integer(self, env): # If not already some C integer type, coerce to longint. if self.type.is_int: return self else: return self.coerce_to(PyrexTypes.c_long_type, env) def coerce_to_temp(self, env): # Ensure that the result is in a temporary. if self.result_in_temp(): return self else: return CoerceToTempNode(self, env) def coerce_to_simple(self, env): # Ensure that the result is simple (see is_simple). if self.is_simple(): return self else: return self.coerce_to_temp(env) def is_simple(self): # A node is simple if its result is something that can # be referred to without performing any operations, e.g. # a constant, local var, C global var, struct member # reference, or temporary. return self.result_in_temp() class AtomicExprNode(ExprNode): # Abstract base class for expression nodes which have # no sub-expressions. subexprs = [] class PyConstNode(AtomicExprNode): # Abstract base class for constant Python values. def is_simple(self): return 1 def analyse_types(self, env): self.type = py_object_type def calculate_result_code(self): return self.value def generate_result_code(self, code): pass class NoneNode(PyConstNode): # The constant value None value = "Py_None" def compile_time_value(self, denv): return None class EllipsisNode(PyConstNode): # '...' in a subscript list. value = "Py_Ellipsis" def compile_time_value(self, denv): return Ellipsis class ConstNode(AtomicExprNode): # Abstract base type for literal constant nodes. # # value string C code fragment is_literal = 1 def is_simple(self): return 1 def analyse_types(self, env): pass # Types are held in class variables def check_const(self): pass def calculate_result_code(self): return str(self.value) def generate_result_code(self, code): pass class NullNode(ConstNode): type = PyrexTypes.c_null_ptr_type value = "NULL" class CharNode(ConstNode): type = PyrexTypes.c_char_type def compile_time_value(self, denv): return ord(self.value) def calculate_result_code(self): return "'%s'" % self.value class IntNode(ConstNode): type = PyrexTypes.c_long_type def compile_time_value(self, denv): return int(self.value, 0) class FloatNode(ConstNode): type = PyrexTypes.c_double_type def compile_time_value(self, denv): return float(self.value) def calculate_result_code(self): strval = str(self.value) if strval == 'nan': return "NAN" elif strval == 'inf': return "INFINITY" elif strval == '-inf': return "(-INFINITY)" else: return strval class StringNode(ConstNode): # #entry Symtab.Entry type = PyrexTypes.c_char_ptr_type def compile_time_value(self, denv): return eval('"%s"' % self.value) # def analyse_types(self, env): # self.entry = env.add_string_const(self.value) def coerce_to(self, dst_type, env): # Arrange for a Python version of the string to be pre-allocated # when coercing to a Python type. if dst_type.is_pyobject and not self.type.is_pyobject: node = self.as_py_string_node(env) else: node = self # We still need to perform normal coerce_to processing on the # result, because we might be coercing to an extension type, # in which case a type test node will be needed. return ConstNode.coerce_to(node, dst_type, env) def as_py_string_node(self, env): # Return a new StringNode with the same value as this node # but whose type is a Python type instead of a C type. #entry = self.entry #env.add_py_string(entry) return StringNode(self.pos, type = py_object_type, value = self.value) def generate_evaluation_code(self, code): if self.type.is_pyobject: self.result_code = code.get_py_string_const(self.value) else: self.result_code = code.get_string_const(self.value) def calculate_result_code(self): return self.result_code class LongNode(AtomicExprNode): # Python long integer literal # # value string def compile_time_value(self, denv): return long(self.value) gil_message = "Constructing Python long int" def analyse_types(self, env): self.type = py_object_type self.gil_check(env) self.is_temp = 1 def generate_evaluation_code(self, code): result = self.result() code.putln( '%s = PyLong_FromString("%s", 0, 0); if (!%s) %s' % ( self.result(), self.value, self.result(), code.error_goto(self.pos))) class ImagNode(AtomicExprNode): # Imaginary number literal # # value float imaginary part def compile_time_value(self, denv): return complex(0.0, self.value) gil_message = "Constructing complex number" def analyse_types(self, env): self.type = py_object_type self.gil_check(env) self.is_temp = 1 def generate_evaluation_code(self, code): result = self.result() code.putln( "%s = PyComplex_FromDoubles(0.0, %s); if (!%s) %s" % ( self.result(), self.value, self.result(), code.error_goto(self.pos))) class NameNode(AtomicExprNode): # Reference to a local or global variable name. # # name string Python name of the variable # # entry Entry Symbol table entry # type_entry Entry For extension type names, the original type entry # interned_cname string is_name = 1 entry = None type_entry = None def compile_time_value(self, denv): try: return denv.lookup(self.name) except KeyError: error(self.pos, "Compile-time name '%s' not defined" % self.name) def coerce_to(self, dst_type, env): # If coercing to a generic pyobject and this is a builtin # C function with a Python equivalent, manufacture a NameNode # referring to the Python builtin. #print "NameNode.coerce_to:", self.name, dst_type ### if dst_type is py_object_type: entry = self.entry if entry.is_cfunction: var_entry = entry.as_variable if var_entry: node = NameNode(self.pos, name = self.name) node.entry = var_entry node.analyse_rvalue_entry(env) return node return AtomicExprNode.coerce_to(self, dst_type, env) def analyse_as_module(self, env): # Try to interpret this as a reference to a cimported module. # Returns the module scope, or None. entry = env.lookup(self.name) if entry and entry.as_module: return entry.as_module return None def analyse_as_extension_type(self, env): # Try to interpret this as a reference to an extension type. # Returns the extension type, or None. entry = env.lookup(self.name) if entry and entry.is_type and entry.type.is_extension_type: return entry.type else: return None def analyse_target_declaration(self, env): self.entry = env.lookup_here(self.name) if not self.entry: self.entry = env.declare_var(self.name, py_object_type, self.pos) def analyse_types(self, env): self.lookup_entry(env) self.analyse_rvalue_entry(env) def lookup_entry(self, env): self.entry = env.lookup(self.name) if not self.entry: self.entry = env.declare_builtin(self.name, self.pos) def analyse_target_types(self, env): self.analyse_entry(env) self.finish_analysing_lvalue() def analyse_inplace_types(self, env): self.analyse_rvalue_entry(env) self.finish_analysing_lvalue() def finish_analysing_lvalue(self): if self.entry.is_readonly: error(self.pos, "Assignment to read-only name '%s'" % self.name) elif not self.is_lvalue(): error(self.pos, "Assignment to non-lvalue '%s'" % self.name) self.type = PyrexTypes.error_type self.entry.used = 1 def analyse_as_function(self, env): self.lookup_entry(env) if self.entry.is_type: self.analyse_constructor_entry(env) else: self.analyse_rvalue_entry(env) def analyse_constructor_entry(self, env): entry = self.entry type = entry.type if type.is_struct_or_union: self.type = entry.type.cplus_constructor_type elif type.is_pyobject: self.analyse_rvalue_entry(env) else: error(self.pos, "Type '%s' not callable as a C++ constructor" % type) self.type = error_type def analyse_rvalue_entry(self, env): #print "NameNode.analyse_rvalue_entry:", self.name ### #print "Entry:", self.entry.__dict__ ### self.analyse_entry(env) entry = self.entry if entry.is_declared_generic: self.result_ctype = py_object_type if entry.is_pyglobal or entry.is_builtin: self.is_temp = 1 self.gil_check(env) gil_message = "Accessing Python global or builtin" def analyse_entry(self, env): #print "NameNode.analyse_entry:", self.name ### self.check_identifier_kind() entry = self.entry type = entry.type ctype = entry.ctype self.type = type if ctype: self.result_ctype = ctype if entry.is_pyglobal or entry.is_builtin: assert type.is_pyobject, "Python global or builtin not a Python object" #self.interned_cname = env.intern(self.entry.name) def check_identifier_kind(self): # Check that this is an appropriate kind of name for use in an expression. # Also finds the variable entry associated with an extension type. entry = self.entry if entry.is_type and entry.type.is_extension_type: self.type_entry = entry if not (entry.is_const or entry.is_variable or entry.is_builtin or entry.is_cfunction): if self.entry.as_variable: self.entry = self.entry.as_variable else: error(self.pos, "'%s' is not a constant, variable or function identifier" % self.name) def is_simple(self): # If it's not a C variable, it'll be in a temp. return 1 def calculate_target_results(self, env): pass def check_const(self): entry = self.entry if not (entry.is_const or entry.is_cfunction): self.not_const() def check_const_addr(self): entry = self.entry if not (entry.is_cglobal or entry.is_cfunction): self.addr_not_const() def is_lvalue(self): entry = self.entry return entry.is_variable and \ not entry.type.is_array and \ not entry.is_readonly def is_inplace_lvalue(self): return self.is_lvalue() def is_ephemeral(self): # Name nodes are never ephemeral, even if the # result is in a temporary. return 0 def allocate_temp(self, env, result = None): AtomicExprNode.allocate_temp(self, env, result) entry = self.entry if entry: entry.used = 1 def calculate_result_code(self): entry = self.entry if not entry: return "" # There was an error earlier return entry.cname def generate_result_code(self, code): assert hasattr(self, 'entry') entry = self.entry if entry is None: return # There was an error earlier if entry.utility_code: code.use_utility_code(entry.utility_code) if entry.is_pyglobal or entry.is_builtin: if entry.is_builtin: namespace = Naming.builtins_cname else: # entry.is_pyglobal namespace = entry.namespace_cname result = self.result() cname = code.intern(self.entry.name) code.use_utility_code(get_name_interned_utility_code) code.putln( '%s = __Pyx_GetName(%s, %s); if (!%s) %s' % ( result, namespace, cname, result, code.error_goto(self.pos))) def generate_setattr_code(self, value_code, code): entry = self.entry namespace = self.entry.namespace_cname cname = code.intern(self.entry.name) code.putln( 'if (PyObject_SetAttr(%s, %s, %s) < 0) %s' % ( namespace, cname, value_code, code.error_goto(self.pos))) def generate_assignment_code(self, rhs, code): #print "NameNode.generate_assignment_code:", self.name ### entry = self.entry if entry is None: return # There was an error earlier if entry.is_pyglobal: self.generate_setattr_code(rhs.py_result(), code) if debug_disposal_code: print "NameNode.generate_assignment_code:" print "...generating disposal code for", rhs rhs.generate_disposal_code(code) else: if self.type.is_pyobject: rhs.make_owned_reference(code) code.put_decref(self.py_result()) code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype()))) if debug_disposal_code: print "NameNode.generate_assignment_code:" print "...generating post-assignment code for", rhs rhs.generate_post_assignment_code(code) def generate_inplace_assignment_code(self, operator, rhs, code): entry = self.entry if entry is None: return # There was an error earlier if self.type.is_pyobject: self.generate_result_code(code) self.generate_inplace_operation_code(operator, rhs, code) if entry.is_pyglobal: self.generate_setattr_code(self.inplace_result, code) self.generate_inplace_result_disposal_code(code) else: code.put_decref(self.py_result()) cast_inplace_result = typecast(self.ctype(), py_object_type, self.inplace_result) code.putln('%s = %s;' % (self.result(), cast_inplace_result)) else: code.putln("%s %s %s;" % (self.result(), operator, rhs.result())) rhs.generate_disposal_code(code) def generate_deletion_code(self, code): if self.entry is None: return # There was an error earlier if not self.entry.is_pyglobal: error(self.pos, "Deletion of local or C global name not supported") return cname = code.intern(self.entry.name) code.putln( 'if (PyObject_DelAttr(%s, %s) < 0) %s' % ( Naming.module_cname, cname, code.error_goto(self.pos))) def mark_vars_used(self): if self.entry: self.entry.used = 1 class BackquoteNode(ExprNode): # `expr` # # arg ExprNode subexprs = ['arg'] def analyse_types(self, env): self.arg.analyse_types(env) self.arg = self.arg.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Backquote expression" def generate_result_code(self, code): result = self.result() code.putln( "%s = PyObject_Repr(%s); if (!%s) %s" % ( self.result(), self.arg.py_result(), self.result(), code.error_goto(self.pos))) class ImportNode(ExprNode): # Used as part of import statement implementation. # Implements result = # __import__(module_name, globals(), None, name_list) # # module_name StringNode dotted name of module # name_list ListNode or None list of names to be imported subexprs = ['module_name', 'name_list'] def analyse_types(self, env): self.module_name.analyse_types(env) self.module_name = self.module_name.coerce_to_pyobject(env) if self.name_list: self.name_list.analyse_types(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 # env.use_utility_code(import_utility_code) gil_message = "Python import" def generate_result_code(self, code): if self.name_list: name_list_code = self.name_list.py_result() else: name_list_code = "0" code.use_utility_code(import_utility_code) result = self.result() code.putln( "%s = __Pyx_Import(%s, %s); if (!%s) %s" % ( result, self.module_name.py_result(), name_list_code, result, code.error_goto(self.pos))) class IteratorNode(ExprNode): # Used as part of for statement implementation. # Implements result = iter(sequence) # # sequence ExprNode subexprs = ['sequence'] def analyse_types(self, env): self.sequence.analyse_types(env) self.sequence = self.sequence.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Iterating over Python object" def generate_result_code(self, code): result = self.result() code.putln( "%s = PyObject_GetIter(%s); if (!%s) %s" % ( result, self.sequence.py_result(), result, code.error_goto(self.pos))) class NextNode(AtomicExprNode): # Used as part of for statement implementation. # Implements result = iterator.next() # Created during analyse_types phase. # The iterator is not owned by this node. # # iterator ExprNode def __init__(self, iterator, env): self.pos = iterator.pos self.iterator = iterator self.type = py_object_type self.is_temp = 1 def generate_result_code(self, code): result = self.result() code.putln( "%s = PyIter_Next(%s);" % ( result, self.iterator.py_result())) code.putln( "if (!%s) {" % result) code.putln( "if (PyErr_Occurred()) %s" % code.error_goto(self.pos)) code.putln( "break;") code.putln( "}") class ExcValueNode(AtomicExprNode): # Node created during analyse_types phase # of an ExceptClauseNode to fetch the current # exception or traceback value. def __init__(self, pos, env, var): ExprNode.__init__(self, pos) self.type = py_object_type self.var = var def calculate_result_code(self): return self.var def generate_result_code(self, code): pass class TempNode(AtomicExprNode): # Node created during analyse_types phase # of some nodes to hold a temporary value. def __init__(self, pos, type, env): ExprNode.__init__(self, pos) self.type = type if type.is_pyobject: self.result_ctype = py_object_type self.is_temp = 1 def generate_result_code(self, code): pass class PyTempNode(TempNode): # TempNode holding a Python value. def __init__(self, pos, env): TempNode.__init__(self, pos, PyrexTypes.py_object_type, env) #------------------------------------------------------------------- # # Trailer nodes # #------------------------------------------------------------------- class IndexNode(ExprNode): # Sequence indexing. # # base ExprNode # index ExprNode subexprs = ['base', 'index'] def compile_time_value(self, denv): base = self.base.compile_time_value(denv) index = self.index.compile_time_value(denv) try: return base[index] except Exception, e: self.compile_time_value_error(e) def is_ephemeral(self): return self.base.is_ephemeral() def analyse_target_declaration(self, env): pass def analyse_types(self, env): self.analyse_base_and_index_types(env, getting = 1) def analyse_target_types(self, env): self.analyse_base_and_index_types(env, setting = 1) def analyse_inplace_types(self, env): self.analyse_base_and_index_types(env, getting = 1, setting = 1) def analyse_base_and_index_types(self, env, getting = 0, setting = 0): self.base.analyse_types(env) self.index.analyse_types(env) btype = self.base.type if btype.is_pyobject: itype = self.index.type if not (btype.is_sequence and itype.is_int and itype.signed): self.index = self.index.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 else: if self.base.type.is_ptr or self.base.type.is_array: self.type = self.base.type.base_type else: error(self.pos, "Attempting to index non-array type '%s'" % self.base.type) self.type = PyrexTypes.error_type if self.index.type.is_pyobject: self.index = self.index.coerce_to( PyrexTypes.c_py_ssize_t_type, env) if not self.index.type.is_int: error(self.pos, "Invalid index type '%s'" % self.index.type) gil_message = "Indexing Python object" def check_const_addr(self): self.base.check_const_addr() self.index.check_const() def is_lvalue(self): return 1 def is_inplace_lvalue(self): return 1 def calculate_result_code(self): return "(%s[%s])" % ( self.base.result(), self.index.result()) def generate_result_code(self, code): if self.type.is_pyobject: itype = self.index.type if itype.is_int and itype.signed: code.use_utility_code(getitem_int_utility_code) function = "__Pyx_GetItemInt" index_code = self.index.result() else: function = "PyObject_GetItem" index_code = self.index.py_result() result = self.result() code.putln( "%s = %s(%s, %s); if (!%s) %s" % ( result, function, self.base.py_result(), index_code, result, code.error_goto(self.pos))) def generate_setitem_code(self, value_code, code): itype = self.index.type if itype.is_int and itype.signed: code.use_utility_code(setitem_int_utility_code) function = "__Pyx_SetItemInt" index_code = self.index.result() else: function = "PyObject_SetItem" index_code = self.index.py_result() code.putln( "if (%s(%s, %s, %s) < 0) %s" % ( function, self.base.py_result(), index_code, value_code, code.error_goto(self.pos))) def generate_assignment_code(self, rhs, code): self.generate_subexpr_evaluation_code(code) if self.type.is_pyobject: self.generate_setitem_code(rhs.py_result(), code) else: code.putln( "%s = %s;" % ( self.result(), rhs.result())) self.generate_subexpr_disposal_code(code) rhs.generate_disposal_code(code) def generate_inplace_assignment_code(self, operator, rhs, code): self.generate_subexpr_evaluation_code(code) if self.type.is_pyobject: self.generate_result_code(code) self.generate_inplace_operation_code(operator, rhs, code) self.generate_setitem_code(self.inplace_result, code) self.generate_inplace_result_disposal_code(code) else: code.putln("%s %s %s;" % (self.result(), operator, rhs.result())) rhs.generate_disposal_code(code) self.generate_subexpr_disposal_code(code) def generate_deletion_code(self, code): self.generate_subexpr_evaluation_code(code) if self.base.type.is_sequence and self.index.type.is_int: function = "PySequence_DelItem" index_code = self.index.result() else: function = "PyObject_DelItem" index_code = self.index.py_result() code.putln( "if (%s(%s, %s) < 0) %s" % ( function, self.base.py_result(), index_code, code.error_goto(self.pos))) #else: # error(self.pos, "Cannot delete non-Python variable") self.generate_subexpr_disposal_code(code) class SliceIndexNode(ExprNode): # 2-element slice indexing # # base ExprNode # start ExprNode or None # stop ExprNode or None subexprs = ['base', 'start', 'stop'] def is_inplace_lvalue(self): return 1 def compile_time_value(self, denv): base = self.base.compile_time_value(denv) start = self.start.compile_time_value(denv) stop = self.stop.compile_time_value(denv) try: return base[start:stop] except Exception, e: self.compile_time_value_error(e) def analyse_target_declaration(self, env): pass def analyse_types(self, env): self.base.analyse_types(env) if self.start: self.start.analyse_types(env) if self.stop: self.stop.analyse_types(env) self.base = self.base.coerce_to_pyobject(env) c_int = PyrexTypes.c_py_ssize_t_type if self.start: self.start = self.start.coerce_to(c_int, env) if self.stop: self.stop = self.stop.coerce_to(c_int, env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Slicing Python object" def generate_result_code(self, code): result = self.result() code.putln( "%s = PySequence_GetSlice(%s, %s, %s); if (!%s) %s" % ( result, self.base.py_result(), self.start_code(), self.stop_code(), result, code.error_goto(self.pos))) def generate_setslice_code(self, value_code, code): code.putln( "if (PySequence_SetSlice(%s, %s, %s, %s) < 0) %s" % ( self.base.py_result(), self.start_code(), self.stop_code(), value_code, code.error_goto(self.pos))) def generate_assignment_code(self, rhs, code): self.generate_subexpr_evaluation_code(code) self.generate_setslice_code(rhs.result(), code) self.generate_subexpr_disposal_code(code) rhs.generate_disposal_code(code) def generate_inplace_assignment_code(self, operator, rhs, code): self.generate_subexpr_evaluation_code(code) self.generate_result_code(code) self.generate_inplace_operation_code(operator, rhs, code) self.generate_setslice_code(self.inplace_result, code) self.generate_inplace_result_disposal_code(code) self.generate_subexpr_disposal_code(code) def generate_deletion_code(self, code): self.generate_subexpr_evaluation_code(code) code.putln( "if (PySequence_DelSlice(%s, %s, %s) < 0) %s" % ( self.base.py_result(), self.start_code(), self.stop_code(), code.error_goto(self.pos))) self.generate_subexpr_disposal_code(code) def start_code(self): if self.start: return self.start.result() else: return "0" def stop_code(self): if self.stop: return self.stop.result() else: return "PY_SSIZE_T_MAX" # def calculate_result_code(self): # # self.result_code is not used, but this method must exist # return "" class SliceNode(ExprNode): # start:stop:step in subscript list # # start ExprNode # stop ExprNode # step ExprNode def compile_time_value(self, denv): start = self.start.compile_time_value(denv) stop = self.stop.compile_time_value(denv) step = step.step.compile_time_value(denv) try: return slice(start, stop, step) except Exception, e: self.compile_time_value_error(e) subexprs = ['start', 'stop', 'step'] def analyse_types(self, env): self.start.analyse_types(env) self.stop.analyse_types(env) self.step.analyse_types(env) self.start = self.start.coerce_to_pyobject(env) self.stop = self.stop.coerce_to_pyobject(env) self.step = self.step.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Constructing Python slice object" def generate_result_code(self, code): result = self.result() code.putln( "%s = PySlice_New(%s, %s, %s); if (!%s) %s" % ( result, self.start.py_result(), self.stop.py_result(), self.step.py_result(), result, code.error_goto(self.pos))) class CallNode(ExprNode): def gil_check(self, env): # Make sure we're not in a nogil environment if env.nogil: error(self.pos, "Calling gil-requiring function without gil") class SimpleCallNode(CallNode): # Function call without keyword, * or ** args. # # function ExprNode # args [ExprNode] # arg_tuple ExprNode or None used internally # self ExprNode or None used internally # coerced_self ExprNode or None used internally # function_type PyrexType used internally subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple'] self = None coerced_self = None arg_tuple = None is_new = False cplus_argless_constr_type = CFuncType(None, []) def compile_time_value(self, denv): function = self.function.compile_time_value(denv) args = [arg.compile_time_value(denv) for arg in self.args] try: return function(*args) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): #print "SimpleCallNode.analyse_types:", self.pos ### function = self.function function.is_called = 1 function.analyse_as_function(env) if function.is_name or function.is_attribute: #print "SimpleCallNode.analyse_types:", self.pos, "is name or attribute" ### func_entry = function.entry if func_entry: if func_entry.is_cmethod or func_entry.is_builtin_method: # Take ownership of the object from which the attribute # was obtained, because we need to pass it as 'self'. #print "SimpleCallNode: Snarfing self argument" ### self.self = function.obj function.obj = CloneNode(self.self) elif self.is_new: if not (func_entry.is_type and func_entry.type.is_struct_or_union and func_entry.type.scope.is_cplus): error(self.pos, "'new' operator can only be used on a C++ struct type") self.type = error_type return else: #print "SimpleCallNode.analyse_types:", self.pos, "not name or attribute" ### if self.is_new: error(self.pos, "Invalid use of 'new' operator") self.type = error_type return func_type = self.function.type if func_type.is_ptr: func_type = func_type.base_type self.function_type = func_type if func_type.is_pyobject: #print "SimpleCallNode: Python call" ### if self.args: self.arg_tuple = TupleNode(self.pos, args = self.args) self.arg_tuple.analyse_types(env) else: self.arg_tuple = None self.args = None if function.is_name and function.type_entry: # We are calling an extension type constructor self.type = function.type_entry.type self.result_ctype = py_object_type else: self.type = py_object_type self.gil_check(env) self.is_temp = 1 else: #print "SimpleCallNode: C call" ### for arg in self.args: arg.analyse_types(env) if func_type.is_cfunction: self.type = func_type.return_type if self.is_new: self.type = CPtrType(self.type) if func_type.is_overloaded: func_type = self.resolve_overloading() if not func_type: self.type = error_type return if self.self and func_type.args: #print "SimpleCallNode: Inserting self into argument list" ### # Coerce 'self' to the type expected by the method. expected_type = func_type.args[0].type self.coerced_self = CloneNode(self.self).coerce_to( expected_type, env) # Insert coerced 'self' argument into argument list. self.args.insert(0, self.coerced_self) self.analyse_c_function_call(env) def resolve_overloading(self): func_type = self.function_type arg_types = [arg.type for arg in self.args] signatures = func_type.signatures or [self.cplus_argless_constr_type] for signature in signatures: if signature.callable_with(arg_types): signature.return_type = func_type.return_type self.function_type = signature return signature def display_types(types): return ", ".join([str(type) for type in types]) error(self.pos, "No matching signature found for argument types (%s)" % display_types(arg_types)) if signatures: error(self.pos, "Candidates are:") for signature in signatures: error(signature.pos, "(%s)" % display_types(signature.args)) def analyse_c_function_call(self, env): func_type = self.function_type # Check function type if not func_type.is_cfunction: if not func_type.is_error: error(self.pos, "Calling non-function type '%s'" % func_type) self.type = PyrexTypes.error_type return # Check no. of args expected_nargs = len(func_type.args) actual_nargs = len(self.args) if actual_nargs < expected_nargs \ or (not func_type.has_varargs and actual_nargs > expected_nargs): expected_str = str(expected_nargs) if func_type.has_varargs: expected_str = "at least " + expected_str error(self.pos, "Call with wrong number of arguments (expected %s, got %s)" % (expected_str, actual_nargs)) self.args = None self.type = PyrexTypes.error_type return # Coerce arguments for i in range(expected_nargs): formal_type = func_type.args[i].type self.args[i] = self.args[i].coerce_to(formal_type, env) for i in range(expected_nargs, actual_nargs): if self.args[i].type.is_pyobject: error(self.args[i].pos, "Python object cannot be passed as a varargs parameter") # Calc result code fragment #print "SimpleCallNode.analyse_c_function_call: self.type =", self.type ### if self.type.is_pyobject \ or func_type.exception_value is not None \ or func_type.exception_check: self.is_temp = 1 if self.type.is_pyobject: self.result_ctype = py_object_type # Check gil if not func_type.nogil: self.gil_check(env) if func_type.exception_check and env.nogil: self.gil_error("Calling 'except ?' or 'except *' function") def calculate_result_code(self): return self.c_call_code() def c_call_code(self): if self.type.is_error or self.args is None or not self.function_type.is_cfunction: return "" func_type = self.function_type formal_args = func_type.args arg_list_code = [] for (formal_arg, actual_arg) in zip(formal_args, self.args): arg_code = actual_arg.result_as(formal_arg.type) arg_list_code.append(arg_code) for actual_arg in self.args[len(formal_args):]: arg_list_code.append(actual_arg.result()) result = "%s(%s)" % (self.function.result(), join(arg_list_code, ",")) if self.is_new: result = "new " + result return result def generate_result_code(self, code): if self.type.is_error: return func_type = self.function_type result = self.result() if func_type.is_pyobject: if self.arg_tuple: arg_code = self.arg_tuple.py_result() else: arg_code = "0" code.putln( "%s = PyObject_CallObject(%s, %s); if (!%s) %s" % ( result, self.function.py_result(), arg_code, result, code.error_goto(self.pos))) elif func_type.is_cfunction: exc_checks = [] if self.type.is_pyobject: exc_checks.append("!%s" % result) else: exc_val = func_type.exception_value exc_check = func_type.exception_check if exc_val is not None: exc_checks.append("%s == %s" % (self.result(), exc_val)) if exc_check: exc_checks.append("PyErr_Occurred()") if self.is_temp or exc_checks: rhs = self.c_call_code() result = self.result() if result: lhs = "%s = " % result if self.is_temp and self.type.is_pyobject: #return_type = self.type # func_type.return_type #print "SimpleCallNode.generate_result_code: casting", rhs, \ # "from", return_type, "to pyobject" ### rhs = typecast(py_object_type, self.type, rhs) else: lhs = "" code.putln( "%s%s; if (%s) %s" % ( lhs, rhs, " && ".join(exc_checks), code.error_goto(self.pos))) class GeneralCallNode(CallNode): # General Python function call, including keyword, # * and ** arguments. # # function ExprNode # positional_args ExprNode Tuple of positional arguments # keyword_args ExprNode or None Dict of keyword arguments # starstar_arg ExprNode or None Dict of extra keyword args subexprs = ['function', 'positional_args', 'keyword_args', 'starstar_arg'] def compile_time_value(self, denv): function = self.function.compile_time_value(denv) positional_args = self.positional_args.compile_time_value(denv) keyword_args = self.keyword_args.compile_time_value(denv) starstar_arg = self.starstar_arg.compile_time_value(denv) try: keyword_args.update(starstar_arg) return function(*positional_args, **keyword_args) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): function = self.function function.analyse_types(env) self.positional_args.analyse_types(env) if self.keyword_args: self.keyword_args.analyse_types(env) if self.starstar_arg: self.starstar_arg.analyse_types(env) self.function = self.function.coerce_to_pyobject(env) self.positional_args = \ self.positional_args.coerce_to_pyobject(env) if self.starstar_arg: self.starstar_arg = \ self.starstar_arg.coerce_to_pyobject(env) if function.is_name and function.type_entry: # We are calling an extension type constructor self.type = function.type_entry.type self.result_ctype = py_object_type else: self.type = py_object_type self.gil_check(env) self.is_temp = 1 def generate_result_code(self, code): if self.keyword_args and self.starstar_arg: code.putln( "if (PyDict_Update(%s, %s) < 0) %s" % ( self.keyword_args.py_result(), self.starstar_arg.py_result(), code.error_goto(self.pos))) keyword_code = self.keyword_args.py_result() elif self.keyword_args: keyword_code = self.keyword_args.py_result() elif self.starstar_arg: keyword_code = self.starstar_arg.py_result() else: keyword_code = None if not keyword_code: call_code = "PyObject_CallObject(%s, %s)" % ( self.function.py_result(), self.positional_args.py_result()) else: call_code = "PyEval_CallObjectWithKeywords(%s, %s, %s)" % ( self.function.py_result(), self.positional_args.py_result(), keyword_code) result = self.result() code.putln( "%s = %s; if (!%s) %s" % ( result, call_code, result, code.error_goto(self.pos))) class AsTupleNode(ExprNode): # Convert argument to tuple. Used for normalising # the * argument of a function call. # # arg ExprNode subexprs = ['arg'] def compile_time_value(self, denv): arg = self.arg.compile_time_value(denv) try: return tuple(arg) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): self.arg.analyse_types(env) self.arg = self.arg.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Constructing Python tuple" def generate_result_code(self, code): result = self.result() code.putln( "%s = PySequence_Tuple(%s); if (!%s) %s" % ( result, self.arg.py_result(), result, code.error_goto(self.pos))) class AttributeNode(ExprNode): # obj.attribute # # obj ExprNode # attribute string # # Used internally: # # is_py_attr boolean Is a Python getattr operation # member string C name of struct member # is_called boolean Function call is being done on result # entry Entry Symbol table entry of attribute # interned_attr_cname string C name of interned attribute name is_attribute = 1 subexprs = ['obj'] type = PyrexTypes.error_type result_code = "" entry = None is_called = 0 def compile_time_value(self, denv): attr = self.attribute if attr.startswith("__") and attr.endswith("__"): self.error("Invalid attribute name '%s' in compile-time expression" % attr) return None obj = self.obj.compile_time_value(denv) try: return getattr(obj, attr) except Exception, e: self.compile_time_value_error(e) def analyse_target_declaration(self, env): pass def analyse_target_types(self, env): self.analyse_types(env, target = 1) def analyse_as_function(self, env): module_scope = self.obj.analyse_as_module(env) if module_scope: entry = module_scope.lookup_here(self.attribute) if entry and entry.is_type: self.mutate_into_name_node(entry) self.analyse_constructor_entry(env) return self.analyse_types(env) def analyse_types(self, env, target = 0): if self.analyse_as_cimported_attribute(env, target): return if not target and self.analyse_as_unbound_cmethod(env): return self.analyse_as_ordinary_attribute(env, target) def analyse_as_cimported_attribute(self, env, target = 0, allow_type = 0): # Try to interpret this as a reference to an imported # C const, type, var or function. If successful, mutates # this node into a NameNode and returns 1, otherwise # returns 0. module_scope = self.obj.analyse_as_module(env) if module_scope: entry = module_scope.lookup_here(self.attribute) if entry and ( entry.is_cglobal or entry.is_cfunction or entry.is_type or entry.is_const): self.mutate_into_name_node(entry) if entry.is_type and allow_type: pass elif target: self.analyse_target_types(env) else: self.analyse_rvalue_entry(env) return 1 return 0 def analyse_as_unbound_cmethod(self, env): # Try to interpret this as a reference to an unbound # C method of an extension type. If successful, mutates # this node into a NameNode and returns 1, otherwise # returns 0. type = self.obj.analyse_as_extension_type(env) if type: entry = type.scope.lookup_here(self.attribute) if entry and entry.is_cmethod: # Create a temporary entry describing the C method # as an ordinary function. ubcm_entry = Symtab.Entry(entry.name, "%s->%s" % (type.vtabptr_cname, entry.cname), entry.type) ubcm_entry.is_cfunction = 1 ubcm_entry.func_cname = entry.func_cname self.mutate_into_name_node(ubcm_entry) self.analyse_rvalue_entry(env) return 1 return 0 def analyse_as_extension_type(self, env): # Try to interpret this as a reference to an extension type # in a cimported module. Returns the extension type, or None. module_scope = self.obj.analyse_as_module(env) if module_scope: entry = module_scope.lookup_here(self.attribute) if entry and entry.is_type and entry.type.is_extension_type: return entry.type return None def analyse_as_module(self, env): # Try to interpret this as a reference to a cimported module # in another cimported module. Returns the module scope, or None. module_scope = self.obj.analyse_as_module(env) if module_scope: entry = module_scope.lookup_here(self.attribute) if entry and entry.as_module: return entry.as_module return None def mutate_into_name_node(self, entry): # Turn this node into a NameNode with the given entry. self.__class__ = NameNode self.name = self.attribute self.entry = entry del self.obj del self.attribute def analyse_as_ordinary_attribute(self, env, target): self.obj.analyse_types(env) self.analyse_attribute(env) if self.entry and self.entry.is_cmethod and not self.is_called: error(self.pos, "C method can only be called") if self.is_py_attr: if not target: self.is_temp = 1 self.result_ctype = py_object_type def analyse_attribute(self, env): # Look up attribute and set self.type and self.member. self.is_py_attr = 0 self.member = self.attribute if self.obj.type.is_string: self.obj = self.obj.coerce_to_pyobject(env) obj_type = self.obj.type if obj_type.is_ptr: obj_type = obj_type.base_type self.op = "->" elif obj_type.is_extension_type: self.op = "->" else: self.op = "." if obj_type.has_attributes: entry = None if obj_type.attributes_known(): entry = obj_type.scope.lookup_here(self.attribute) else: error(self.pos, "Cannot select attribute of incomplete type '%s'" % obj_type) obj_type = PyrexTypes.error_type self.entry = entry if entry: if obj_type.is_extension_type and entry.name == "__weakref__": error(self.pos, "Illegal use of special attribute __weakref__") if entry.is_variable or entry.is_cmethod: self.type = entry.type self.member = entry.cname return if entry.is_builtin_method and self.is_called: # Mutate into NameNode referring to C function #print "AttributeNode: Mutating builtin method into NameNode" ### self.type = entry.type self.__class__ = NameNode return else: # If it's not a variable or C method, it must be a Python # method of an extension type, so we treat it like a Python # attribute. pass # If we get here, the base object is not a struct/union/extension # type, or it is an extension type and the attribute is either not # declared or is declared as a Python method. Treat it as a Python # attribute reference. if obj_type.is_pyobject: self.type = py_object_type self.is_py_attr = 1 #self.interned_attr_cname = env.intern(self.attribute) self.gil_check(env) else: if not obj_type.is_error: error(self.pos, "Object of type '%s' has no attribute '%s'" % (obj_type, self.attribute)) gil_message = "Accessing Python attribute" def is_simple(self): if self.obj: return self.result_in_temp() or self.obj.is_simple() else: return NameNode.is_simple(self) def is_lvalue(self): if self.obj: return 1 else: return NameNode.is_lvalue(self) def is_inplace_lvalue(self): return self.is_lvalue() def is_ephemeral(self): if self.obj: return self.obj.is_ephemeral() else: return NameNode.is_ephemeral(self) def calculate_result_code(self): obj = self.obj obj_code = obj.result_as(obj.type) if self.entry and self.entry.is_cmethod: return "((struct %s *)%s%s%s)->%s" % ( obj.type.vtabstruct_cname, obj_code, self.op, obj.type.vtabslot_cname, self.member) else: return "%s%s%s" % (obj_code, self.op, self.member) def generate_result_code(self, code): if self.is_py_attr: result = self.result() cname = code.intern(self.attribute) code.putln( '%s = PyObject_GetAttr(%s, %s); if (!%s) %s' % ( result, self.obj.py_result(), cname, result, code.error_goto(self.pos))) def generate_setattr_code(self, value_code, code): cname = code.intern(self.attribute) code.putln( 'if (PyObject_SetAttr(%s, %s, %s) < 0) %s' % ( self.obj.py_result(), cname, value_code, code.error_goto(self.pos))) def generate_assignment_code(self, rhs, code): self.obj.generate_evaluation_code(code) if self.is_py_attr: self.generate_setattr_code(rhs.py_result(), code) rhs.generate_disposal_code(code) else: select_code = self.result() if self.type.is_pyobject: rhs.make_owned_reference(code) code.put_decref(select_code, self.ctype()) code.putln( "%s = %s;" % ( select_code, rhs.result_as(self.ctype()))) rhs.generate_post_assignment_code(code) self.obj.generate_disposal_code(code) def generate_inplace_assignment_code(self, operator, rhs, code): self.obj.generate_evaluation_code(code) select_code = self.result() if self.type.is_pyobject: self.generate_result_code(code) self.generate_inplace_operation_code(operator, rhs, code) if self.is_py_attr: self.generate_setattr_code(self.inplace_result, code) self.generate_inplace_result_disposal_code(code) else: code.put_decref(select_code, self.ctype()) cast_inplace_result = typecast(self.ctype(), py_object_type, self.inplace_result) code.putln("%s = %s;" % (select_code, cast_inplace_result)) else: code.putln("%s %s %s;" % (select_code, operator, rhs.result())) rhs.generate_disposal_code(code) self.obj.generate_disposal_code(code) def generate_deletion_code(self, code): self.obj.generate_evaluation_code(code) if self.is_py_attr: cname = code.intern(self.attribute) code.putln( 'if (PyObject_DelAttr(%s, %s) < 0) %s' % ( self.obj.py_result(), cname, code.error_goto(self.pos))) else: error(self.pos, "Cannot delete C attribute of extension type") self.obj.generate_disposal_code(code) #------------------------------------------------------------------- # # Constructor nodes # #------------------------------------------------------------------- class SequenceNode(ExprNode): # Base class for list and tuple constructor nodes. # Contains common code for performing sequence unpacking. # # args [ExprNode] # iterator ExprNode # unpacked_items [ExprNode] or None # coerced_unpacked_items [ExprNode] or None subexprs = ['args'] is_sequence_constructor = 1 unpacked_items = None def compile_time_value_list(self, denv): return [arg.compile_time_value(denv) for arg in self.args] def analyse_target_declaration(self, env): for arg in self.args: arg.analyse_target_declaration(env) def analyse_types(self, env): for i in range(len(self.args)): arg = self.args[i] arg.analyse_types(env) self.args[i] = arg.coerce_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 def analyse_target_types(self, env): self.iterator = PyTempNode(self.pos, env) self.unpacked_items = [] self.coerced_unpacked_items = [] for arg in self.args: arg.analyse_target_types(env) unpacked_item = PyTempNode(self.pos, env) coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env) self.unpacked_items.append(unpacked_item) self.coerced_unpacked_items.append(coerced_unpacked_item) self.type = py_object_type # env.use_utility_code(unpacking_utility_code) def allocate_target_temps(self, env, rhs): self.iterator.allocate_temps(env) if rhs: rhs.release_temp(env) for arg, node in zip(self.args, self.coerced_unpacked_items): node.allocate_temps(env) arg.allocate_target_temps(env, node) #arg.release_target_temp(env) #node.release_temp(env) self.iterator.release_temp(env) # def release_target_temp(self, env): # #for arg in self.args: # # arg.release_target_temp(env) # #for node in self.coerced_unpacked_items: # # node.release_temp(env) # self.iterator.release_temp(env) def generate_result_code(self, code): self.generate_operation_code(code) def generate_assignment_code(self, rhs, code): iter_result = self.iterator.result() code.putln( "%s = PyObject_GetIter(%s); if (!%s) %s" % ( iter_result, rhs.py_result(), iter_result, code.error_goto(self.pos))) rhs.generate_disposal_code(code) for i in range(len(self.args)): item = self.unpacked_items[i] code.use_utility_code(unpacking_utility_code) unpack_code = "__Pyx_UnpackItem(%s)" % ( self.iterator.py_result()) item_result = item.result() code.putln( "%s = %s; if (!%s) %s" % ( item_result, typecast(item.ctype(), py_object_type, unpack_code), item_result, code.error_goto(self.pos))) value_node = self.coerced_unpacked_items[i] value_node.generate_evaluation_code(code) self.args[i].generate_assignment_code(value_node, code) code.putln( "if (__Pyx_EndUnpack(%s) < 0) %s" % ( self.iterator.py_result(), code.error_goto(self.pos))) if debug_disposal_code: print "UnpackNode.generate_assignment_code:" print "...generating disposal code for", rhs self.iterator.generate_disposal_code(code) class TupleNode(SequenceNode): # Tuple constructor. gil_message = "Constructing Python tuple" def compile_time_value(self, denv): values = self.compile_time_value_list(denv) try: return tuple(values) except Exception, e: self.compile_time_value_error(e) def generate_operation_code(self, code): result = self.result() code.putln( "%s = PyTuple_New(%s); if (!%s) %s" % ( result, len(self.args), result, code.error_goto(self.pos))) for i in range(len(self.args)): arg = self.args[i] arg_result = arg.py_result() # ??? Change this to use make_owned_reference? if not arg.result_in_temp(): code.put_incref(arg_result) code.putln( "PyTuple_SET_ITEM(%s, %s, %s);" % ( result, i, arg_result)) def generate_subexpr_disposal_code(self, code): # We call generate_post_assignment_code here instead # of generate_disposal_code, because values were stored # in the tuple using a reference-stealing operation. for arg in self.args: arg.generate_post_assignment_code(code) class ListNode(SequenceNode): # List constructor. gil_message = "Constructing Python list" def compile_time_value(self, denv): return self.compile_time_value_list(denv) def generate_operation_code(self, code): result = self.result() code.putln("%s = PyList_New(%s); if (!%s) %s" % (result, len(self.args), result, code.error_goto(self.pos))) for i in range(len(self.args)): arg = self.args[i] arg_result = arg.py_result() #if not arg.is_temp: if not arg.result_in_temp(): code.put_incref(arg_result) code.putln("PyList_SET_ITEM(%s, %s, %s);" % (result, i, arg_result)) def generate_subexpr_disposal_code(self, code): # We call generate_post_assignment_code here instead # of generate_disposal_code, because values were stored # in the list using a reference-stealing operation. for arg in self.args: arg.generate_post_assignment_code(code) class DictNode(ExprNode): # Dictionary constructor. # # key_value_pairs [(ExprNode, ExprNode)] def compile_time_value(self, denv): pairs = [(key.compile_time_value(denv), value.compile_time_value(denv)) for (key, value) in self.key_value_pairs] try: return dict(pairs) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): new_pairs = [] for key, value in self.key_value_pairs: key.analyse_types(env) value.analyse_types(env) key = key.coerce_to_pyobject(env) value = value.coerce_to_pyobject(env) new_pairs.append((key, value)) self.key_value_pairs = new_pairs self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Constructing Python dict" def allocate_temps(self, env, result = None): # Custom method used here because key-value # pairs are evaluated and used one at a time. self.allocate_temp(env, result) for key, value in self.key_value_pairs: key.allocate_temps(env) value.allocate_temps(env) key.release_temp(env) value.release_temp(env) def generate_evaluation_code(self, code): # Custom method used here because key-value # pairs are evaluated and used one at a time. result = self.result() code.putln( "%s = PyDict_New(); if (!%s) %s" % ( result, result, code.error_goto(self.pos))) for key, value in self.key_value_pairs: key.generate_evaluation_code(code) value.generate_evaluation_code(code) code.putln( "if (PyDict_SetItem(%s, %s, %s) < 0) %s" % ( result, key.py_result(), value.py_result(), code.error_goto(self.pos))) key.generate_disposal_code(code) value.generate_disposal_code(code) class ClassNode(ExprNode): # Helper class used in the implementation of Python # class definitions. Constructs a class object given # a name, tuple of bases and class dictionary. # # name ExprNode Name of the class # bases ExprNode Base class tuple # dict ExprNode Class dict (not owned by this node) # doc ExprNode or None Doc string # module_name string Name of defining module subexprs = ['name', 'bases', 'doc'] def analyse_types(self, env): self.name.analyse_types(env) self.name = self.name.coerce_to_pyobject(env) self.bases.analyse_types(env) if self.doc: self.doc.analyse_types(env) self.doc = self.doc.coerce_to_pyobject(env) self.module_name = env.global_scope().qualified_name self.type = py_object_type self.gil_check(env) self.is_temp = 1 # env.use_utility_code(create_class_utility_code) gil_message = "Constructing Python class" def generate_result_code(self, code): result = self.result() if self.doc: code.putln( 'if (PyDict_SetItemString(%s, "__doc__", %s) < 0) %s' % ( self.dict.py_result(), self.doc.py_result(), code.error_goto(self.pos))) code.use_utility_code(create_class_utility_code) code.putln( '%s = __Pyx_CreateClass(%s, %s, %s, "%s"); if (!%s) %s' % ( result, self.bases.py_result(), self.dict.py_result(), self.name.py_result(), self.module_name, result, code.error_goto(self.pos))) class UnboundMethodNode(ExprNode): # Helper class used in the implementation of Python # class definitions. Constructs an unbound method # object from a class and a function. # # class_cname string C var holding the class object # function ExprNode Function object subexprs = ['function'] def analyse_types(self, env): self.function.analyse_types(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 gil_message = "Constructing an unbound method" def generate_result_code(self, code): result = self.result() code.putln( "%s = PyMethod_New(%s, 0, %s); if (!%s) %s" % ( result, self.function.py_result(), self.class_cname, result, code.error_goto(self.pos))) class PyCFunctionNode(AtomicExprNode): # Helper class used in the implementation of Python # class definitions. Constructs a PyCFunction object # from a PyMethodDef struct. # # pymethdef_cname string PyMethodDef structure # module_name string Name of defining module def analyse_types(self, env): self.type = py_object_type self.module_name = env.global_scope().module_name self.gil_check(env) self.is_temp = 1 gil_message = "Constructing Python function" def generate_result_code(self, code): result = self.result() code.putln( "%s = PyCFunction_NewEx(&%s, 0, %s); if (!%s) %s" % ( result, self.pymethdef_cname, code.get_py_string_const(self.module_name), result, code.error_goto(self.pos))) #------------------------------------------------------------------- # # Unary operator nodes # #------------------------------------------------------------------- compile_time_unary_operators = { 'not': operator.not_, '~': operator.inv, '-': operator.neg, '+': operator.pos, } class UnopNode(ExprNode): # operator string # operand ExprNode # # Processing during analyse_expressions phase: # # analyse_c_operation # Called when the operand is not a pyobject. # - Check operand type and coerce if needed. # - Determine result type and result code fragment. # - Allocate temporary for result if needed. subexprs = ['operand'] def compile_time_value(self, denv): func = compile_time_unary_operators.get(self.operator) if not func: error(self.pos, "Unary '%s' not supported in compile-time expression" % self.operator) operand = self.operand.compile_time_value(denv) try: return func(operand) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): self.operand.analyse_types(env) if self.is_py_operation(): self.coerce_operand_to_pyobject(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 else: self.analyse_c_operation(env) def check_const(self): self.operand.check_const() def is_py_operation(self): return self.operand.type.is_pyobject def coerce_operand_to_pyobject(self, env): self.operand = self.operand.coerce_to_pyobject(env) def generate_result_code(self, code): if self.operand.type.is_pyobject: self.generate_py_operation_code(code) else: if self.is_temp: self.generate_c_operation_code(code) def generate_py_operation_code(self, code): function = self.py_operation_function() result = self.result() code.putln( "%s = %s(%s); if (!%s) %s" % ( result, function, self.operand.py_result(), result, code.error_goto(self.pos))) def type_error(self): if not self.operand.type.is_error: error(self.pos, "Invalid operand type for '%s' (%s)" % (self.operator, self.operand.type)) self.type = PyrexTypes.error_type class NotNode(ExprNode): # 'not' operator # # operand ExprNode def compile_time_value(self, denv): operand = self.operand.compile_time_value(denv) try: return not operand except Exception, e: self.compile_time_value_error(e) subexprs = ['operand'] def analyse_types(self, env): self.operand.analyse_types(env) self.operand = self.operand.coerce_to_boolean(env) self.type = PyrexTypes.c_int_type def calculate_result_code(self): return "(!%s)" % self.operand.result() def generate_result_code(self, code): pass class UnaryPlusNode(UnopNode): # unary '+' operator operator = '+' def analyse_c_operation(self, env): self.type = self.operand.type def py_operation_function(self): return "PyNumber_Positive" def calculate_result_code(self): return self.operand.result() class UnaryMinusNode(UnopNode): # unary '-' operator operator = '-' def analyse_c_operation(self, env): if self.operand.type.is_numeric: self.type = self.operand.type else: self.type_error() def py_operation_function(self): return "PyNumber_Negative" def calculate_result_code(self): return "(-%s)" % self.operand.result() class TildeNode(UnopNode): # unary '~' operator def analyse_c_operation(self, env): if self.operand.type.is_int: self.type = self.operand.type else: self.type_error() def py_operation_function(self): return "PyNumber_Invert" def calculate_result_code(self): return "(~%s)" % self.operand.result() class AmpersandNode(ExprNode): # The C address-of operator. # # operand ExprNode subexprs = ['operand'] def analyse_types(self, env): self.operand.analyse_types(env) argtype = self.operand.type if not (argtype.is_cfunction or self.operand.is_lvalue()): self.error("Taking address of non-lvalue") return if argtype.is_pyobject: self.error("Cannot take address of Python variable") return self.type = PyrexTypes.c_ptr_type(argtype) def check_const(self): self.operand.check_const_addr() def error(self, mess): error(self.pos, mess) self.type = PyrexTypes.error_type self.result_code = "" def calculate_result_code(self): return "(&%s)" % self.operand.result() def generate_result_code(self, code): pass unop_node_classes = { "+": UnaryPlusNode, "-": UnaryMinusNode, "~": TildeNode, } def unop_node(pos, operator, operand): # Construct unnop node of appropriate class for # given operator. return unop_node_classes[operator](pos, operator = operator, operand = operand) class TypecastNode(ExprNode): # C type cast # # base_type CBaseTypeNode # declarator CDeclaratorNode # operand ExprNode subexprs = ['operand'] def analyse_types(self, env): base_type = self.base_type.analyse(env) _, self.type = self.declarator.analyse(base_type, env) if self.type.is_cfunction: error(self.pos, "Cannot cast to a function type") self.type = PyrexTypes.error_type self.operand.analyse_types(env) to_py = self.type.is_pyobject from_py = self.operand.type.is_pyobject if from_py and not to_py and self.operand.is_ephemeral(): error(self.pos, "Casting temporary Python object to non-Python type") # Must do the following, so that the result can be increfed without # the operand getting evaluated twice. if to_py and not from_py: #self.result_ctype = py_object_type #self.is_temp = 1 self.operand = self.operand.coerce_to_simple(env) def check_const(self): self.operand.check_const() def calculate_result_code(self): opnd = self.operand result_code = self.type.cast_code(opnd.result()) return result_code def result_as(self, type): if not self.is_temp and type.is_pyobject and self.type.is_pyobject: # Optimise away some unnecessary casting return self.operand.result_as(type) else: return ExprNode.result_as(self, type) def generate_result_code(self, code): if self.is_temp: code.putln( "%s = %s;" % ( self.result(), self.operand.py_result())) code.put_incref(self.py_result()) class SizeofNode(ExprNode): # Base class for sizeof(x) expression nodes. # # sizeof_code string subexprs = [] def check_const(self): pass def analyse_types(self, env): self.analyse_argument(env) self.type = PyrexTypes.c_size_t_type def analyse_type_argument(self, arg_type): if arg_type.is_pyobject: error(self.pos, "Cannot take sizeof Python object") elif arg_type.is_void: error(self.pos, "Cannot take sizeof void") elif not arg_type.is_complete(): error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type) arg_code = arg_type.declaration_code("") self.sizeof_code = "(sizeof(%s))" % arg_code def calculate_result_code(self): return self.sizeof_code def generate_result_code(self, code): pass class SizeofTypeNode(SizeofNode): # C sizeof function applied to a type # # base_type CBaseTypeNode # declarator CDeclaratorNode def analyse_argument(self, env): base_type = self.base_type.analyse(env) _, arg_type = self.declarator.analyse(base_type, env) self.analyse_type_argument(arg_type) class SizeofVarNode(SizeofNode): # C sizeof function applied to a variable or qualified name # (which may actually refer to a type) # # operand ExprNode #subexprs = ['operand'] def analyse_argument(self, env): is_type = 0 operand = self.operand if operand.analyse_as_cimported_attribute(env, allow_type = 1): if operand.entry.is_type: is_type = 1 self.analyse_type_argument(operand.entry.type) else: self.operand.analyse_types(env) self.operand.mark_vars_used() if not is_type: self.sizeof_code = "(sizeof(%s))" % operand.result() #------------------------------------------------------------------- # # Binary operator nodes # #------------------------------------------------------------------- compile_time_binary_operators = { '<': operator.lt, '<=': operator.le, '==': operator.eq, '!=': operator.ne, '>=': operator.ge, '>': operator.gt, 'is': operator.is_, 'is_not': operator.is_not, '+': operator.add, '&': operator.and_, '/': operator.div, '//': operator.floordiv, '<<': operator.lshift, '%': operator.mod, '*': operator.mul, '|': operator.or_, '**': operator.pow, '>>': operator.rshift, '-': operator.sub, #'/': operator.truediv, '^': operator.xor, 'in': lambda x, y: x in y, 'not_in': lambda x, y: x not in y, } def get_compile_time_binop(node): func = compile_time_binary_operators.get(node.operator) if not func: error(node.pos, "Binary '%s' not supported in compile-time expression" % node.operator) return func class BinopNode(ExprNode): # operator string # operand1 ExprNode # operand2 ExprNode # # Processing during analyse_expressions phase: # # analyse_c_operation # Called when neither operand is a pyobject. # - Check operand types and coerce if needed. # - Determine result type and result code fragment. # - Allocate temporary for result if needed. subexprs = ['operand1', 'operand2'] def compile_time_value(self, denv): func = get_compile_time_binop(self) operand1 = self.operand1.compile_time_value(denv) operand2 = self.operand2.compile_time_value(denv) try: return func(operand1, operand2) except Exception, e: self.compile_time_value_error(e) def analyse_types(self, env): self.operand1.analyse_types(env) self.operand2.analyse_types(env) if self.is_py_operation(): self.coerce_operands_to_pyobjects(env) self.type = py_object_type self.gil_check(env) self.is_temp = 1 else: self.analyse_c_operation(env) def is_py_operation(self): return (self.operand1.type.is_pyobject or self.operand2.type.is_pyobject) def coerce_operands_to_pyobjects(self, env): self.operand1 = self.operand1.coerce_to_pyobject(env) self.operand2 = self.operand2.coerce_to_pyobject(env) def check_const(self): self.operand1.check_const() self.operand2.check_const() def generate_result_code(self, code): #print "BinopNode.generate_result_code:", self.operand1, self.operand2 ### if self.operand1.type.is_pyobject: function = self.py_operation_function() if function == "PyNumber_Power": extra_args = ", Py_None" else: extra_args = "" result = self.result() code.putln( "%s = %s(%s, %s%s); if (!%s) %s" % ( result, function, self.operand1.py_result(), self.operand2.py_result(), extra_args, result, code.error_goto(self.pos))) else: if self.is_temp: self.generate_c_operation_code(code) def type_error(self): if not (self.operand1.type.is_error or self.operand2.type.is_error): error(self.pos, "Invalid operand types for '%s' (%s; %s)" % (self.operator, self.operand1.type, self.operand2.type)) self.type = PyrexTypes.error_type class NumBinopNode(BinopNode): # Binary operation taking numeric arguments. def analyse_c_operation(self, env): type1 = self.operand1.type type2 = self.operand2.type if self.operator == "**" and type1.is_int and type2.is_int: error(self.pos, "** with two C int types is ambiguous") self.type = error_type return self.type = self.compute_c_result_type(type1, type2) if not self.type: self.type_error() def compute_c_result_type(self, type1, type2): if self.c_types_okay(type1, type2): return PyrexTypes.widest_numeric_type(type1, type2) else: return None def c_types_okay(self, type1, type2): #print "NumBinopNode.c_types_okay:", type1, type2 ### return (type1.is_numeric or type1.is_enum) \ and (type2.is_numeric or type2.is_enum) def calculate_result_code(self): return "(%s %s %s)" % ( self.operand1.result(), self.operator, self.operand2.result()) def py_operation_function(self): return self.py_functions[self.operator] py_functions = { "|": "PyNumber_Or", "^": "PyNumber_Xor", "&": "PyNumber_And", "<<": "PyNumber_Lshift", ">>": "PyNumber_Rshift", "+": "PyNumber_Add", "-": "PyNumber_Subtract", "*": "PyNumber_Multiply", "/": "PyNumber_Divide", "%": "PyNumber_Remainder", "**": "PyNumber_Power" } class IntBinopNode(NumBinopNode): # Binary operation taking integer arguments. def c_types_okay(self, type1, type2): #print "IntBinopNode.c_types_okay:", type1, type2 ### return (type1.is_int or type1.is_enum) \ and (type2.is_int or type2.is_enum) class AddNode(NumBinopNode): # '+' operator. def is_py_operation(self): if self.operand1.type.is_string \ and self.operand2.type.is_string: return 1 else: return NumBinopNode.is_py_operation(self) def compute_c_result_type(self, type1, type2): #print "AddNode.compute_c_result_type:", type1, self.operator, type2 ### if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum): return type1 elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum): return type2 else: return NumBinopNode.compute_c_result_type( self, type1, type2) class SubNode(NumBinopNode): # '-' operator. def compute_c_result_type(self, type1, type2): if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum): return type1 elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array): return PyrexTypes.c_int_type else: return NumBinopNode.compute_c_result_type( self, type1, type2) class MulNode(NumBinopNode): # '*' operator. def is_py_operation(self): type1 = self.operand1.type type2 = self.operand2.type if (type1.is_string and type2.is_int) \ or (type2.is_string and type1.is_int): return 1 else: return NumBinopNode.is_py_operation(self) class ModNode(IntBinopNode): # '%' operator. def is_py_operation(self): return (self.operand1.type.is_string or self.operand2.type.is_string or IntBinopNode.is_py_operation(self)) class PowNode(NumBinopNode): # '**' operator. def analyse_types(self, env): env.pow_function_used = 1 NumBinopNode.analyse_types(self, env) def compute_c_result_type(self, type1, type2): if self.c_types_okay(type1, type2): return PyrexTypes.c_double_type else: return None def calculate_result_code(self): return "pow(%s, %s)" % ( self.operand1.result(), self.operand2.result()) class BoolBinopNode(ExprNode): # Short-circuiting boolean operation. # # operator string # operand1 ExprNode # operand2 ExprNode # temp_bool ExprNode used internally temp_bool = None subexprs = ['operand1', 'operand2', 'temp_bool'] def compile_time_value(self, denv): if self.operator == 'and': return self.operand1.compile_time_value(denv) \ and self.operand2.compile_time_value(denv) else: return self.operand1.compile_time_value(denv) \ or self.operand2.compile_time_value(denv) def analyse_types(self, env): self.operand1.analyse_types(env) self.operand2.analyse_types(env) if self.operand1.type.is_pyobject or \ self.operand2.type.is_pyobject: self.operand1 = self.operand1.coerce_to_pyobject(env) self.operand2 = self.operand2.coerce_to_pyobject(env) self.temp_bool = TempNode(self.pos, PyrexTypes.c_int_type, env) self.type = py_object_type self.gil_check(env) else: self.operand1 = self.operand1.coerce_to_boolean(env) self.operand2 = self.operand2.coerce_to_boolean(env) self.type = PyrexTypes.c_int_type # For what we're about to do, it's vital that # both operands be temp nodes. self.operand1 = self.operand1.coerce_to_temp(env) #CTT self.operand2 = self.operand2.coerce_to_temp(env) self.is_temp = 1 gil_message = "Truth-testing Python object" def allocate_temps(self, env, result_code = None): # We don't need both operands at the same time, and # one of the operands will also be our result. So we # use an allocation strategy here which results in # this node and both its operands sharing the same # result variable. This allows us to avoid some # assignments and increfs/decrefs that would otherwise # be necessary. self.allocate_temp(env, result_code) self.operand1.allocate_temps(env, self.result_code) if self.temp_bool: self.temp_bool.allocate_temp(env) self.temp_bool.release_temp(env) self.operand2.allocate_temps(env, self.result_code) # We haven't called release_temp on either operand, # because although they are temp nodes, they don't own # their result variable. And because they are temp # nodes, any temps in their subnodes will have been # released before their allocate_temps returned. # Therefore, they contain no temp vars that need to # be released. def check_const(self): self.operand1.check_const() self.operand2.check_const() def calculate_result_code(self): return "(%s %s %s)" % ( self.operand1.result(), self.py_to_c_op[self.operator], self.operand2.result()) py_to_c_op = {'and': "&&", 'or': "||"} def generate_evaluation_code(self, code): self.operand1.generate_evaluation_code(code) test_result = self.generate_operand1_test(code) if self.operator == 'and': sense = "" else: sense = "!" code.putln( "if (%s%s) {" % ( sense, test_result)) self.operand1.generate_disposal_code(code) self.operand2.generate_evaluation_code(code) code.putln( "}") def generate_operand1_test(self, code): # Generate code to test the truth of the first operand. if self.type.is_pyobject: test_result = self.temp_bool.result() code.putln( "%s = PyObject_IsTrue(%s); if (%s < 0) %s" % ( test_result, self.operand1.py_result(), test_result, code.error_goto(self.pos))) else: test_result = self.operand1.result() return test_result class CmpNode: # Mixin class containing code common to PrimaryCmpNodes # and CascadedCmpNodes. def cascaded_compile_time_value(self, operand1, denv): func = get_compile_time_binop(self) operand2 = self.operand2.compile_time_value(denv) try: result = func(operand1, operand2) except Exception, e: self.compile_time_value_error(e) result = None if result: cascade = self.cascade if cascade: result = result and cascade.compile_time_value(operand2, denv) return result def is_python_comparison(self): return (self.has_python_operands() or (self.cascade and self.cascade.is_python_comparison()) or self.operator in ('in', 'not_in')) def check_types(self, env, operand1, op, operand2): if not self.types_okay(operand1, op, operand2): error(self.pos, "Invalid types for '%s' (%s, %s)" % (self.operator, operand1.type, operand2.type)) def types_okay(self, operand1, op, operand2): type1 = operand1.type type2 = operand2.type if type1.is_error or type2.is_error: return 1 if type1.is_pyobject: # type2 will be, too return 1 elif type1.is_ptr or type1.is_array: return type1.is_null_ptr or type2.is_null_ptr \ or ((type2.is_ptr or type2.is_array) and type1.base_type.same_as(type2.base_type)) elif ((type1.is_numeric and type2.is_numeric or type1.is_enum and (type2.is_int or type1.same_as(type2)) or type1.is_int and type2.is_enum) and op not in ('is', 'is_not')): return 1 else: return 0 def generate_operation_code(self, code, result, operand1, op , operand2): if op == 'in' or op == 'not_in': code.putln( "%s = PySequence_Contains(%s, %s); if (%s < 0) %s" % ( result, operand2.py_result(), operand1.py_result(), result, code.error_goto(self.pos))) if op == 'not_in': code.putln( "%s = !%s;" % ( result, result)) elif (operand1.type.is_pyobject and op not in ('is', 'is_not')): code.putln( "if (PyObject_Cmp(%s, %s, &%s) < 0) %s" % ( operand1.py_result(), operand2.py_result(), result, code.error_goto(self.pos))) code.putln( "%s = %s %s 0;" % ( result, result, op)) else: type1 = operand1.type type2 = operand2.type if (type1.is_extension_type or type2.is_extension_type) \ and not operand1.ctype().same_as(operand2.ctype()): code1 = operand1.result_as(py_object_type) code2 = operand2.result_as(py_object_type) else: code1 = operand1.result() code2 = operand2.result() code.putln("%s = %s %s %s;" % ( result, code1, self.c_operator(op), code2)) def c_operator(self, op): if op == 'is': return "==" elif op == 'is_not': return "!=" else: return op class PrimaryCmpNode(ExprNode, CmpNode): # Non-cascaded comparison or first comparison of # a cascaded sequence. # # operator string # operand1 ExprNode # operand2 ExprNode # cascade CascadedCmpNode # We don't use the subexprs mechanism, because # things here are too complicated for it to handle. # Instead, we override all the framework methods # which use it. cascade = None def compile_time_value(self, denv): operand1 = self.operand1.compile_time_value(denv) return self.cascaded_compile_time_value(operand1, denv) def analyse_types(self, env): self.operand1.analyse_types(env) self.operand2.analyse_types(env) if self.cascade: self.cascade.analyse_types(env, self.operand2) self.is_pycmp = self.is_python_comparison() if self.is_pycmp: self.coerce_operands_to_pyobjects(env) if self.cascade: self.operand2 = self.operand2.coerce_to_simple(env) self.cascade.coerce_cascaded_operands_to_temp(env) self.check_operand_types(env) self.type = PyrexTypes.c_int_type if self.is_pycmp or self.cascade: self.is_temp = 1 def check_operand_types(self, env): self.check_types(env, self.operand1, self.operator, self.operand2) if self.cascade: self.cascade.check_operand_types(env, self.operand2) def has_python_operands(self): return (self.operand1.type.is_pyobject or self.operand2.type.is_pyobject) def coerce_operands_to_pyobjects(self, env): self.operand1 = self.operand1.coerce_to_pyobject(env) self.operand2 = self.operand2.coerce_to_pyobject(env) if self.cascade: self.cascade.coerce_operands_to_pyobjects(env) def allocate_subexpr_temps(self, env): self.operand1.allocate_temps(env) self.operand2.allocate_temps(env) if self.cascade: self.cascade.allocate_subexpr_temps(env) def release_subexpr_temps(self, env): self.operand1.release_temp(env) self.operand2.release_temp(env) if self.cascade: self.cascade.release_subexpr_temps(env) def check_const(self): self.operand1.check_const() self.operand2.check_const() if self.cascade: self.not_const() def calculate_result_code(self): return "(%s %s %s)" % ( self.operand1.result(), self.c_operator(self.operator), self.operand2.result()) def generate_evaluation_code(self, code): self.operand1.generate_evaluation_code(code) self.operand2.generate_evaluation_code(code) if self.is_temp: result = self.result() self.generate_operation_code(code, result, self.operand1, self.operator, self.operand2) if self.cascade: self.cascade.generate_evaluation_code(code, result, self.operand2) self.operand1.generate_disposal_code(code) self.operand2.generate_disposal_code(code) def generate_subexpr_disposal_code(self, code): # If this is called, it is a non-cascaded cmp, # so only need to dispose of the two main operands. self.operand1.generate_disposal_code(code) self.operand2.generate_disposal_code(code) class CascadedCmpNode(Node, CmpNode): # A CascadedCmpNode is not a complete expression node. It # hangs off the side of another comparison node, shares # its left operand with that node, and shares its result # with the PrimaryCmpNode at the head of the chain. # # operator string # operand2 ExprNode # cascade CascadedCmpNode cascade = None def analyse_types(self, env, operand1): self.operand2.analyse_types(env) if self.cascade: self.cascade.analyse_types(env, self.operand2) def check_operand_types(self, env, operand1): self.check_types(env, operand1, self.operator, self.operand2) if self.cascade: self.cascade.check_operand_types(env, self.operand2) def has_python_operands(self): return self.operand2.type.is_pyobject def coerce_operands_to_pyobjects(self, env): self.operand2 = self.operand2.coerce_to_pyobject(env) if self.cascade: self.cascade.coerce_operands_to_pyobjects(env) def coerce_cascaded_operands_to_temp(self, env): if self.cascade: #self.operand2 = self.operand2.coerce_to_temp(env) #CTT self.operand2 = self.operand2.coerce_to_simple(env) self.cascade.coerce_cascaded_operands_to_temp(env) def allocate_subexpr_temps(self, env): self.operand2.allocate_temps(env) if self.cascade: self.cascade.allocate_subexpr_temps(env) def release_subexpr_temps(self, env): self.operand2.release_temp(env) if self.cascade: self.cascade.release_subexpr_temps(env) def generate_evaluation_code(self, code, result, operand1): code.putln("if (%s) {" % result) self.operand2.generate_evaluation_code(code) self.generate_operation_code(code, result, operand1, self.operator, self.operand2) if self.cascade: self.cascade.generate_evaluation_code( code, result, self.operand2) # Cascaded cmp result is always temp self.operand2.generate_disposal_code(code) code.putln("}") binop_node_classes = { "or": BoolBinopNode, "and": BoolBinopNode, "|": IntBinopNode, "^": IntBinopNode, "&": IntBinopNode, "<<": IntBinopNode, ">>": IntBinopNode, "+": AddNode, "-": SubNode, "*": MulNode, "/": NumBinopNode, "%": ModNode, "**": PowNode } def binop_node(pos, operator, operand1, operand2): # Construct binop node of appropriate class for # given operator. return binop_node_classes[operator](pos, operator = operator, operand1 = operand1, operand2 = operand2) #------------------------------------------------------------------- # # Coercion nodes # # Coercion nodes are special in that they are created during # the analyse_types phase of parse tree processing. # Their __init__ methods consequently incorporate some aspects # of that phase. # #------------------------------------------------------------------- class CoercionNode(ExprNode): # Abstract base class for coercion nodes. # # arg ExprNode node being coerced subexprs = ['arg'] def __init__(self, arg): self.pos = arg.pos self.arg = arg if debug_coercion: print self, "Coercing", self.arg class CastNode(CoercionNode): # Wrap a node in a C type cast. def __init__(self, arg, new_type): CoercionNode.__init__(self, arg) self.type = new_type def calculate_result_code(self): return self.arg.result_as(self.type) def generate_result_code(self, code): self.arg.generate_result_code(code) class PyTypeTestNode(CoercionNode): # This node is used to check that a generic Python # object is an instance of a particular extension type. # This node borrows the result of its argument node. def __init__(self, arg, dst_type, env): # The arg is know to be a Python object, and # the dst_type is known to be an extension type. assert dst_type.is_extension_type, "PyTypeTest on non extension type" CoercionNode.__init__(self, arg) self.type = dst_type self.result_ctype = arg.ctype() # env.use_utility_code(type_test_utility_code) self.gil_check(env) gil_message = "Python type test" def result_in_temp(self): return self.arg.result_in_temp() def is_ephemeral(self): return self.arg.is_ephemeral() def calculate_result_code(self): return self.arg.result() def generate_result_code(self, code): if self.type.typeobj_is_available(): code.use_utility_code(type_test_utility_code) code.putln( "if (!__Pyx_TypeTest(%s, %s)) %s" % ( self.arg.py_result(), self.type.typeptr_cname, code.error_goto(self.pos))) else: error(self.pos, "Cannot test type of extern C class " "without type object name specification") def generate_post_assignment_code(self, code): self.arg.generate_post_assignment_code(code) class CoerceToPyTypeNode(CoercionNode): # This node is used to convert a C data type # to a Python object. def __init__(self, arg, env): CoercionNode.__init__(self, arg) self.type = py_object_type self.gil_check(env) self.is_temp = 1 if not arg.type.to_py_function: error(arg.pos, "Cannot convert '%s' to Python object" % arg.type) gil_message = "Converting to Python object" def generate_result_code(self, code): function = self.arg.type.to_py_function result = self.result() code.putln('%s = %s(%s); if (!%s) %s' % ( result, function, self.arg.result(), result, code.error_goto(self.pos))) class CoerceFromPyTypeNode(CoercionNode): # This node is used to convert a Python object # to a C data type. def __init__(self, result_type, arg, env): CoercionNode.__init__(self, arg) self.type = result_type self.is_temp = 1 if not result_type.from_py_function: error(arg.pos, "Cannot convert Python object to '%s'" % result_type) if self.type.is_string and self.arg.is_ephemeral(): error(arg.pos, "Obtaining char * from temporary Python value") def generate_result_code(self, code): function = self.type.from_py_function operand = self.arg.py_result() rhs = "%s(%s)" % (function, operand) if self.type.is_enum: rhs = typecast(self.type, c_long_type, rhs) result = self.result() if self.type.is_string: err_code = "!%s" % result else: err_code = "PyErr_Occurred()" code.putln('%s = %s; if (%s) %s' % ( result, rhs, err_code, code.error_goto(self.pos))) class CoerceToBooleanNode(CoercionNode): # This node is used when a result needs to be used # in a boolean context. def __init__(self, arg, env): CoercionNode.__init__(self, arg) self.type = PyrexTypes.c_int_type if arg.type.is_pyobject: if env.nogil: self.gil_error() self.is_temp = 1 gil_message = "Truth-testing Python object" def check_const(self): if self.is_temp: self.not_const() self.arg.check_const() def calculate_result_code(self): return "(%s != 0)" % self.arg.result() def generate_result_code(self, code): if self.arg.type.is_pyobject: result = self.result() code.putln( "%s = PyObject_IsTrue(%s); if (%s < 0) %s" % ( result, self.arg.py_result(), result, code.error_goto(self.pos))) class CoerceToTempNode(CoercionNode): # This node is used to force the result of another node # to be stored in a temporary. It is only used if the # argument node's result is not already in a temporary. def __init__(self, arg, env): CoercionNode.__init__(self, arg) self.type = self.arg.type self.is_temp = 1 if self.type.is_pyobject: self.gil_check(env) self.result_ctype = py_object_type gil_message = "Creating temporary Python reference" def generate_result_code(self, code): #self.arg.generate_evaluation_code(code) # Already done # by generic generate_subexpr_evaluation_code! code.putln("%s = %s;" % ( self.result(), self.arg.result_as(self.ctype()))) if self.type.is_pyobject: code.put_incref(self.py_result()) class CloneNode(CoercionNode): # This node is employed when the result of another node needs # to be used multiple times. The argument node's result must # be in a temporary. This node "borrows" the result from the # argument node, and does not generate any evaluation or # disposal code for it. The original owner of the argument # node is responsible for doing those things. subexprs = [] # Arg is not considered a subexpr def __init__(self, arg): CoercionNode.__init__(self, arg) self.type = arg.type self.result_ctype = arg.result_ctype def calculate_result_code(self): return self.arg.result() def generate_evaluation_code(self, code): pass def generate_result_code(self, code): pass #------------------------------------------------------------------------------------ # # Runtime support code # #------------------------------------------------------------------------------------ get_name_utility_code = [ """ static PyObject *__Pyx_GetName(PyObject *dict, char *name); /*proto*/ """,""" static PyObject *__Pyx_GetName(PyObject *dict, char *name) { PyObject *result; result = PyObject_GetAttrString(dict, name); if (!result) PyErr_SetString(PyExc_NameError, name); return result; } """] get_name_interned_utility_code = [ """ static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/ """,""" static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) { PyObject *result; result = PyObject_GetAttr(dict, name); if (!result) PyErr_SetObject(PyExc_NameError, name); return result; } """] #------------------------------------------------------------------------------------ import_utility_code = [ """ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/ """,""" static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) { PyObject *__import__ = 0; PyObject *empty_list = 0; PyObject *module = 0; PyObject *global_dict = 0; PyObject *empty_dict = 0; PyObject *list; __import__ = PyObject_GetAttrString(%(BUILTINS)s, "__import__"); if (!__import__) goto bad; if (from_list) list = from_list; else { empty_list = PyList_New(0); if (!empty_list) goto bad; list = empty_list; } global_dict = PyModule_GetDict(%(GLOBALS)s); if (!global_dict) goto bad; empty_dict = PyDict_New(); if (!empty_dict) goto bad; module = PyObject_CallFunction(__import__, "OOOO", name, global_dict, empty_dict, list); bad: Py_XDECREF(empty_list); Py_XDECREF(__import__); Py_XDECREF(empty_dict); return module; } """ % { "BUILTINS": Naming.builtins_cname, "GLOBALS": Naming.module_cname, }] #------------------------------------------------------------------------------------ # #get_exception_utility_code = [ #""" #static PyObject *__Pyx_GetExcValue(void); /*proto*/ #""",""" #static PyObject *__Pyx_GetExcValue(void) { # PyObject *type = 0, *value = 0, *tb = 0; # PyObject *result = 0; # PyThreadState *tstate = PyThreadState_Get(); # PyErr_Fetch(&type, &value, &tb); # PyErr_NormalizeException(&type, &value, &tb); # if (PyErr_Occurred()) # goto bad; # if (!value) { # value = Py_None; # Py_INCREF(value); # } # Py_XDECREF(tstate->exc_type); # Py_XDECREF(tstate->exc_value); # Py_XDECREF(tstate->exc_traceback); # tstate->exc_type = type; # tstate->exc_value = value; # tstate->exc_traceback = tb; # result = value; # Py_XINCREF(result); # type = 0; # value = 0; # tb = 0; #bad: # Py_XDECREF(type); # Py_XDECREF(value); # Py_XDECREF(tb); # return result; #} #"""] # #------------------------------------------------------------------------------------ unpacking_utility_code = [ """ static PyObject *__Pyx_UnpackItem(PyObject *); /*proto*/ static int __Pyx_EndUnpack(PyObject *); /*proto*/ """,""" static void __Pyx_UnpackError(void) { PyErr_SetString(PyExc_ValueError, "unpack sequence of wrong size"); } static PyObject *__Pyx_UnpackItem(PyObject *iter) { PyObject *item; if (!(item = PyIter_Next(iter))) { if (!PyErr_Occurred()) __Pyx_UnpackError(); } return item; } static int __Pyx_EndUnpack(PyObject *iter) { PyObject *item; if ((item = PyIter_Next(iter))) { Py_DECREF(item); __Pyx_UnpackError(); return -1; } else if (!PyErr_Occurred()) return 0; else return -1; } """] #------------------------------------------------------------------------------------ type_test_utility_code = [ """ static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/ """,""" static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) { if (!type) { PyErr_Format(PyExc_SystemError, "Missing type object"); return 0; } if (obj == Py_None || PyObject_TypeCheck(obj, type)) return 1; PyErr_Format(PyExc_TypeError, "Cannot convert %s to %s", obj->ob_type->tp_name, type->tp_name); return 0; } """] #------------------------------------------------------------------------------------ create_class_utility_code = [ """ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/ """,""" static PyObject *__Pyx_CreateClass( PyObject *bases, PyObject *dict, PyObject *name, char *modname) { PyObject *py_modname; PyObject *result = 0; py_modname = PyString_FromString(modname); if (!py_modname) goto bad; if (PyDict_SetItemString(dict, "__module__", py_modname) < 0) goto bad; result = PyClass_New(bases, dict, name); bad: Py_XDECREF(py_modname); return result; } """] #------------------------------------------------------------------------------------ getitem_int_utility_code = [ """ static PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i); /*proto*/ """,""" static PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i) { PyTypeObject *t = o->ob_type; PyObject *r; if (t->tp_as_sequence && t->tp_as_sequence->sq_item) r = PySequence_GetItem(o, i); else { PyObject *j = PyInt_FromLong(i); if (!j) return 0; r = PyObject_GetItem(o, j); Py_DECREF(j); } return r; } """] #------------------------------------------------------------------------------------ setitem_int_utility_code = [ """ static int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v); /*proto*/ """,""" static int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v) { PyTypeObject *t = o->ob_type; int r; if (t->tp_as_sequence && t->tp_as_sequence->sq_item) r = PySequence_SetItem(o, i, v); else { PyObject *j = PyInt_FromLong(i); if (!j) return -1; r = PyObject_SetItem(o, j, v); Py_DECREF(j); } return r; } """]