mirror of
https://github.com/python/cpython
synced 2024-09-16 00:17:02 +00:00
bpo-42197: Don't create f_locals
dictionary unless we actually need it. (GH-32055)
* `PyFrame_FastToLocalsWithError` and `PyFrame_LocalsToFast` are no longer called during profile and tracing. (Contributed by Fabio Zadrozny) * Make accesses to a frame's `f_locals` safe from C code, not relying on calls to `PyFrame_FastToLocals` or `PyFrame_LocalsToFast`. * Document new `PyFrame_GetLocals` C-API function.
This commit is contained in:
parent
b68431fadb
commit
d7163bb35d
|
@ -41,6 +41,17 @@ See also :ref:`Reflection <reflection>`.
|
|||
.. versionadded:: 3.9
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame)
|
||||
|
||||
Get the *frame*'s ``f_locals`` attribute (:class:`dict`).
|
||||
|
||||
Return a :term:`strong reference`.
|
||||
|
||||
*frame* must not be ``NULL``.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
||||
.. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)
|
||||
|
||||
Return the line number that *frame* is currently executing.
|
||||
|
|
|
@ -969,7 +969,7 @@ Porting to Python 3.11
|
|||
Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use
|
||||
:c:func:`PyFrame_GetLineNumber` instead.
|
||||
* ``f_lineno``: use :c:func:`PyFrame_GetLineNumber`
|
||||
* ``f_locals``: use ``PyObject_GetAttrString((PyObject*)frame, "f_locals")``.
|
||||
* ``f_locals``: use :c:func:`PyFrame_GetLocals`.
|
||||
* ``f_stackdepth``: removed.
|
||||
* ``f_state``: no public API (renamed to ``f_frame.f_state``).
|
||||
* ``f_trace``: no public API.
|
||||
|
@ -983,6 +983,12 @@ Porting to Python 3.11
|
|||
computed lazily. The :c:func:`PyFrame_GetBack` function must be called
|
||||
instead.
|
||||
|
||||
Debuggers that accessed the ``f_locals`` directly *must* call
|
||||
`:c:func:`PyFrame_GetLocals` instead. They no longer need to call
|
||||
`:c:func:`PyFrame_FastToLocalsWithError` or :c:func:`PyFrame_LocalsToFast`,
|
||||
in fact they should not call those functions. The necessary updating of the
|
||||
frame is now managed by the virtual machine.
|
||||
|
||||
Code defining ``PyFrame_GetCode()`` on Python 3.8 and older::
|
||||
|
||||
#if PY_VERSION_HEX < 0x030900B1
|
||||
|
|
|
@ -23,3 +23,4 @@ PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
|
|||
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
|
||||
|
||||
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
|
||||
PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame);
|
||||
|
|
|
@ -15,6 +15,7 @@ struct _frame {
|
|||
int f_lineno; /* Current line number. Only valid if non-zero */
|
||||
char f_trace_lines; /* Emit per-line trace events? */
|
||||
char f_trace_opcodes; /* Emit per-opcode trace events? */
|
||||
char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */
|
||||
/* The frame data, if this frame object owns the frame */
|
||||
PyObject *_f_frame_data[1];
|
||||
};
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
:c:func:`PyFrame_FastToLocalsWithError` and :c:func:`PyFrame_LocalsToFast` are no longer
|
||||
called during profiling nor tracing. C code can access the ``f_locals`` attribute of :c:type:`PyFrameObject` by calling :c:func:`PyFrame_GetLocals`.
|
|
@ -840,6 +840,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
|
|||
f->f_trace = NULL;
|
||||
f->f_trace_lines = 1;
|
||||
f->f_trace_opcodes = 0;
|
||||
f->f_fast_as_locals = 0;
|
||||
f->f_lineno = 0;
|
||||
return f;
|
||||
}
|
||||
|
@ -1004,7 +1005,11 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
|
|||
PyErr_BadInternalCall();
|
||||
return -1;
|
||||
}
|
||||
return _PyFrame_FastToLocalsWithError(f->f_frame);
|
||||
int err = _PyFrame_FastToLocalsWithError(f->f_frame);
|
||||
if (err == 0) {
|
||||
f->f_fast_as_locals = 1;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1028,8 +1033,9 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
|
|||
PyObject *error_type, *error_value, *error_traceback;
|
||||
PyCodeObject *co;
|
||||
locals = frame->f_locals;
|
||||
if (locals == NULL)
|
||||
if (locals == NULL) {
|
||||
return;
|
||||
}
|
||||
fast = _PyFrame_GetLocalsArray(frame);
|
||||
co = frame->f_code;
|
||||
|
||||
|
@ -1088,13 +1094,12 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
|
|||
void
|
||||
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
||||
{
|
||||
if (f == NULL || _PyFrame_GetState(f) == FRAME_CLEARED) {
|
||||
return;
|
||||
if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) {
|
||||
_PyFrame_LocalsToFast(f->f_frame, clear);
|
||||
f->f_fast_as_locals = 0;
|
||||
}
|
||||
_PyFrame_LocalsToFast(f->f_frame, clear);
|
||||
}
|
||||
|
||||
|
||||
PyCodeObject *
|
||||
PyFrame_GetCode(PyFrameObject *frame)
|
||||
{
|
||||
|
@ -1118,6 +1123,12 @@ PyFrame_GetBack(PyFrameObject *frame)
|
|||
return back;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
PyFrame_GetLocals(PyFrameObject *frame)
|
||||
{
|
||||
return frame_getlocals(frame, NULL);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
_PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals)
|
||||
{
|
||||
|
|
|
@ -924,15 +924,19 @@ static PyObject *
|
|||
call_trampoline(PyThreadState *tstate, PyObject* callback,
|
||||
PyFrameObject *frame, int what, PyObject *arg)
|
||||
{
|
||||
if (PyFrame_FastToLocalsWithError(frame) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *stack[3];
|
||||
stack[0] = (PyObject *)frame;
|
||||
stack[1] = whatstrings[what];
|
||||
stack[2] = (arg != NULL) ? arg : Py_None;
|
||||
|
||||
/* Discard any previous modifications the frame's fast locals */
|
||||
if (frame->f_fast_as_locals) {
|
||||
if (PyFrame_FastToLocalsWithError(frame) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* call the Python-level function */
|
||||
PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3);
|
||||
|
||||
|
|
Loading…
Reference in a new issue