Commit a4cb6877 authored by Stefan Behnel's avatar Stefan Behnel

undo the Python-optimisationism of assigning bound/unbound methods to local...

undo the Python-optimisationism of assigning bound/unbound methods to local variables when Cython can call a builtin C function instead

--HG--
extra : amend_source : 121eadd540f25f9e43f6e6b60853eeab18d11620
parent 358f47d0
...@@ -1891,6 +1891,52 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1891,6 +1891,52 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
error(node.pos, "%s(%s) called with wrong number of args, %sfound %d" % ( error(node.pos, "%s(%s) called with wrong number of args, %sfound %d" % (
function_name, arg_str, expected_str, len(args))) function_name, arg_str, expected_str, len(args)))
### generic fallbacks
def _handle_function(self, node, function_name, function, arg_list, kwargs):
return node
def _handle_method(self, node, type_name, attr_name, function,
arg_list, is_unbound_method, kwargs):
"""
Try to inject C-API calls for unbound method calls to builtin types.
While the method declarations in Builtin.py already handle this, we
can additionally resolve bound and unbound methods here that were
assigned to variables ahead of time.
"""
if kwargs:
return node
if not function or not function.is_attribute or not function.obj.is_name:
# cannot track unbound method calls over more than one indirection as
# the names might have been reassigned in the meantime
return node
type_entry = self.current_env().lookup(type_name)
if not type_entry:
return node
method = ExprNodes.AttributeNode(
node.function.pos,
obj=ExprNodes.NameNode(
function.pos,
name=type_name,
entry=type_entry,
type=type_entry.type),
attribute=attr_name,
is_called=True).analyse_as_unbound_cmethod_node(self.current_env())
if method is None:
return node
args = node.args
if args is None and node.arg_tuple:
args = node.arg_tuple.args
call_node = ExprNodes.SimpleCallNode(
node.pos,
function=method,
args=args)
if not is_unbound_method:
call_node.self = function.obj
call_node.analyse_c_function_call(self.current_env())
call_node.analysed = True
return call_node.coerce_to(node.type, self.current_env())
### builtin types ### builtin types
PyDict_Copy_func_type = PyrexTypes.CFuncType( PyDict_Copy_func_type = PyrexTypes.CFuncType(
......
...@@ -582,7 +582,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -582,7 +582,7 @@ class MethodDispatcherTransform(EnvTransform):
function_handler = self._find_handler( function_handler = self._find_handler(
"function_%s" % function.name, kwargs) "function_%s" % function.name, kwargs)
if function_handler is None: if function_handler is None:
return node return self._handle_function(node, function.name, function, arg_list, kwargs)
if kwargs: if kwargs:
return function_handler(node, function, arg_list, kwargs) return function_handler(node, function, arg_list, kwargs)
else: else:
...@@ -621,7 +621,9 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -621,7 +621,9 @@ class MethodDispatcherTransform(EnvTransform):
method_handler = self._find_handler( method_handler = self._find_handler(
"slot%s" % attr_name, kwargs) "slot%s" % attr_name, kwargs)
if method_handler is None: if method_handler is None:
return node return self._handle_method(
node, type_name, attr_name, function,
arg_list, is_unbound_method, kwargs)
if self_arg is not None: if self_arg is not None:
arg_list = [self_arg] + list(arg_list) arg_list = [self_arg] + list(arg_list)
if kwargs: if kwargs:
...@@ -631,6 +633,15 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -631,6 +633,15 @@ class MethodDispatcherTransform(EnvTransform):
return method_handler( return method_handler(
node, function, arg_list, is_unbound_method) node, function, arg_list, is_unbound_method)
def _handle_function(self, node, function_name, function, arg_list, kwargs):
"""Fallback handler"""
return node
def _handle_method(self, node, type_name, attr_name, function,
arg_list, is_unbound_method, kwargs):
"""Fallback handler"""
return node
class RecursiveNodeReplacer(VisitorTransform): class RecursiveNodeReplacer(VisitorTransform):
""" """
......
...@@ -50,3 +50,69 @@ def test_dict_items_bound_no_assignment(dict d): ...@@ -50,3 +50,69 @@ def test_dict_items_bound_no_assignment(dict d):
>>> test_dict_items_bound_no_assignment({1:2}) >>> test_dict_items_bound_no_assignment({1:2})
""" """
d.items d.items
def list_pop(list l):
"""
>>> list_pop([1,2,3])
(2, [1, 3])
"""
pop = l.pop
r = pop(1)
return r, l
def list_pop_literal():
"""
>>> list_pop_literal()
(2, [1, 3])
"""
l = [1,2,3]
pop = l.pop
r = pop(1)
return r, l
def list_pop_reassign():
"""
>>> list_pop_reassign()
2
"""
l = [1,2,3]
pop = l.pop
l = None
r = pop(1)
return r
def list_insert(list l):
"""
>>> list_insert([1,2,3])
(None, [1, 4, 2, 3])
"""
insert = l.insert
r = insert(1, 4)
return r, l
def list_insert_literal():
"""
>>> list_insert_literal()
(None, [1, 4, 2, 3])
"""
l = [1,2,3]
insert = l.insert
r = insert(1, 4)
return r, l
def list_insert_reassign():
"""
>>> list_insert_reassign()
(None, [1, 4, 2, 3])
"""
l = [1,2,3]
insert = l.insert
m, l = l, None
r = insert(1, 4)
return r, m
def list_insert(list l):
"""
>>> list_insert([1,2,3])
[1, 4, 2, 3]
"""
list.insert(l, 1, 4)
return l
def list_insert_literal():
"""
>>> list_insert_literal()
(None, [1, 4, 2, 3])
"""
l = [1,2,3]
r = list.insert(l, 1, 4)
return r, l
def list_insert_assigned():
"""
>>> list_insert_assigned()
(None, [1, 4, 2, 3])
"""
insert = list.insert
l = [1,2,3]
r = insert(l, 1, 4)
return r, l
def list_pop():
"""
>>> list_pop()
(2, [1, 3])
"""
l = [1,2,3]
r = list.pop(l, 1)
return r, l
def list_pop_assigned():
"""
>>> list_pop_assigned()
[1, 3]
"""
pop = list.pop
l = [1,2,3]
pop(l, 1)
return l
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