GH-100987: Allow objects other than code objects as the "executable" of an internal frame. (GH-105727)

* Add table describing possible executable classes for out-of-process debuggers.

* Remove shim code object creation code as it is no longer needed.

* Make lltrace a bit more robust w.r.t. non-standard frames.
This commit is contained in:
Mark Shannon 2023-06-14 13:46:37 +01:00 committed by GitHub
parent ad56340b66
commit 7199584ac8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 541 additions and 606 deletions

View File

@ -447,19 +447,6 @@ adaptive_counter_backoff(uint16_t counter) {
return adaptive_counter_bits(value, backoff);
}
/* Line array cache for tracing */
typedef struct _PyShimCodeDef {
const uint8_t *code;
int codelen;
int stacksize;
const char *cname;
} _PyShimCodeDef;
extern PyCodeObject *
_Py_MakeShimCode(const _PyShimCodeDef *code);
extern uint32_t _Py_next_func_version;

View File

@ -47,7 +47,7 @@ enum _frameowner {
};
typedef struct _PyInterpreterFrame {
PyCodeObject *f_code; /* Strong reference */
PyObject *f_executable; /* Strong reference */
struct _PyInterpreterFrame *previous;
PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
@ -73,20 +73,25 @@ typedef struct _PyInterpreterFrame {
} _PyInterpreterFrame;
#define _PyInterpreterFrame_LASTI(IF) \
((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
((int)((IF)->prev_instr - _PyCode_CODE(_PyFrame_GetCode(IF))))
static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) {
assert(PyCode_Check(f->f_executable));
return (PyCodeObject *)f->f_executable;
}
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
return f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus;
}
static inline PyObject *_PyFrame_StackPeek(_PyInterpreterFrame *f) {
assert(f->stacktop > f->f_code->co_nlocalsplus);
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
assert(f->localsplus[f->stacktop-1] != NULL);
return f->localsplus[f->stacktop-1];
}
static inline PyObject *_PyFrame_StackPop(_PyInterpreterFrame *f) {
assert(f->stacktop > f->f_code->co_nlocalsplus);
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
f->stacktop--;
return f->localsplus[f->stacktop];
}
@ -119,7 +124,7 @@ _PyFrame_Initialize(
PyObject *locals, PyCodeObject *code, int null_locals_from)
{
frame->f_funcobj = (PyObject *)func;
frame->f_code = (PyCodeObject *)Py_NewRef(code);
frame->f_executable = Py_NewRef(code);
frame->f_builtins = func->func_builtins;
frame->f_globals = func->func_globals;
frame->f_locals = locals;
@ -172,8 +177,11 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer)
static inline bool
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
{
if (frame->owner == FRAME_OWNED_BY_CSTACK) {
return true;
}
return frame->owner != FRAME_OWNED_BY_GENERATOR &&
frame->prev_instr < _PyCode_CODE(frame->f_code) + frame->f_code->_co_firsttraceable;
frame->prev_instr < _PyCode_CODE(_PyFrame_GetCode(frame)) + _PyFrame_GetCode(frame)->_co_firsttraceable;
}
static inline _PyInterpreterFrame *
@ -272,6 +280,14 @@ PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame)
return (PyGenObject *)(((char *)frame) - offset_in_gen);
}
#define PY_EXECUTABLE_KIND_SKIP 0
#define PY_EXECUTABLE_KIND_PY_FUNCTION 1
#define PY_EXECUTABLE_KIND_BUILTIN_FUNCTION 3
#define PY_EXECUTABLE_KIND_METHOD_DESCRIPTOR 4
#define PY_EXECUTABLE_KINDS 5
PyAPI_DATA(const PyTypeObject *) const PyUnstable_ExecutableKinds[PY_EXECUTABLE_KINDS+1];
#ifdef __cplusplus
}
#endif

View File

@ -565,7 +565,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(newline));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(open_br));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(percent));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(shim_name));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(type_params));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_STR(utf_8));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(CANCELLED));

View File

@ -51,7 +51,6 @@ struct _Py_global_strings {
STRUCT_FOR_STR(newline, "\n")
STRUCT_FOR_STR(open_br, "{")
STRUCT_FOR_STR(percent, "%")
STRUCT_FOR_STR(shim_name, "<shim>")
STRUCT_FOR_STR(type_params, ".type_params")
STRUCT_FOR_STR(utf_8, "utf-8")
} literals;

View File

