From d737295f5a6b58176326f061b6d762ff9289ef81 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 21 Jul 2010 00:45:26 +0200 Subject: [PATCH] generate the expected (fast) code for isinstance() checks on builtin types --- Cython/Compiler/Optimize.py | 53 +++++++++++++++++++++++++++++++ tests/run/isinstance.pyx | 63 ++++++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 12 deletions(-) diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 9e661aa6..f25f401a 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -1839,6 +1839,59 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): is_temp = False) return ExprNodes.CastNode(node, PyrexTypes.py_object_type) + Py_type_check_func_type = PyrexTypes.CFuncType( + PyrexTypes.c_bint_type, [ + PyrexTypes.CFuncTypeArg("arg", PyrexTypes.py_object_type, None) + ]) + + def _handle_simple_function_isinstance(self, node, pos_args): + """Replace isinstance() checks against builtin types by the + corresponding C-API call. + """ + if len(pos_args) != 2: + return node + arg, types = pos_args + temp = None + if isinstance(types, ExprNodes.TupleNode): + types = types.args + arg = temp = UtilNodes.ResultRefNode(arg) + elif types.type is Builtin.type_type: + types = [types] + else: + return node + + tests = [] + test_nodes = [] + env = self.current_env() + for test_type_node in types: + if not test_type_node.entry: + return node + entry = env.lookup(test_type_node.entry.name) + if not entry or not entry.type or not entry.type.is_builtin_type: + return node + type_check_function = entry.type.type_check_function(exact=False) + if not type_check_function: + return node + if type_check_function not in tests: + tests.append(type_check_function) + test_nodes.append( + ExprNodes.PythonCapiCallNode( + test_type_node.pos, type_check_function, self.Py_type_check_func_type, + args = [arg], + is_temp = True, + )) + + def join_with_or(a,b, make_binop_node=ExprNodes.binop_node): + or_node = make_binop_node(node.pos, 'or', a, b) + or_node.type = PyrexTypes.c_bint_type + or_node.is_temp = True + return or_node + + test_node = reduce(join_with_or, test_nodes).coerce_to(node.type, env) + if temp is not None: + test_node = UtilNodes.EvalWithTempExprNode(temp, test_node) + return test_node + ### special methods Pyx_tp_new_func_type = PyrexTypes.CFuncType( diff --git a/tests/run/isinstance.pyx b/tests/run/isinstance.pyx index 4b36aa4e..75a9f536 100644 --- a/tests/run/isinstance.pyx +++ b/tests/run/isinstance.pyx @@ -1,9 +1,32 @@ + +cimport cython + cdef class A: pass -def test_all(): +@cython.test_assert_path_exists('//SimpleCallNode//SimpleCallNode') +@cython.test_fail_if_path_exists('//SimpleCallNode//PythonCapiCallNode', + '//PythonCapiCallNode//SimpleCallNode') +def test_non_optimised(): """ - >>> test_all() + >>> test_non_optimised() + True + """ + # Non-optimized + cdef object foo = A + assert isinstance(A(), foo) + assert isinstance(0, (int, long)) + assert not isinstance(u"xyz", (int, long)) + assert isinstance(complex(), complex) # FIXME: this should be optimised, too! + return True + +@cython.test_assert_path_exists('//PythonCapiCallNode', + '//PythonCapiCallNode//SimpleCallNode') +@cython.test_fail_if_path_exists('//SimpleCallNode//SimpleCallNode', + '//SimpleCallNode//PythonCapiCallNode') +def test_optimised(): + """ + >>> test_optimised() True """ new_type = type('a',(),{}) @@ -14,23 +37,39 @@ def test_all(): assert isinstance(int(), int) assert isinstance(long(), long) assert isinstance(float(), float) - assert isinstance(complex(), complex) assert isinstance(bytes(), bytes) assert isinstance(str(), str) assert isinstance(unicode(), unicode) assert isinstance(tuple(), tuple) assert isinstance(list(), list) assert isinstance(dict(), dict) -# if py_ver > (2, 3): -# assert isinstance(set(), set) + assert isinstance(set(), set) assert isinstance(slice(0), slice) + assert not isinstance(u"foo", int) assert isinstance(A, type) + return True + +@cython.test_assert_path_exists('//PythonCapiCallNode') +@cython.test_fail_if_path_exists('//SimpleCallNode//SimpleCallNode', + '//SimpleCallNode//PythonCapiCallNode', + '//TupleNode//NameNode') +def test_optimised_tuple(): + """ + >>> test_optimised_tuple() + True + """ + assert isinstance(bool(), (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice)) + assert isinstance(int(), (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice)) + assert isinstance(list(), (bool, int, long, float, bytes, str, unicode, tuple, list, dict, set, slice)) + return True + +@cython.test_assert_path_exists('//SimpleCallNode//SimpleCallNode') +@cython.test_fail_if_path_exists('//SimpleCallNode//PythonCapiCallNode', + '//PythonCapiCallNode//SimpleCallNode') +def test_custom(): + """ + >>> test_custom() + True + """ assert isinstance(A(), A) - assert not isinstance(u"foo", int) - - # Non-optimized - cdef object foo = A - assert isinstance(A(), foo) - assert isinstance(0, (int, long)) - assert not isinstance(u"xyz", (int, long)) return True -- 2.26.2