Commit f1099181 authored by Vitja Makarov's avatar Vitja Makarov

Make Refnanny happy, fix some errors. More testcases.

parent 1f609768
...@@ -4990,8 +4990,6 @@ class YieldExprNode(ExprNode): ...@@ -4990,8 +4990,6 @@ class YieldExprNode(ExprNode):
save_cname = self.temp_allocator.allocate_temp(type) save_cname = self.temp_allocator.allocate_temp(type)
saved.append((cname, save_cname, type)) saved.append((cname, save_cname, type))
code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname)) code.putln('%s->%s = %s;' % (Naming.cur_scope_cname, save_cname, cname))
if type.is_pyobject:
code.put_giveref(cname)
self.label_name = code.new_label('resume_from_yield') self.label_name = code.new_label('resume_from_yield')
code.use_label(self.label_name) code.use_label(self.label_name)
self.allocate_temp_result(code) self.allocate_temp_result(code)
...@@ -5008,6 +5006,9 @@ class YieldExprNode(ExprNode): ...@@ -5008,6 +5006,9 @@ class YieldExprNode(ExprNode):
else: else:
code.put_init_to_py_none(Naming.retval_cname, py_object_type) code.put_init_to_py_none(Naming.retval_cname, py_object_type)
# XXX: safe here as all used temps are handled but not clean
self.temp_allocator.put_giveref(code)
code.put_xgiveref(Naming.retval_cname)
code.put_finish_refcount_context() code.put_finish_refcount_context()
code.putln("/* return from function, yielding value */") code.putln("/* return from function, yielding value */")
code.putln("%s->%s.resume_label = %d;" % (Naming.cur_scope_cname, Naming.obj_base_cname, self.label_num)) code.putln("%s->%s.resume_label = %d;" % (Naming.cur_scope_cname, Naming.obj_base_cname, self.label_num))
...@@ -5018,18 +5019,21 @@ class YieldExprNode(ExprNode): ...@@ -5018,18 +5019,21 @@ class YieldExprNode(ExprNode):
code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname)) code.putln('%s = %s->%s;' % (cname, Naming.cur_scope_cname, save_cname))
if type.is_pyobject: if type.is_pyobject:
code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname)) code.putln('%s->%s = 0;' % (Naming.cur_scope_cname, save_cname))
code.put_gotref(cname)
code.putln('%s = __pyx_send_value;' % self.result()) code.putln('%s = __pyx_send_value;' % self.result())
code.put_incref(self.result(), py_object_type) code.put_incref(self.result(), py_object_type)
class StopIterationNode(YieldExprNode): class StopIterationNode(Node):
subexprs = [] # XXX: is it okay?
child_attrs = []
def generate_evaluation_code(self, code): def analyse_expressions(self, env):
self.allocate_temp_result(code) pass
self.label_name = code.new_label('resume_from_yield')
code.use_label(self.label_name) def generate_function_definitions(self, env, code):
code.put_label(self.label_name) pass
def generate_execution_code(self, code):
code.putln('/* Stop iteration */')
code.putln('PyErr_SetNone(PyExc_StopIteration); %s' % code.error_goto(self.pos)) code.putln('PyErr_SetNone(PyExc_StopIteration); %s' % code.error_goto(self.pos))
#------------------------------------------------------------------- #-------------------------------------------------------------------
...@@ -8324,6 +8328,11 @@ static CYTHON_INLINE PyObject *__CyGenerator_SendEx(struct __CyGenerator *self, ...@@ -8324,6 +8328,11 @@ static CYTHON_INLINE PyObject *__CyGenerator_SendEx(struct __CyGenerator *self,
} }
} }
if (self->resume_label == -1) {
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
self->is_running = 1; self->is_running = 1;
retval = self->body((PyObject *) self, value, is_exc); retval = self->body((PyObject *) self, value, is_exc);
self->is_running = 0; self->is_running = 0;
......
...@@ -1321,6 +1321,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1321,6 +1321,8 @@ class FuncDefNode(StatNode, BlockNode):
Naming.cur_scope_cname, Naming.cur_scope_cname,
lenv.scope_class.type.declaration_code(''), lenv.scope_class.type.declaration_code(''),
Naming.self_cname)) Naming.self_cname))
gotref_code = code.insertion_point()
elif self.needs_closure: elif self.needs_closure:
code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % ( code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % (
Naming.cur_scope_cname, Naming.cur_scope_cname,
...@@ -1480,6 +1482,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1480,6 +1482,8 @@ class FuncDefNode(StatNode, BlockNode):
code.put_var_decref(entry) code.put_var_decref(entry)
if self.needs_closure and not self.is_generator: if self.needs_closure and not self.is_generator:
code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type) code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
if self.is_generator:
code.putln('%s->%s.resume_label = -1;' % (Naming.cur_scope_cname, Naming.obj_base_cname))
# ----- Return # ----- Return
# This code is duplicated in ModuleNode.generate_module_init_func # This code is duplicated in ModuleNode.generate_module_init_func
...@@ -1535,6 +1539,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1535,6 +1539,8 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_wrapper_functions(code) self.generate_wrapper_functions(code)
if self.is_generator: if self.is_generator:
gotref_code.putln('/* Make refnanny happy */')
self.temp_allocator.put_gotref(gotref_code)
self.generator.generate_function_body(self.local_scope, code) self.generator.generate_function_body(self.local_scope, code)
def declare_argument(self, env, arg): def declare_argument(self, env, arg):
...@@ -1920,18 +1926,22 @@ class GeneratorWrapperNode(object): ...@@ -1920,18 +1926,22 @@ class GeneratorWrapperNode(object):
code.put_gotref(Naming.cur_scope_cname) code.put_gotref(Naming.cur_scope_cname)
if self.def_node.needs_outer_scope: if self.def_node.needs_outer_scope:
code.putln("%s->%s = (%s)%s;" % ( outer_scope_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.outer_scope_cname)
Naming.cur_scope_cname, code.putln("%s = (%s)%s;" % (
Naming.outer_scope_cname, outer_scope_cname,
cenv.scope_class.type.declaration_code(''), cenv.scope_class.type.declaration_code(''),
Naming.self_cname)) Naming.self_cname))
code.put_incref(outer_scope_cname, cenv.scope_class.type)
self.def_node.generate_argument_parsing_code(env, code) self.def_node.generate_argument_parsing_code(env, code)
generator_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.obj_base_cname) generator_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.obj_base_cname)
code.putln('%s.resume_label = 0;' % generator_cname) code.putln('%s.resume_label = 0;' % generator_cname)
code.putln('%s.body = (void *) %s;' % (generator_cname, self.body_cname)) code.putln('%s.body = %s;' % (generator_cname, self.body_cname))
for entry in lenv.scope_class.type.scope.entries.values():
if entry.type.is_pyobject:
code.put_xgiveref('%s->%s' % (Naming.cur_scope_cname, entry.cname))
code.put_giveref(Naming.cur_scope_cname) code.put_giveref(Naming.cur_scope_cname)
code.put_finish_refcount_context() code.put_finish_refcount_context()
code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname); code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
......
...@@ -1343,21 +1343,35 @@ class ClosureTempAllocator(object): ...@@ -1343,21 +1343,35 @@ class ClosureTempAllocator(object):
self.temps_count = 0 self.temps_count = 0
def reset(self): def reset(self):
for type, cnames in self.temps_allocated: for type, cnames in self.temps_allocated.items():
self.temps_free[type] = list(cnames) self.temps_free[type] = list(cnames)
def allocate_temp(self, type): def allocate_temp(self, type):
if not type in self.temps_allocated: if not type in self.temps_allocated:
self.temps_allocated[type] = [] self.temps_allocated[type] = []
self.temps_free[type] = [] self.temps_free[type] = []
if self.temps_free[type]: elif self.temps_free[type]:
return self.temps_free[type].pop(0) return self.temps_free[type].pop(0)
cname = '%s_%d' % (Naming.codewriter_temp_prefix, self.temps_count) cname = '%s%d' % (Naming.codewriter_temp_prefix, self.temps_count)
self.klass.declare_var(pos=None, name=cname, cname=cname, type=type, is_cdef=True) self.klass.declare_var(pos=None, name=cname, cname=cname, type=type, is_cdef=True)
self.temps_allocated[type].append(cname) self.temps_allocated[type].append(cname)
self.temps_count += 1 self.temps_count += 1
return cname return cname
def put_gotref(self, code):
for entry in self.klass.entries.values():
if entry.cname == Naming.outer_scope_cname: # XXX
continue
if entry.type.is_pyobject:
code.put_xgotref('%s->%s' % (Naming.cur_scope_cname, entry.cname))
def put_giveref(self, code):
for entry in self.klass.entries.values():
if entry.cname == Naming.outer_scope_cname: # XXX
continue
if entry.type.is_pyobject:
code.put_xgiveref('%s->%s' % (Naming.cur_scope_cname, entry.cname))
class YieldCollector(object): class YieldCollector(object):
def __init__(self, node): def __init__(self, node):
self.node = node self.node = node
...@@ -1394,12 +1408,11 @@ class MarkGeneratorVisitor(CythonTransform): ...@@ -1394,12 +1408,11 @@ class MarkGeneratorVisitor(CythonTransform):
elif collector.yields: elif collector.yields:
allocator = ClosureTempAllocator() allocator = ClosureTempAllocator()
stop_node = ExprNodes.StopIterationNode(node.pos, arg=None) stop_node = ExprNodes.StopIterationNode(node.pos, arg=None)
collector.yields.append(stop_node) # XXX: move allocator inside local scope
for y in collector.yields: # XXX: find a better way for y in collector.yields:
y.temp_allocator = allocator y.temp_allocator = allocator
node.temp_allocator = allocator node.temp_allocator = allocator
stop_node.label_num = len(collector.yields) node.body.stats.append(stop_node)
node.body.stats.append(Nodes.ExprStatNode(node.pos, expr=stop_node))
node.is_generator = True node.is_generator = True
node.needs_closure = True node.needs_closure = True
node.yields = collector.yields node.yields = collector.yields
......
def very_simple():
"""
>>> x = very_simple()
>>> next(x)
1
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> next(x)
Traceback (most recent call last):
StopIteration
"""
yield 1
def simple(): def simple():
""" """
>>> x = simple() >>> x = simple()
...@@ -32,6 +46,18 @@ def simple_send(): ...@@ -32,6 +46,18 @@ def simple_send():
while True: while True:
i = yield i i = yield i
def raising():
"""
>>> x = raising()
>>> next(x)
Traceback (most recent call last):
KeyError: 'foo'
>>> next(x)
Traceback (most recent call last):
StopIteration
"""
yield {}['foo']
def with_outer(*args): def with_outer(*args):
""" """
>>> x = with_outer(1, 2, 3) >>> x = with_outer(1, 2, 3)
...@@ -42,3 +68,15 @@ def with_outer(*args): ...@@ -42,3 +68,15 @@ def with_outer(*args):
for i in args: for i in args:
yield i yield i
return generator return generator
def with_outer_raising(*args):
"""
>>> x = with_outer_raising(1, 2, 3)
>>> list(x())
[1, 2, 3]
"""
def generator():
for i in args:
yield i
raise StopIteration
return generator
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment