from Symtab import ModuleScope, LocalScope, ClosureScope, \
StructOrUnionScope, PyClassScope, CClassScope, CppClassScope
from Cython.Utils import open_new_file, replace_suffix
-from Code import UtilityCode
+from Code import UtilityCode, ClosureTempAllocator
from StringEncoding import EncodedString, escape_byte_string, split_string_literal
import Options
import ControlFlow
overridable = 0
optional_arg_count = 0
- def analyse(self, return_type, env, nonempty = 0):
+ def analyse(self, return_type, env, nonempty = 0, directive_locals = {}):
if nonempty:
nonempty -= 1
func_type_args = []
name_declarator, type = arg_node.analyse(env, nonempty = nonempty,
is_self_arg = (i == 0 and env.is_c_class_scope))
name = name_declarator.name
+ if name in directive_locals:
+ type_node = directive_locals[name]
+ other_type = type_node.analyse_as_type(env)
+ if other_type is None:
+ error(type_node.pos, "Not a type")
+ elif (type is not PyrexTypes.py_object_type
+ and not type.same_as(other_type)):
+ error(self.base.pos, "Signature does not agree with previous declaration")
+ error(type_node.pos, "Previous declaration here")
+ else:
+ type = other_type
if name_declarator.cname:
error(self.pos,
"Function argument cannot have C name specification")
visibility = self.visibility
for declarator in self.declarators:
- name_declarator, type = declarator.analyse(base_type, env)
+ if isinstance(declarator, CFuncDeclaratorNode):
+ name_declarator, type = declarator.analyse(base_type, env, directive_locals=self.directive_locals)
+ else:
+ name_declarator, type = declarator.analyse(base_type, env)
if not type.is_complete():
if not (self.visibility == 'extern' and type.is_array):
error(declarator.pos,
# api boolean
# in_pxd boolean
# entry Entry
-
+
child_attrs = ["items"]
def analyse_declarations(self, env):
if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value.analyse_const_expression(env)
- entry = env.declare_const(self.name, enum_entry.type,
+ entry = env.declare_const(self.name, enum_entry.type,
self.value, self.pos, cname = self.cname,
visibility = enum_entry.visibility, api = enum_entry.api)
enum_entry.enum_values.append(entry)
cname = cname, visibility = self.visibility, api = self.api)
if self.in_pxd and not env.in_cinclude:
entry.defined_in_pxd = 1
-
+
def analyse_expressions(self, env):
pass
def generate_execution_code(self, code):
assmt = None
needs_closure = False
needs_outer_scope = False
+ is_generator = False
+ is_generator_body = False
modifiers = []
def analyse_default_values(self, env):
other_type = type_node.analyse_as_type(env)
if other_type is None:
error(type_node.pos, "Not a type")
- elif (type is not PyrexTypes.py_object_type
+ elif (type is not PyrexTypes.py_object_type
and not type.same_as(other_type)):
error(arg.base_type.pos, "Signature does not agree with previous declaration")
error(type_node.pos, "Previous declaration here")
lenv.directives = env.directives
return lenv
+ def generate_function_body(self, env, code):
+ self.body.generate_execution_code(code)
+
def generate_function_definitions(self, env, code):
import Buffer
(self.return_type.declaration_code(Naming.retval_cname),
init))
tempvardecl_code = code.insertion_point()
+ if not lenv.nogil:
+ code.put_declare_refcount_context()
self.generate_keyword_list(code)
if profile:
code.put_trace_declarations()
# -------------------------
# ----- Function body -----
# -------------------------
- self.body.generate_execution_code(code)
+ self.generate_function_body(env, code)
# ----- Default return value
code.putln("")
if entry.type.is_pyobject:
if entry.used and not entry.in_closure:
code.put_var_decref(entry)
- elif entry.in_closure and self.needs_closure:
- code.put_giveref(entry.cname)
# Decref any increfed args
for entry in lenv.arg_entries:
if entry.type.is_pyobject:
- if entry.in_closure:
- code.put_var_giveref(entry)
- elif acquire_gil or entry.assignments:
+ if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_decref(entry)
if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
info = self.local_scope.arg_entries[1].cname
# Python 3.0 betas have a bug in memoryview which makes it call
# getbuffer with a NULL parameter. For now we work around this;
- # the following line should be removed when this bug is fixed.
- code.putln("if (%s == NULL) return 0;" % info)
+ # the following block should be removed when this bug is fixed.
+ code.putln("if (%s != NULL) {" % info)
code.putln("%s->obj = Py_None; __Pyx_INCREF(Py_None);" % info)
code.put_giveref("%s->obj" % info) # Do not refnanny object within structs
+ code.putln("}")
def getbuffer_error_cleanup(self, code):
info = self.local_scope.arg_entries[1].cname
+ code.putln("if (%s != NULL && %s->obj != NULL) {"
+ % (info, info))
code.put_gotref("%s->obj" % info)
- code.putln("__Pyx_DECREF(%s->obj); %s->obj = NULL;" %
- (info, info))
+ code.putln("__Pyx_DECREF(%s->obj); %s->obj = NULL;"
+ % (info, info))
+ code.putln("}")
def getbuffer_normal_cleanup(self, code):
info = self.local_scope.arg_entries[1].cname
- code.putln("if (%s->obj == Py_None) {" % info)
+ code.putln("if (%s != NULL && %s->obj == Py_None) {" % (info, info))
code.put_gotref("Py_None")
code.putln("__Pyx_DECREF(Py_None); %s->obj = NULL;" % info)
code.putln("}")
self.directive_locals.update(env.directives['locals'])
base_type = self.base_type.analyse(env)
# The 2 here is because we need both function and argument names.
- name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
+ if isinstance(self.declarator, CFuncDeclaratorNode):
+ name_declarator, type = self.declarator.analyse(base_type, env,
+ nonempty = 2 * (self.body is not None),
+ directive_locals = self.directive_locals)
+ else:
+ name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
if not type.is_cfunction:
error(self.pos,
"Suite attached to non-function declaration")
self.modifiers[self.modifiers.index('inline')] = 'cython_inline'
if self.modifiers:
modifiers = "%s " % ' '.join(self.modifiers).upper()
-
+
header = self.return_type.declaration_code(entity, dll_linkage=dll_linkage)
#print (storage_class, modifiers, header)
code.putln("%s%s%s {" % (storage_class, modifiers, header))
num_required_kw_args = 0
reqd_kw_flags_cname = "0"
is_wrapper = 0
+ no_assignment_synthesis = 0
decorators = None
return_type_annotation = None
entry = None
api = False,
directive_locals = getattr(cfunc, 'directive_locals', {}))
+ def is_cdef_func_compatible(self):
+ """Determines if the function's signature is compatible with a
+ cdef function. This can be used before calling
+ .as_cfunction() to see if that will be successful.
+ """
+ if self.needs_closure:
+ return False
+ if self.star_arg or self.starstar_arg:
+ return False
+ return True
+
def analyse_declarations(self, env):
self.is_classmethod = self.is_staticmethod = False
if self.decorators:
entry.doc = None
def declare_lambda_function(self, env):
- name = self.name
- prefix = env.scope_prefix
- func_cname = \
- Naming.lambda_func_prefix + u'funcdef' + prefix + self.lambda_name
- entry = env.declare_lambda_function(func_cname, self.pos)
- entry.pymethdef_cname = \
- Naming.lambda_func_prefix + u'methdef' + prefix + self.lambda_name
- entry.qualified_name = env.qualify_name(self.lambda_name)
+ entry = env.declare_lambda_function(self.lambda_name, self.pos)
entry.doc = None
self.entry = entry
self.synthesize_assignment_node(env)
def needs_assignment_synthesis(self, env, code=None):
+ if self.no_assignment_synthesis:
+ return False
# Should enable for module level as well, that will require more testing...
if self.entry.is_anonymous:
return True
code.putln("0};")
def generate_argument_parsing_code(self, env, code):
- # Generate PyArg_ParseTuple call for generic
- # arguments, if any.
+ # Generate fast equivalent of PyArg_ParseTuple call for
+ # generic arguments, if any, including args/kwargs
if self.entry.signature.has_dummy_arg and not self.self_in_stararg:
# get rid of unused argument warning
code.putln("%s = %s;" % (Naming.self_cname, Naming.self_cname))
if code.label_used(end_label):
code.put_label(end_label)
+ # fix refnanny view on closure variables here, instead of
+ # doing it separately for each arg parsing special case
+ if self.star_arg and self.star_arg.entry.in_closure:
+ code.put_var_giveref(self.star_arg.entry)
+ if self.starstar_arg and self.starstar_arg.entry.in_closure:
+ code.put_var_giveref(self.starstar_arg.entry)
+ for arg in self.args:
+ if arg.type.is_pyobject and arg.entry.in_closure:
+ code.put_var_giveref(arg.entry)
+
def generate_arg_assignment(self, arg, item, code):
if arg.type.is_pyobject:
if arg.is_generic:
item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type, item)
entry = arg.entry
- code.putln("%s = %s;" % (entry.cname, item))
if entry.in_closure:
- code.put_var_incref(entry)
+ code.put_incref(item, PyrexTypes.py_object_type)
+ code.putln("%s = %s;" % (entry.cname, item))
else:
func = arg.type.from_py_function
if func:
code.putln('if (PyTuple_GET_SIZE(%s) > %d) {' % (
Naming.args_cname,
max_positional_args))
- code.put('%s = PyTuple_GetSlice(%s, %d, PyTuple_GET_SIZE(%s)); ' % (
+ code.putln('%s = PyTuple_GetSlice(%s, %d, PyTuple_GET_SIZE(%s));' % (
self.star_arg.entry.cname, Naming.args_cname,
max_positional_args, Naming.args_cname))
- code.put_gotref(self.star_arg.entry.cname)
+ code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname)
if self.starstar_arg:
- code.putln("")
- code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname)
code.put_decref_clear(self.starstar_arg.entry.cname, py_object_type)
- code.putln('return %s;' % self.error_value())
- code.putln('}')
- else:
- code.putln("if (unlikely(!%s)) return %s;" % (
- self.star_arg.entry.cname, self.error_value()))
+ if self.needs_closure:
+ code.put_decref(Naming.cur_scope_cname, self.local_scope.scope_class.type)
+ code.put_finish_refcount_context()
+ code.putln('return %s;' % self.error_value())
+ code.putln('}')
+ code.put_gotref(self.star_arg.entry.cname)
code.putln('} else {')
code.put("%s = %s; " % (self.star_arg.entry.cname, Naming.empty_tuple))
code.put_incref(Naming.empty_tuple, py_object_type)
if arg.needs_conversion:
self.generate_arg_conversion(arg, code)
elif arg.entry.in_closure:
- code.putln('%s = %s;' % (arg.entry.cname, arg.hdr_cname))
if arg.type.is_pyobject:
- code.put_var_incref(arg.entry)
+ code.put_incref(arg.hdr_cname, py_object_type)
+ code.putln('%s = %s;' % (arg.entry.cname, arg.hdr_cname))
def generate_arg_conversion(self, arg, code):
# Generate conversion code for one argument.
def caller_will_check_exceptions(self):
return 1
+
+class GeneratorDefNode(DefNode):
+ # Generator DefNode.
+ #
+ # gbody GeneratorBodyDefNode
+ #
+
+ is_generator = True
+ needs_closure = True
+
+ child_attrs = DefNode.child_attrs + ["gbody"]
+
+ def __init__(self, **kwargs):
+ # XXX: don't actually needs a body
+ kwargs['body'] = StatListNode(kwargs['pos'], stats=[])
+ super(GeneratorDefNode, self).__init__(**kwargs)
+
+ def analyse_declarations(self, env):
+ super(GeneratorDefNode, self).analyse_declarations(env)
+ self.gbody.local_scope = self.local_scope
+ self.gbody.analyse_declarations(env)
+
+ def generate_function_body(self, env, code):
+ body_cname = self.gbody.entry.func_cname
+ generator_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.obj_base_cname)
+
+ code.putln('%s.resume_label = 0;' % generator_cname)
+ code.putln('%s.body = (__pyx_generator_body_t) %s;' % (generator_cname, body_cname))
+ code.put_giveref(Naming.cur_scope_cname)
+ code.put_finish_refcount_context()
+ code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
+
+ def generate_function_definitions(self, env, code):
+ self.gbody.generate_function_header(code, proto=True)
+ super(GeneratorDefNode, self).generate_function_definitions(env, code)
+ self.gbody.generate_function_definitions(env, code)
+
+
+class GeneratorBodyDefNode(DefNode):
+ # Generator body DefNode.
+ #
+
+ is_generator_body = True
+
+ def __init__(self, pos=None, name=None, body=None):
+ super(GeneratorBodyDefNode, self).__init__(pos=pos, body=body, name=name, doc=None,
+ args=[],
+ star_arg=None, starstar_arg=None)
+
+ def declare_generator_body(self, env):
+ prefix = env.next_id(env.scope_prefix)
+ name = env.next_id('generator')
+ entry = env.declare_var(prefix + name, py_object_type, self.pos, visibility='private')
+ entry.func_cname = Naming.genbody_prefix + prefix + name
+ entry.qualified_name = EncodedString(self.name)
+ self.entry = entry
+
+ def analyse_declarations(self, env):
+ self.analyse_argument_types(env)
+ self.declare_generator_body(env)
+
+ def generate_function_header(self, code, proto=False):
+ header = "static PyObject *%s(%s, PyObject *%s)" % (
+ self.entry.func_cname,
+ self.local_scope.scope_class.type.declaration_code(Naming.cur_scope_cname),
+ Naming.sent_value_cname)
+ if proto:
+ code.putln('%s; /* proto */' % header)
+ else:
+ code.putln('%s /* generator body */\n{' % header);
+
+ def generate_function_definitions(self, env, code):
+ lenv = self.local_scope
+
+ # Generate closure function definitions
+ self.body.generate_function_definitions(lenv, code)
+
+ # Generate C code for header and body of function
+ code.enter_cfunc_scope()
+ code.return_from_error_cleanup_label = code.new_label()
+
+ # ----- Top-level constants used by this function
+ code.mark_pos(self.pos)
+ self.generate_cached_builtins_decls(lenv, code)
+ # ----- Function header
+ code.putln("")
+ self.generate_function_header(code)
+ # ----- Local variables
+ code.putln("PyObject *%s = NULL;" % Naming.retval_cname)
+ tempvardecl_code = code.insertion_point()
+ code.put_declare_refcount_context()
+ code.put_setup_refcount_context(self.entry.name)
+
+ # ----- Resume switch point.
+ code.funcstate.init_closure_temps(lenv.scope_class.type.scope)
+ resume_code = code.insertion_point()
+ first_run_label = code.new_label('first_run')
+ code.use_label(first_run_label)
+ code.put_label(first_run_label)
+ code.putln('%s' %
+ (code.error_goto_if_null(Naming.sent_value_cname, self.pos)))
+
+ # ----- Function body
+ self.generate_function_body(env, code)
+ code.putln('PyErr_SetNone(PyExc_StopIteration); %s' % code.error_goto(self.pos))
+ # ----- Error cleanup
+ if code.error_label in code.labels_used:
+ code.put_goto(code.return_label)
+ code.put_label(code.error_label)
+ for cname, type in code.funcstate.all_managed_temps():
+ code.put_xdecref(cname, type)
+ code.putln('__Pyx_AddTraceback("%s");' % self.entry.qualified_name)
+
+ # ----- Non-error return cleanup
+ code.put_label(code.return_label)
+ code.put_xdecref(Naming.retval_cname, py_object_type)
+ code.putln('%s->%s.resume_label = -1;' % (Naming.cur_scope_cname, Naming.obj_base_cname))
+ code.put_finish_refcount_context()
+ code.putln('return NULL;');
+ code.putln("}")
+
+ # ----- Go back and insert temp variable declarations
+ tempvardecl_code.put_temp_declarations(code.funcstate)
+ # ----- Generator resume code
+ resume_code.putln("switch (%s->%s.resume_label) {" % (Naming.cur_scope_cname, Naming.obj_base_cname));
+ resume_code.putln("case 0: goto %s;" % first_run_label)
+
+ from ParseTreeTransforms import YieldNodeCollector
+ collector = YieldNodeCollector()
+ collector.visitchildren(self)
+ for yield_expr in collector.yields:
+ resume_code.putln("case %d: goto %s;" % (yield_expr.label_num, yield_expr.label_name));
+ resume_code.putln("default: /* CPython raises the right error here */");
+ resume_code.put_finish_refcount_context()
+ resume_code.putln("return NULL;");
+ resume_code.putln("}");
+
+ code.exit_cfunc_scope()
+
+
class OverrideCheckNode(StatNode):
# A Node for dispatching to the def method if it
# is overriden.
visibility = self.visibility,
typedef_flag = self.typedef_flag,
api = self.api,
- buffer_defaults = buffer_defaults,
+ buffer_defaults = buffer_defaults,
shadow = self.shadow)
if self.shadow:
home_scope.lookup(self.class_name).as_variable = self.entry
pass
+class NonlocalNode(StatNode):
+ # Nonlocal variable declaration via the 'nonlocal' keyword.
+ #
+ # names [string]
+
+ child_attrs = []
+
+ def analyse_declarations(self, env):
+ for name in self.names:
+ env.declare_nonlocal(name, self.pos)
+
+ def analyse_expressions(self, env):
+ pass
+
+ def generate_execution_code(self, code):
+ pass
+
+
class ExprStatNode(StatNode):
# Expression used as a statement.
#
self.__class__ = PassStatNode
def analyse_expressions(self, env):
+ self.expr.result_is_used = False # hint that .result() may safely be left empty
self.expr.analyse_expressions(env)
def nogil_check(self, env):
if self.exc_type and not self.exc_value and not self.exc_tb:
exc = self.exc_type
import ExprNodes
- if isinstance(exc, ExprNodes.SimpleCallNode) and not exc.args:
+ if (isinstance(exc, ExprNodes.SimpleCallNode) and
+ not (exc.args or (exc.arg_tuple is not None and
+ exc.arg_tuple.args))):
exc = exc.function # extract the exception type
if exc.is_name and exc.entry.is_builtin:
self.builtin_exc_name = exc.name
try_continue_label = code.new_label('try_continue')
try_end_label = code.new_label('try_end')
+ exc_save_vars = [code.funcstate.allocate_temp(py_object_type, False)
+ for i in xrange(3)]
code.putln("{")
- code.putln("PyObject %s;" %
- ', '.join(['*%s' % var for var in Naming.exc_save_vars]))
code.putln("__Pyx_ExceptionSave(%s);" %
- ', '.join(['&%s' % var for var in Naming.exc_save_vars]))
- for var in Naming.exc_save_vars:
+ ', '.join(['&%s' % var for var in exc_save_vars]))
+ for var in exc_save_vars:
code.put_xgotref(var)
code.putln(
"/*try:*/ {")
self.else_clause.generate_execution_code(code)
code.putln(
"}")
- for var in Naming.exc_save_vars:
+ for var in exc_save_vars:
code.put_xdecref_clear(var, py_object_type)
code.put_goto(try_end_label)
if code.label_used(try_return_label):
code.put_label(try_return_label)
- for var in Naming.exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
- ', '.join(Naming.exc_save_vars))
+ ', '.join(exc_save_vars))
code.put_goto(old_return_label)
code.put_label(our_error_label)
for temp_name, type in temps_to_clean_up:
if error_label_used or not self.has_default_clause:
if error_label_used:
code.put_label(except_error_label)
- for var in Naming.exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
- ', '.join(Naming.exc_save_vars))
+ ', '.join(exc_save_vars))
code.put_goto(old_error_label)
for exit_label, old_label in zip(
if code.label_used(exit_label):
code.put_label(exit_label)
- for var in Naming.exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
- ', '.join(Naming.exc_save_vars))
+ ', '.join(exc_save_vars))
code.put_goto(old_label)
if code.label_used(except_end_label):
code.put_label(except_end_label)
- for var in Naming.exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
- ', '.join(Naming.exc_save_vars))
+ ', '.join(exc_save_vars))
code.put_label(try_end_label)
code.putln("}")
+ for cname in exc_save_vars:
+ code.funcstate.release_temp(cname)
+
code.return_label = old_return_label
code.break_label = old_break_label
code.continue_label = old_continue_label
#------------------------------------------------------------------------------------
+swap_exception_utility_code = UtilityCode(
+proto = """
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb); /*proto*/
+""",
+impl = """
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyThreadState *tstate = PyThreadState_GET();
+
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+
+ tstate->exc_type = *type;
+ tstate->exc_value = *value;
+ tstate->exc_traceback = *tb;
+
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+""")
+
+#------------------------------------------------------------------------------------
+
arg_type_test_utility_code = UtilityCode(
proto = """
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,