Repository navigation
Expand file tree
/
Copy pathpycore_frame.h
More file actions
254 lines (217 loc) · 7.95 KB
/
Copy pathpycore_frame.h
File metadata and controls
254 lines (217 loc) · 7.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#ifndef Py_INTERNAL_FRAME_H
#define Py_INTERNAL_FRAME_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
/* Starting in CPython 3.11, CPython separates the frame state between the
* full frame objects exposed by the Python and C runtime state introspection
* APIs, and internal lighter weight frame data structs, which are simple C
* structures owned by either the interpreter eval loop (while executing
* ordinary functions), by a generator or coroutine object (for frames that
* are able to be suspended), or by their corresponding full frame object (if
* a state instrospection API has been invoked and the full frame object has
* taken responsibility for the lifecycle of the frame data storage).
*
* This split storage eliminates a lot of allocation and deallocation of full
* Python objects during code execution, providing a significant speed gain
* over the previous approach of using full Python objects for both
* introspection and code execution.
*
* Field naming conventions:
*
* * full frame object fields have an "f_*" prefix
* * frame data struct fields have no prefix
*
* Local variable and function argument naming conventions:
*
* * "frame", "f", and "frameobj" are used for full frame objects
* * Exception: "current_frame" in the thread state cframe struct is a frame data struct
* * "fdata" is used for frame data structs
*
* Function/macro naming conventions:
*
* * "PyFrame_*" functions accept a full frame object
* * "_PyFrame_*" functions accept either a full frame object or a frame
* data struct. Check the specific function signatures for details.
* * Other public C API functions that relate to frames only accept full
* frame objects
* * Other private C API functions that relate to frames may accept either a
* full frame object or a frame data struct. Check the specific function
* signatures for details
*
* Function return types:
* * Public C API functions will only ever return full frame objects
* * Private C API functions with an underscore prefix may return frame
* data structs instead
*/
struct _frame {
PyObject_HEAD
PyFrameObject *f_back; /* previous frame, or NULL */
struct _Py_framedata *f_fdata; /* points to the frame data */
PyObject *f_trace; /* Trace function */
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_owns_frame; /* This frame owns the frame */
/* The frame data, if this frame object owns the frame */
struct _Py_framedata *_f_owned_fdata[1];
};
extern PyFrameObject* _PyFrame_New_NoTrack(PyCodeObject *code);
/* runtime lifecycle */
extern void _PyFrame_Fini(PyInterpreterState *interp);
/* other API */
/* These values are chosen so that the inline functions below all
* compare fdata->state to zero while keeping the property that most
* state transitions move to a higher value frame state.
*/
enum _framestate {
FRAME_CREATED = -2,
FRAME_SUSPENDED = -1,
FRAME_EXECUTING = 0,
FRAME_RETURNED = 1,
FRAME_UNWINDING = 2,
FRAME_RAISED = 3,
FRAME_CLEARED = 4
};
typedef signed char PyFrameState;
/*
frame->lasti refers to the index of the last instruction,
unless it's -1 in which case next_instr should be first_instr.
*/
typedef struct _Py_framedata {
PyFunctionObject *func; /* Strong reference */
PyObject *globals; /* Borrowed reference */
PyObject *builtins; /* Borrowed reference */
PyObject *locals; /* Strong reference, may be NULL */
PyCodeObject *code; /* Strong reference */
PyFrameObject *frame_obj; /* Strong reference, may be NULL */
struct _Py_framedata *previous;
int lasti; /* Last instruction if called */
int stacktop; /* Offset of TOS from localsplus */
PyFrameState state; /* What state the frame is in */
bool is_entry; // Whether this is the "root" frame for the current _PyCFrame.
bool is_generator;
PyObject *localsplus[1];
} _Py_framedata;
static inline int _PyFrame_IsRunnable(_Py_framedata *f) {
return f->state < FRAME_EXECUTING;
}
static inline int _PyFrame_IsExecuting(_Py_framedata *f) {
return f->state == FRAME_EXECUTING;
}
static inline int _PyFrameHasCompleted(_Py_framedata *f) {
return f->state > FRAME_EXECUTING;
}
static inline PyObject **_PyFrame_Stackbase(_Py_framedata *f) {
return f->localsplus + f->code->co_nlocalsplus;
}
static inline PyObject *_PyFrame_StackPeek(_Py_framedata *f) {
assert(f->stacktop > f->code->co_nlocalsplus);
assert(f->localsplus[f->stacktop-1] != NULL);
return f->localsplus[f->stacktop-1];
}
static inline PyObject *_PyFrame_StackPop(_Py_framedata *f) {
assert(f->stacktop > f->code->co_nlocalsplus);
f->stacktop--;
return f->localsplus[f->stacktop];
}
static inline void _PyFrame_StackPush(_Py_framedata *fdata, PyObject *value) {
fdata->localsplus[fdata->stacktop] = value;
fdata->stacktop++;
}
#define FRAME_SPECIALS_SIZE ((sizeof(_Py_framedata)-1)/sizeof(PyObject *))
void _PyFrame_Copy(_Py_framedata *src, _Py_framedata *dest);
/* Consumes reference to func */
static inline void
_PyFrame_InitializeSpecials(
_Py_framedata *fdata, PyFunctionObject *func,
PyObject *locals, int nlocalsplus)
{
fdata->func = func;
fdata->code = (PyCodeObject *)Py_NewRef(func->func_code);
fdata->builtins = func->func_builtins;
fdata->globals = func->func_globals;
fdata->locals = Py_XNewRef(locals);
fdata->stacktop = nlocalsplus;
fdata->frame_obj = NULL;
fdata->lasti = -1;
fdata->state = FRAME_CREATED;
fdata->is_entry = false;
fdata->is_generator = false;
}
/* Gets the pointer to the locals array
* that precedes this frame.
*/
static inline PyObject**
_PyFrame_GetLocalsArray(_Py_framedata *fdata)
{
return fdata->localsplus;
}
static inline PyObject**
_PyFrame_GetStackPointer(_Py_framedata *fdata)
{
return fdata->localsplus+fdata->stacktop;
}
static inline void
_PyFrame_SetStackPointer(_Py_framedata *fdata, PyObject **stack_pointer)
{
fdata->stacktop = (int)(stack_pointer - fdata->localsplus);
}
/* For use by _PyFrame_GetFrameObject
Do not call directly. */
PyFrameObject *
_PyFrame_MakeAndSetFrameObject(_Py_framedata *fdata);
/* Gets the PyFrameObject for this frame, lazily
* creating it if necessary.
* Returns a borrowed referennce */
static inline PyFrameObject *
_PyFrame_GetFrameObject(_Py_framedata *fdata)
{
PyFrameObject *res = fdata->frame_obj;
if (res != NULL) {
return res;
}
return _PyFrame_MakeAndSetFrameObject(fdata);
}
/* Clears all references in the frame.
* If take is non-zero, then the frame data
* may be transfered to the frame object it references
* instead of being cleared. Either way
* the caller no longer owns the references
* in the frame.
* take should be set to 1 for heap allocated
* frames like the ones in generators and coroutines.
*/
void
_PyFrame_Clear(_Py_framedata * fdata);
int
_PyFrame_Traverse(_Py_framedata *fdata, visitproc visit, void *arg);
int
_PyFrame_FastToLocalsWithError(_Py_framedata *fdata);
void
_PyFrame_LocalsToFast(_Py_framedata *fdata, int clear);
extern _Py_framedata *
_PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size);
static inline _Py_framedata *
_PyThreadState_BumpFramePointer(PyThreadState *tstate, size_t size)
{
PyObject **base = tstate->datastack_top;
if (base) {
PyObject **top = base + size;
assert(tstate->datastack_limit);
if (top < tstate->datastack_limit) {
tstate->datastack_top = top;
return (_Py_framedata *)base;
}
}
return _PyThreadState_BumpFramePointerSlow(tstate, size);
}
void _PyThreadState_PopFrame(PyThreadState *tstate, _Py_framedata *fdata);
/* Consume reference to func */
_Py_framedata *
_PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func);
#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_FRAME_H */