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