Panda3D

extend_frozen.c
1 #define PY_SSIZE_T_CLEAN
2 #include <Python.h>
3
4 #ifdef _WIN32
5 #define DLLEXPORT __declspec(dllexport)
6 #else
7 #define DLLEXPORT
8 #endif
9
10 /*
11 * This pointer is kept internally to this module. It represents the
12 * locally-allocated FrozenModules array. If the
13 * PyImport_FrozenModules is any other value, then it wasn't allocated
14 * via this module.
15 */
16 static struct _frozen *frozen_modules = NULL;
17 static int num_frozen_modules = 0;
18
19 /*
20 * Call this function to extend the frozen modules array with a new
21 * array of frozen modules, provided in a C-style array, at runtime.
22 * Returns the total number of frozen modules.
23 */
24 static int
25 extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
26 int orig_count;
27 struct _frozen *realloc_FrozenModules;
28
29 if (PyImport_FrozenModules == frozen_modules) {
30 /* If the previous array was allocated through this module, we
31 already know the count. */
32 orig_count = num_frozen_modules;
33
34 } else {
35 /* If the previous array came from anywhere else, we have to count
36 up its length. */
37 orig_count = 0;
38 while (PyImport_FrozenModules[orig_count].name != NULL) {
39 ++orig_count;
40 }
41 }
42
43 if (new_count == 0) {
44 /* Trivial no-op. */
45 return orig_count;
46 }
47
48 /* Reallocate the PyImport_FrozenModules array bigger to make room
49 for the additional frozen modules. */
50 realloc_FrozenModules = (struct _frozen *)malloc((orig_count + new_count + 1) * sizeof(struct _frozen));
51
52 /* If the previous array was allocated through this module, we can
53 free it; otherwise, we have to leak it. */
54 if (frozen_modules != NULL) {
55 free(frozen_modules);
56 frozen_modules = NULL;
57 }
58
59 /* The new frozen modules go at the front of the list. */
60 memcpy(realloc_FrozenModules, new_modules, new_count * sizeof(struct _frozen));
61
62 /* Then the original set of frozen modules. */
63 memcpy(realloc_FrozenModules + new_count, PyImport_FrozenModules, orig_count * sizeof(struct _frozen));
64
65 /* Finally, a single 0-valued entry marks the end of the array. */
66 memset(realloc_FrozenModules + orig_count + new_count, 0, sizeof(struct _frozen));
67
68 /* Assign the new pointer. */
69 PyImport_FrozenModules = realloc_FrozenModules;
70 frozen_modules = realloc_FrozenModules;
71 num_frozen_modules = orig_count + new_count;
72
73 return num_frozen_modules;
74 }
75
76 /*
77 * Call this function to extend the frozen modules array with a new
78 * list of frozen modules, provided in a Python-style list of (name,
79 * code) tuples, at runtime. This function is designed to be called
80 * from Python.
81 *
82 * Returns the total number of frozen modules.
83 */
84 static PyObject *
85 py_extend_frozen_modules(PyObject *self, PyObject *args) {
86 PyObject *list;
87 int num_elements;
88 int i;
89 struct _frozen *new_modules;
90 int total_count;
91
92 if (!PyArg_ParseTuple(args, "O", &list)) {
93 return NULL;
94 }
95
96 if (!PySequence_Check(list)) {
97 Py_DECREF(list);
98 PyErr_SetString(PyExc_TypeError, "List required");
99 return NULL;
100 }
101
102 num_elements = PySequence_Size(list);
103 new_modules = (struct _frozen *)malloc(sizeof(struct _frozen) * num_elements);
104
105 for (i = 0; i < num_elements; ++i) {
106 PyObject *tuple;
107 const char *name;
108 const char *code;
109 Py_ssize_t size;
110
111 tuple = PySequence_GetItem(list, i);
112 if (!PyArg_ParseTuple(tuple, "ss#", &name, &code, &size)) {
113 return NULL;
114 }
115
116 /* We have to malloc new pointers for the name and code arrays.
117 These pointers will never be freed. */
118 new_modules[i].name = strdup(name);
119 new_modules[i].code = (unsigned char *)malloc(size);
120 new_modules[i].size = size;
121 memcpy(new_modules[i].code, code, size);
122
123 Py_DECREF(tuple);
124 }
125
126 Py_DECREF(list);
127
128 total_count = extend_frozen_modules(new_modules, num_elements);
129 free(new_modules);
130
131 return Py_BuildValue("i", total_count);
132 }
133
134 /*
135 * Call this function to query whether the named module is already a
136 * frozen module or not.
137 */
138 static PyObject *
139 py_is_frozen_module(PyObject *self, PyObject *args) {
140 const char *name;
141 int i;
142
143 if (!PyArg_ParseTuple(args, "s", &name)) {
144 return NULL;
145 }
146
147 i = 0;
148 while (PyImport_FrozenModules[i].name != NULL) {
149 if (strcmp(PyImport_FrozenModules[i].name, name) == 0) {
150 Py_INCREF(Py_True);
151 return Py_True;
152 }
153 ++i;
154 }
155
156 Py_INCREF(Py_False);
157 return Py_False;
158 }
159
160 /*
161 * This returns the tuple (code, isPackage), where code is the code
162 * string associated with the named frozen module, and isPackage is
163 * true if the module is a package, or false if it is a normal
164 * module). The return value is None if there is no such frozen
165 * module. You must use the marshal module to convert the code string
166 * to a code object.
167 */
168 static PyObject *
169 py_get_frozen_module_code(PyObject *self, PyObject *args) {
170 const char *name;
171 int i;
172
173 if (!PyArg_ParseTuple(args, "s", &name)) {
174 return NULL;
175 }
176
177 i = 0;
178 while (PyImport_FrozenModules[i].name != NULL) {
179 if (strcmp(PyImport_FrozenModules[i].name, name) == 0) {
180 int is_package = (PyImport_FrozenModules[i].size < 0);
181 return Py_BuildValue("(s#i)", PyImport_FrozenModules[i].code,
182 (Py_ssize_t) abs(PyImport_FrozenModules[i].size),
183 is_package);
184 }
185 ++i;
186 }
187
188 return Py_BuildValue("");
189 }
190
191 /*
192 * Call this function to return a list of the existing frozen module
193 * names.
194 */
195 static PyObject *
196 py_get_frozen_module_names(PyObject *self, PyObject *args) {
197 int i;
198 PyObject *list;
199
200 if (!PyArg_ParseTuple(args, "")) {
201 return NULL;
202 }
203
204 list = PyList_New(0);
205 i = 0;
206 while (PyImport_FrozenModules[i].name != NULL) {
207 PyObject *name = PyString_FromString(PyImport_FrozenModules[i].name);
208 PyList_Append(list, name);
209 Py_DECREF(name);
210 ++i;
211 }
212
213 return list;
214 }
215
216 /* Initializes the Python module with our functions. */
217 DLLEXPORT void initextend_frozen() {
218 static PyMethodDef extend_frozen_methods[] = {
219 { "extend", py_extend_frozen_modules, METH_VARARGS,
220 "Adds new frozen modules at runtime." },
221 { "is_frozen_module", py_is_frozen_module, METH_VARARGS,
222 "Returns true if the named module is a frozen module." },
223 { "get_frozen_module_code", py_get_frozen_module_code, METH_VARARGS,
224 "Returns the code string associated with the named module." },
225 { "get_frozen_module_names", py_get_frozen_module_names, METH_VARARGS,
226 "Returns a list of frozen module names." },
227 { NULL, NULL, 0, NULL } /* Sentinel */
228 };
229
230 Py_InitModule("extend_frozen", extend_frozen_methods);
231 }