# [ExprNode or [ExprNode or None] or None]
# Cached result of subexpr_nodes()
# use_managed_ref boolean use ref-counted temps/assignments/etc.
+ # result_is_used boolean indicates that the result will be dropped and the
+ # result_code/temp_result can safely be set to None
result_ctype = None
type = None
temp_code = None
old_temp = None # error checker for multiple frees etc.
use_managed_ref = True # can be set by optimisation transforms
+ result_is_used = True
# The Analyse Expressions phase for expressions is split
# into two sub-phases:
else:
self.not_implemented("infer_type")
+ def nonlocally_immutable(self):
+ # Returns whether this variable is a safe reference, i.e.
+ # can't be modified as part of globals or closures.
+ return self.is_temp or self.type.is_array or self.type.is_cfunction
+
# --------------- Type Analysis ------------------
def analyse_as_module(self, env):
def release_temp_result(self, code):
if not self.temp_code:
+ if not self.result_is_used:
+ # not used anyway, so ignore if not set up
+ return
if self.old_temp:
raise RuntimeError("temp %s released multiple times in %s" % (
self.old_temp, self.__class__.__name__))
def generate_disposal_code(self, code):
if self.is_temp:
- if self.type.is_pyobject:
+ if self.type.is_pyobject and self.result():
code.put_decref_clear(self.result(), self.ctype())
else:
# Already done if self.is_temp
def is_simple(self):
return 1
+ def nonlocally_immutable(self):
+ return 1
+
def may_be_none(self):
return False
return self
elif dst_type.is_float:
if self.constant_result is not not_a_constant:
- float_value = float(self.constant_result)
- return FloatNode(self.pos, value=repr(float_value), type=dst_type,
- constant_result=float_value)
+ return FloatNode(self.pos, value='%d.0' % int(self.constant_result), type=dst_type,
+ constant_result=float(self.constant_result))
else:
return FloatNode(self.pos, value=self.value, type=dst_type,
constant_result=not_a_constant)
if not self.can_coerce_to_char_literal():
error(self.pos, "Only single-character string literals can be coerced into ints.")
return self
- if dst_type is PyrexTypes.c_py_unicode_type:
- error(self.pos, "Bytes literals cannot coerce to Py_UNICODE, use a unicode literal instead.")
+ if dst_type.is_unicode_char:
+ error(self.pos, "Bytes literals cannot coerce to Py_UNICODE/Py_UCS4, use a unicode literal instead.")
return self
return CharNode(self.pos, value=self.value)
def coerce_to(self, dst_type, env):
if dst_type is self.type:
pass
- elif dst_type is PyrexTypes.c_py_unicode_type:
+ elif dst_type.is_unicode_char:
if not self.can_coerce_to_char_literal():
- error(self.pos, "Only single-character Unicode string literals can be coerced into Py_UNICODE.")
+ error(self.pos, "Only single-character Unicode string literals or surrogate pairs can be coerced into Py_UCS4/Py_UNICODE.")
return self
int_value = ord(self.value)
- return IntNode(self.pos, value=int_value, constant_result=int_value)
+ return IntNode(self.pos, type=dst_type, value=str(int_value), constant_result=int_value)
elif not dst_type.is_pyobject:
if dst_type.is_string and self.bytes_value is not None:
# special case: '-3' enforced unicode literal used in a C char* context
return BytesNode(self.pos, value=self.bytes_value).coerce_to(dst_type, env)
- error(self.pos, "Unicode literals do not support coercion to C types other than Py_UNICODE.")
+ error(self.pos, "Unicode literals do not support coercion to C types other than Py_UNICODE or Py_UCS4.")
elif dst_type is not py_object_type:
if not self.check_for_coercion_error(dst_type):
self.fail_assignment(dst_type)
def can_coerce_to_char_literal(self):
return len(self.value) == 1
+ ## or (len(self.value) == 2
+ ## and (0xD800 <= self.value[0] <= 0xDBFF)
+ ## and (0xDC00 <= self.value[1] <= 0xDFFF))
def contains_surrogates(self):
# Check if the unicode string contains surrogate code points
if not dst_type.is_pyobject:
return BytesNode(self.pos, value=self.value).coerce_to(dst_type, env)
self.check_for_coercion_error(dst_type, fail=True)
-
- # this will be a unicode string in Py3, so make sure we can decode it
- if self.value.encoding and isinstance(self.value, StringEncoding.BytesLiteral):
- try:
- self.value.decode(self.value.encoding)
- except UnicodeDecodeError:
- error(self.pos, ("Decoding unprefixed string literal from '%s' failed. Consider using"
- "a byte string or unicode string explicitly, "
- "or adjust the source code encoding.") % self.value.encoding)
-
return self
def can_coerce_to_char_literal(self):
def generate_evaluation_code(self, code):
self.result_code = code.get_py_string_const(
- self.value, identifier=self.is_identifier, is_str=True)
+ self.value, identifier=self.is_identifier, is_str=True,
+ unicode_value=self.unicode_value)
def get_constant_c_result_code(self):
return None
if entry and entry.is_cfunction:
var_entry = entry.as_variable
if var_entry:
- if var_entry.is_builtin and Options.cache_builtins:
+ if var_entry.is_builtin and var_entry.is_const:
var_entry = env.declare_builtin(var_entry.name, self.pos)
node = NameNode(self.pos, name = self.name)
node.entry = var_entry
if entry.is_declared_generic:
self.result_ctype = py_object_type
if entry.is_pyglobal or entry.is_builtin:
- if Options.cache_builtins and entry.is_builtin:
+ if entry.is_builtin and entry.is_const:
self.is_temp = 0
else:
self.is_temp = 1
if self.is_used_as_rvalue:
entry = self.entry
if entry.is_builtin:
- if not Options.cache_builtins: # cached builtins are ok
+ if not entry.is_const: # cached builtins are ok
self.gil_error()
elif entry.is_pyglobal:
self.gil_error()
# If it's not a C variable, it'll be in a temp.
return 1
+ def nonlocally_immutable(self):
+ if ExprNode.nonlocally_immutable(self):
+ return True
+ entry = self.entry
+ return entry and (entry.is_local or entry.is_arg) and not entry.in_closure
+
def calculate_target_results(self, env):
pass
entry = self.entry
if entry is None:
return # There was an error earlier
- if entry.is_builtin and Options.cache_builtins:
+ if entry.is_builtin and entry.is_const:
return # Lookup already cached
elif entry.is_pyclass_attr:
assert entry.type.is_pyobject, "Python global or builtin not a Python object"
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
if self.use_managed_ref:
rhs.make_owned_reference(code)
- if entry.is_cglobal:
- code.put_gotref(self.py_result())
+ is_external_ref = entry.is_cglobal or self.entry.in_closure or self.entry.from_closure
if not self.lhs_of_first_assignment:
+ if is_external_ref:
+ code.put_gotref(self.py_result())
if entry.is_local and not Options.init_local_none:
initialized = entry.scope.control_flow.get_state((entry.name, 'initialized'), self.pos)
if initialized is True:
code.put_xdecref(self.result(), self.ctype())
else:
code.put_decref(self.result(), self.ctype())
- if entry.is_cglobal:
+ if is_external_ref:
code.put_giveref(rhs.py_result())
code.putln('%s = %s;' % (self.result(),
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
- if self.entry.is_pyclass_attr:
+ elif self.entry.is_pyclass_attr:
namespace = self.entry.scope.namespace_cname
code.put_error_if_neg(self.pos,
'PyMapping_DelItemString(%s, "%s")' % (
namespace,
self.entry.name))
- else:
+ elif self.entry.is_pyglobal:
code.put_error_if_neg(self.pos,
'__Pyx_DelAttrString(%s, "%s")' % (
Naming.module_cname,
self.entry.name))
+ elif self.entry.type.is_pyobject:
+ # Fake it until we can do it for real...
+ self.generate_assignment_code(NoneNode(self.pos), code)
+ else:
+ error(self.pos, "Deletion of C names not supported")
def annotate(self, code):
if hasattr(self, 'is_called') and self.is_called:
code.put_gotref(self.py_result())
-
class ImportNode(ExprNode):
# Used as part of import statement implementation.
# Implements result =
- # __import__(module_name, globals(), None, name_list)
+ # __import__(module_name, globals(), None, name_list, level)
#
- # module_name StringNode dotted name of module
+ # module_name StringNode dotted name of module. Empty module
+ # name means importing the parent package accourding
+ # to level
# name_list ListNode or None list of names to be imported
+ # level int relative import level:
+ # -1: attempt both relative import and absolute import;
+ # 0: absolute import;
+ # >0: the number of parent directories to search
+ # relative to the current module.
+ # None: decide the level according to language level and
+ # directives
type = py_object_type
subexprs = ['module_name', 'name_list']
def analyse_types(self, env):
+ if self.level is None:
+ if env.directives['language_level'] < 3 or env.directives['py2_import']:
+ self.level = -1
+ else:
+ self.level = 0
self.module_name.analyse_types(env)
self.module_name = self.module_name.coerce_to_pyobject(env)
if self.name_list:
else:
name_list_code = "0"
code.putln(
- "%s = __Pyx_Import(%s, %s); %s" % (
+ "%s = __Pyx_Import(%s, %s, %d); %s" % (
self.result(),
self.module_name.py_result(),
name_list_code,
+ self.level,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
code.putln("}")
+class WithExitCallNode(ExprNode):
+ # The __exit__() call of a 'with' statement. Used in both the
+ # except and finally clauses.
+
+ # with_stat WithStatNode the surrounding 'with' statement
+ # args TupleNode or ResultStatNode the exception info tuple
+
+ subexprs = ['args']
+
+ def analyse_types(self, env):
+ self.args.analyse_types(env)
+ self.type = PyrexTypes.c_bint_type
+ self.is_temp = True
+
+ def generate_result_code(self, code):
+ if isinstance(self.args, TupleNode):
+ # call only if it was not already called (and decref-cleared)
+ code.putln("if (%s) {" % self.with_stat.exit_var)
+ result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
+ code.putln("%s = PyObject_Call(%s, %s, NULL);" % (
+ result_var,
+ self.with_stat.exit_var,
+ self.args.result()))
+ code.put_decref_clear(self.with_stat.exit_var, type=py_object_type)
+ code.putln(code.error_goto_if_null(result_var, self.pos))
+ code.put_gotref(result_var)
+ code.putln("%s = __Pyx_PyObject_IsTrue(%s);" % (self.result(), result_var))
+ code.put_decref_clear(result_var, type=py_object_type)
+ code.putln(code.error_goto_if_neg(self.result(), self.pos))
+ code.funcstate.release_temp(result_var)
+ if isinstance(self.args, TupleNode):
+ code.putln("}")
+
+
class ExcValueNode(AtomicExprNode):
# Node created during analyse_types phase
# of an ExceptClauseNode to fetch the current
subexprs = []
- def __init__(self, pos, type, env):
+ def __init__(self, pos, type, env=None):
ExprNode.__init__(self, pos)
self.type = type
if type.is_pyobject:
def analyse_types(self, env):
return self.type
+ def analyse_target_declaration(self, env):
+ pass
+
def generate_result_code(self, code):
pass
def is_ephemeral(self):
return self.base.is_ephemeral()
+ def is_simple(self):
+ if self.is_buffer_access:
+ return False
+ base = self.base
+ return (base.is_simple() and self.index.is_simple()
+ and base.type and (base.type.is_ptr or base.type.is_array))
+
def analyse_target_declaration(self, env):
pass
if index_type and index_type.is_int or isinstance(self.index, (IntNode, LongNode)):
# indexing!
if base_type is unicode_type:
- # Py_UNICODE will automatically coerce to a unicode string
- # if required, so this is safe. We only infer Py_UNICODE
- # when the index is a C integer type. Otherwise, we may
+ # Py_UCS4 will automatically coerce to a unicode string
+ # if required, so this is safe. We only infer Py_UCS4
+ # when the index is a C integer type. Otherwise, we may
# need to use normal Python item access, in which case
# it's faster to return the one-char unicode string than
# to receive it, throw it away, and potentially rebuild it
# on a subsequent PyObject coercion.
- return PyrexTypes.c_py_unicode_type
+ return PyrexTypes.c_py_ucs4_type
+ elif base_type is str_type:
+ # always returns str - Py2: bytes, Py3: unicode
+ return base_type
elif isinstance(self.base, BytesNode):
#if env.global_scope().context.language_level >= 3:
# # infering 'char' can be made to work in Python 3 mode
return base_type.base_type
# may be slicing or indexing, we don't know
- if base_type is unicode_type:
- # this type always returns its own type on Python indexing/slicing
+ if base_type in (unicode_type, str_type):
+ # these types always returns their own type on Python indexing/slicing
return base_type
else:
# TODO: Handle buffers (hopefully without too much redundancy).
elif not skip_child_analysis:
self.index.analyse_types(env)
self.original_index_type = self.index.type
- if base_type is PyrexTypes.c_py_unicode_type:
- # we infer Py_UNICODE for unicode strings in some
+ if base_type.is_unicode_char:
+ # we infer Py_UNICODE/Py_UCS4 for unicode strings in some
# cases, but indexing must still work for them
if self.index.constant_result in (0, -1):
# FIXME: we know that this node is redundant -
self.index = self.index.coerce_to_pyobject(env)
self.is_temp = 1
if self.index.type.is_int and base_type is unicode_type:
- # Py_UNICODE will automatically coerce to a unicode string
+ # Py_UNICODE/Py_UCS4 will automatically coerce to a unicode string
# if required, so this is fast and safe
- self.type = PyrexTypes.c_py_unicode_type
+ self.type = PyrexTypes.c_py_ucs4_type
elif is_slice and base_type in (bytes_type, str_type, unicode_type, list_type, tuple_type):
self.type = base_type
else:
return "PyList_GET_ITEM(%s, %s)" % (self.base.result(), self.index.result())
elif self.base.type is tuple_type:
return "PyTuple_GET_ITEM(%s, %s)" % (self.base.result(), self.index.result())
- elif self.base.type is unicode_type and self.type is PyrexTypes.c_py_unicode_type:
+ elif self.base.type is unicode_type and self.type.is_unicode_char:
return "PyUnicode_AS_UNICODE(%s)[%s]" % (self.base.result(), self.index.result())
elif (self.type.is_ptr or self.type.is_array) and self.type == self.base.type:
error(self.pos, "Invalid use of pointer slice")
self.result(),
code.error_goto(self.pos)))
code.put_gotref(self.py_result())
- elif self.type is PyrexTypes.c_py_unicode_type and self.base.type is unicode_type:
+ elif self.type.is_unicode_char and self.base.type is unicode_type:
assert self.index.type.is_int
index_code = self.index.result()
function = "__Pyx_GetItemInt_Unicode"
elif base_type in (bytes_type, str_type, unicode_type,
list_type, tuple_type):
return base_type
+ elif base_type.is_ptr or base_type.is_array:
+ return PyrexTypes.c_array_type(base_type.base_type, None)
return py_object_type
def calculate_constant_result(self):
# stop ExprNode
# step ExprNode
+ subexprs = ['start', 'stop', 'step']
+
type = py_object_type
is_temp = 1
def calculate_constant_result(self):
- self.constant_result = self.base.constant_result[
- self.start.constant_result : \
- self.stop.constant_result : \
- self.step.constant_result]
+ self.constant_result = slice(
+ self.start.constant_result,
+ self.stop.constant_result,
+ self.step.constant_result)
def compile_time_value(self, denv):
start = self.start.compile_time_value(denv)
- if self.stop is None:
- stop = None
- else:
- stop = self.stop.compile_time_value(denv)
- if self.step is None:
- step = None
- else:
- step = self.step.compile_time_value(denv)
+ stop = self.stop.compile_time_value(denv)
+ step = self.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.start = self.start.coerce_to_pyobject(env)
self.stop = self.stop.coerce_to_pyobject(env)
self.step = self.step.coerce_to_pyobject(env)
+ if self.start.is_literal and self.stop.is_literal and self.step.is_literal:
+ self.is_literal = True
+ self.is_temp = False
gil_message = "Constructing Python slice object"
+ def calculate_result_code(self):
+ return self.result_code
+
def generate_result_code(self, code):
+ if self.is_literal:
+ self.result_code = code.get_py_const(py_object_type, 'slice_', cleanup_level=2)
+ code = code.get_cached_constants_writer()
+ code.mark_pos(self.pos)
+
code.putln(
"%s = PySlice_New(%s, %s, %s); %s" % (
self.result(),
self.step.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
+ if self.is_literal:
+ code.put_giveref(self.py_result())
class CallNode(ExprNode):
self.is_temp = 1
# Coerce arguments
some_args_in_temps = False
- for i in range(min(max_nargs, actual_nargs)):
+ for i in xrange(min(max_nargs, actual_nargs)):
formal_type = func_type.args[i].type
- arg = self.args[i].coerce_to(formal_type, env).coerce_to_simple(env)
+ arg = self.args[i].coerce_to(formal_type, env)
if arg.is_temp:
- some_args_in_temps = True
+ if i > 0:
+ # first argument in temp doesn't impact subsequent arguments
+ some_args_in_temps = True
elif arg.type.is_pyobject and not env.nogil:
- if not arg.is_name or arg.entry and (not arg.entry.is_local or arg.entry.in_closure):
+ if i == 0 and self.self is not None:
+ # a method's cloned "self" argument is ok
+ pass
+ elif arg.nonlocally_immutable():
+ # plain local variables are ok
+ pass
+ else:
# we do not safely own the argument's reference,
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
- some_args_in_temps = True
+ if i > 0: # first argument doesn't matter
+ some_args_in_temps = True
arg = arg.coerce_to_temp(env)
self.args[i] = arg
+ # handle additional varargs parameters
+ for i in xrange(max_nargs, actual_nargs):
+ arg = self.args[i]
+ if arg.type.is_pyobject:
+ arg_ctype = arg.type.default_coerced_ctype()
+ if arg_ctype is None:
+ error(self.args[i].pos,
+ "Python object cannot be passed as a varargs parameter")
+ else:
+ self.args[i] = arg = arg.coerce_to(arg_ctype, env)
+ if arg.is_temp and i > 0:
+ some_args_in_temps = True
if some_args_in_temps:
# if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make
- # sure they are either all temps or all not temps
- for i in range(min(max_nargs, actual_nargs)-1):
+ # sure they are either all temps or all not temps (except
+ # for the last argument, which is evaluated last in any
+ # case)
+ for i in xrange(actual_nargs-1):
+ if i == 0 and self.self is not None:
+ continue # self is ok
arg = self.args[i]
- if arg.is_name and arg.entry and (
- (arg.entry.is_local and not arg.entry.in_closure)
- or arg.entry.type.is_cfunction):
- # local variables and C functions are safe
+ if arg.nonlocally_immutable():
+ # locals, C functions, unassignable types are safe.
+ pass
+ elif arg.type.is_cpp_class:
+ # Assignment has side effects, avoid.
pass
elif env.nogil and arg.type.is_pyobject:
# can't copy a Python reference into a temp in nogil
# nogil anyway)
pass
else:
- self.args[i] = arg.coerce_to_temp(env)
- for i in range(max_nargs, actual_nargs):
- arg = self.args[i]
- if arg.type.is_pyobject:
- arg_ctype = arg.type.default_coerced_ctype()
- if arg_ctype is None:
- error(self.args[i].pos,
- "Python object cannot be passed as a varargs parameter")
- else:
- self.args[i] = arg.coerce_to(arg_ctype, env)
+ #self.args[i] = arg.coerce_to_temp(env)
+ # instead: issue a warning
+ if i > 0 or i == 1 and self.self is not None: # skip first arg
+ warning(arg.pos, "Argument evaluation order in C function call is undefined and may not be as expected", 0)
+ break
# Calc result type and code fragment
if isinstance(self.function, NewExprNode):
self.type = PyrexTypes.CPtrType(self.function.class_type)
# either temp or constant => always simple
return True
+ def nonlocally_immutable(self):
+ # either temp or constant => always safe
+ return True
+
def calculate_result_code(self):
if len(self.args) > 0:
return self.result_code
self.value_expr.annotate(code)
-class GeneratorExpressionNode(ScopedExprNode):
- # A generator expression, e.g. (i for i in range(10))
- #
- # Result is a generator.
+class InlinedGeneratorExpressionNode(ScopedExprNode):
+ # An inlined generator expression for which the result is
+ # calculated inside of the loop. This will only be created by
+ # transforms when replacing builtin calls on generator
+ # expressions.
#
- # loop ForStatNode the for-loop, containing a YieldExprNode
+ # loop ForStatNode the for-loop, not containing any YieldExprNodes
+ # result_node ResultRefNode the reference to the result value temp
+ # orig_func String the name of the builtin function this node replaces
child_attrs = ["loop"]
-
+ loop_analysed = False
type = py_object_type
def analyse_scoped_declarations(self, env):
self.loop.analyse_expressions(env)
self.is_temp = True
- def analyse_scoped_expressions(self, env):
- if self.has_local_scope:
- self.loop.analyse_expressions(env)
-
def may_be_none(self):
return False
def annotate(self, code):
self.loop.annotate(code)
-
-class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
- # An inlined generator expression for which the result is
- # calculated inside of the loop. This will only be created by
- # transforms when replacing builtin calls on generator
- # expressions.
- #
- # loop ForStatNode the for-loop, not containing any YieldExprNodes
- # result_node ResultRefNode the reference to the result value temp
- # orig_func String the name of the builtin function this node replaces
-
- child_attrs = ["loop"]
- loop_analysed = False
-
def infer_type(self, env):
return self.result_node.infer_type(env)
def analyse_scoped_expressions(self, env):
self.loop_analysed = True
- GeneratorExpressionNode.analyse_scoped_expressions(self, env)
+ if self.has_local_scope:
+ self.loop.analyse_expressions(env)
def coerce_to(self, dst_type, env):
if self.orig_func == 'sum' and dst_type.is_numeric and not self.loop_analysed:
# assignments.
self.result_node.type = self.type = dst_type
return self
- return GeneratorExpressionNode.coerce_to(self, dst_type, env)
+ return super(InlinedGeneratorExpressionNode, self).coerce_to(dst_type, env)
def generate_result_code(self, code):
self.result_node.result_code = self.result()
self.pymethdef_cname = self.def_node.entry.pymethdef_cname
env.add_lambda_def(self.def_node)
+
+class GeneratorExpressionNode(LambdaNode):
+ # A generator expression, e.g. (i for i in range(10))
+ #
+ # Result is a generator.
+ #
+ # loop ForStatNode the for-loop, containing a YieldExprNode
+ # def_node DefNode the underlying generator 'def' node
+
+ name = StringEncoding.EncodedString('genexpr')
+ binding = False
+
+ def analyse_declarations(self, env):
+ self.def_node.no_assignment_synthesis = True
+ self.def_node.analyse_declarations(env)
+ env.add_lambda_def(self.def_node)
+
+ def generate_result_code(self, code):
+ code.putln(
+ '%s = %s(%s, NULL); %s' % (
+ self.result(),
+ self.def_node.entry.func_cname,
+ self.self_result_code(),
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_gotref(self.py_result())
+
+
class YieldExprNode(ExprNode):
# Yield expression node
#
# arg ExprNode the value to return from the generator
# label_name string name of the C label used for this yield
+ # label_num integer yield label number
subexprs = ['arg']
type = py_object_type
+ label_num = 0
def analyse_types(self, env):
+ if not self.label_num:
+ error(self.pos, "'yield' not supported here")
self.is_temp = 1
if self.arg is not None:
self.arg.analyse_types(env)
if not self.arg.type.is_pyobject:
self.arg = self.arg.coerce_to_pyobject(env)
- error(self.pos, "Generators are not supported")
+ env.use_utility_code(generator_utility_code)
- def generate_result_code(self, code):
+ def generate_evaluation_code(self, code):
self.label_name = code.new_label('resume_from_yield')
code.use_label(self.label_name)
- code.putln("/* FIXME: save temporary variables */")
- code.putln("/* FIXME: return from function, yielding value */")
+ if self.arg:
+ self.arg.generate_evaluation_code(code)
+ self.arg.make_owned_reference(code)
+ code.putln(
+ "%s = %s;" % (
+ Naming.retval_cname,
+ self.arg.result_as(py_object_type)))
+ self.arg.generate_post_assignment_code(code)
+ #self.arg.generate_disposal_code(code)
+ self.arg.free_temps(code)
+ else:
+ code.put_init_to_py_none(Naming.retval_cname, py_object_type)
+ saved = []
+ code.funcstate.closure_temps.reset()
+ for cname, type, manage_ref in code.funcstate.temps_in_use():
+ save_cname = code.funcstate.closure_temps.allocate_temp(type)
+ saved.append((cname, save_cname, type))
+ if type.is_pyobject:
+ code.put_xgiveref(cname)
+ code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
+
+ code.put_xgiveref(Naming.retval_cname)
+ code.put_finish_refcount_context()
+ code.putln("/* return from generator, yielding value */")
+ code.putln("%s->%s.resume_label = %d;" % (Naming.cur_scope_cname, Naming.obj_base_cname, self.label_num))
+ code.putln("return %s;" % Naming.retval_cname);
code.put_label(self.label_name)
- code.putln("/* FIXME: restore temporary variables and */")
- code.putln("/* FIXME: extract sent value from closure */")
-
+ for cname, save_cname, type in saved:
+ code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
+ if type.is_pyobject:
+ code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
+ if type.is_pyobject:
+ code.put_xgotref(cname)
+ if self.result_is_used:
+ self.allocate_temp_result(code)
+ code.putln('%s = %s; %s' %
+ (self.result(), Naming.sent_value_cname,
+ code.error_goto_if_null(self.result(), self.pos)))
+ code.put_incref(self.result(), py_object_type)
+ else:
+ code.putln(code.error_goto_if_null(Naming.sent_value_cname, self.pos))
#-------------------------------------------------------------------
#
operator = '+'
def analyse_c_operation(self, env):
- self.type = self.operand.type
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
def py_operation_function(self):
return "PyNumber_Positive"
def analyse_c_operation(self, env):
if self.operand.type.is_numeric:
- self.type = self.operand.type
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
else:
self.type_error()
if self.type.is_complex:
def analyse_c_operation(self, env):
if self.operand.type.is_int:
- self.type = self.operand.type
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
else:
self.type_error()
# unary ++/-- operator
def analyse_c_operation(self, env):
- if self.operand.type.is_ptr or self.operand.type.is_numeric:
+ if self.operand.type.is_numeric:
+ self.type = PyrexTypes.widest_numeric_type(
+ self.operand.type, PyrexTypes.c_int_type)
+ elif self.operand.type.is_ptr:
self.type = self.operand.type
else:
self.type_error()
elif self.type.is_complex and self.operand.type.is_complex:
self.operand = self.operand.coerce_to_simple(env)
+ def is_simple(self):
+ # either temp or a C cast => no side effects
+ return True
+
+ def nonlocally_immutable(self):
+ return self.operand.nonlocally_immutable()
+
def nogil_check(self, env):
if self.type and self.type.is_pyobject and self.is_temp:
self.gil_error()
if self.operator not in '|^&':
# False + False == 0 # not False!
widest_type = PyrexTypes.c_int_type
+ else:
+ widest_type = PyrexTypes.widest_numeric_type(
+ widest_type, PyrexTypes.c_int_type)
return widest_type
else:
return None
self.operand2.result())
def is_py_operation_types(self, type1, type2):
- return (type1 is PyrexTypes.c_py_unicode_type or
- type2 is PyrexTypes.c_py_unicode_type or
+ return (type1.is_unicode_char or
+ type2.is_unicode_char or
BinopNode.is_py_operation_types(self, type1, type2))
def py_operation_function(self):
operand2 = self.operand2.compile_time_value(denv)
try:
func = self.find_compile_time_binary_operator(
- self, operand1, operand2)
+ operand1, operand2)
return func(operand1, operand2)
except Exception, e:
self.compile_time_value_error(e)
return self.operator in ('in', 'not_in') and \
((self.operand1.type.is_int
and (self.operand2.type.is_string or self.operand2.type is bytes_type)) or
- (self.operand1.type is PyrexTypes.c_py_unicode_type
+ (self.operand1.type.is_unicode_char
and self.operand2.type is unicode_type))
def is_ptr_contains(self):
""",
impl="""
static CYTHON_INLINE int __Pyx_UnicodeContains(PyObject* unicode, Py_UNICODE character) {
+ Py_UNICODE* pos;
const Py_ssize_t length = PyUnicode_GET_SIZE(unicode);
Py_UNICODE* char_start = PyUnicode_AS_UNICODE(unicode);
+
+ for (pos=char_start; pos < char_start+length; pos++) {
+ if (unlikely(character == pos[0])) return 1;
+ }
+ return 0;
+}
+""")
+
+py_ucs4_in_unicode_utility_code = UtilityCode(
+proto="""
+static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character); /*proto*/
+""",
+# additionally handles surrogate pairs in 16bit Unicode builds
+impl="""
+static CYTHON_INLINE int __Pyx_UnicodeContainsUCS4(PyObject* unicode, Py_UCS4 character) {
Py_UNICODE* pos;
+ Py_UNICODE uchar;
+ const Py_ssize_t length = PyUnicode_GET_SIZE(unicode);
+ Py_UNICODE* char_start = PyUnicode_AS_UNICODE(unicode);
+
+ #if Py_UNICODE_SIZE == 2
+ if (unlikely(character > 65535)) {
+ Py_UNICODE high_val, low_val;
+ high_val = (Py_UNICODE) (0xD800 | (((character - 0x10000) >> 10) & ((1<<10)-1)));
+ low_val = (Py_UNICODE) (0xDC00 | ( (character - 0x10000) & ((1<<10)-1)));
+ for (pos=char_start; pos < char_start+length-1; pos++) {
+ if (unlikely(high_val == pos[0]) & unlikely(low_val == pos[1])) return 1;
+ }
+ return 0;
+ }
+ #endif
+ uchar = (Py_UNICODE) character;
for (pos=char_start; pos < char_start+length; pos++) {
- if (character == pos[0]) return 1;
+ if (unlikely(uchar == pos[0])) return 1;
}
return 0;
}
error(self.pos, "Cascading comparison not yet supported for 'int_val in string'.")
return
if self.operand2.type is unicode_type:
- env.use_utility_code(pyunicode_in_unicode_utility_code)
+ self.uchar_test_type = PyrexTypes.widest_numeric_type(
+ self.operand1.type, PyrexTypes.c_py_unicode_type)
+ if self.uchar_test_type is PyrexTypes.c_py_unicode_type:
+ env.use_utility_code(pyunicode_in_unicode_utility_code)
+ else:
+ env.use_utility_code(py_ucs4_in_unicode_utility_code)
else:
if self.operand1.type is PyrexTypes.c_uchar_type:
self.operand1 = self.operand1.coerce_to(PyrexTypes.c_char_type, env)
self.operand1.result(),
self.operand2.result())
elif self.is_c_string_contains():
- if self.operand2.type is bytes_type:
- method = "__Pyx_BytesContains"
+ if self.operand2.type is unicode_type:
+ if self.uchar_test_type is PyrexTypes.c_py_unicode_type:
+ method = "__Pyx_UnicodeContains"
+ else:
+ method = "__Pyx_UnicodeContainsUCS4"
else:
- method = "__Pyx_UnicodeContains"
+ method = "__Pyx_BytesContains"
if self.operator == "not_in":
negation = "!"
else:
return False
return self.arg.may_be_none()
+ def is_simple(self):
+ return self.arg.is_simple()
+
def result_in_temp(self):
return self.arg.result_in_temp()
def may_be_none(self):
return False
+ def is_simple(self):
+ return self.arg.is_simple()
+
def result_in_temp(self):
return self.arg.result_in_temp()
# be specific about some known types
if arg.type.is_string:
self.type = bytes_type
- elif arg.type is PyrexTypes.c_py_unicode_type:
+ elif arg.type.is_unicode_char:
self.type = unicode_type
elif arg.type.is_complex:
self.type = Builtin.complex_type
if hasattr(self.arg, 'entry'):
self.entry = self.arg.entry
+ def is_simple(self):
+ return True # result is always in a temp (or a name)
+
def generate_evaluation_code(self, code):
pass
import_utility_code = UtilityCode(
proto = """
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
+static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level); /*proto*/
""",
impl = """
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) {
+static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, long level) {
PyObject *py_import = 0;
PyObject *empty_list = 0;
PyObject *module = 0;
empty_dict = PyDict_New();
if (!empty_dict)
goto bad;
+ #if PY_VERSION_HEX >= 0x02050000
+ {
+ PyObject *py_level = PyInt_FromLong(level);
+ if (!py_level)
+ goto bad;
+ module = PyObject_CallFunctionObjArgs(py_import,
+ name, global_dict, empty_dict, list, py_level, NULL);
+ Py_DECREF(py_level);
+ }
+ #else
+ if (level>0) {
+ PyErr_SetString(PyExc_RuntimeError, "Relative import is not supported for Python <=2.4.");
+ goto bad;
+ }
module = PyObject_CallFunctionObjArgs(py_import,
name, global_dict, empty_dict, list, NULL);
+ #endif
bad:
Py_XDECREF(empty_list);
Py_XDECREF(py_import);
#if PY_MAJOR_VERSION < 3
if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
PyObject *base = PyTuple_GET_ITEM(bases, 0);
- metaclass = PyObject_GetAttrString(base, "__class__");
+ metaclass = PyObject_GetAttrString(base, (char *)"__class__");
if (!metaclass) {
PyErr_Clear();
metaclass = (PyObject*) Py_TYPE(base);
PyObject *ns;
PyObject *str;
- prep = PyObject_GetAttrString(metaclass, "__prepare__");
+ prep = PyObject_GetAttrString(metaclass, (char *)"__prepare__");
if (!prep) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
return NULL;
}
""" % Naming.__dict__)
+
+generator_utility_code = UtilityCode(
+proto="""
+static PyObject *__Pyx_Generator_Next(PyObject *self);
+static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value);
+static PyObject *__Pyx_Generator_Close(PyObject *self);
+static PyObject *__Pyx_Generator_Throw(PyObject *gen, PyObject *args, CYTHON_UNUSED PyObject *kwds);
+
+typedef PyObject *(*__pyx_generator_body_t)(PyObject *, PyObject *);
+""",
+impl="""
+static CYTHON_INLINE void __Pyx_Generator_ExceptionClear(struct __pyx_Generator_object *self)
+{
+ Py_XDECREF(self->exc_type);
+ Py_XDECREF(self->exc_value);
+ Py_XDECREF(self->exc_traceback);
+
+ self->exc_type = NULL;
+ self->exc_value = NULL;
+ self->exc_traceback = NULL;
+}
+
+static CYTHON_INLINE PyObject *__Pyx_Generator_SendEx(struct __pyx_Generator_object *self, PyObject *value)
+{
+ PyObject *retval;
+
+ if (self->is_running) {
+ PyErr_SetString(PyExc_ValueError,
+ "generator already executing");
+ return NULL;
+ }
+
+ if (self->resume_label == 0) {
+ if (value && value != Py_None) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't send non-None value to a "
+ "just-started generator");
+ return NULL;
+ }
+ }
+
+ if (self->resume_label == -1) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+
+ if (value)
+ __Pyx_ExceptionSwap(&self->exc_type, &self->exc_value, &self->exc_traceback);
+ else
+ __Pyx_Generator_ExceptionClear(self);
+
+ self->is_running = 1;
+ retval = self->body((PyObject *) self, value);
+ self->is_running = 0;
+
+ if (retval)
+ __Pyx_ExceptionSwap(&self->exc_type, &self->exc_value, &self->exc_traceback);
+ else
+ __Pyx_Generator_ExceptionClear(self);
+
+ return retval;
+}
+
+static PyObject *__Pyx_Generator_Next(PyObject *self)
+{
+ return __Pyx_Generator_SendEx((struct __pyx_Generator_object *) self, Py_None);
+}
+
+static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value)
+{
+ return __Pyx_Generator_SendEx((struct __pyx_Generator_object *) self, value);
+}
+
+static PyObject *__Pyx_Generator_Close(PyObject *self)
+{
+ struct __pyx_Generator_object *generator = (struct __pyx_Generator_object *) self;
+ PyObject *retval;
+#if PY_VERSION_HEX < 0x02050000
+ PyErr_SetNone(PyExc_StopIteration);
+#else
+ PyErr_SetNone(PyExc_GeneratorExit);
+#endif
+ retval = __Pyx_Generator_SendEx(generator, NULL);
+ if (retval) {
+ Py_DECREF(retval);
+ PyErr_SetString(PyExc_RuntimeError,
+ "generator ignored GeneratorExit");
+ return NULL;
+ }
+#if PY_VERSION_HEX < 0x02050000
+ if (PyErr_ExceptionMatches(PyExc_StopIteration))
+#else
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)
+ || PyErr_ExceptionMatches(PyExc_GeneratorExit))
+#endif
+ {
+ PyErr_Clear(); /* ignore these errors */
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ return NULL;
+}
+
+static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args, CYTHON_UNUSED PyObject *kwds)
+{
+ struct __pyx_Generator_object *generator = (struct __pyx_Generator_object *) self;
+ PyObject *typ;
+ PyObject *tb = NULL;
+ PyObject *val = NULL;
+
+ if (!PyArg_UnpackTuple(args, (char *)"throw", 1, 3, &typ, &val, &tb))
+ return NULL;
+ __Pyx_Raise(typ, val, tb, NULL);
+ return __Pyx_Generator_SendEx(generator, NULL);
+}
+""",
+proto_block='utility_code_proto_before_types',
+requires=[Nodes.raise_utility_code, Nodes.swap_exception_utility_code],
+)