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 }