Skip to content

Commit 6ec11c7

Browse files
committed
gh-152315: Pin first argument for missing self hint
The hint is computed after the surplus argument cleanup, which may close args[0] (when co_argcount is 0, all arguments are surplus). Pin a strong reference to the first argument before the cleanup and release it on all exit paths.
1 parent 57aca39 commit 6ec11c7

1 file changed

Lines changed: 13 additions & 3 deletions

File tree

Python/ceval.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,14 +1670,13 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co,
16701670

16711671
static int
16721672
suggest_missing_self(PyFunctionObject *func, PyCodeObject *co,
1673-
_PyStackRef const *args, Py_ssize_t argcount)
1673+
PyObject *first_argument, Py_ssize_t argcount)
16741674
{
16751675
/* Missing self shows up as exactly one extra positional argument. */
16761676
if ((co->co_argcount + 1) != argcount) {
16771677
return 0;
16781678
}
16791679

1680-
PyObject *first_argument = PyStackRef_AsPyObjectBorrow(args[0]);
16811680
if (first_argument == NULL || PyType_Check(first_argument)) {
16821681
// When first arg is NULL, it's not really about self
16831682
// If its a type object, then its a classmethod.
@@ -1791,6 +1790,15 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
17911790
kwdict = NULL;
17921791
}
17931792

1793+
/* Pin the first argument for the "missing self" hint: the surplus
1794+
argument cleanup below may close args[0] before the hint is computed.
1795+
The pin is only needed when the "too many positional arguments"
1796+
error is about to be raised. */
1797+
PyObject *first_argument = NULL;
1798+
if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
1799+
first_argument = Py_NewRef(PyStackRef_AsPyObjectBorrow(args[0]));
1800+
}
1801+
17941802
/* Copy all positional arguments into local variables */
17951803
Py_ssize_t j, n;
17961804
if (argcount > co->co_argcount) {
@@ -1935,7 +1943,7 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
19351943

19361944
/* Check the number of positional arguments */
19371945
if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) {
1938-
int missing_self_hint = suggest_missing_self(func, co, args, argcount);
1946+
int missing_self_hint = suggest_missing_self(func, co, first_argument, argcount);
19391947
too_many_positional(tstate, co, argcount, func->func_defaults, localsplus,
19401948
func->func_qualname, missing_self_hint);
19411949
goto fail_post_args;
@@ -1996,6 +2004,7 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
19962004
goto fail_post_args;
19972005
}
19982006
}
2007+
Py_XDECREF(first_argument);
19992008
return 0;
20002009

20012010
fail_pre_positional:
@@ -2012,6 +2021,7 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
20122021
}
20132022
/* fall through */
20142023
fail_post_args:
2024+
Py_XDECREF(first_argument);
20152025
return -1;
20162026
}
20172027

0 commit comments

Comments
 (0)