@ -159,7 +159,6 @@ struct _is {
struct ast_state ast;
struct types_state types;
struct callable_cache callable_cache;
PyCodeObject *interpreter_trampoline;
_PyOptimizerObject *optimizer;
uint16_t optimizer_resume_threshold;
uint16_t optimizer_backedge_threshold;

View File

@ -557,7 +557,6 @@ extern "C" {
INIT_STR(newline, "\n"), \
INIT_STR(open_br, "{"), \
INIT_STR(percent, "%"), \
INIT_STR(shim_name, "<shim>"), \
INIT_STR(type_params, ".type_params"), \
INIT_STR(utf_8, "utf-8"), \
}

View File

@ -0,0 +1,4 @@
Allow objects other than code objects as the "executable" in internal
frames. In the long term, this can help tools like Cython and PySpy interact
more efficiently. In the shorter term, it allows us to perform some
optimizations more simply.

View File

@ -2316,76 +2316,3 @@ _PyStaticCode_Init(PyCodeObject *co)
}
#define MAX_CODE_UNITS_PER_LOC_ENTRY 8
PyCodeObject *
_Py_MakeShimCode(const _PyShimCodeDef *codedef)
{
PyObject *name = NULL;
PyObject *co_code = NULL;
PyObject *lines = NULL;
PyCodeObject *codeobj = NULL;
uint8_t *loc_table = NULL;
name = _PyUnicode_FromASCII(codedef->cname, strlen(codedef->cname));
if (name == NULL) {
goto cleanup;
}
co_code = PyBytes_FromStringAndSize(
(const char *)codedef->code, codedef->codelen);
if (co_code == NULL) {
goto cleanup;
}
int code_units = codedef->codelen / sizeof(_Py_CODEUNIT);
int loc_entries = (code_units + MAX_CODE_UNITS_PER_LOC_ENTRY - 1) /
MAX_CODE_UNITS_PER_LOC_ENTRY;
loc_table = PyMem_Malloc(loc_entries);
if (loc_table == NULL) {
PyErr_NoMemory();
goto cleanup;
}
for (int i = 0; i < loc_entries-1; i++) {
loc_table[i] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | 7;
code_units -= MAX_CODE_UNITS_PER_LOC_ENTRY;
}
assert(loc_entries > 0);
assert(code_units > 0 && code_units <= MAX_CODE_UNITS_PER_LOC_ENTRY);
loc_table[loc_entries-1] = 0x80 |
(PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1);
lines = PyBytes_FromStringAndSize((const char *)loc_table, loc_entries);
PyMem_Free(loc_table);
if (lines == NULL) {
goto cleanup;
}
_Py_DECLARE_STR(shim_name, "<shim>");
struct _PyCodeConstructor con = {
.filename = &_Py_STR(shim_name),
.name = name,
.qualname = name,
.flags = CO_NEWLOCALS | CO_OPTIMIZED,
.code = co_code,
.firstlineno = 1,
.linetable = lines,
.consts = (PyObject *)&_Py_SINGLETON(tuple_empty),
.names = (PyObject *)&_Py_SINGLETON(tuple_empty),
.localsplusnames = (PyObject *)&_Py_SINGLETON(tuple_empty),
.localspluskinds = (PyObject *)&_Py_SINGLETON(bytes_empty),
.argcount = 0,
.posonlyargcount = 0,
.kwonlyargcount = 0,
.stacksize = codedef->stacksize,
.exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty),
};
codeobj = _PyCode_New(&con);
cleanup:
Py_XDECREF(name);
Py_XDECREF(co_code);
Py_XDECREF(lines);
return codeobj;
}

View File

@ -642,6 +642,7 @@ _PyFrame_GetState(PyFrameObject *frame)
static int
frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))
{
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
if (p_new_lineno == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
@ -719,7 +720,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
new_lineno = (int)l_new_lineno;
if (new_lineno < f->f_frame->f_code->co_firstlineno) {
if (new_lineno < code->co_firstlineno) {
PyErr_Format(PyExc_ValueError,
"line %d comes before the current code block",
new_lineno);
@ -728,8 +729,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
/* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this
* should never overflow. */
int len = (int)Py_SIZE(f->f_frame->f_code);
int *lines = marklines(f->f_frame->f_code, len);
int len = (int)Py_SIZE(code);
int *lines = marklines(code, len);
if (lines == NULL) {
return -1;
}
@ -743,7 +744,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
return -1;
}
int64_t *stacks = mark_stacks(f->f_frame->f_code, len);
int64_t *stacks = mark_stacks(code, len);
if (stacks == NULL) {
PyMem_Free(lines);
return -1;
@ -788,7 +789,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
// in the new location. Rather than crashing or changing co_code, just bind
// None instead:
int unbound = 0;
for (int i = 0; i < f->f_frame->f_code->co_nlocalsplus; i++) {
for (int i = 0; i < code->co_nlocalsplus; i++) {
// Counting every unbound local is overly-cautious, but a full flow
// analysis (like we do in the compiler) is probably too expensive:
unbound += f->f_frame->localsplus[i] == NULL;
@ -801,7 +802,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
// Do this in a second pass to avoid writing a bunch of Nones when
// warnings are being treated as errors and the previous bit raises:
for (int i = 0; i < f->f_frame->f_code->co_nlocalsplus; i++) {
for (int i = 0; i < code->co_nlocalsplus; i++) {
if (f->f_frame->localsplus[i] == NULL) {
f->f_frame->localsplus[i] = Py_NewRef(Py_None);
unbound--;
@ -832,7 +833,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
/* Finally set the new lasti and return OK. */
f->f_lineno = 0;
f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr;
f->f_frame->prev_instr = _PyCode_CODE(code) + best_addr;
return 0;
}
@ -886,15 +887,15 @@ frame_dealloc(PyFrameObject *f)
}
Py_TRASHCAN_BEGIN(f, frame_dealloc);
PyCodeObject *co = NULL;
PyObject *co = NULL;
/* Kill all local variables including specials, if we own them */
if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data);
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
/* Don't clear code object until the end */
co = frame->f_code;
frame->f_code = NULL;
co = frame->f_executable;
frame->f_executable = NULL;
Py_CLEAR(frame->f_funcobj);
Py_CLEAR(frame->f_locals);
PyObject **locals = _PyFrame_GetLocalsArray(frame);
@ -968,7 +969,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{
Py_ssize_t res;
res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);
PyCodeObject *code = f->f_frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *);
return PyLong_FromSsize_t(res);
}
@ -980,7 +981,7 @@ static PyObject *
frame_repr(PyFrameObject *f)
{
int lineno = PyFrame_GetLineNumber(f);
PyCodeObject *code = f->f_frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
return PyUnicode_FromFormat(
"<frame at %p, file %R, line %d, code %S>",
f, code->co_filename, lineno, code->co_name);
@ -1102,7 +1103,7 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
// This only works when opcode is a non-quickened form:
assert(_PyOpcode_Deopt[opcode] == opcode);
int check_oparg = 0;
for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code);
for (_Py_CODEUNIT *instruction = _PyCode_CODE(_PyFrame_GetCode(frame));
instruction < frame->prev_instr; instruction++)
{
int check_opcode = _PyOpcode_Deopt[instruction->op.code];
@ -1128,7 +1129,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
{
// COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt
// here:
PyCodeObject *co = frame->f_code;
PyCodeObject *co = _PyFrame_GetCode(frame);
int lasti = _PyInterpreterFrame_LASTI(frame);
if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS
&& PyFunction_Check(frame->f_funcobj)))
@ -1145,7 +1146,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
frame->localsplus[offset + i] = Py_NewRef(o);
}
// COPY_FREE_VARS doesn't have inline CACHEs, either:
frame->prev_instr = _PyCode_CODE(frame->f_code);
frame->prev_instr = _PyCode_CODE(_PyFrame_GetCode(frame));
}
@ -1213,7 +1214,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
frame_init_get_vars(frame);
PyCodeObject *co = frame->f_code;
PyCodeObject *co = _PyFrame_GetCode(frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *value; // borrowed reference
if (!frame_get_var(frame, co, i, &value)) {
@ -1257,7 +1258,7 @@ PyFrame_GetVar(PyFrameObject *frame_obj, PyObject *name)
_PyInterpreterFrame *frame = frame_obj->f_frame;
frame_init_get_vars(frame);
PyCodeObject *co = frame->f_code;
PyCodeObject *co = _PyFrame_GetCode(frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *var_name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
if (!_PyUnicode_Equal(var_name, name)) {
@ -1331,7 +1332,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
return;
}
fast = _PyFrame_GetLocalsArray(frame);
co = frame->f_code;
co = _PyFrame_GetCode(frame);
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < co->co_nlocalsplus; i++) {
@ -1417,7 +1418,7 @@ PyFrame_GetCode(PyFrameObject *frame)
{
assert(frame != NULL);
assert(!_PyFrame_IsIncomplete(frame->f_frame));
PyCodeObject *code = frame->f_frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame->f_frame);
assert(code != NULL);
return (PyCodeObject*)Py_NewRef(code);
}

View File

@ -29,7 +29,7 @@ static const char *ASYNC_GEN_IGNORED_EXIT_MSG =
static inline PyCodeObject *
_PyGen_GetCode(PyGenObject *gen) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe);
return frame->f_code;
return _PyFrame_GetCode(frame);
}
PyCodeObject *
@ -957,7 +957,7 @@ static PyObject *
gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
PyObject *name, PyObject *qualname)
{
PyCodeObject *code = f->f_frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
int size = code->co_nlocalsplus + code->co_stacksize;
PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size);
if (gen == NULL) {
@ -1339,7 +1339,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
}
frame = current_frame;
for (int i = 0; i < frame_count; ++i) {
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
int line = PyUnstable_InterpreterFrame_GetLine(frame);
PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, line,
code->co_name);

View File

@ -10405,7 +10405,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co,
return -1;
}
assert(cframe->f_code->co_nlocalsplus > 0);
assert(_PyFrame_GetCode(cframe)->co_nlocalsplus > 0);
PyObject *firstarg = _PyFrame_GetLocalsArray(cframe)[0];
// The first argument might be a cell.
if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) {
@ -10498,7 +10498,7 @@ super_init_impl(PyObject *self, PyTypeObject *type, PyObject *obj) {
"super(): no current frame");
return -1;
}
int res = super_init_without_args(frame, frame->f_code, &type, &obj);
int res = super_init_without_args(frame, _PyFrame_GetCode(frame), &type, &obj);
if (res < 0) {
return -1;

View File

@ -898,7 +898,7 @@ setup_context(Py_ssize_t stack_level,
}
else {
globals = f->f_frame->f_globals;
*filename = Py_NewRef(f->f_frame->f_code->co_filename);
*filename = Py_NewRef(_PyFrame_GetCode(f->f_frame)->co_filename);
*lineno = PyFrame_GetLineNumber(f);
Py_DECREF(f);
}

View File

@ -137,8 +137,8 @@ dummy_func(
assert(tstate->cframe == &cframe);
assert(frame == cframe.current_frame);
/* Possibly combine this with eval breaker */
if (frame->f_code->_co_instrumentation_version != tstate->interp->monitoring_version) {
int err = _Py_Instrument(frame->f_code, tstate->interp);
if (_PyFrame_GetCode(frame)->_co_instrumentation_version != tstate->interp->monitoring_version) {
int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
ERROR_IF(err, error);
next_instr--;
}
@ -152,8 +152,8 @@ dummy_func(
* We need to check the eval breaker anyway, can we
* combine the instrument verison check and the eval breaker test?
*/
if (frame->f_code->_co_instrumentation_version != tstate->interp->monitoring_version) {
if (_Py_Instrument(frame->f_code, tstate->interp)) {
if (_PyFrame_GetCode(frame)->_co_instrumentation_version != tstate->interp->monitoring_version) {
if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) {
goto error;
}
next_instr--;
@ -666,8 +666,6 @@ dummy_func(
inst(INTERPRETER_EXIT, (retval --)) {
assert(frame == &entry_frame);
assert(_PyFrame_IsIncomplete(frame));
STACK_SHRINK(1); // Since we're not going to DISPATCH()
assert(EMPTY());
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
assert(tstate->cframe->current_frame == frame->previous);
@ -971,7 +969,7 @@ dummy_func(
if (oparg) {
PyObject *lasti = values[0];
if (PyLong_Check(lasti)) {
frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti);
frame->prev_instr = _PyCode_CODE(_PyFrame_GetCode(frame)) + PyLong_AsLong(lasti);
assert(!_PyErr_Occurred(tstate));
}
else {
@ -1385,7 +1383,7 @@ dummy_func(
// Can't use ERROR_IF here.
// Fortunately we don't need its superpower.
if (oldobj == NULL) {
format_exc_unbound(tstate, frame->f_code, oparg);
format_exc_unbound(tstate, _PyFrame_GetCode(frame), oparg);
goto error;
}
PyCell_SET(cell, NULL);
@ -1395,8 +1393,8 @@ dummy_func(
inst(LOAD_FROM_DICT_OR_DEREF, (class_dict -- value)) {
PyObject *name;
assert(class_dict);
assert(oparg >= 0 && oparg < frame->f_code->co_nlocalsplus);
name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, oparg);
assert(oparg >= 0 && oparg < _PyFrame_GetCode(frame)->co_nlocalsplus);
name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg);
if (PyDict_CheckExact(class_dict)) {
value = PyDict_GetItemWithError(class_dict, name);
if (value != NULL) {
@ -1422,7 +1420,7 @@ dummy_func(
PyObject *cell = GETLOCAL(oparg);
value = PyCell_GET(cell);
if (value == NULL) {
format_exc_unbound(tstate, frame->f_code, oparg);
format_exc_unbound(tstate, _PyFrame_GetCode(frame), oparg);
goto error;
}
Py_INCREF(value);
@ -1433,7 +1431,7 @@ dummy_func(
PyObject *cell = GETLOCAL(oparg);
value = PyCell_GET(cell);
if (value == NULL) {
format_exc_unbound(tstate, frame->f_code, oparg);
format_exc_unbound(tstate, _PyFrame_GetCode(frame), oparg);
ERROR_IF(true, error);
}
Py_INCREF(value);
@ -1448,7 +1446,7 @@ dummy_func(
inst(COPY_FREE_VARS, (--)) {
/* Copy closure variables to free variables */
PyCodeObject *co = frame->f_code;
PyCodeObject *co = _PyFrame_GetCode(frame);
assert(PyFunction_Check(frame->f_funcobj));
PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
assert(oparg == co->co_nfreevars);
@ -2175,7 +2173,8 @@ dummy_func(
};
inst(ENTER_EXECUTOR, (--)) {
_PyExecutorObject *executor = (_PyExecutorObject *)frame->f_code->co_executors->executors[oparg];
PyCodeObject *code = _PyFrame_GetCode(frame);
_PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg];
Py_INCREF(executor);
frame = executor->execute(executor, frame, stack_pointer);
if (frame == NULL) {
@ -2292,7 +2291,7 @@ dummy_func(
/* before: [obj]; after [getiter(obj)] */
if (PyCoro_CheckExact(iterable)) {
/* `iterable` is a coroutine */
if (!(frame->f_code->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
/* and it is used in a 'yield from' expression of a
regular generator. */
_PyErr_SetString(tstate, PyExc_TypeError,

View File

@ -129,6 +129,9 @@ lltrace_instruction(_PyInterpreterFrame *frame,
PyObject **stack_pointer,
_Py_CODEUNIT *next_instr)
{
if (frame->owner == FRAME_OWNED_BY_CSTACK) {
return;
}
/* This dump_stack() operation is risky, since the repr() of some
objects enters the interpreter recursively. It is also slow.
So you might want to comment it out. */
@ -137,7 +140,7 @@ lltrace_instruction(_PyInterpreterFrame *frame,
int opcode = next_instr->op.code;
const char *opname = _PyOpcode_OpName[opcode];
assert(opname != NULL);
int offset = (int)(next_instr - _PyCode_CODE(frame->f_code));
int offset = (int)(next_instr - _PyCode_CODE(_PyFrame_GetCode(frame)));
if (HAS_ARG((int)_PyOpcode_Deopt[opcode])) {
printf("%d: %s %d\n", offset * 2, opname, oparg);
}
@ -150,7 +153,7 @@ static void
lltrace_resume_frame(_PyInterpreterFrame *frame)
{
PyObject *fobj = frame->f_funcobj;
if (frame->owner == FRAME_OWNED_BY_CSTACK ||
if (!PyCode_Check(frame->f_executable) ||
fobj == NULL ||
!PyFunction_Check(fobj)
) {
@ -621,6 +624,13 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) {
tstate->py_recursion_remaining++;
}
static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = {
/* Put a NOP at the start, so that the IP points into
* the code, rather than before it */
{ .op.code = NOP, .op.arg = 0 },
{ .op.code = INTERPRETER_EXIT, .op.arg = 0 },
{ .op.code = RESUME, .op.arg = 0 }
};
/* Disable unused label warnings. They are handy for debugging, even
if computed gotos aren't used. */
@ -668,7 +678,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
cframe.previous = prev_cframe;
tstate->cframe = &cframe;
assert(tstate->interp->interpreter_trampoline != NULL);
#ifdef Py_DEBUG
/* Set these to invalid but identifiable values for debugging. */
entry_frame.f_funcobj = (PyObject*)0xaaa0;
@ -677,9 +686,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
entry_frame.f_globals = (PyObject*)0xaaa3;
entry_frame.f_builtins = (PyObject*)0xaaa4;
#endif
entry_frame.f_code = tstate->interp->interpreter_trampoline;
entry_frame.prev_instr =
_PyCode_CODE(tstate->interp->interpreter_trampoline);
entry_frame.f_executable = Py_None;
entry_frame.prev_instr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS;
entry_frame.stacktop = 0;
entry_frame.owner = FRAME_OWNED_BY_CSTACK;
entry_frame.return_offset = 0;
@ -701,7 +709,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
}
/* Because this avoids the RESUME,
* we need to update instrumentation */
_Py_Instrument(frame->f_code, tstate->interp);
_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
monitor_throw(tstate, frame, frame->prev_instr);
/* TO DO -- Monitor throw entry. */
goto resume_with_error;
@ -715,7 +723,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
/* Sets the above local variables from the frame */
#define SET_LOCALS_FROM_FRAME() \
assert(_PyInterpreterFrame_LASTI(frame) >= -1); \
/* Jump back to the last instruction executed... */ \
next_instr = frame->prev_instr + 1; \
stack_pointer = _PyFrame_GetStackPointer(frame);
@ -874,7 +881,7 @@ handle_eval_breaker:
opcode = next_instr->op.code;
_PyErr_Format(tstate, PyExc_SystemError,
"%U:%d: unknown opcode %d",
frame->f_code->co_filename,
_PyFrame_GetCode(frame)->co_filename,
PyUnstable_InterpreterFrame_GetLine(frame),
opcode);
goto error;
@ -889,7 +896,7 @@ unbound_local_error:
{
format_exc_check_arg(tstate, PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG,
PyTuple_GetItem(frame->f_code->co_localsplusnames, oparg)
PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg)
);
goto error;
}
@ -929,7 +936,7 @@ exception_unwind:
/* We can't use frame->f_lasti here, as RERAISE may have set it */
int offset = INSTR_OFFSET()-1;
int level, handler, lasti;
if (get_exception_handler(frame->f_code, offset, &level, &handler, &lasti) == 0) {
if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) {
// No handlers, so exit.
assert(_PyErr_Occurred(tstate));
@ -1523,12 +1530,12 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
assert(frame->owner == FRAME_OWNED_BY_THREAD);
// Make sure that this is, indeed, the top frame. We can't check this in
// _PyThreadState_PopFrame, since f_code is already cleared at that point:
assert((PyObject **)frame + frame->f_code->co_framesize ==
assert((PyObject **)frame + _PyFrame_GetCode(frame)->co_framesize ==
tstate->datastack_top);
tstate->c_recursion_remaining--;
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
_PyFrame_ClearExceptCode(frame);
Py_DECREF(frame->f_code);
Py_DECREF(frame->f_executable);
tstate->c_recursion_remaining++;
_PyThreadState_PopFrame(tstate, frame);
}
@ -2013,7 +2020,7 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
static inline int
no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
{
_PyCoMonitoringData *data = frame->f_code->_co_monitoring;
_PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring;
if (data) {
if (data->active_monitors.tools[event] == 0) {
return 1;
@ -2331,7 +2338,7 @@ PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
int result = cf->cf_flags != 0;
if (current_frame != NULL) {
const int codeflags = current_frame->f_code->co_flags;
const int codeflags = _PyFrame_GetCode(current_frame)->co_flags;
const int compilerflags = codeflags & PyCF_MASK;
if (compilerflags) {
result = 1;

View File

@ -138,13 +138,13 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* Code access macros */
/* The integer overflow is checked by an assertion below. */
#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(_PyFrame_GetCode(frame))))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = word.op.code; \
oparg = word.op.arg; \
} while (0)
#define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x))
#define JUMPTO(x) (next_instr = _PyCode_CODE(_PyFrame_GetCode(frame)) + (x))
#define JUMPBY(x) (next_instr += (x))
/* OpCode prediction macros
@ -182,7 +182,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* The stack can grow at most MAXINT deep, as co_nlocals and
co_stacksize are ints. */
#define STACK_LEVEL() ((int)(stack_pointer - _PyFrame_Stackbase(frame)))
#define STACK_SIZE() (frame->f_code->co_stacksize)
#define STACK_SIZE() (_PyFrame_GetCode(frame)->co_stacksize)
#define EMPTY() (STACK_LEVEL() == 0)
#define TOP() (stack_pointer[-1])
#define SECOND() (stack_pointer[-2])
@ -221,8 +221,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* Data access macros */
#define FRAME_CO_CONSTS (frame->f_code->co_consts)
#define FRAME_CO_NAMES (frame->f_code->co_names)
#define FRAME_CO_CONSTS (_PyFrame_GetCode(frame)->co_consts)
#define FRAME_CO_NAMES (_PyFrame_GetCode(frame)->co_names)
/* Local variable macros */
@ -270,6 +270,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
#define GLOBALS() frame->f_globals
#define BUILTINS() frame->f_builtins
#define LOCALS() frame->f_locals
#define CONSTS() _PyFrame_GetCode(frame)->co_consts
#define NAMES() _PyFrame_GetCode(frame)->co_names
#define DTRACE_FUNCTION_ENTRY() \
if (PyDTrace_FUNCTION_ENTRY_ENABLED()) { \

View File

@ -14,7 +14,7 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg)
Py_VISIT(frame->frame_obj);
Py_VISIT(frame->f_locals);
Py_VISIT(frame->f_funcobj);
Py_VISIT(frame->f_code);
Py_VISIT(_PyFrame_GetCode(frame));
/* locals */
PyObject **locals = _PyFrame_GetLocalsArray(frame);
int i = 0;
@ -31,7 +31,7 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame)
assert(frame->frame_obj == NULL);
PyObject *exc = PyErr_GetRaisedException();
PyFrameObject *f = _PyFrame_New_NoTrack(frame->f_code);
PyFrameObject *f = _PyFrame_New_NoTrack(_PyFrame_GetCode(frame));
if (f == NULL) {
Py_XDECREF(exc);
return NULL;
@ -65,7 +65,7 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame)
void
_PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest)
{
assert(src->stacktop >= src->f_code->co_nlocalsplus);
assert(src->stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus);
Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src;
memcpy(dest, src, size);
// Don't leave a dangling pointer to the old frame when creating generators
@ -81,7 +81,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT);
assert(frame->owner != FRAME_CLEARED);
Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame;
Py_INCREF(frame->f_code);
Py_INCREF(_PyFrame_GetCode(frame));
memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size);
frame = (_PyInterpreterFrame *)f->_f_frame_data;
f->f_frame = frame;
@ -89,7 +89,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
if (_PyFrame_IsIncomplete(frame)) {
// This may be a newly-created generator or coroutine frame. Since it's
// dead anyways, just pretend that the first RESUME ran:
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
}
assert(!_PyFrame_IsIncomplete(frame));
@ -149,7 +149,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
PyObject *
PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame)
{
PyObject *code = (PyObject *)frame->f_code;
PyObject *code = frame->f_executable;
Py_INCREF(code);
return code;
}
@ -164,5 +164,13 @@ int
PyUnstable_InterpreterFrame_GetLine(_PyInterpreterFrame *frame)
{
int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT);
return PyCode_Addr2Line(frame->f_code, addr);
return PyCode_Addr2Line(_PyFrame_GetCode(frame), addr);
}
const PyTypeObject *const PyUnstable_ExecutableKinds[PY_EXECUTABLE_KINDS+1] = {
[PY_EXECUTABLE_KIND_SKIP] = &_PyNone_Type,
[PY_EXECUTABLE_KIND_PY_FUNCTION] = &PyCode_Type,
[PY_EXECUTABLE_KIND_BUILTIN_FUNCTION] = &PyMethod_Type,
[PY_EXECUTABLE_KIND_METHOD_DESCRIPTOR] = &PyMethodDescr_Type,
[PY_EXECUTABLE_KINDS] = NULL,
};

File diff suppressed because it is too large Load Diff

View File

@ -936,7 +936,7 @@ call_instrumentation_vector(
}
assert(!_PyErr_Occurred(tstate));
assert(args[0] == NULL);
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(code->_co_instrumentation_version == tstate->interp->monitoring_version);
assert(is_version_up_to_date(code, tstate->interp));
assert(instrumentation_cross_checks(tstate->interp, code));
@ -1017,7 +1017,7 @@ _Py_call_instrumentation_jump(
assert(frame->prev_instr == instr);
/* Event should occur after the jump */
frame->prev_instr = target;
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
int to = (int)(target - _PyCode_CODE(code));
PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT));
if (to_obj == NULL) {
@ -1094,7 +1094,7 @@ int
_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *prev)
{
frame->prev_instr = instr;
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(is_version_up_to_date(code, tstate->interp));
assert(instrumentation_cross_checks(tstate->interp, code));
int i = (int)(instr - _PyCode_CODE(code));
@ -1162,7 +1162,7 @@ done:
int
_Py_call_instrumentation_instruction(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr)
{
PyCodeObject *code = frame->f_code;
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(is_version_up_to_date(code, tstate->interp));
assert(instrumentation_cross_checks(tstate->interp, code));
int offset = (int)(instr - _PyCode_CODE(code));
@ -1632,7 +1632,7 @@ instrument_all_executing_code_objects(PyInterpreterState *interp) {
_PyInterpreterFrame *frame = ts->cframe->current_frame;
while (frame) {
if (frame->owner != FRAME_OWNED_BY_CSTACK) {
if (_Py_Instrument(frame->f_code, interp)) {
if (_Py_Instrument(_PyFrame_GetCode(frame), interp)) {
return -1;
}
}

View File

@ -148,14 +148,14 @@ stopiteration_error(PyThreadState* tstate, PyObject *exc)
const char *msg = NULL;
if (PyErr_GivenExceptionMatches(exc, PyExc_StopIteration)) {
msg = "generator raised StopIteration";
if (frame->f_code->co_flags & CO_ASYNC_GENERATOR) {
if (_PyFrame_GetCode(frame)->co_flags & CO_ASYNC_GENERATOR) {
msg = "async generator raised StopIteration";
}
else if (frame->f_code->co_flags & CO_COROUTINE) {
else if (_PyFrame_GetCode(frame)->co_flags & CO_COROUTINE) {
msg = "coroutine raised StopIteration";
}
}
else if ((frame->f_code->co_flags & CO_ASYNC_GENERATOR) &&
else if ((_PyFrame_GetCode(frame)->co_flags & CO_ASYNC_GENERATOR) &&
PyErr_GivenExceptionMatches(exc, PyExc_StopAsyncIteration))
{
/* code in `gen` raised a StopAsyncIteration error:

View File

@ -244,7 +244,7 @@ sys_trace_line_func(
"Missing frame when calling trace function.");
return NULL;
}
assert(args[0] == (PyObject *)frame->f_frame->f_code);
assert(args[0] == (PyObject *)_PyFrame_GetCode(frame->f_frame));
return trace_line(tstate, self, frame, line);
}
@ -286,7 +286,6 @@ sys_trace_jump_func(
"Missing frame when calling trace function.");
return NULL;
}
assert(code == frame->f_frame->f_code);
if (!frame->f_trace_lines) {
Py_RETURN_NONE;
}

View File

@ -148,15 +148,17 @@ PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer)
_PyInterpreterFrame *
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
{
PyCodeObject *code = (PyCodeObject *)frame->f_executable;
assert(PyCode_Check(code));
PyInterpreterState *interp = PyInterpreterState_Get();
int index = get_executor_index(frame->f_code, src);
int index = get_executor_index(code, src);
if (index < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
return frame;
}
_PyOptimizerObject *opt = interp->optimizer;
_PyExecutorObject *executor;
int err = opt->optimize(opt, frame->f_code, dest, &executor);
int err = opt->optimize(opt, code, dest, &executor);
if (err <= 0) {
if (err < 0) {
return NULL;
@ -164,7 +166,7 @@ _PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNI
_PyFrame_SetStackPointer(frame, stack_pointer);
return frame;
}
insert_executor(frame->f_code, src, index, executor);
insert_executor(code, src, index, executor);
return executor->execute(executor, frame, stack_pointer);
}

View File

@ -335,7 +335,7 @@ py_trampoline_evaluator(PyThreadState *ts, _PyInterpreterFrame *frame,
perf_status == PERF_STATUS_NO_INIT) {
goto default_eval;
}
PyCodeObject *co = frame->f_code;
PyCodeObject *co = _PyFrame_GetCode(frame);
py_trampoline f = NULL;
assert(extra_code_index != -1);
int ret = _PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f);

View File

@ -737,22 +737,6 @@ pycore_init_types(PyInterpreterState *interp)
return _PyStatus_OK();
}
static const uint8_t INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = {
/* Put a NOP at the start, so that the IP points into
* the code, rather than before it */
NOP, 0,
INTERPRETER_EXIT, 0,
/* RESUME at end makes sure that the frame appears incomplete */
RESUME, 0
};
static const _PyShimCodeDef INTERPRETER_TRAMPOLINE_CODEDEF = {
INTERPRETER_TRAMPOLINE_INSTRUCTIONS,
sizeof(INTERPRETER_TRAMPOLINE_INSTRUCTIONS),
1,
"<interpreter trampoline>"
};
static PyStatus
pycore_init_builtins(PyThreadState *tstate)
{
@ -786,10 +770,6 @@ pycore_init_builtins(PyThreadState *tstate)
PyObject *object__getattribute__ = _PyType_Lookup(&PyBaseObject_Type, &_Py_ID(__getattribute__));
assert(object__getattribute__);
interp->callable_cache.object__getattribute__ = object__getattribute__;
interp->interpreter_trampoline = _Py_MakeShimCode(&INTERPRETER_TRAMPOLINE_CODEDEF);
if (interp->interpreter_trampoline == NULL) {
return _PyStatus_ERR("failed to create interpreter trampoline.");
}
if (_PyBuiltins_AddExceptions(bimod) < 0) {
return _PyStatus_ERR("failed to add exceptions to builtins");
}

View File

@ -892,7 +892,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
PyDict_Clear(interp->builtins);
Py_CLEAR(interp->sysdict);
Py_CLEAR(interp->builtins);
Py_CLEAR(interp->interpreter_trampoline);
if (tstate->interp == interp) {
/* We are now safe to fix tstate->_status.cleared. */

View File

@ -785,7 +785,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
}
int code_offset = tb->tb_lasti;
PyCodeObject* code = frame->f_frame->f_code;
PyCodeObject* code = _PyFrame_GetCode(frame->f_frame);
const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
int start_line;
@ -1164,7 +1164,7 @@ done:
static void
dump_frame(int fd, _PyInterpreterFrame *frame)
{
PyCodeObject *code = frame->f_code;
PyCodeObject *code =_PyFrame_GetCode(frame);
PUTS(fd, " File ");
if (code->co_filename != NULL
&& PyUnicode_Check(code->co_filename))

View File

@ -249,6 +249,7 @@ hashtable_compare_traceback(const void *key1, const void *key2)
static void
tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
{
assert(PyCode_Check(pyframe->f_executable));
frame->filename = &_Py_STR(anon_unknown);
int lineno = PyUnstable_InterpreterFrame_GetLine(pyframe);
if (lineno < 0) {
@ -256,7 +257,7 @@ tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
}
frame->lineno = (unsigned int)lineno;
PyObject *filename = pyframe->f_code->co_filename;
PyObject *filename = filename = ((PyCodeObject *)pyframe->f_executable)->co_filename;
if (filename == NULL) {
#ifdef TRACE_DEBUG

View File

@ -327,6 +327,7 @@ Parser/parser.c - soft_keywords -
Parser/tokenizer.c - type_comment_prefix -
Python/ast_opt.c fold_unaryop ops -
Python/ceval.c - binary_ops -
Python/ceval.c - _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS -
Python/codecs.c - Py_hexdigits -
Python/codecs.c - ucnhash_capi -
Python/codecs.c _PyCodecRegistry_Init methods -

Can't render this file because it has a wrong number of fields in line 4.

View File

@ -1009,6 +1009,7 @@ class PyFramePtr:
self._gdbval = gdbval
if not self.is_optimized_out():
try:
self.co = self._f_code()
self.co_name = self.co.pyop_field('co_name')
self.co_filename = self.co.pyop_field('co_filename')
@ -1017,6 +1018,9 @@ class PyFramePtr:
self.co_nlocals = int_from_int(self.co.field('co_nlocals'))
pnames = self.co.field('co_localsplusnames')
self.co_localsplusnames = PyTupleObjectPtr.from_pyobject_ptr(pnames)
self._is_code = True
except:
self._is_code = False
def is_optimized_out(self):
return self._gdbval.is_optimized_out
@ -1051,7 +1055,10 @@ class PyFramePtr:
return self._f_special("f_builtins")
def _f_code(self):
return self._f_special("f_code", PyCodeObjectPtr.from_pyobject_ptr)
return self._f_special("f_executable", PyCodeObjectPtr.from_pyobject_ptr)
def _f_executable(self):
return self._f_special("f_executable")
def _f_nlocalsplus(self):
return self._f_special("nlocalsplus", int_from_int)