Commit ddb0c9e7 authored by Stefan Behnel's avatar Stefan Behnel

optimise calls on variables that reference builtin methods (e.g. pre-assigned unbound methods)

--HG--
extra : rebase_source : 4aeefab40694c611bfdaa4ecf56a3f4f6cdf3d04
parent e2edd243
...@@ -1856,7 +1856,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1856,7 +1856,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("dict", Builtin.dict_type, None) PyrexTypes.CFuncTypeArg("dict", Builtin.dict_type, None)
]) ])
def _handle_simple_function_dict(self, node, pos_args): def _handle_simple_function_dict(self, node, function, pos_args):
"""Replace dict(some_dict) by PyDict_Copy(some_dict). """Replace dict(some_dict) by PyDict_Copy(some_dict).
""" """
if len(pos_args) != 1: if len(pos_args) != 1:
...@@ -1876,7 +1876,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1876,7 +1876,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("list", Builtin.list_type, None) PyrexTypes.CFuncTypeArg("list", Builtin.list_type, None)
]) ])
def _handle_simple_function_tuple(self, node, pos_args): def _handle_simple_function_tuple(self, node, function, pos_args):
"""Replace tuple([...]) by a call to PyList_AsTuple. """Replace tuple([...]) by a call to PyList_AsTuple.
""" """
if len(pos_args) != 1: if len(pos_args) != 1:
...@@ -1902,7 +1902,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1902,7 +1902,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
exception_value = "((double)-1)", exception_value = "((double)-1)",
exception_check = True) exception_check = True)
def _handle_simple_function_set(self, node, pos_args): def _handle_simple_function_set(self, node, function, pos_args):
if len(pos_args) == 1 and isinstance(pos_args[0], (ExprNodes.ListNode, if len(pos_args) == 1 and isinstance(pos_args[0], (ExprNodes.ListNode,
ExprNodes.TupleNode)): ExprNodes.TupleNode)):
# We can optimise set([x,y,z]) safely into a set literal, # We can optimise set([x,y,z]) safely into a set literal,
...@@ -1923,7 +1923,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1923,7 +1923,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
return result return result
return node return node
def _handle_simple_function_float(self, node, pos_args): def _handle_simple_function_float(self, node, function, pos_args):
"""Transform float() into either a C type cast or a faster C """Transform float() into either a C type cast or a faster C
function call. function call.
""" """
...@@ -1952,7 +1952,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -1952,7 +1952,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
utility_code = load_c_utility('pyobject_as_double'), utility_code = load_c_utility('pyobject_as_double'),
py_name = "float") py_name = "float")
def _handle_simple_function_bool(self, node, pos_args): def _handle_simple_function_bool(self, node, function, pos_args):
"""Transform bool(x) into a type coercion to a boolean. """Transform bool(x) into a type coercion to a boolean.
""" """
if len(pos_args) == 0: if len(pos_args) == 0:
...@@ -2000,7 +2000,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2000,7 +2000,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
_ext_types_with_pysize = set(["cpython.array.array"]) _ext_types_with_pysize = set(["cpython.array.array"])
def _handle_simple_function_len(self, node, pos_args): def _handle_simple_function_len(self, node, function, pos_args):
"""Replace len(char*) by the equivalent call to strlen(), """Replace len(char*) by the equivalent call to strlen(),
len(Py_UNICODE) by the equivalent Py_UNICODE_strlen() and len(Py_UNICODE) by the equivalent Py_UNICODE_strlen() and
len(known_builtin_type) by an equivalent C-API call. len(known_builtin_type) by an equivalent C-API call.
...@@ -2051,7 +2051,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2051,7 +2051,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("object", PyrexTypes.py_object_type, None) PyrexTypes.CFuncTypeArg("object", PyrexTypes.py_object_type, None)
]) ])
def _handle_simple_function_type(self, node, pos_args): def _handle_simple_function_type(self, node, function, pos_args):
"""Replace type(o) by a macro call to Py_TYPE(o). """Replace type(o) by a macro call to Py_TYPE(o).
""" """
if len(pos_args) != 1: if len(pos_args) != 1:
...@@ -2067,7 +2067,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2067,7 +2067,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.py_object_type, None) PyrexTypes.CFuncTypeArg("arg", PyrexTypes.py_object_type, None)
]) ])
def _handle_simple_function_isinstance(self, node, pos_args): def _handle_simple_function_isinstance(self, node, function, pos_args):
"""Replace isinstance() checks against builtin types by the """Replace isinstance() checks against builtin types by the
corresponding C-API call. corresponding C-API call.
""" """
...@@ -2122,7 +2122,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2122,7 +2122,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
test_node = UtilNodes.EvalWithTempExprNode(temp, test_node) test_node = UtilNodes.EvalWithTempExprNode(temp, test_node)
return test_node return test_node
def _handle_simple_function_ord(self, node, pos_args): def _handle_simple_function_ord(self, node, function, pos_args):
"""Unpack ord(Py_UNICODE) and ord('X'). """Unpack ord(Py_UNICODE) and ord('X').
""" """
if len(pos_args) != 1: if len(pos_args) != 1:
...@@ -2165,10 +2165,11 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2165,10 +2165,11 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None), PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
]) ])
def _handle_any_slot__new__(self, node, args, is_unbound_method, kwargs=None): def _handle_any_slot__new__(self, node, function, args,
is_unbound_method, kwargs=None):
"""Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new() """Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
""" """
obj = node.function.obj obj = function.obj
if not is_unbound_method or len(args) < 1: if not is_unbound_method or len(args) < 1:
return node return node
type_arg = args[0] type_arg = args[0]
...@@ -2244,7 +2245,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2244,7 +2245,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None), PyrexTypes.CFuncTypeArg("item", PyrexTypes.py_object_type, None),
]) ])
def _handle_simple_method_object_append(self, node, args, is_unbound_method): def _handle_simple_method_object_append(self, node, function, args, is_unbound_method):
"""Optimistic optimisation as X.append() is almost always """Optimistic optimisation as X.append() is almost always
referring to a list. referring to a list.
""" """
...@@ -2270,7 +2271,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2270,7 +2271,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_long_type, None), PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_long_type, None),
]) ])
def _handle_simple_method_object_pop(self, node, args, is_unbound_method): def _handle_simple_method_object_pop(self, node, function, args, is_unbound_method):
"""Optimistic optimisation as X.pop([n]) is almost always """Optimistic optimisation as X.pop([n]) is almost always
referring to a list. referring to a list.
""" """
...@@ -2312,13 +2313,13 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2312,13 +2313,13 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
], ],
exception_value = "-1") exception_value = "-1")
def _handle_simple_method_list_sort(self, node, args, is_unbound_method): def _handle_simple_method_list_sort(self, node, function, args, is_unbound_method):
"""Call PyList_Sort() instead of the 0-argument l.sort(). """Call PyList_Sort() instead of the 0-argument l.sort().
""" """
if len(args) != 1: if len(args) != 1:
return node return node
return self._substitute_method_call( return self._substitute_method_call(
node, "PyList_Sort", self.single_param_func_type, node, function, "PyList_Sort", self.single_param_func_type,
'sort', is_unbound_method, args).coerce_to(node.type, self.current_env) 'sort', is_unbound_method, args).coerce_to(node.type, self.current_env)
Pyx_PyDict_GetItem_func_type = PyrexTypes.CFuncType( Pyx_PyDict_GetItem_func_type = PyrexTypes.CFuncType(
...@@ -2328,7 +2329,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2328,7 +2329,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None), PyrexTypes.CFuncTypeArg("default", PyrexTypes.py_object_type, None),
]) ])
def _handle_simple_method_dict_get(self, node, args, is_unbound_method): def _handle_simple_method_dict_get(self, node, function, args, is_unbound_method):
"""Replace dict.get() by a call to PyDict_GetItem(). """Replace dict.get() by a call to PyDict_GetItem().
""" """
if len(args) == 2: if len(args) == 2:
...@@ -2338,7 +2339,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2338,7 +2339,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
return node return node
return self._substitute_method_call( return self._substitute_method_call(
node, "__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type, node, function,
"__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type,
'get', is_unbound_method, args, 'get', is_unbound_method, args,
may_return_none = True, may_return_none = True,
utility_code = load_c_utility("dict_getitem_default")) utility_code = load_c_utility("dict_getitem_default"))
...@@ -2351,7 +2353,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2351,7 +2353,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("is_safe_type", PyrexTypes.c_int_type, None), PyrexTypes.CFuncTypeArg("is_safe_type", PyrexTypes.c_int_type, None),
]) ])
def _handle_simple_method_dict_setdefault(self, node, args, is_unbound_method): def _handle_simple_method_dict_setdefault(self, node, function, args, is_unbound_method):
"""Replace dict.setdefault() by calls to PyDict_GetItem() and PyDict_SetItem(). """Replace dict.setdefault() by calls to PyDict_GetItem() and PyDict_SetItem().
""" """
if len(args) == 2: if len(args) == 2:
...@@ -2371,7 +2373,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2371,7 +2373,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node.pos, value=is_safe_type, constant_result=is_safe_type)) node.pos, value=is_safe_type, constant_result=is_safe_type))
return self._substitute_method_call( return self._substitute_method_call(
node, "__Pyx_PyDict_SetDefault", self.Pyx_PyDict_SetDefault_func_type, node, function,
"__Pyx_PyDict_SetDefault", self.Pyx_PyDict_SetDefault_func_type,
'setdefault', is_unbound_method, args, 'setdefault', is_unbound_method, args,
may_return_none=True, may_return_none=True,
utility_code=load_c_utility('dict_setdefault')) utility_code=load_c_utility('dict_setdefault'))
...@@ -2384,7 +2387,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2384,7 +2387,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None), PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
]) ])
def _inject_unicode_predicate(self, node, args, is_unbound_method): def _inject_unicode_predicate(self, node, function, args, is_unbound_method):
if is_unbound_method or len(args) != 1: if is_unbound_method or len(args) != 1:
return node return node
ustring = args[0] ustring = args[0]
...@@ -2392,7 +2395,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2392,7 +2395,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
not ustring.arg.type.is_unicode_char: not ustring.arg.type.is_unicode_char:
return node return node
uchar = ustring.arg uchar = ustring.arg
method_name = node.function.attribute method_name = function.attribute
if method_name == 'istitle': if method_name == 'istitle':
# istitle() doesn't directly map to Py_UNICODE_ISTITLE() # istitle() doesn't directly map to Py_UNICODE_ISTITLE()
utility_code = UtilityCode.load_cached( utility_code = UtilityCode.load_cached(
...@@ -2402,7 +2405,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2402,7 +2405,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
utility_code = None utility_code = None
function_name = 'Py_UNICODE_%s' % method_name.upper() function_name = 'Py_UNICODE_%s' % method_name.upper()
func_call = self._substitute_method_call( func_call = self._substitute_method_call(
node, function_name, self.PyUnicode_uchar_predicate_func_type, node, function,
function_name, self.PyUnicode_uchar_predicate_func_type,
method_name, is_unbound_method, [uchar], method_name, is_unbound_method, [uchar],
utility_code = utility_code) utility_code = utility_code)
if node.type.is_pyobject: if node.type.is_pyobject:
...@@ -2424,7 +2428,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2424,7 +2428,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None), PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
]) ])
def _inject_unicode_character_conversion(self, node, args, is_unbound_method): def _inject_unicode_character_conversion(self, function, node, args, is_unbound_method):
if is_unbound_method or len(args) != 1: if is_unbound_method or len(args) != 1:
return node return node
ustring = args[0] ustring = args[0]
...@@ -2432,10 +2436,11 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2432,10 +2436,11 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
not ustring.arg.type.is_unicode_char: not ustring.arg.type.is_unicode_char:
return node return node
uchar = ustring.arg uchar = ustring.arg
method_name = node.function.attribute method_name = function.attribute
function_name = 'Py_UNICODE_TO%s' % method_name.upper() function_name = 'Py_UNICODE_TO%s' % method_name.upper()
func_call = self._substitute_method_call( func_call = self._substitute_method_call(
node, function_name, self.PyUnicode_uchar_conversion_func_type, node, function,
function_name, self.PyUnicode_uchar_conversion_func_type,
method_name, is_unbound_method, [uchar]) method_name, is_unbound_method, [uchar])
if node.type.is_pyobject: if node.type.is_pyobject:
func_call = func_call.coerce_to_pyobject(self.current_env) func_call = func_call.coerce_to_pyobject(self.current_env)
...@@ -2451,7 +2456,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2451,7 +2456,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("keepends", PyrexTypes.c_bint_type, None), PyrexTypes.CFuncTypeArg("keepends", PyrexTypes.c_bint_type, None),
]) ])
def _handle_simple_method_unicode_splitlines(self, node, args, is_unbound_method): def _handle_simple_method_unicode_splitlines(self, function, node, args, is_unbound_method):
"""Replace unicode.splitlines(...) by a direct call to the """Replace unicode.splitlines(...) by a direct call to the
corresponding C-API function. corresponding C-API function.
""" """
...@@ -2461,7 +2466,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2461,7 +2466,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
self._inject_bint_default_argument(node, args, 1, False) self._inject_bint_default_argument(node, args, 1, False)
return self._substitute_method_call( return self._substitute_method_call(
node, "PyUnicode_Splitlines", self.PyUnicode_Splitlines_func_type, node, function,
"PyUnicode_Splitlines", self.PyUnicode_Splitlines_func_type,
'splitlines', is_unbound_method, args) 'splitlines', is_unbound_method, args)
PyUnicode_Split_func_type = PyrexTypes.CFuncType( PyUnicode_Split_func_type = PyrexTypes.CFuncType(
...@@ -2472,7 +2478,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2472,7 +2478,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
] ]
) )
def _handle_simple_method_unicode_split(self, node, args, is_unbound_method): def _handle_simple_method_unicode_split(self, node, function, args, is_unbound_method):
"""Replace unicode.split(...) by a direct call to the """Replace unicode.split(...) by a direct call to the
corresponding C-API function. corresponding C-API function.
""" """
...@@ -2485,7 +2491,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2485,7 +2491,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node, args, 2, PyrexTypes.c_py_ssize_t_type, "-1") node, args, 2, PyrexTypes.c_py_ssize_t_type, "-1")
return self._substitute_method_call( return self._substitute_method_call(
node, "PyUnicode_Split", self.PyUnicode_Split_func_type, node, function,
"PyUnicode_Split", self.PyUnicode_Split_func_type,
'split', is_unbound_method, args) 'split', is_unbound_method, args)
PyString_Tailmatch_func_type = PyrexTypes.CFuncType( PyString_Tailmatch_func_type = PyrexTypes.CFuncType(
...@@ -2498,17 +2505,17 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2498,17 +2505,17 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
], ],
exception_value = '-1') exception_value = '-1')
def _handle_simple_method_unicode_endswith(self, node, args, is_unbound_method): def _handle_simple_method_unicode_endswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'unicode', 'endswith', node, function, args, is_unbound_method, 'unicode', 'endswith',
unicode_tailmatch_utility_code, +1) unicode_tailmatch_utility_code, +1)
def _handle_simple_method_unicode_startswith(self, node, args, is_unbound_method): def _handle_simple_method_unicode_startswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'unicode', 'startswith', node, function, args, is_unbound_method, 'unicode', 'startswith',
unicode_tailmatch_utility_code, -1) unicode_tailmatch_utility_code, -1)
def _inject_tailmatch(self, node, args, is_unbound_method, type_name, def _inject_tailmatch(self, node, function, args, is_unbound_method, type_name,
method_name, utility_code, direction): method_name, utility_code, direction):
"""Replace unicode.startswith(...) and unicode.endswith(...) """Replace unicode.startswith(...) and unicode.endswith(...)
by a direct call to the corresponding C-API function. by a direct call to the corresponding C-API function.
...@@ -2524,7 +2531,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2524,7 +2531,8 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node.pos, value=str(direction), type=PyrexTypes.c_int_type)) node.pos, value=str(direction), type=PyrexTypes.c_int_type))
method_call = self._substitute_method_call( method_call = self._substitute_method_call(
node, "__Pyx_Py%s_Tailmatch" % type_name.capitalize(), node, function,
"__Pyx_Py%s_Tailmatch" % type_name.capitalize(),
self.PyString_Tailmatch_func_type, self.PyString_Tailmatch_func_type,
method_name, is_unbound_method, args, method_name, is_unbound_method, args,
utility_code = utility_code) utility_code = utility_code)
...@@ -2540,15 +2548,15 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2540,15 +2548,15 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
], ],
exception_value = '-2') exception_value = '-2')
def _handle_simple_method_unicode_find(self, node, args, is_unbound_method): def _handle_simple_method_unicode_find(self, node, function, args, is_unbound_method):
return self._inject_unicode_find( return self._inject_unicode_find(
node, args, is_unbound_method, 'find', +1) node, function, args, is_unbound_method, 'find', +1)
def _handle_simple_method_unicode_rfind(self, node, args, is_unbound_method): def _handle_simple_method_unicode_rfind(self, node, function, args, is_unbound_method):
return self._inject_unicode_find( return self._inject_unicode_find(
node, args, is_unbound_method, 'rfind', -1) node, function, args, is_unbound_method, 'rfind', -1)
def _inject_unicode_find(self, node, args, is_unbound_method, def _inject_unicode_find(self, node, function, args, is_unbound_method,
method_name, direction): method_name, direction):
"""Replace unicode.find(...) and unicode.rfind(...) by a """Replace unicode.find(...) and unicode.rfind(...) by a
direct call to the corresponding C-API function. direct call to the corresponding C-API function.
...@@ -2564,7 +2572,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2564,7 +2572,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node.pos, value=str(direction), type=PyrexTypes.c_int_type)) node.pos, value=str(direction), type=PyrexTypes.c_int_type))
method_call = self._substitute_method_call( method_call = self._substitute_method_call(
node, "PyUnicode_Find", self.PyUnicode_Find_func_type, node, function, "PyUnicode_Find", self.PyUnicode_Find_func_type,
method_name, is_unbound_method, args) method_name, is_unbound_method, args)
return method_call.coerce_to_pyobject(self.current_env()) return method_call.coerce_to_pyobject(self.current_env())
...@@ -2577,7 +2585,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2577,7 +2585,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
], ],
exception_value = '-1') exception_value = '-1')
def _handle_simple_method_unicode_count(self, node, args, is_unbound_method): def _handle_simple_method_unicode_count(self, node, function, args, is_unbound_method):
"""Replace unicode.count(...) by a direct call to the """Replace unicode.count(...) by a direct call to the
corresponding C-API function. corresponding C-API function.
""" """
...@@ -2590,7 +2598,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2590,7 +2598,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node, args, 3, PyrexTypes.c_py_ssize_t_type, "PY_SSIZE_T_MAX") node, args, 3, PyrexTypes.c_py_ssize_t_type, "PY_SSIZE_T_MAX")
method_call = self._substitute_method_call( method_call = self._substitute_method_call(
node, "PyUnicode_Count", self.PyUnicode_Count_func_type, node, function, "PyUnicode_Count", self.PyUnicode_Count_func_type,
'count', is_unbound_method, args) 'count', is_unbound_method, args)
return method_call.coerce_to_pyobject(self.current_env()) return method_call.coerce_to_pyobject(self.current_env())
...@@ -2602,7 +2610,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2602,7 +2610,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
PyrexTypes.CFuncTypeArg("maxcount", PyrexTypes.c_py_ssize_t_type, None), PyrexTypes.CFuncTypeArg("maxcount", PyrexTypes.c_py_ssize_t_type, None),
]) ])
def _handle_simple_method_unicode_replace(self, node, args, is_unbound_method): def _handle_simple_method_unicode_replace(self, node, function, args, is_unbound_method):
"""Replace unicode.replace(...) by a direct call to the """Replace unicode.replace(...) by a direct call to the
corresponding C-API function. corresponding C-API function.
""" """
...@@ -2613,7 +2621,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2613,7 +2621,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
node, args, 3, PyrexTypes.c_py_ssize_t_type, "-1") node, args, 3, PyrexTypes.c_py_ssize_t_type, "-1")
return self._substitute_method_call( return self._substitute_method_call(
node, "PyUnicode_Replace", self.PyUnicode_Replace_func_type, node, function, "PyUnicode_Replace", self.PyUnicode_Replace_func_type,
'replace', is_unbound_method, args) 'replace', is_unbound_method, args)
PyUnicode_AsEncodedString_func_type = PyrexTypes.CFuncType( PyUnicode_AsEncodedString_func_type = PyrexTypes.CFuncType(
...@@ -2634,7 +2642,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2634,7 +2642,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
_special_codecs = [ (name, codecs.getencoder(name)) _special_codecs = [ (name, codecs.getencoder(name))
for name in _special_encodings ] for name in _special_encodings ]
def _handle_simple_method_unicode_encode(self, node, args, is_unbound_method): def _handle_simple_method_unicode_encode(self, node, function, args, is_unbound_method):
"""Replace unicode.encode(...) by a direct C-API call to the """Replace unicode.encode(...) by a direct C-API call to the
corresponding codec. corresponding codec.
""" """
...@@ -2647,7 +2655,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2647,7 +2655,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
if len(args) == 1: if len(args) == 1:
null_node = ExprNodes.NullNode(node.pos) null_node = ExprNodes.NullNode(node.pos)
return self._substitute_method_call( return self._substitute_method_call(
node, "PyUnicode_AsEncodedString", node, function, "PyUnicode_AsEncodedString",
self.PyUnicode_AsEncodedString_func_type, self.PyUnicode_AsEncodedString_func_type,
'encode', is_unbound_method, [string_node, null_node, null_node]) 'encode', is_unbound_method, [string_node, null_node, null_node])
...@@ -2675,12 +2683,12 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2675,12 +2683,12 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
if codec_name is not None: if codec_name is not None:
encode_function = "PyUnicode_As%sString" % codec_name encode_function = "PyUnicode_As%sString" % codec_name
return self._substitute_method_call( return self._substitute_method_call(
node, encode_function, node, function, encode_function,
self.PyUnicode_AsXyzString_func_type, self.PyUnicode_AsXyzString_func_type,
'encode', is_unbound_method, [string_node]) 'encode', is_unbound_method, [string_node])
return self._substitute_method_call( return self._substitute_method_call(
node, "PyUnicode_AsEncodedString", node, function, "PyUnicode_AsEncodedString",
self.PyUnicode_AsEncodedString_func_type, self.PyUnicode_AsEncodedString_func_type,
'encode', is_unbound_method, 'encode', is_unbound_method,
[string_node, encoding_node, error_handling_node]) [string_node, encoding_node, error_handling_node])
...@@ -2714,7 +2722,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2714,7 +2722,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
_decode_cpp_string_func_type = None # lazy init _decode_cpp_string_func_type = None # lazy init
def _handle_simple_method_bytes_decode(self, node, args, is_unbound_method): def _handle_simple_method_bytes_decode(self, node, function, args, is_unbound_method):
"""Replace char*.decode() by a direct C-API call to the """Replace char*.decode() by a direct C-API call to the
corresponding codec, possibly resolving a slice on the char*. corresponding codec, possibly resolving a slice on the char*.
""" """
...@@ -2884,29 +2892,29 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2884,29 +2892,29 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
encoding = node = None encoding = node = None
return encoding, node return encoding, node
def _handle_simple_method_str_endswith(self, node, args, is_unbound_method): def _handle_simple_method_str_endswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'str', 'endswith', node, function, args, is_unbound_method, 'str', 'endswith',
str_tailmatch_utility_code, +1) str_tailmatch_utility_code, +1)
def _handle_simple_method_str_startswith(self, node, args, is_unbound_method): def _handle_simple_method_str_startswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'str', 'startswith', node, function, args, is_unbound_method, 'str', 'startswith',
str_tailmatch_utility_code, -1) str_tailmatch_utility_code, -1)
def _handle_simple_method_bytes_endswith(self, node, args, is_unbound_method): def _handle_simple_method_bytes_endswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'bytes', 'endswith', node, function, args, is_unbound_method, 'bytes', 'endswith',
bytes_tailmatch_utility_code, +1) bytes_tailmatch_utility_code, +1)
def _handle_simple_method_bytes_startswith(self, node, args, is_unbound_method): def _handle_simple_method_bytes_startswith(self, node, function, args, is_unbound_method):
return self._inject_tailmatch( return self._inject_tailmatch(
node, args, is_unbound_method, 'bytes', 'startswith', node, function, args, is_unbound_method, 'bytes', 'startswith',
bytes_tailmatch_utility_code, -1) bytes_tailmatch_utility_code, -1)
### helpers ### helpers
def _substitute_method_call(self, node, name, func_type, def _substitute_method_call(self, node, function, name, func_type,
attr_name, is_unbound_method, args=(), attr_name, is_unbound_method, args=(),
utility_code=None, is_temp=None, utility_code=None, is_temp=None,
may_return_none=ExprNodes.PythonCapiCallNode.may_return_none): may_return_none=ExprNodes.PythonCapiCallNode.may_return_none):
...@@ -2916,7 +2924,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2916,7 +2924,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
if is_unbound_method: if is_unbound_method:
self_arg = self_arg.as_none_safe_node( self_arg = self_arg.as_none_safe_node(
"descriptor '%s' requires a '%s' object but received a 'NoneType'", "descriptor '%s' requires a '%s' object but received a 'NoneType'",
format_args = [attr_name, node.function.obj.name]) format_args=[attr_name, function.obj.name])
else: else:
self_arg = self_arg.as_none_safe_node( self_arg = self_arg.as_none_safe_node(
"'NoneType' object has no attribute '%s'", "'NoneType' object has no attribute '%s'",
......
...@@ -37,7 +37,7 @@ class TestMethodDispatcherTransform(TransformTest): ...@@ -37,7 +37,7 @@ class TestMethodDispatcherTransform(TransformTest):
def test_builtin_method(self): def test_builtin_method(self):
calls = [0] calls = [0]
class Test(MethodDispatcherTransform): class Test(MethodDispatcherTransform):
def _handle_simple_method_dict_get(self, node, args, unbound): def _handle_simple_method_dict_get(self, node, func, args, unbound):
calls[0] += 1 calls[0] += 1
return node return node
...@@ -48,10 +48,10 @@ class TestMethodDispatcherTransform(TransformTest): ...@@ -48,10 +48,10 @@ class TestMethodDispatcherTransform(TransformTest):
def test_binop_method(self): def test_binop_method(self):
calls = {'bytes': 0, 'object': 0} calls = {'bytes': 0, 'object': 0}
class Test(MethodDispatcherTransform): class Test(MethodDispatcherTransform):
def _handle_simple_method_bytes___mul__(self, node, args, unbound): def _handle_simple_method_bytes___mul__(self, node, func, args, unbound):
calls['bytes'] += 1 calls['bytes'] += 1
return node return node
def _handle_simple_method_object___mul__(self, node, args, unbound): def _handle_simple_method_object___mul__(self, node, func, args, unbound):
calls['object'] += 1 calls['object'] += 1
return node return node
......
...@@ -517,7 +517,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -517,7 +517,7 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
node = self._dispatch_to_method_handler( node = self._dispatch_to_method_handler(
special_method_name, None, False, type_name, special_method_name, None, False, type_name,
node, [operand1, operand2], None) node, None, [operand1, operand2], None)
return node return node
def visit_UnopNode(self, node): def visit_UnopNode(self, node):
...@@ -532,7 +532,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -532,7 +532,7 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
node = self._dispatch_to_method_handler( node = self._dispatch_to_method_handler(
special_method_name, None, False, type_name, special_method_name, None, False, type_name,
node, [operand], None) node, None, [operand], None)
return node return node
### dispatch to specific handlers ### dispatch to specific handlers
...@@ -544,6 +544,22 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -544,6 +544,22 @@ class MethodDispatcherTransform(EnvTransform):
handler = getattr(self, '_handle_any_%s' % match_name, None) handler = getattr(self, '_handle_any_%s' % match_name, None)
return handler return handler
def _delegate_to_assigned_value(self, node, function, arg_list, kwargs):
assignment = function.cf_state[0]
value = assignment.rhs
if value.is_name:
if not value.entry or len(value.entry.cf_assignments) > 1:
# the variable might have been reassigned => play safe
return node
elif value.is_attribute:
if not value.obj.entry or len(value.obj.entry.cf_assignments) > 1:
# the underlying variable might have been reassigned => play safe
return node
else:
return node
return self._dispatch_to_handler(
node, value, arg_list, kwargs)
def _dispatch_to_handler(self, node, function, arg_list, kwargs): def _dispatch_to_handler(self, node, function, arg_list, kwargs):
if function.is_name: if function.is_name:
# we only consider functions that are either builtin # we only consider functions that are either builtin
...@@ -551,18 +567,24 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -551,18 +567,24 @@ class MethodDispatcherTransform(EnvTransform):
# into a C function call (defined in the builtin scope) # into a C function call (defined in the builtin scope)
if not function.entry: if not function.entry:
return node return node
is_builtin = function.entry.is_builtin or\ is_builtin = (
function.entry is self.current_env().builtin_scope().lookup_here(function.name) function.entry.is_builtin or
function.entry is self.current_env().builtin_scope().lookup_here(function.name))
if not is_builtin: if not is_builtin:
if function.cf_state and function.cf_state.is_single:
# we know the value of the variable
# => see if it's usable instead
return self._delegate_to_assigned_value(
node, function, arg_list, kwargs)
return node return node
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 node
if kwargs: if kwargs:
return function_handler(node, arg_list, kwargs) return function_handler(node, function, arg_list, kwargs)
else: else:
return function_handler(node, arg_list) return function_handler(node, function, arg_list)
elif function.is_attribute and function.type.is_pyobject: elif function.is_attribute and function.type.is_pyobject:
attr_name = function.attribute attr_name = function.attribute
self_arg = function.obj self_arg = function.obj
...@@ -582,13 +604,13 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -582,13 +604,13 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
return self._dispatch_to_method_handler( return self._dispatch_to_method_handler(
attr_name, self_arg, is_unbound_method, type_name, attr_name, self_arg, is_unbound_method, type_name,
node, arg_list, kwargs) node, function, arg_list, kwargs)
else: else:
return node return node
def _dispatch_to_method_handler(self, attr_name, self_arg, def _dispatch_to_method_handler(self, attr_name, self_arg,
is_unbound_method, type_name, is_unbound_method, type_name,
node, arg_list, kwargs): node, function, arg_list, kwargs):
method_handler = self._find_handler( method_handler = self._find_handler(
"method_%s_%s" % (type_name, attr_name), kwargs) "method_%s_%s" % (type_name, attr_name), kwargs)
if method_handler is None: if method_handler is None:
...@@ -601,9 +623,11 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -601,9 +623,11 @@ class MethodDispatcherTransform(EnvTransform):
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:
return method_handler(node, arg_list, is_unbound_method, kwargs) return method_handler(
node, function, arg_list, is_unbound_method, kwargs)
else: else:
return method_handler(node, arg_list, is_unbound_method) return method_handler(
node, function, arg_list, is_unbound_method)
class RecursiveNodeReplacer(VisitorTransform): class RecursiveNodeReplacer(VisitorTransform):
......
# mode: run
# tag: builtins
cimport cython
@cython.test_assert_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
def unbound_dict_get(d):
"""
>>> unbound_dict_get({})
>>> unbound_dict_get({1:2})
2
"""
get = dict.get
return get(d, 1)
@cython.test_assert_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
def bound_dict_get(dict d):
"""
>>> bound_dict_get({})
>>> bound_dict_get({1:2})
2
"""
get = d.get
return get(1)
@cython.test_fail_if_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
@cython.test_assert_path_exists(
'//ReturnStatNode//SimpleCallNode')
def bound_dict_get_reassign(dict d):
"""
>>> bound_dict_get_reassign({})
>>> bound_dict_get_reassign({1:2})
2
"""
get = d.get
d = {1: 3}
return get(1)
@cython.test_assert_path_exists(
'//PythonCapiCallNode//NameNode[@name="l"]')
def unbound_list_sort(list l):
"""
>>> unbound_list_sort([1, 3, 2])
[1, 2, 3]
>>> unbound_list_sort([1, 3, 2])
[1, 2, 3]
"""
sort = list.sort
sort(l)
return l
@cython.test_assert_path_exists(
'//PythonCapiCallNode//NameNode[@name="l"]')
def bound_list_sort(list l):
"""
>>> bound_list_sort([1, 3, 2])
[1, 2, 3]
>>> bound_list_sort([1, 3, 2])
[1, 2, 3]
"""
sort = l.sort
sort()
return l
@cython.test_fail_if_path_exists(
'//PythonCapiCallNode')
def bound_list_sort_reassign(list l):
"""
>>> bound_list_sort_reassign([1, 3, 2])
[3, 2, 1]
>>> bound_list_sort_reassign([1, 3, 2])
[3, 2, 1]
"""
sort = l.sort
l = [3, 2, 1]
sort()
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