Commit f7a50b7d authored by Stefan Behnel's avatar Stefan Behnel

implement tp_new() optimisation with args/kwargs

parent 4b080588
......@@ -2142,14 +2142,22 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
Pyx_tp_new_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("type", Builtin.type_type, None)
PyrexTypes.CFuncTypeArg("type", Builtin.type_type, None),
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
])
def _handle_simple_slot__new__(self, node, args, is_unbound_method):
"""Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new()
Pyx_tp_new_kwargs_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("type", Builtin.type_type, None),
PyrexTypes.CFuncTypeArg("args", Builtin.tuple_type, None),
PyrexTypes.CFuncTypeArg("kwargs", Builtin.dict_type, None),
])
def _handle_any_slot__new__(self, node, args, is_unbound_method, kwargs=None):
"""Replace 'exttype.__new__(exttype, ...)' by a call to exttype->tp_new()
"""
obj = node.function.obj
if not is_unbound_method or len(args) != 1:
if not is_unbound_method or len(args) < 1:
return node
type_arg = args[0]
if not obj.is_name or not type_arg.is_name:
......@@ -2177,11 +2185,24 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
type_arg = type_arg.as_none_safe_node(
"object.__new__(X): X is not a type object (NoneType)")
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
args=[type_arg],
utility_code=UtilityCode.load_cached('tp_new', 'ObjectHandling.c'),
is_temp=node.is_temp
args_tuple = ExprNodes.TupleNode(node.pos, args=args[1:])
args_tuple = args_tuple.analyse_types(
self.current_env(), skip_children=True)
utility_code = UtilityCode.load_cached('tp_new', 'ObjectHandling.c')
if kwargs:
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new_kwargs", self.Pyx_tp_new_kwargs_func_type,
args=[type_arg, args_tuple, kwargs],
utility_code=utility_code,
is_temp=node.is_temp
)
else:
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new", self.Pyx_tp_new_func_type,
args=[type_arg, args_tuple],
utility_code=utility_code,
is_temp=node.is_temp
)
### methods of builtin types
......
......@@ -653,9 +653,9 @@ bad:
/////////////// tp_new.proto ///////////////
//@substitute: naming
static CYTHON_INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
#define __Pyx_tp_new(type_obj, args) __Pyx_tp_new_kwargs(type_obj, args, NULL)
static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* args, PyObject* kwargs) {
return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
(PyTypeObject*)(type_obj), $empty_tuple, NULL));
(PyTypeObject*)(type_obj), args, kwargs));
}
......@@ -2,28 +2,32 @@
cimport cython
cdef class MyType:
def __cinit__(self):
cdef public args, kwargs
def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT"
def __init__(self):
def __init__(self, *args, **kwargs):
print "INIT"
cdef class MySubType(MyType):
def __cinit__(self):
def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT(SUB)"
def __init__(self):
def __init__(self, *args, **kwargs):
print "INIT"
class MyClass(object):
def __cinit__(self):
def __cinit__(self, *args, **kwargs):
self.args, self.kwargs = args, kwargs
print "CINIT"
def __init__(self):
def __init__(self, *args, **kwargs):
print "INIT"
class MyTypeSubClass(MyType):
def __cinit__(self):
def __cinit__(self, *args, **kwargs):
# not called: Python class!
print "CINIT(PYSUB)"
def __init__(self):
def __init__(self, *args, **kwargs):
print "INIT"
# only these can be safely optimised:
......@@ -51,6 +55,36 @@ def make_new_typed_target():
m = MyType.__new__(MyType)
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_with_args():
"""
>>> isinstance(make_new_with_args(), MyType)
CINIT
(1, 2, 3)
{}
True
"""
m = MyType.__new__(MyType, 1, 2 ,3)
print m.args
print m.kwargs
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_with_args_kwargs():
"""
>>> isinstance(make_new_with_args_kwargs(), MyType)
CINIT
(1, 2, 3)
{'a': 4}
True
"""
m = MyType.__new__(MyType, 1, 2 ,3, a=4)
print m.args
print m.kwargs
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_builtin():
......
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