Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
104b9e0c
Commit
104b9e0c
authored
Feb 05, 2015
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix many custom mro() edge cases and improve code quality (#22735)
Patch by Eldar Abusalimov.
parent
9125fe2e
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
622 additions
and
254 deletions
+622
-254
Lib/test/test_descr.py
Lib/test/test_descr.py
+226
-1
Misc/ACKS
Misc/ACKS
+1
-0
Misc/NEWS
Misc/NEWS
+3
-0
Objects/typeobject.c
Objects/typeobject.c
+392
-253
No files found.
Lib/test/test_descr.py
View file @
104b9e0c
...
...
@@ -4996,11 +4996,236 @@ class SharedKeyTests(unittest.TestCase):
self
.
assertLess
(
sys
.
getsizeof
(
vars
(
b
)),
sys
.
getsizeof
({}))
class
DebugHelperMeta
(
type
):
"""
Sets default __doc__ and simplifies repr() output.
"""
def
__new__
(
mcls
,
name
,
bases
,
attrs
):
if
attrs
.
get
(
'__doc__'
)
is
None
:
attrs
[
'__doc__'
]
=
name
# helps when debugging with gdb
return
type
.
__new__
(
mcls
,
name
,
bases
,
attrs
)
def
__repr__
(
cls
):
return
repr
(
cls
.
__name__
)
class
MroTest
(
unittest
.
TestCase
):
"""
Regressions for some bugs revealed through
mcsl.mro() customization (typeobject.c: mro_internal()) and
cls.__bases__ assignment (typeobject.c: type_set_bases()).
"""
def
setUp
(
self
):
self
.
step
=
0
self
.
ready
=
False
def
step_until
(
self
,
limit
):
ret
=
(
self
.
step
<
limit
)
if
ret
:
self
.
step
+=
1
return
ret
def
test_incomplete_set_bases_on_self
(
self
):
"""
type_set_bases must be aware that type->tp_mro can be NULL.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
step_until
(
1
):
assert
cls
.
__mro__
is
None
cls
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_reent_set_bases_on_base
(
self
):
"""
Deep reentrancy must not over-decref old_mro.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
not
None
and
cls
.
__name__
==
'B'
:
# 4-5 steps are usually enough to make it crash somewhere
if
self
.
step_until
(
10
):
A
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B
(
A
):
pass
B
.
__bases__
+=
()
def
test_reent_set_bases_on_direct_base
(
self
):
"""
Similar to test_reent_set_bases_on_base, but may crash differently.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
base
=
cls
.
__bases__
[
0
]
if
base
is
not
object
:
if
self
.
step_until
(
5
):
base
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B
(
A
):
pass
class
C
(
B
):
pass
def
test_reent_set_bases_tp_base_cycle
(
self
):
"""
type_set_bases must check for an inheritance cycle not only through
MRO of the type, which may be not yet updated in case of reentrance,
but also through tp_base chain, which is assigned before diving into
inner calls to mro().
Otherwise, the following snippet can loop forever:
do {
// ...
type = type->tp_base;
} while (type != NULL);
Functions that rely on tp_base (like solid_base and PyType_IsSubtype)
would not be happy in that case, causing a stack overflow.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
:
if
cls
.
__name__
==
'B1'
:
B2
.
__bases__
=
(
B1
,)
if
cls
.
__name__
==
'B2'
:
B1
.
__bases__
=
(
B2
,)
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
self
.
ready
=
True
with
self
.
assertRaises
(
TypeError
):
B1
.
__bases__
+=
()
def
test_tp_subclasses_cycle_in_update_slots
(
self
):
"""
type_set_bases must check for reentrancy upon finishing its job
by updating tp_subclasses of old/new bases of the type.
Otherwise, an implicit inheritance cycle through tp_subclasses
can break functions that recurse on elements of that field
(like recurse_down_subclasses and mro_hierarchy) eventually
leading to a stack overflow.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
and
cls
.
__name__
==
'C'
:
self
.
ready
=
False
C
.
__bases__
=
(
B2
,)
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
class
C
(
A
):
pass
self
.
ready
=
True
C
.
__bases__
=
(
B1
,)
B1
.
__bases__
=
(
C
,)
self
.
assertEqual
(
C
.
__bases__
,
(
B2
,))
self
.
assertEqual
(
B2
.
__subclasses__
(),
[
C
])
self
.
assertEqual
(
B1
.
__subclasses__
(),
[])
self
.
assertEqual
(
B1
.
__bases__
,
(
C
,))
self
.
assertEqual
(
C
.
__subclasses__
(),
[
B1
])
def
test_tp_subclasses_cycle_error_return_path
(
self
):
"""
The same as test_tp_subclasses_cycle_in_update_slots, but tests
a code path executed on error (goto bail).
"""
class
E
(
Exception
):
pass
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
and
cls
.
__name__
==
'C'
:
if
C
.
__bases__
==
(
B2
,):
self
.
ready
=
False
else
:
C
.
__bases__
=
(
B2
,)
raise
E
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
class
C
(
A
):
pass
self
.
ready
=
True
with
self
.
assertRaises
(
E
):
C
.
__bases__
=
(
B1
,)
B1
.
__bases__
=
(
C
,)
self
.
assertEqual
(
C
.
__bases__
,
(
B2
,))
self
.
assertEqual
(
C
.
__mro__
,
tuple
(
type
.
mro
(
C
)))
def
test_incomplete_extend
(
self
):
"""
Extending an unitialized type with type->tp_mro == NULL must
throw a reasonable TypeError exception, instead of failing
with PyErr_BadInternalCall.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
None
and
cls
.
__name__
!=
'X'
:
with
self
.
assertRaises
(
TypeError
):
class
X
(
cls
):
pass
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_incomplete_super
(
self
):
"""
Attrubute lookup on a super object must be aware that
its target type can be uninitialized (type->tp_mro == NULL).
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
None
:
with
self
.
assertRaises
(
AttributeError
):
super
(
cls
,
cls
).
xxx
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_main
():
# Run all local test cases, with PTypesLongInitTest first.
support
.
run_unittest
(
PTypesLongInitTest
,
OperatorsTest
,
ClassPropertiesAndMethods
,
DictProxyTests
,
MiscTests
,
PicklingTests
,
SharedKeyTests
)
MiscTests
,
PicklingTests
,
SharedKeyTests
,
MroTest
)
if
__name__
==
"__main__"
:
test_main
()
Misc/ACKS
View file @
104b9e0c
...
...
@@ -16,6 +16,7 @@ Michael Abbott
Rajiv Abraham
David Abrahams
Marc Abramowitz
Eldar Abusalimov
Ron Adam
Anton Afanasyev
Ali Afshar
...
...
Misc/NEWS
View file @
104b9e0c
...
...
@@ -11,6 +11,9 @@ Release date: TBA
Core and Builtins
-----------------
- Issue #22735: Fix many edge cases (including crashes) involving custom mro()
implementations.
- Issue #22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
and PyObject_AsWriteBuffer().
...
...
Objects/typeobject.c
View file @
104b9e0c
...
...
@@ -539,9 +539,11 @@ type_get_bases(PyTypeObject *type, void *context)
}
static
PyTypeObject
*
best_base
(
PyObject
*
);
static
int
mro_internal
(
PyTypeObject
*
);
static
int
mro_internal
(
PyTypeObject
*
,
PyObject
**
);
static
int
type_is_subtype_base_chain
(
PyTypeObject
*
,
PyTypeObject
*
);
static
int
compatible_for_assignment
(
PyTypeObject
*
,
PyTypeObject
*
,
char
*
);
static
int
add_subclass
(
PyTypeObject
*
,
PyTypeObject
*
);
static
int
add_all_subclasses
(
PyTypeObject
*
type
,
PyObject
*
bases
);
static
void
remove_subclass
(
PyTypeObject
*
,
PyTypeObject
*
);
static
void
remove_all_subclasses
(
PyTypeObject
*
type
,
PyObject
*
bases
);
static
void
update_all_slots
(
PyTypeObject
*
);
...
...
@@ -551,131 +553,147 @@ static int update_subclasses(PyTypeObject *type, PyObject *name,
update_callback
callback
,
void
*
data
);
static
int
recurse_down_subclasses
(
PyTypeObject
*
type
,
PyObject
*
name
,
update_callback
callback
,
void
*
data
);
static
PyObject
*
type_subclasses
(
PyTypeObject
*
type
,
PyObject
*
ignored
);
static
int
mro_
subclasses
(
PyTypeObject
*
type
,
PyObject
*
temp
)
mro_
hierarchy
(
PyTypeObject
*
type
,
PyObject
*
temp
)
{
PyTypeObject
*
subclass
;
PyObject
*
ref
,
*
subclasses
,
*
old_mro
;
Py_ssize_t
i
;
int
res
;
PyObject
*
new_mro
,
*
old_mro
;
PyObject
*
tuple
;
PyObject
*
subclasses
;
Py_ssize_t
i
,
n
;
subclasses
=
type
->
tp_subclasses
;
if
(
subclasses
==
NULL
)
return
0
;
assert
(
PyDict_CheckExact
(
subclasses
))
;
i
=
0
;
res
=
mro_internal
(
type
,
&
old_mro
)
;
if
(
res
<=
0
)
/* error / reentrance */
return
res
;
new_mro
=
type
->
tp_mro
;
while
(
PyDict_Next
(
subclasses
,
&
i
,
NULL
,
&
ref
))
{
assert
(
PyWeakref_CheckRef
(
ref
));
subclass
=
(
PyTypeObject
*
)
PyWeakref_GET_OBJECT
(
ref
);
assert
(
subclass
!=
NULL
);
if
((
PyObject
*
)
subclass
==
Py_None
)
continue
;
assert
(
PyType_Check
(
subclass
));
old_mro
=
subclass
->
tp_mro
;
if
(
mro_internal
(
subclass
)
<
0
)
{
subclass
->
tp_mro
=
old_mro
;
return
-
1
;
}
else
{
PyObject
*
tuple
;
tuple
=
PyTuple_Pack
(
2
,
subclass
,
old_mro
);
Py_DECREF
(
old_mro
);
if
(
!
tuple
)
return
-
1
;
if
(
PyList_Append
(
temp
,
tuple
)
<
0
)
if
(
old_mro
!=
NULL
)
tuple
=
PyTuple_Pack
(
3
,
type
,
new_mro
,
old_mro
);
else
tuple
=
PyTuple_Pack
(
2
,
type
,
new_mro
);
if
(
tuple
!=
NULL
)
res
=
PyList_Append
(
temp
,
tuple
);
else
res
=
-
1
;
Py_XDECREF
(
tuple
);
if
(
res
<
0
)
{
type
->
tp_mro
=
old_mro
;
Py_DECREF
(
new_mro
);
return
-
1
;
Py_DECREF
(
tuple
);
}
if
(
mro_subclasses
(
subclass
,
temp
)
<
0
)
Py_XDECREF
(
old_mro
);
/* Obtain a copy of subclasses list to iterate over.
Otherwise type->tp_subclasses might be altered
in the middle of the loop, for example, through a custom mro(),
by invoking type_set_bases on some subclass of the type
which in turn calls remove_subclass/add_subclass on this type.
Finally, this makes things simple avoiding the need to deal
with dictionary iterators and weak references.
*/
subclasses
=
type_subclasses
(
type
,
NULL
);
if
(
subclasses
==
NULL
)
return
-
1
;
n
=
PyList_GET_SIZE
(
subclasses
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
PyTypeObject
*
subclass
;
subclass
=
(
PyTypeObject
*
)
PyList_GET_ITEM
(
subclasses
,
i
);
res
=
mro_hierarchy
(
subclass
,
temp
);
if
(
res
<
0
)
break
;
}
return
0
;
Py_DECREF
(
subclasses
);
return
res
;
}
static
int
type_set_bases
(
PyTypeObject
*
type
,
PyObject
*
value
,
void
*
context
)
type_set_bases
(
PyTypeObject
*
type
,
PyObject
*
new_bases
,
void
*
context
)
{
Py_ssize_t
i
;
int
r
=
0
;
PyObject
*
o
b
,
*
temp
;
int
res
=
0
;
PyObject
*
temp
;
PyObject
*
o
ld_bases
;
PyTypeObject
*
new_base
,
*
old_base
;
Py
Object
*
old_bases
,
*
old_mro
;
Py
_ssize_t
i
;
if
(
!
check_set_special_type_attr
(
type
,
value
,
"__bases__"
))
if
(
!
check_set_special_type_attr
(
type
,
new_bases
,
"__bases__"
))
return
-
1
;
if
(
!
PyTuple_Check
(
value
))
{
if
(
!
PyTuple_Check
(
new_bases
))
{
PyErr_Format
(
PyExc_TypeError
,
"can only assign tuple to %s.__bases__, not %s"
,
type
->
tp_name
,
Py_TYPE
(
value
)
->
tp_name
);
type
->
tp_name
,
Py_TYPE
(
new_bases
)
->
tp_name
);
return
-
1
;
}
if
(
PyTuple_GET_SIZE
(
value
)
==
0
)
{
if
(
PyTuple_GET_SIZE
(
new_bases
)
==
0
)
{
PyErr_Format
(
PyExc_TypeError
,
"can only assign non-empty tuple to %s.__bases__, not ()"
,
type
->
tp_name
);
return
-
1
;
}
for
(
i
=
0
;
i
<
PyTuple_GET_SIZE
(
value
);
i
++
)
{
ob
=
PyTuple_GET_ITEM
(
value
,
i
);
for
(
i
=
0
;
i
<
PyTuple_GET_SIZE
(
new_bases
);
i
++
)
{
PyObject
*
ob
;
PyTypeObject
*
base
;
ob
=
PyTuple_GET_ITEM
(
new_bases
,
i
);
if
(
!
PyType_Check
(
ob
))
{
PyErr_Format
(
PyExc_TypeError
,
"%s.__bases__ must be tuple of classes, not '%s'"
,
type
->
tp_name
,
Py_TYPE
(
ob
)
->
tp_name
);
return
-
1
;
}
if
(
PyType_IsSubtype
((
PyTypeObject
*
)
ob
,
type
))
{
base
=
(
PyTypeObject
*
)
ob
;
if
(
PyType_IsSubtype
(
base
,
type
)
||
/* In case of reentering here again through a custom mro()
the above check is not enough since it relies on
base->tp_mro which would gonna be updated inside
mro_internal only upon returning from the mro().
However, base->tp_base has already been assigned (see
below), which in turn may cause an inheritance cycle
through tp_base chain. And this is definitely
not what you want to ever happen. */
(
base
->
tp_mro
!=
NULL
&&
type_is_subtype_base_chain
(
base
,
type
)))
{
PyErr_SetString
(
PyExc_TypeError
,
"a __bases__ item causes an inheritance cycle"
);
return
-
1
;
}
}
new_base
=
best_base
(
value
);
if
(
!
new_base
)
new_base
=
best_base
(
new_bases
);
if
(
new_base
==
NULL
)
return
-
1
;
if
(
!
compatible_for_assignment
(
type
->
tp_base
,
new_base
,
"__bases__"
))
return
-
1
;
Py_INCREF
(
new_bases
);
Py_INCREF
(
new_base
);
Py_INCREF
(
value
);
old_bases
=
type
->
tp_bases
;
old_base
=
type
->
tp_base
;
old_mro
=
type
->
tp_mro
;
type
->
tp_bases
=
value
;
type
->
tp_bases
=
new_bases
;
type
->
tp_base
=
new_base
;
if
(
mro_internal
(
type
)
<
0
)
{
goto
bail
;
}
temp
=
PyList_New
(
0
);
if
(
!
temp
)
goto
bail
;
r
=
mro_subclasses
(
type
,
temp
);
if
(
r
<
0
)
{
for
(
i
=
0
;
i
<
PyList_Size
(
temp
);
i
++
)
{
PyTypeObject
*
cls
;
PyObject
*
mro
;
PyArg_UnpackTuple
(
PyList_GET_ITEM
(
temp
,
i
),
""
,
2
,
2
,
&
cls
,
&
mro
);
Py_INCREF
(
mro
);
ob
=
cls
->
tp_mro
;
cls
->
tp_mro
=
mro
;
Py_DECREF
(
ob
);
}
Py_DECREF
(
temp
);
if
(
temp
==
NULL
)
goto
bail
;
}
if
(
mro_hierarchy
(
type
,
temp
)
<
0
)
goto
undo
;
Py_DECREF
(
temp
);
/* Take no action in case if type->tp_bases has been replaced
through reentrance. */
if
(
type
->
tp_bases
==
new_bases
)
{
/* any base that was in __bases__ but now isn't, we
need to remove |type| from its tp_subclasses.
conversely, any class now in __bases__ that wasn't
...
...
@@ -683,35 +701,46 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
/* for now, sod that: just remove from all old_bases,
add to all new_bases */
remove_all_subclasses
(
type
,
old_bases
);
for
(
i
=
PyTuple_GET_SIZE
(
value
)
-
1
;
i
>=
0
;
i
--
)
{
ob
=
PyTuple_GET_ITEM
(
value
,
i
);
if
(
PyType_Check
(
ob
))
{
if
(
add_subclass
((
PyTypeObject
*
)
ob
,
type
)
<
0
)
r
=
-
1
;
}
}
res
=
add_all_subclasses
(
type
,
new_bases
);
update_all_slots
(
type
);
}
Py_DECREF
(
old_bases
);
Py_DECREF
(
old_base
);
Py_DECREF
(
old_mro
);
return
r
;
return
r
es
;
bail:
Py_DECREF
(
type
->
tp_bases
);
Py_DECREF
(
type
->
tp_base
);
if
(
type
->
tp_mro
!=
old_mro
)
{
Py_DECREF
(
type
->
tp_mro
);
undo:
for
(
i
=
PyList_GET_SIZE
(
temp
)
-
1
;
i
>=
0
;
i
--
)
{
PyTypeObject
*
cls
;
PyObject
*
new_mro
,
*
old_mro
=
NULL
;
PyArg_UnpackTuple
(
PyList_GET_ITEM
(
temp
,
i
),
""
,
2
,
3
,
&
cls
,
&
new_mro
,
&
old_mro
);
/* Do not rollback if cls has a newer version of MRO. */
if
(
cls
->
tp_mro
==
new_mro
)
{
Py_XINCREF
(
old_mro
);
cls
->
tp_mro
=
old_mro
;
Py_DECREF
(
new_mro
);
}
}
Py_DECREF
(
temp
);
bail:
if
(
type
->
tp_bases
==
new_bases
)
{
assert
(
type
->
tp_base
==
new_base
);
type
->
tp_bases
=
old_bases
;
type
->
tp_base
=
old_base
;
type
->
tp_mro
=
old_mro
;
Py_DECREF
(
new_bases
);
Py_DECREF
(
new_base
);
}
else
{
Py_DECREF
(
old_bases
);
Py_DECREF
(
old_base
);
}
return
-
1
;
}
...
...
@@ -1284,6 +1313,18 @@ static PyTypeObject *solid_base(PyTypeObject *type);
/* type test with subclassing support */
Py_LOCAL_INLINE
(
int
)
type_is_subtype_base_chain
(
PyTypeObject
*
a
,
PyTypeObject
*
b
)
{
do
{
if
(
a
==
b
)
return
1
;
a
=
a
->
tp_base
;
}
while
(
a
!=
NULL
);
return
(
b
==
&
PyBaseObject_Type
);
}
int
PyType_IsSubtype
(
PyTypeObject
*
a
,
PyTypeObject
*
b
)
{
...
...
@@ -1302,15 +1343,9 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
}
return
0
;
}
else
{
else
/* a is not completely initilized yet; follow tp_base */
do
{
if
(
a
==
b
)
return
1
;
a
=
a
->
tp_base
;
}
while
(
a
!=
NULL
);
return
b
==
&
PyBaseObject_Type
;
}
return
type_is_subtype_base_chain
(
a
,
b
);
}
/* Internal routines to do a method lookup in the type
...
...
@@ -1577,10 +1612,11 @@ consistent method resolution\norder (MRO) for bases");
}
static
int
pmerge
(
PyObject
*
acc
,
PyObject
*
to_merge
)
{
pmerge
(
PyObject
*
acc
,
PyObject
*
to_merge
)
{
int
res
=
0
;
Py_ssize_t
i
,
j
,
to_merge_size
,
empty_cnt
;
int
*
remain
;
int
ok
;
to_merge_size
=
PyList_GET_SIZE
(
to_merge
);
...
...
@@ -1618,15 +1654,13 @@ pmerge(PyObject *acc, PyObject* to_merge) {
candidate
=
PyList_GET_ITEM
(
cur_list
,
remain
[
i
]);
for
(
j
=
0
;
j
<
to_merge_size
;
j
++
)
{
PyObject
*
j_lst
=
PyList_GET_ITEM
(
to_merge
,
j
);
if
(
tail_contains
(
j_lst
,
remain
[
j
],
candidate
))
{
if
(
tail_contains
(
j_lst
,
remain
[
j
],
candidate
))
goto
skip
;
/* continue outer loop */
}
}
ok
=
PyList_Append
(
acc
,
candidate
);
if
(
ok
<
0
)
{
PyMem_FREE
(
remain
);
return
-
1
;
}
res
=
PyList_Append
(
acc
,
candidate
);
if
(
res
<
0
)
goto
out
;
for
(
j
=
0
;
j
<
to_merge_size
;
j
++
)
{
PyObject
*
j_lst
=
PyList_GET_ITEM
(
to_merge
,
j
);
if
(
remain
[
j
]
<
PyList_GET_SIZE
(
j_lst
)
&&
...
...
@@ -1638,22 +1672,25 @@ pmerge(PyObject *acc, PyObject* to_merge) {
skip:
;
}
if
(
empty_cnt
==
to_merge_size
)
{
PyMem_FREE
(
remain
);
return
0
;
}
if
(
empty_cnt
!=
to_merge_size
)
{
set_mro_error
(
to_merge
,
remain
);
res
=
-
1
;
}
out:
PyMem_FREE
(
remain
);
return
-
1
;
return
res
;
}
static
PyObject
*
mro_implementation
(
PyTypeObject
*
type
)
{
Py_ssize_t
i
,
n
;
int
ok
;
PyObject
*
bases
,
*
result
;
PyObject
*
result
=
NULL
;
PyObject
*
bases
;
PyObject
*
to_merge
,
*
bases_aslist
;
int
res
;
Py_ssize_t
i
,
n
;
if
(
type
->
tp_dict
==
NULL
)
{
if
(
PyType_Ready
(
type
)
<
0
)
...
...
@@ -1677,42 +1714,44 @@ mro_implementation(PyTypeObject *type)
return
NULL
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
PyObject
*
base
=
PyTuple_GET_ITEM
(
bases
,
i
);
PyObject
*
parentMRO
;
parentMRO
=
PySequence_List
(((
PyTypeObject
*
)
base
)
->
tp_mro
);
if
(
parentMRO
==
NULL
)
{
Py_DECREF
(
to_merge
);
return
NULL
;
PyTypeObject
*
base
;
PyObject
*
base_mro_aslist
;
base
=
(
PyTypeObject
*
)
PyTuple_GET_ITEM
(
bases
,
i
);
if
(
base
->
tp_mro
==
NULL
)
{
PyErr_Format
(
PyExc_TypeError
,
"Cannot extend an incomplete type '%.100s'"
,
base
->
tp_name
);
goto
out
;
}
PyList_SET_ITEM
(
to_merge
,
i
,
parentMRO
);
base_mro_aslist
=
PySequence_List
(
base
->
tp_mro
);
if
(
base_mro_aslist
==
NULL
)
goto
out
;
PyList_SET_ITEM
(
to_merge
,
i
,
base_mro_aslist
);
}
bases_aslist
=
PySequence_List
(
bases
);
if
(
bases_aslist
==
NULL
)
{
Py_DECREF
(
to_merge
);
return
NULL
;
}
if
(
bases_aslist
==
NULL
)
goto
out
;
/* This is just a basic sanity check. */
if
(
check_duplicates
(
bases_aslist
)
<
0
)
{
Py_DECREF
(
to_merge
);
Py_DECREF
(
bases_aslist
);
return
NULL
;
goto
out
;
}
PyList_SET_ITEM
(
to_merge
,
n
,
bases_aslist
);
result
=
Py_BuildValue
(
"[O]"
,
(
PyObject
*
)
type
);
if
(
result
==
NULL
)
{
Py_DECREF
(
to_merge
);
return
NULL
;
}
if
(
result
==
NULL
)
goto
out
;
ok
=
pmerge
(
result
,
to_merge
);
res
=
pmerge
(
result
,
to_merge
);
if
(
res
<
0
)
Py_CLEAR
(
result
);
out:
Py_DECREF
(
to_merge
);
if
(
ok
<
0
)
{
Py_DECREF
(
result
);
return
NULL
;
}
return
result
;
}
...
...
@@ -1726,59 +1765,133 @@ mro_external(PyObject *self)
}
static
int
mro_
internal
(
PyTypeObject
*
type
)
mro_
check
(
PyTypeObject
*
type
,
PyObject
*
mro
)
{
PyObject
*
mro
,
*
result
,
*
tuple
;
int
checkit
=
0
;
if
(
Py_TYPE
(
type
)
==
&
PyType_Type
)
{
result
=
mro_implementation
(
type
);
}
else
{
_Py_IDENTIFIER
(
mro
);
checkit
=
1
;
mro
=
lookup_method
((
PyObject
*
)
type
,
&
PyId_mro
);
if
(
mro
==
NULL
)
return
-
1
;
result
=
PyObject_CallObject
(
mro
,
NULL
);
Py_DECREF
(
mro
);
}
if
(
result
==
NULL
)
return
-
1
;
tuple
=
PySequence_Tuple
(
result
);
Py_DECREF
(
result
);
if
(
tuple
==
NULL
)
return
-
1
;
if
(
checkit
)
{
Py_ssize_t
i
,
len
;
PyObject
*
cls
;
PyTypeObject
*
solid
;
Py_ssize_t
i
,
n
;
solid
=
solid_base
(
type
);
len
=
PyTuple_GET_SIZE
(
tuple
);
n
=
PyTuple_GET_SIZE
(
mro
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
PyTypeObject
*
base
;
PyObject
*
tmp
;
for
(
i
=
0
;
i
<
len
;
i
++
)
{
PyTypeObject
*
t
;
cls
=
PyTuple_GET_ITEM
(
tuple
,
i
);
if
(
!
PyType_Check
(
cls
))
{
PyErr_Format
(
PyExc_TypeError
,
tmp
=
PyTuple_GET_ITEM
(
mro
,
i
);
if
(
!
PyType_Check
(
tmp
))
{
PyErr_Format
(
PyExc_TypeError
,
"mro() returned a non-class ('%.500s')"
,
Py_TYPE
(
cls
)
->
tp_name
);
Py_DECREF
(
tuple
);
Py_TYPE
(
tmp
)
->
tp_name
);
return
-
1
;
}
t
=
(
PyTypeObject
*
)
cls
;
if
(
!
PyType_IsSubtype
(
solid
,
solid_base
(
t
)))
{
PyErr_Format
(
PyExc_TypeError
,
base
=
(
PyTypeObject
*
)
tmp
;
if
(
!
PyType_IsSubtype
(
solid
,
solid_base
(
base
)))
{
PyErr_Format
(
PyExc_TypeError
,
"mro() returned base with unsuitable layout ('%.500s')"
,
t
->
tp_name
);
Py_DECREF
(
tuple
);
base
->
tp_name
);
return
-
1
;
}
}
return
0
;
}
/* Lookups an mcls.mro method, invokes it and checks the result (if needed,
in case of a custom mro() implementation).
Keep in mind that during execution of this function type->tp_mro
can be replaced due to possible reentrance (for example,
through type_set_bases):
- when looking up the mcls.mro attribute (it could be
a user-provided descriptor);
- from inside a custom mro() itself;
- through a finalizer of the return value of mro().
*/
static
PyObject
*
mro_invoke
(
PyTypeObject
*
type
)
{
PyObject
*
mro_result
;
PyObject
*
new_mro
;
int
custom
=
(
Py_TYPE
(
type
)
!=
&
PyType_Type
);
if
(
custom
)
{
_Py_IDENTIFIER
(
mro
);
PyObject
*
mro_meth
=
lookup_method
((
PyObject
*
)
type
,
&
PyId_mro
);
if
(
mro_meth
==
NULL
)
return
NULL
;
mro_result
=
PyObject_CallObject
(
mro_meth
,
NULL
);
Py_DECREF
(
mro_meth
);
}
else
{
mro_result
=
mro_implementation
(
type
);
}
if
(
mro_result
==
NULL
)
return
NULL
;
new_mro
=
PySequence_Tuple
(
mro_result
);
Py_DECREF
(
mro_result
);
if
(
new_mro
==
NULL
)
return
NULL
;
if
(
custom
&&
mro_check
(
type
,
new_mro
)
<
0
)
{
Py_DECREF
(
new_mro
);
return
NULL
;
}
return
new_mro
;
}
/* Calculates and assigns a new MRO to type->tp_mro.
Return values and invariants:
- Returns 1 if a new MRO value has been set to type->tp_mro due to
this call of mro_internal (no tricky reentrancy and no errors).
In case if p_old_mro argument is not NULL, a previous value
of type->tp_mro is put there, and the ownership of this
reference is transferred to a caller.
Otherwise, the previous value (if any) is decref'ed.
- Returns 0 in case when type->tp_mro gets changed because of
reentering here through a custom mro() (see a comment to mro_invoke).
In this case, a refcount of an old type->tp_mro is adjusted
somewhere deeper in the call stack (by the innermost mro_internal
or its caller) and may become zero upon returning from here.
This also implies that the whole hierarchy of subclasses of the type
has seen the new value and updated their MRO accordingly.
- Returns -1 in case of an error.
*/
static
int
mro_internal
(
PyTypeObject
*
type
,
PyObject
**
p_old_mro
)
{
PyObject
*
new_mro
,
*
old_mro
;
int
reent
;
/* Keep a reference to be able to do a reentrancy check below.
Don't let old_mro be GC'ed and its address be reused for
another object, like (suddenly!) a new tp_mro. */
old_mro
=
type
->
tp_mro
;
Py_XINCREF
(
old_mro
);
new_mro
=
mro_invoke
(
type
);
/* might cause reentrance */
reent
=
(
type
->
tp_mro
!=
old_mro
);
Py_XDECREF
(
old_mro
);
if
(
new_mro
==
NULL
)
return
-
1
;
if
(
reent
)
{
Py_DECREF
(
new_mro
);
return
0
;
}
type
->
tp_mro
=
tuple
;
type
->
tp_mro
=
new_mro
;
type_mro_modified
(
type
,
type
->
tp_mro
);
/* corner case: the super class might have been hidden
...
...
@@ -1787,7 +1900,12 @@ mro_internal(PyTypeObject *type)
PyType_Modified
(
type
);
return
0
;
if
(
p_old_mro
!=
NULL
)
*
p_old_mro
=
old_mro
;
/* transfer the ownership */
else
Py_XDECREF
(
old_mro
);
return
1
;
}
...
...
@@ -4661,9 +4779,8 @@ PyType_Ready(PyTypeObject *type)
}
/* Calculate method resolution order */
if
(
mro_internal
(
type
)
<
0
)
{
if
(
mro_internal
(
type
,
NULL
)
<
0
)
goto
error
;
}
/* Inherit special flags from dominant base */
if
(
type
->
tp_base
!=
NULL
)
...
...
@@ -4812,6 +4929,24 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
return
result
;
}
static
int
add_all_subclasses
(
PyTypeObject
*
type
,
PyObject
*
bases
)
{
int
res
=
0
;
if
(
bases
)
{
Py_ssize_t
i
;
for
(
i
=
0
;
i
<
PyTuple_GET_SIZE
(
bases
);
i
++
)
{
PyObject
*
base
=
PyTuple_GET_ITEM
(
bases
,
i
);
if
(
PyType_Check
(
base
)
&&
add_subclass
((
PyTypeObject
*
)
base
,
type
)
<
0
)
res
=
-
1
;
}
}
return
res
;
}
static
void
remove_subclass
(
PyTypeObject
*
base
,
PyTypeObject
*
type
)
{
...
...
@@ -6765,70 +6900,74 @@ static PyObject *
super_getattro
(
PyObject
*
self
,
PyObject
*
name
)
{
superobject
*
su
=
(
superobject
*
)
self
;
int
skip
=
su
->
obj_type
==
NULL
;
PyTypeObject
*
starttype
;
PyObject
*
mro
;
Py_ssize_t
i
,
n
;
starttype
=
su
->
obj_type
;
if
(
starttype
==
NULL
)
goto
skip
;
if
(
!
skip
)
{
/* We want __class__ to return the class of the super object
(i.e. super, or a subclass), not the class of su->obj. */
skip
=
(
PyUnicode_Check
(
name
)
&&
if
(
PyUnicode_Check
(
name
)
&&
PyUnicode_GET_LENGTH
(
name
)
==
9
&&
_PyUnicode_CompareWithId
(
name
,
&
PyId___class__
)
==
0
);
}
_PyUnicode_CompareWithId
(
name
,
&
PyId___class__
)
==
0
)
goto
skip
;
if
(
!
skip
)
{
PyObject
*
mro
,
*
res
,
*
tmp
,
*
dict
;
PyTypeObject
*
starttype
;
descrgetfunc
f
;
Py_ssize_t
i
,
n
;
starttype
=
su
->
obj_type
;
mro
=
starttype
->
tp_mro
;
if
(
mro
==
NULL
)
n
=
0
;
else
{
goto
skip
;
assert
(
PyTuple_Check
(
mro
));
n
=
PyTuple_GET_SIZE
(
mro
);
}
for
(
i
=
0
;
i
<
n
;
i
++
)
{
/* No need to check the last one: it's gonna be skipped anyway. */
for
(
i
=
0
;
i
+
1
<
n
;
i
++
)
{
if
((
PyObject
*
)(
su
->
type
)
==
PyTuple_GET_ITEM
(
mro
,
i
))
break
;
}
i
++
;
res
=
NULL
;
i
++
;
/* skip su->type (if any) */
if
(
i
>=
n
)
goto
skip
;
/* keep a strong reference to mro because starttype->tp_mro can be
replaced during PyDict_GetItem(dict, name) */
Py_INCREF
(
mro
);
for
(;
i
<
n
;
i
++
)
{
do
{
PyObject
*
res
,
*
tmp
,
*
dict
;
descrgetfunc
f
;
tmp
=
PyTuple_GET_ITEM
(
mro
,
i
);
if
(
PyType_Check
(
tmp
))
assert
(
PyType_Check
(
tmp
));
dict
=
((
PyTypeObject
*
)
tmp
)
->
tp_dict
;
else
continue
;
assert
(
dict
!=
NULL
&&
PyDict_Check
(
dict
));
res
=
PyDict_GetItem
(
dict
,
name
);
if
(
res
!=
NULL
)
{
Py_INCREF
(
res
);
f
=
Py_TYPE
(
res
)
->
tp_descr_get
;
if
(
f
!=
NULL
)
{
tmp
=
f
(
res
,
/* Only pass 'obj' param if
this is instance-mode super
(See SF ID #743627)
*/
(
su
->
obj
==
(
PyObject
*
)
su
->
obj_type
?
(
PyObject
*
)
NULL
:
su
->
obj
),
/* Only pass 'obj' param if this is instance-mode super
(See SF ID #743627) */
(
su
->
obj
==
(
PyObject
*
)
starttype
)
?
NULL
:
su
->
obj
,
(
PyObject
*
)
starttype
);
Py_DECREF
(
res
);
res
=
tmp
;
}
Py_DECREF
(
mro
);
return
res
;
}
}
i
++
;
}
while
(
i
<
n
);
Py_DECREF
(
mro
);
}
skip:
return
PyObject_GenericGetAttr
(
self
,
name
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment