Nuitka
The Python compiler
Loading...
Searching...
No Matches
compiled_frame.h
1// Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3#ifndef __NUITKA_COMPILED_FRAME_H__
4#define __NUITKA_COMPILED_FRAME_H__
5
6/* This file is included from another C file, help IDEs to still parse it on its own. */
7#ifdef __IDE_ONLY__
8#include "nuitka/prelude.h"
9#endif
10
11// Removed flag in 3.11, but we keep code compatible for now. We do not use old
12// value, but 0 because it might get reused. TODO: Probably better to #ifdef
13// usages of it away.
14#if PYTHON_VERSION >= 0x3b0
15#define CO_NOFREE 0
16#endif
17
18// With Python 3.11 or higher, a lightweight object needs to be put into thread
19// state, rather than the full blown frame, that is more similar to current
20// Nuitka frames.
21#if PYTHON_VERSION < 0x3b0
22typedef PyFrameObject Nuitka_ThreadStateFrameType;
23#else
24typedef _PyInterpreterFrame Nuitka_ThreadStateFrameType;
25#endif
26
27// Print a description of given frame objects in frame debug mode
28#if _DEBUG_FRAME
29extern void PRINT_TOP_FRAME(char const *prefix);
30extern void PRINT_PYTHON_FRAME(char const *prefix, PyFrameObject *frame);
31extern void PRINT_COMPILED_FRAME(char const *prefix, struct Nuitka_FrameObject *frame);
32extern void PRINT_INTERPRETER_FRAME(char const *prefix, Nuitka_ThreadStateFrameType *frame);
33#else
34#define PRINT_TOP_FRAME(prefix)
35#define PRINT_PYTHON_FRAME(prefix, frame)
36#define PRINT_COMPILED_FRAME(prefix, frame)
37#define PRINT_INTERPRETER_FRAME(prefix, frame)
38#endif
39
40// Create a frame object for the given code object, frame or module.
41extern struct Nuitka_FrameObject *MAKE_MODULE_FRAME(PyCodeObject *code, PyObject *module);
42extern struct Nuitka_FrameObject *MAKE_FUNCTION_FRAME(PyThreadState *tstate, PyCodeObject *code, PyObject *module,
43 Py_ssize_t locals_size);
44extern struct Nuitka_FrameObject *MAKE_CLASS_FRAME(PyThreadState *tstate, PyCodeObject *code, PyObject *module,
45 PyObject *f_locals, Py_ssize_t locals_size);
46extern void Nuitka_Frame_AssignLocals(struct Nuitka_FrameObject *frame_object, PyObject *locals_value);
47extern void Nuitka_Frame_ClearLocals(struct Nuitka_FrameObject *frame_object);
48
49// Create a code object for the given filename and function name
50
51#if PYTHON_VERSION < 0x300
52#define MAKE_CODE_OBJECT(filename, line, flags, function_name, function_qualname, arg_names, free_vars, arg_count, \
53 kw_only_count, pos_only_count) \
54 makeCodeObject(filename, line, flags, function_name, arg_names, free_vars, arg_count)
55extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
56 PyObject *arg_names, PyObject *free_vars, int arg_count);
57#elif PYTHON_VERSION < 0x380
58#define MAKE_CODE_OBJECT(filename, line, flags, function_name, function_qualname, arg_names, free_vars, arg_count, \
59 kw_only_count, pos_only_count) \
60 makeCodeObject(filename, line, flags, function_name, arg_names, free_vars, arg_count, kw_only_count)
61extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
62 PyObject *arg_names, PyObject *free_vars, int arg_count, int kw_only_count);
63#elif PYTHON_VERSION < 0x3b0
64#define MAKE_CODE_OBJECT(filename, line, flags, function_name, function_qualname, arg_names, free_vars, arg_count, \
65 kw_only_count, pos_only_count) \
66 makeCodeObject(filename, line, flags, function_name, arg_names, free_vars, arg_count, kw_only_count, pos_only_count)
67extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
68 PyObject *arg_names, PyObject *free_vars, int arg_count, int kw_only_count,
69 int pos_only_count);
70#else
71#define MAKE_CODE_OBJECT(filename, line, flags, function_name, function_qualname, arg_names, free_vars, arg_count, \
72 kw_only_count, pos_only_count) \
73 makeCodeObject(filename, line, flags, function_name, function_qualname, arg_names, free_vars, arg_count, \
74 kw_only_count, pos_only_count)
75extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
76 PyObject *function_qualname, PyObject *arg_names, PyObject *free_vars,
77 int arg_count, int kw_only_count, int pos_only_count);
78#endif
79
80NUITKA_MAY_BE_UNUSED static inline bool isFakeCodeObject(PyCodeObject *code) {
81#if PYTHON_VERSION < 0x300
82 return code->co_code == const_str_empty;
83#elif PYTHON_VERSION < 0x3b0
84 return code->co_code == const_bytes_empty;
85#else
86 // Starting for Python3.11, we just proper bytecode that raises
87 // "RuntimeError" itself, so this function is only used to
88 // optimize checks away.
89 return false;
90#endif
91}
92
93// Prepare code object for use, patching up its filename.
94extern PyCodeObject *USE_CODE_OBJECT(PyThreadState *tstate, PyObject *code_object, PyObject *module_filename_obj);
95
96extern PyTypeObject Nuitka_Frame_Type;
97
98static inline bool Nuitka_Frame_CheckExact(PyObject *object) {
99 CHECK_OBJECT(object);
100 return Py_TYPE(object) == &Nuitka_Frame_Type;
101}
102
103static inline bool Nuitka_Frame_Check(PyObject *object) {
104 assert(object);
105
106 if (!_PyObject_GC_IS_TRACKED(object)) {
107 return false;
108 }
109
110 CHECK_OBJECT(object);
111
112 if (Nuitka_Frame_CheckExact(object)) {
113 return true;
114 }
115
116 return strcmp(Py_TYPE(object)->tp_name, "compiled_frame") == 0;
117}
118
120 PyFrameObject m_frame;
121
122#if PYTHON_VERSION >= 0x3b0
123 PyObject *m_generator;
124 PyFrameState m_frame_state;
125 _PyInterpreterFrame m_interpreter_frame;
126
127 // In Python 3.11, the frame object is no longer variable size, and as such
128 // we inherit the wrong kind of header, not PyVarObject, leading to f_back
129 // the PyFrameObject and and ob_size aliasing, which is not good, but we
130 // want to expose the same binary interface, while still being variable size,
131 // so what we do is to preserve the size in this field instead.
132 Py_ssize_t m_ob_size;
133
134#endif
135
136 // Our own extra stuff, attached variables.
137 char const *m_type_description;
138 char m_locals_storage[1];
139};
140
141inline static void CHECK_CODE_OBJECT(PyCodeObject *code_object) { CHECK_OBJECT(code_object); }
142
143NUITKA_MAY_BE_UNUSED static inline bool isFrameUnusable(struct Nuitka_FrameObject *frame_object) {
144 CHECK_OBJECT_X(frame_object);
145
146 bool result =
147 // Never used.
148 frame_object == NULL ||
149 // Still in use
150 Py_REFCNT(frame_object) > 1 ||
151#if PYTHON_VERSION < 0x300
152 // Last used by another thread (TODO: Could just set it when reusing)
153 frame_object->m_frame.f_tstate != PyThreadState_GET() ||
154#endif
155 // Not currently linked.
156 frame_object->m_frame.f_back != NULL;
157
158#if _DEBUG_REFRAME
159 if (result && frame_object != NULL) {
160 PRINT_COMPILED_FRAME("NOT REUSING FRAME:", frame_object);
161 }
162#endif
163
164 return result;
165}
166
167#if _DEBUG_REFCOUNTS
168extern int count_active_frame_cache_instances;
169extern int count_allocated_frame_cache_instances;
170extern int count_released_frame_cache_instances;
171extern int count_hit_frame_cache_instances;
172#endif
173
174#if _DEBUG_FRAME
175extern void dumpFrameStack(void);
176#endif
177
178#if PYTHON_VERSION >= 0x3b0
179inline static PyCodeObject *Nuitka_InterpreterFrame_GetCodeObject(_PyInterpreterFrame *frame) {
180#if PYTHON_VERSION < 0x3d0
181 PyCodeObject *result = frame->f_code;
182#elif PYTHON_VERSION < 0x3e0
183 PyCodeObject *result = (PyCodeObject *)frame->f_executable;
184#else
185 assert(!PyStackRef_IsNull(frame->f_executable));
186 PyCodeObject *result = (PyCodeObject *)PyStackRef_AsPyObjectBorrow(frame->f_executable);
187 CHECK_CODE_OBJECT(result);
188#endif
189 assert(result == (PyCodeObject *)Py_None || PyCode_Check(result));
190 return result;
191}
192#endif
193
194inline static PyCodeObject *Nuitka_Frame_GetCodeObject(PyFrameObject *frame) {
195#if PYTHON_VERSION >= 0x3b0
196 assert(frame->f_frame);
197 return Nuitka_InterpreterFrame_GetCodeObject(frame->f_frame);
198#else
199 return frame->f_code;
200#endif
201}
202
203inline static void assertPythonFrameObject(PyFrameObject *frame_object) {
204
205 // TODO: Need to do this manually, as this is making frame caching code
206 // vulnerable to mistakes, but so far the compiled frame type is private
207 // assert(PyObject_IsInstance((PyObject *)frame_object, (PyObject *)&PyFrame_Type));
208 CHECK_OBJECT(frame_object);
209
210 CHECK_CODE_OBJECT(Nuitka_Frame_GetCodeObject(frame_object));
211}
212
213inline static void assertFrameObject(struct Nuitka_FrameObject *frame_object) {
214 CHECK_OBJECT(frame_object);
215
216 // TODO: Need to do this manually, as this is making frame caching code
217 // vulnerable to mistakes, but so far the compiled frame type is private
218 // assert(PyObject_IsInstance((PyObject *)frame_object, (PyObject *)&PyFrame_Type));
219
220 assertPythonFrameObject(&frame_object->m_frame);
221}
222
223inline static void assertThreadFrameObject(Nuitka_ThreadStateFrameType *frame) {
224#if PYTHON_VERSION < 0x3b0
225 assertPythonFrameObject(frame);
226#else
227 // For uncompiled frames of Python 3.11 these often do not exist. TODO: Figure
228 // out what to check or how to know it's a compiled one.
229 if (frame->frame_obj) {
230 assertPythonFrameObject(frame->frame_obj);
231 }
232#endif
233}
234
235// Mark frame as currently executed. Starting with Python 3 that means it
236// can or cannot be cleared, or should lead to a generator close. For Python2
237// this is a no-op. Using a define to spare the compile from inlining an empty
238// function.
239#if PYTHON_VERSION >= 0x300
240
241#if PYTHON_VERSION < 0x3b0
242
243static inline void Nuitka_PythonFrame_MarkAsExecuting(PyFrameObject *frame) {
244#if PYTHON_VERSION >= 0x3a0
245 frame->f_state = FRAME_EXECUTING;
246#else
247 frame->f_executing = 1;
248#endif
249}
250
251#endif
252
253static inline void Nuitka_Frame_MarkAsExecuting(struct Nuitka_FrameObject *frame) {
254 CHECK_OBJECT(frame);
255#if PYTHON_VERSION >= 0x3b0
256 frame->m_frame_state = FRAME_EXECUTING;
257#elif PYTHON_VERSION >= 0x3a0
258 frame->m_frame.f_state = FRAME_EXECUTING;
259#else
260 frame->m_frame.f_executing = 1;
261#endif
262}
263#else
264#define Nuitka_Frame_MarkAsExecuting(frame) ;
265#endif
266
267#if PYTHON_VERSION >= 0x300
268static inline void Nuitka_Frame_MarkAsNotExecuting(struct Nuitka_FrameObject *frame) {
269 CHECK_OBJECT(frame);
270#if PYTHON_VERSION >= 0x3b0
271 frame->m_frame_state = FRAME_SUSPENDED;
272#elif PYTHON_VERSION >= 0x3a0
273 frame->m_frame.f_state = FRAME_SUSPENDED;
274#else
275 frame->m_frame.f_executing = 0;
276#endif
277}
278#else
279#define Nuitka_Frame_MarkAsNotExecuting(frame) ;
280#define Nuitka_PythonFrame_MarkAsExecuting(frame) ;
281#endif
282
283#if PYTHON_VERSION >= 0x300
284static inline bool Nuitka_Frame_IsExecuting(struct Nuitka_FrameObject *frame) {
285 CHECK_OBJECT(frame);
286#if PYTHON_VERSION >= 0x3b0
287 return frame->m_frame_state == FRAME_EXECUTING;
288#elif PYTHON_VERSION >= 0x3a0
289 return frame->m_frame.f_state == FRAME_EXECUTING;
290#else
291 return frame->m_frame.f_executing == 1;
292#endif
293}
294#endif
295
296#if PYTHON_VERSION >= 0x3d0
297static inline bool Nuitka_Frame_IsSuspended(struct Nuitka_FrameObject *frame) {
298 CHECK_OBJECT(frame);
299 return frame->m_frame_state == FRAME_SUSPENDED;
300}
301#endif
302
303#if PYTHON_VERSION >= 0x3b0
304
305#if PYTHON_VERSION < 0x3d0
306#define CURRENT_TSTATE_INTERPRETER_FRAME(tstate) tstate->cframe->current_frame
307#else
308#define CURRENT_TSTATE_INTERPRETER_FRAME(tstate) tstate->current_frame
309#endif
310
311NUITKA_MAY_BE_UNUSED inline static void pushFrameStackInterpreterFrame(PyThreadState *tstate,
312 _PyInterpreterFrame *interpreter_frame) {
313 _PyInterpreterFrame *old = CURRENT_TSTATE_INTERPRETER_FRAME(tstate);
314 interpreter_frame->previous = old;
315 CURRENT_TSTATE_INTERPRETER_FRAME(tstate) = interpreter_frame;
316
317 if (old != NULL && !_PyFrame_IsIncomplete(old) && interpreter_frame->frame_obj) {
318 interpreter_frame->frame_obj->f_back = old->frame_obj;
319 CHECK_OBJECT_X(old->frame_obj);
320
321 Py_XINCREF(old->frame_obj);
322 }
323}
324#else
325// Put frame at the top of the frame stack and mark as executing.
326NUITKA_MAY_BE_UNUSED inline static void pushFrameStackPythonFrame(PyThreadState *tstate, PyFrameObject *frame_object) {
327 PRINT_TOP_FRAME("Normal push entry top frame:");
328 PRINT_COMPILED_FRAME("Pushing:", frame_object);
329
330 // Make sure it's healthy.
331 assertPythonFrameObject(frame_object);
332
333 PyFrameObject *old = tstate->frame;
334 CHECK_OBJECT_X(old);
335
336 if (old) {
337 assertPythonFrameObject(old);
338 CHECK_CODE_OBJECT(Nuitka_Frame_GetCodeObject(old));
339 }
340
341 // No recursion with identical frames allowed, assert against it.
342 assert(old != frame_object);
343
344 // Push the new frame as the currently active one.
345 tstate->frame = frame_object;
346
347 // Transfer ownership of old frame.
348 if (old != NULL) {
349 frame_object->f_back = old;
350 }
351
352 Nuitka_PythonFrame_MarkAsExecuting(frame_object);
353 Py_INCREF(frame_object);
354
355 PRINT_TOP_FRAME("Normal push exit top frame:");
356}
357#endif
358
359NUITKA_MAY_BE_UNUSED inline static void pushFrameStackCompiledFrame(PyThreadState *tstate,
360 struct Nuitka_FrameObject *frame_object) {
361#if PYTHON_VERSION < 0x3b0
362 pushFrameStackPythonFrame(tstate, &frame_object->m_frame);
363#else
364 pushFrameStackInterpreterFrame(tstate, &frame_object->m_interpreter_frame);
365
366 Nuitka_Frame_MarkAsExecuting(frame_object);
367 Py_INCREF(frame_object);
368#endif
369}
370
371NUITKA_MAY_BE_UNUSED inline static void popFrameStack(PyThreadState *tstate) {
372#if _DEBUG_FRAME
373 PRINT_TOP_FRAME("Normal pop entry top frame:");
374#endif
375
376#if PYTHON_VERSION < 0x3b0
377 struct Nuitka_FrameObject *frame_object = (struct Nuitka_FrameObject *)(tstate->frame);
378 CHECK_OBJECT(frame_object);
379
380#if _DEBUG_FRAME
381 printf("Taking off frame %s %s\n", Nuitka_String_AsString(PyObject_Str((PyObject *)frame_object)),
382 Nuitka_String_AsString(PyObject_Repr((PyObject *)Nuitka_Frame_GetCodeObject(&frame_object->m_frame))));
383#endif
384
385 // Put previous frame on top.
386 tstate->frame = frame_object->m_frame.f_back;
387 frame_object->m_frame.f_back = NULL;
388
389 Nuitka_Frame_MarkAsNotExecuting(frame_object);
390 Py_DECREF(frame_object);
391#else
392 assert(CURRENT_TSTATE_INTERPRETER_FRAME(tstate));
393
394 struct Nuitka_FrameObject *frame_object =
395 (struct Nuitka_FrameObject *)CURRENT_TSTATE_INTERPRETER_FRAME(tstate)->frame_obj;
396 CHECK_OBJECT(frame_object);
397
398 CURRENT_TSTATE_INTERPRETER_FRAME(tstate) = CURRENT_TSTATE_INTERPRETER_FRAME(tstate)->previous;
399
400 Nuitka_Frame_MarkAsNotExecuting(frame_object);
401
402 CHECK_OBJECT_X(frame_object->m_frame.f_back);
403 Py_CLEAR(frame_object->m_frame.f_back);
404
405 Py_DECREF(frame_object);
406
407 frame_object->m_interpreter_frame.previous = NULL;
408#endif
409
410#if _DEBUG_FRAME
411 PRINT_TOP_FRAME("Normal pop exit top frame:");
412#endif
413}
414
415#if PYTHON_VERSION >= 0x300
416NUITKA_MAY_BE_UNUSED static void Nuitka_SetFrameGenerator(struct Nuitka_FrameObject *nuitka_frame,
417 PyObject *generator) {
418#if PYTHON_VERSION < 0x3b0
419 nuitka_frame->m_frame.f_gen = generator;
420#else
421 nuitka_frame->m_generator = generator;
422#endif
423
424 // Mark the frame as executing
425 if (generator) {
426 Nuitka_Frame_MarkAsExecuting(nuitka_frame);
427 }
428}
429#endif
430
431NUITKA_MAY_BE_UNUSED static PyCodeObject *Nuitka_GetFrameCodeObject(struct Nuitka_FrameObject *nuitka_frame) {
432#if PYTHON_VERSION < 0x3b0
433 return nuitka_frame->m_frame.f_code;
434#else
435 return Nuitka_InterpreterFrame_GetCodeObject(&nuitka_frame->m_interpreter_frame);
436#endif
437}
438
439NUITKA_MAY_BE_UNUSED static int Nuitka_GetFrameLineNumber(struct Nuitka_FrameObject *nuitka_frame) {
440 return nuitka_frame->m_frame.f_lineno;
441}
442
443NUITKA_MAY_BE_UNUSED static PyObject **Nuitka_GetCodeVarNames(PyCodeObject *code_object) {
444#if PYTHON_VERSION < 0x3b0
445 return &PyTuple_GET_ITEM(code_object->co_varnames, 0);
446#else
447 // TODO: Might get away with co_names which will be much faster, that functions
448 // that build a new tuple, that we would have to keep around, but it might be
449 // merged with closure variable names, etc. as as such might become wrong.
450 return &PyTuple_GET_ITEM(code_object->co_localsplusnames, 0);
451#endif
452}
453
454// Attach locals to a frame object. TODO: Upper case, this is for generated code only.
455extern void Nuitka_Frame_AttachLocals(struct Nuitka_FrameObject *frame, char const *type_description, ...);
456
457NUITKA_MAY_BE_UNUSED static Nuitka_ThreadStateFrameType *_Nuitka_GetThreadStateFrame(PyThreadState *tstate) {
458#if PYTHON_VERSION < 0x3b0
459 return tstate->frame;
460#else
461 return CURRENT_TSTATE_INTERPRETER_FRAME(tstate);
462#endif
463}
464
465NUITKA_MAY_BE_UNUSED inline static void pushFrameStackGenerator(PyThreadState *tstate,
466 Nuitka_ThreadStateFrameType *frame_object) {
467#if PYTHON_VERSION < 0x3b0
468 Nuitka_ThreadStateFrameType *return_frame = _Nuitka_GetThreadStateFrame(tstate);
469
470 Py_XINCREF(return_frame);
471 // Put the generator back on the frame stack.
472 pushFrameStackPythonFrame(tstate, frame_object);
473 Py_DECREF(frame_object);
474#else
475 pushFrameStackInterpreterFrame(tstate, frame_object);
476#endif
477}
478
479NUITKA_MAY_BE_UNUSED inline static void pushFrameStackGeneratorCompiledFrame(PyThreadState *tstate,
480 struct Nuitka_FrameObject *frame_object) {
481#if PYTHON_VERSION < 0x3b0
482 pushFrameStackGenerator(tstate, &frame_object->m_frame);
483#else
484 pushFrameStackGenerator(tstate, &frame_object->m_interpreter_frame);
485#endif
486}
487
488// Codes used for type_description.
489#define NUITKA_TYPE_DESCRIPTION_NULL 'N'
490#define NUITKA_TYPE_DESCRIPTION_CELL 'c'
491#define NUITKA_TYPE_DESCRIPTION_OBJECT 'o'
492#define NUITKA_TYPE_DESCRIPTION_OBJECT_PTR 'O'
493#define NUITKA_TYPE_DESCRIPTION_BOOL 'b'
494#define NUITKA_TYPE_DESCRIPTION_NILONG 'L'
495
496#if _DEBUG_REFCOUNTS
497extern int count_active_Nuitka_Frame_Type;
498extern int count_allocated_Nuitka_Frame_Type;
499extern int count_released_Nuitka_Frame_Type;
500#endif
501
502#endif
503
504// Part of "Nuitka", an optimizing Python compiler that is compatible and
505// integrates with CPython, but also works on its own.
506//
507// Licensed under the GNU Affero General Public License, Version 3 (the "License");
508// you may not use this file except in compliance with the License.
509// You may obtain a copy of the License at
510//
511// http://www.gnu.org/licenses/agpl.txt
512//
513// Unless required by applicable law or agreed to in writing, software
514// distributed under the License is distributed on an "AS IS" BASIS,
515// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
516// See the License for the specific language governing permissions and
517// limitations under the License.
Definition compiled_frame.h:119