Commit 80359387 authored by Stefan Behnel's avatar Stefan Behnel

optimise MyType.__new__(MyType) into a tp_new() slot call

parent 7138b1d2
...@@ -7,6 +7,7 @@ import UtilNodes ...@@ -7,6 +7,7 @@ import UtilNodes
import TypeSlots import TypeSlots
import Symtab import Symtab
import Options import Options
import Naming
from Code import UtilityCode from Code import UtilityCode
from StringEncoding import EncodedString, BytesLiteral from StringEncoding import EncodedString, BytesLiteral
...@@ -850,6 +851,9 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -850,6 +851,9 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
method_handler = self._find_handler( method_handler = self._find_handler(
"method_%s_%s" % (type_name, function.attribute), kwargs) "method_%s_%s" % (type_name, function.attribute), kwargs)
if method_handler is None:
method_handler = self._find_handler(
"methodany_%s" % function.attribute, kwargs)
if method_handler is None: if method_handler is None:
return node return node
if self_arg is not None: if self_arg is not None:
...@@ -1030,6 +1034,55 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1030,6 +1034,55 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
) )
return node return node
### special methods
def _handle_simple_methodany___new__(self, node, args, is_unbound_method):
"""Replace 'exttype.__new__(exttype)' by a call to exttype->tp_new()
"""
obj = node.function.obj
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:
# play safe
return node
if obj.type != Builtin.type_type or type_arg.type != Builtin.type_type:
# not a known type, play safe
return node
if not type_arg.type_entry or not obj.type_entry:
if obj.name != type_arg.name:
return node
# otherwise, we know it's a type and we know it's the same
# type for both - that should do
elif type_arg.type_entry != obj.type_entry:
# different types - do what CPython does at runtime
error(type_arg.pos, "%s.__new__(%s) is not safe, use %s.__new__()" %
(obj.type_entry.name, type_arg.type_entry.name,
type_arg.type_entry.name))
return node
return_type = None
if obj.type_entry:
return_type = obj.type_entry.type
if return_type is None and type_arg.type_entry:
return_type = type_arg.type_entry.type
if return_type is None:
return_type = PyrexTypes.py_object_type
# FIXME: we could potentially look up the actual tp_new C method
# of the extension type and call that instead of the generic slot
func_type = PyrexTypes.CFuncType(
return_type, [
PyrexTypes.CFuncTypeArg("type", PyrexTypes.py_object_type, None)
])
return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_tp_new", func_type,
args = args,
utility_code = tpnew_utility_code,
is_temp = node.is_temp
)
### methods of builtin types ### methods of builtin types
PyObject_Append_func_type = PyrexTypes.CFuncType( PyObject_Append_func_type = PyrexTypes.CFuncType(
...@@ -1317,6 +1370,16 @@ static INLINE PyObject* __Pyx_Type(PyObject* o) { ...@@ -1317,6 +1370,16 @@ static INLINE PyObject* __Pyx_Type(PyObject* o) {
) )
tpnew_utility_code = UtilityCode(
proto = """
static INLINE PyObject* __Pyx_tp_new(PyObject* type_obj) {
return (PyObject*) (((PyTypeObject*)(type_obj))->tp_new(
(PyTypeObject*)(type_obj), %(TUPLE)s, NULL));
}
""" % {'TUPLE' : Naming.empty_tuple}
)
class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
"""Calculate the result of constant expressions to store it in """Calculate the result of constant expressions to store it in
``expr_node.constant_result``, and replace trivial cases by their ``expr_node.constant_result``, and replace trivial cases by their
......
cdef class MyType:
def __init__(self):
print "INIT"
cdef class MySubType(MyType):
def __init__(self):
print "INIT"
cdef class MyOtherType:
def __init__(self):
print "INIT"
def make_new():
m = MyType.__new__(MyType)
m = MyOtherType.__new__(MyOtherType)
return m
def make_new_error():
m = MySubType.__new__(MyType)
m = MyOtherType.__new__(MyType)
m = MyOtherType.__new__(MySubType)
return m
_ERRORS = """
20:32: MySubType.__new__(MyType) is not safe, use MyType.__new__()
21:34: MyOtherType.__new__(MyType) is not safe, use MyType.__new__()
22:37: MyOtherType.__new__(MySubType) is not safe, use MySubType.__new__()
"""
cimport cython
cdef class MyType:
def __init__(self):
print "INIT"
cdef class MySubType(MyType):
def __init__(self):
print "INIT"
class MyClass(object):
def __init__(self):
print "INIT"
class MyTypeSubClass(MyType):
def __init__(self):
print "INIT"
# only this can be safely optimised:
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new():
"""
>>> isinstance(make_new(), MyType)
True
"""
m = MyType.__new__(MyType)
return m
@cython.test_assert_path_exists('//PythonCapiCallNode')
@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode')
def make_new_builtin():
"""
>>> isinstance(make_new_builtin(), tuple)
True
"""
m = dict.__new__(dict)
m = list.__new__(list)
m = tuple.__new__(tuple)
return m
# these cannot:
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_pyclass():
"""
>>> isinstance(make_new_pyclass(), MyTypeSubClass)
True
"""
m = MyClass.__new__(MyClass)
m = MyTypeSubClass.__new__(MyTypeSubClass)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_args(type t1=None, type t2=None):
"""
>>> isinstance(make_new_args(), MyType)
True
>>> isinstance(make_new_args(MyType), MyType)
True
>>> isinstance(make_new_args(MyType, MyType), MyType)
True
>>> isinstance(make_new_args(MyType, MySubType), MySubType)
Traceback (most recent call last):
TypeError: tp_new.MyType.__new__(tp_new.MySubType) is not safe, use tp_new.MySubType.__new__()
>>> isinstance(make_new_args(MySubType, MyType), MyType)
Traceback (most recent call last):
TypeError: tp_new.MySubType.__new__(tp_new.MyType): tp_new.MyType is not a subtype of tp_new.MySubType
"""
if t1 is None:
t1 = MyType
if t2 is None:
t2 = MyType
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_none(type t1=None, type t2=None):
"""
>>> isinstance(make_new_none(), MyType)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_none_typed(tuple t1=None, tuple t2=None):
"""
>>> isinstance(make_new_none(), MyType)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t1.__new__(t2)
return m
@cython.test_assert_path_exists('//SimpleCallNode/AttributeNode')
@cython.test_fail_if_path_exists('//PythonCapiCallNode')
def make_new_untyped(t):
"""
>>> make_new_untyped(None)
Traceback (most recent call last):
TypeError: object.__new__(X): X is not a type object (NoneType)
"""
m = t.__new__(t)
return m
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