1 //     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2 //
3 //     Part of "Nuitka", an optimizing Python compiler that is compatible and
4 //     integrates with CPython, but also works on its own.
5 //
6 //     Licensed under the Apache License, Version 2.0 (the "License");
7 //     you may not use this file except in compliance with the License.
8 //     You may obtain a copy of the License at
9 //
10 //        http://www.apache.org/licenses/LICENSE-2.0
11 //
12 //     Unless required by applicable law or agreed to in writing, software
13 //     distributed under the License is distributed on an "AS IS" BASIS,
14 //     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 //     See the License for the specific language governing permissions and
16 //     limitations under the License.
17 //
18 #ifndef __NUITKA_COMPILED_FRAME_H__
19 #define __NUITKA_COMPILED_FRAME_H__
20 
21 // Create a frame object for the given code object, frame or module.
22 extern struct Nuitka_FrameObject *MAKE_MODULE_FRAME(PyCodeObject *code, PyObject *module);
23 extern struct Nuitka_FrameObject *MAKE_FUNCTION_FRAME(PyCodeObject *code, PyObject *module, Py_ssize_t locals_size);
24 
25 // Create a code object for the given filename and function name
26 
27 #if PYTHON_VERSION < 0x300
28 #define MAKE_CODEOBJECT(filename, line, flags, function_name, argnames, freevars, arg_count, kw_only_count,            \
29                         pos_only_count)                                                                                \
30     makeCodeObject(filename, line, flags, function_name, argnames, freevars, arg_count)
31 extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
32                                     PyObject *argnames, PyObject *freevars, int arg_count);
33 #elif PYTHON_VERSION < 0x380
34 #define MAKE_CODEOBJECT(filename, line, flags, function_name, argnames, freevars, arg_count, kw_only_count,            \
35                         pos_only_count)                                                                                \
36     makeCodeObject(filename, line, flags, function_name, argnames, freevars, arg_count, kw_only_count)
37 extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
38                                     PyObject *argnames, PyObject *freevars, int arg_count, int kw_only_count);
39 #else
40 #define MAKE_CODEOBJECT(filename, line, flags, function_name, argnames, freevars, arg_count, kw_only_count,            \
41                         pos_only_count)                                                                                \
42     makeCodeObject(filename, line, flags, function_name, argnames, freevars, arg_count, kw_only_count, pos_only_count)
43 extern PyCodeObject *makeCodeObject(PyObject *filename, int line, int flags, PyObject *function_name,
44                                     PyObject *argnames, PyObject *freevars, int arg_count, int kw_only_count,
45                                     int pos_only_count);
46 #endif
47 
48 extern PyTypeObject Nuitka_Frame_Type;
49 
Nuitka_Frame_Check(PyObject * object)50 static inline bool Nuitka_Frame_Check(PyObject *object) {
51     CHECK_OBJECT(object);
52     return Py_TYPE(object) == &Nuitka_Frame_Type;
53 }
54 
55 struct Nuitka_FrameObject {
56     PyFrameObject m_frame;
57 
58     // Our own extra stuff, attached variables.
59     char const *m_type_description;
60     char m_locals_storage[1];
61 };
62 
assertCodeObject(PyCodeObject * code_object)63 inline static void assertCodeObject(PyCodeObject *code_object) { CHECK_OBJECT(code_object); }
64 
isFrameUnusable(struct Nuitka_FrameObject * frame_object)65 NUITKA_MAY_BE_UNUSED static inline bool isFrameUnusable(struct Nuitka_FrameObject *frame_object) {
66     CHECK_OBJECT_X(frame_object);
67 
68     bool result =
69         // Never used.
70         frame_object == NULL ||
71         // Still in use
72         Py_REFCNT(frame_object) > 1 ||
73 #if PYTHON_VERSION < 0x340
74         // Last used by another thread (TODO: Could just set it when re-using)
75         frame_object->m_frame.f_tstate != PyThreadState_GET() ||
76 #endif
77         // Not currently linked.
78         frame_object->m_frame.f_back != NULL;
79 
80 #if _DEBUG_REFRAME
81     if (result && frame_object != NULL) {
82         PRINT_STRING("NOT REUSING FRAME:");
83         PRINT_ITEM((PyObject *)frame_object);
84         PRINT_REFCOUNT((PyObject *)frame_object);
85         if (frame_object->m_frame.f_back) {
86             PRINT_ITEM((PyObject *)frame_object->m_frame.f_back);
87         }
88         PRINT_NEW_LINE();
89     }
90 #endif
91 
92     return result;
93 }
94 
95 #if _DEBUG_REFCOUNTS
96 extern int count_active_frame_cache_instances;
97 extern int count_allocated_frame_cache_instances;
98 extern int count_released_frame_cache_instances;
99 extern int count_hit_frame_cache_instances;
100 #endif
101 
102 extern void dumpFrameStack(void);
103 
assertFrameObject(struct Nuitka_FrameObject * frame_object)104 inline static void assertFrameObject(struct Nuitka_FrameObject *frame_object) {
105     CHECK_OBJECT(frame_object);
106     assertCodeObject(frame_object->m_frame.f_code);
107 }
108 
109 // Mark frame as currently executed. Starting with Python 3.4 that means it
110 // can or cannot be cleared, or should lead to a generator close. For Python2
111 // this is a no-op. Using a define to spare the compile from inlining an empty
112 // function.
113 #if PYTHON_VERSION >= 0x340
Nuitka_Frame_MarkAsExecuting(struct Nuitka_FrameObject * frame)114 static inline void Nuitka_Frame_MarkAsExecuting(struct Nuitka_FrameObject *frame) {
115     CHECK_OBJECT(frame);
116 #if PYTHON_VERSION >= 0x3a0
117     frame->m_frame.f_state = FRAME_EXECUTING;
118 #else
119     frame->m_frame.f_executing = 1;
120 #endif
121 }
122 #else
123 #define Nuitka_Frame_MarkAsExecuting(frame) ;
124 #endif
125 
126 #if PYTHON_VERSION >= 0x340
Nuitka_Frame_MarkAsNotExecuting(struct Nuitka_FrameObject * frame)127 static inline void Nuitka_Frame_MarkAsNotExecuting(struct Nuitka_FrameObject *frame) {
128     CHECK_OBJECT(frame);
129 #if PYTHON_VERSION >= 0x3a0
130     frame->m_frame.f_state = FRAME_SUSPENDED;
131 #else
132     frame->m_frame.f_executing = 0;
133 #endif
134 }
135 #else
136 #define Nuitka_Frame_MarkAsNotExecuting(frame) ;
137 #endif
138 
139 #if PYTHON_VERSION >= 0x340
Nuitka_Frame_IsExecuting(struct Nuitka_FrameObject * frame)140 static inline bool Nuitka_Frame_IsExecuting(struct Nuitka_FrameObject *frame) {
141     CHECK_OBJECT(frame);
142 #if PYTHON_VERSION >= 0x3a0
143     return frame->m_frame.f_state == FRAME_EXECUTING;
144 #else
145     return frame->m_frame.f_executing == 1;
146 #endif
147 }
148 #endif
149 
150 // Put frame at the top of the frame stack and mark as executing.
pushFrameStack(struct Nuitka_FrameObject * frame_object)151 NUITKA_MAY_BE_UNUSED inline static void pushFrameStack(struct Nuitka_FrameObject *frame_object) {
152     // Make sure it's healthy.
153     assertFrameObject(frame_object);
154 
155     // We don't allow frame objects where this is not true.
156     assert(frame_object->m_frame.f_back == NULL);
157 
158     // Look at current frame, "old" is the one previously active.
159     PyThreadState *tstate = PyThreadState_GET();
160     PyFrameObject *old = tstate->frame;
161     CHECK_OBJECT_X(old);
162 
163 #if _DEBUG_FRAME
164     if (old) {
165         assertCodeObject(old->f_code);
166 
167         printf("Upstacking to frame %s %s\n", Nuitka_String_AsString(PyObject_Str((PyObject *)old)),
168                Nuitka_String_AsString(PyObject_Repr((PyObject *)old->f_code)));
169     }
170 #endif
171 
172     // No recursion with identical frames allowed, assert against it.
173     assert(old != &frame_object->m_frame);
174 
175     // Push the new frame as the currently active one.
176     tstate->frame = (PyFrameObject *)frame_object;
177 
178     // Transfer ownership of old frame.
179     if (old != NULL) {
180         assertFrameObject((struct Nuitka_FrameObject *)old);
181 
182         frame_object->m_frame.f_back = old;
183     }
184 
185     Nuitka_Frame_MarkAsExecuting(frame_object);
186     Py_INCREF(frame_object);
187 
188 #if _DEBUG_FRAME
189     printf("Now at top frame %s %s\n", Nuitka_String_AsString(PyObject_Str((PyObject *)tstate->frame)),
190            Nuitka_String_AsString(PyObject_Repr((PyObject *)tstate->frame->f_code)));
191 #endif
192 }
193 
popFrameStack(void)194 NUITKA_MAY_BE_UNUSED inline static void popFrameStack(void) {
195     PyThreadState *tstate = PyThreadState_GET();
196 
197     PyFrameObject *old = tstate->frame;
198     CHECK_OBJECT(old);
199 
200 #if _DEBUG_FRAME
201     printf("Taking off frame %s %s\n", Nuitka_String_AsString(PyObject_Str((PyObject *)old)),
202            Nuitka_String_AsString(PyObject_Repr((PyObject *)old->f_code)));
203 #endif
204 
205     // Put previous frame on top.
206     tstate->frame = old->f_back;
207     old->f_back = NULL;
208 
209     Nuitka_Frame_MarkAsNotExecuting((struct Nuitka_FrameObject *)old);
210     Py_DECREF(old);
211 
212 #if _DEBUG_FRAME
213     if (tstate->frame) {
214         printf("Now at top frame %s %s\n", Nuitka_String_AsString(PyObject_Str((PyObject *)tstate->frame)),
215                Nuitka_String_AsString(PyObject_Repr((PyObject *)tstate->frame->f_code)));
216     } else {
217         printf("Now at top no frame\n");
218     }
219 #endif
220 }
221 
222 // Attach locals to a frame object. TODO: Upper case, this is for generated code only.
223 extern void Nuitka_Frame_AttachLocals(struct Nuitka_FrameObject *frame, char const *type_description, ...);
224 
225 // Codes used for type_description.
226 #define NUITKA_TYPE_DESCRIPTION_NULL 'N'
227 #define NUITKA_TYPE_DESCRIPTION_CELL 'c'
228 #define NUITKA_TYPE_DESCRIPTION_OBJECT 'o'
229 #define NUITKA_TYPE_DESCRIPTION_OBJECT_PTR 'O'
230 #define NUITKA_TYPE_DESCRIPTION_BOOL 'b'
231 
232 #if _DEBUG_REFCOUNTS
233 extern int count_active_Nuitka_Frame_Type;
234 extern int count_allocated_Nuitka_Frame_Type;
235 extern int count_released_Nuitka_Frame_Type;
236 #endif
237 
238 #endif
239