panda3d/direct/src/showbase/extend_frozen.c

231 lines
6.4 KiB
C

#include <Python.h>
#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/*
* This pointer is kept internally to this module. It represents the
* locally-allocated FrozenModules array. If the
* PyImport_FrozenModules is any other value, then it wasn't allocated
* via this module.
*/
static struct _frozen *frozen_modules = NULL;
static int num_frozen_modules = 0;
/*
* Call this function to extend the frozen modules array with a new
* array of frozen modules, provided in a C-style array, at runtime.
* Returns the total number of frozen modules.
*/
static int
extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
int orig_count;
struct _frozen *realloc_FrozenModules;
if (PyImport_FrozenModules == frozen_modules) {
/* If the previous array was allocated through this module, we
already know the count. */
orig_count = num_frozen_modules;
} else {
/* If the previous array came from anywhere else, we have to count
up its length. */
orig_count = 0;
while (PyImport_FrozenModules[orig_count].name != NULL) {
++orig_count;
}
}
if (new_count == 0) {
/* Trivial no-op. */
return orig_count;
}
/* Reallocate the PyImport_FrozenModules array bigger to make room
for the additional frozen modules. */
realloc_FrozenModules = (struct _frozen *)malloc((orig_count + new_count + 1) * sizeof(struct _frozen));
/* If the previous array was allocated through this module, we can
free it; otherwise, we have to leak it. */
if (frozen_modules != NULL) {
free(frozen_modules);
frozen_modules = NULL;
}
/* The new frozen modules go at the front of the list. */
memcpy(realloc_FrozenModules, new_modules, new_count * sizeof(struct _frozen));
/* Then the original set of frozen modules. */
memcpy(realloc_FrozenModules + new_count, PyImport_FrozenModules, orig_count * sizeof(struct _frozen));
/* Finally, a single 0-valued entry marks the end of the array. */
memset(realloc_FrozenModules + orig_count + new_count, 0, sizeof(struct _frozen));
/* Assign the new pointer. */
PyImport_FrozenModules = realloc_FrozenModules;
frozen_modules = realloc_FrozenModules;
num_frozen_modules = orig_count + new_count;
return num_frozen_modules;
}
/*
* Call this function to extend the frozen modules array with a new
* list of frozen modules, provided in a Python-style list of (name,
* code) tuples, at runtime. This function is designed to be called
* from Python.
*
* Returns the total number of frozen modules.
*/
static PyObject *
py_extend_frozen_modules(PyObject *self, PyObject *args) {
PyObject *list;
int num_elements;
int i;
struct _frozen *new_modules;
int total_count;
if (!PyArg_ParseTuple(args, "O", &list)) {
return NULL;
}
if (!PySequence_Check(list)) {
Py_DECREF(list);
PyErr_SetString(PyExc_TypeError, "List required");
return NULL;
}
num_elements = PySequence_Size(list);
new_modules = (struct _frozen *)malloc(sizeof(struct _frozen) * num_elements);
for (i = 0; i < num_elements; ++i) {
PyObject *tuple;
const char *name;
const char *code;
int size;
tuple = PySequence_GetItem(list, i);
if (!PyArg_ParseTuple(tuple, "ss#", &name, &code, &size)) {
return NULL;
}
/* We have to malloc new pointers for the name and code arrays.
These pointers will never be freed. */
new_modules[i].name = strdup(name);
new_modules[i].code = (unsigned char *)malloc(size);
new_modules[i].size = size;
memcpy(new_modules[i].code, code, size);
Py_DECREF(tuple);
}
Py_DECREF(list);
total_count = extend_frozen_modules(new_modules, num_elements);
free(new_modules);
return Py_BuildValue("i", total_count);
}
/*
* Call this function to query whether the named module is already a
* frozen module or not.
*/
static PyObject *
py_is_frozen_module(PyObject *self, PyObject *args) {
const char *name;
int i;
if (!PyArg_ParseTuple(args, "s", &name)) {
return NULL;
}
i = 0;
while (PyImport_FrozenModules[i].name != NULL) {
if (strcmp(PyImport_FrozenModules[i].name, name) == 0) {
Py_INCREF(Py_True);
return Py_True;
}
++i;
}
Py_INCREF(Py_False);
return Py_False;
}
/*
* This returns the tuple (code, isPackage), where code is the code
* string associated with the named frozen module, and isPackage is
* true if the module is a package, or false if it is a normal
* module). The return value is None if there is no such frozen
* module. You must use the marshal module to convert the code string
* to a code object.
*/
static PyObject *
py_get_frozen_module_code(PyObject *self, PyObject *args) {
const char *name;
int i;
if (!PyArg_ParseTuple(args, "s", &name)) {
return NULL;
}
i = 0;
while (PyImport_FrozenModules[i].name != NULL) {
if (strcmp(PyImport_FrozenModules[i].name, name) == 0) {
int is_package = (PyImport_FrozenModules[i].size < 0);
return Py_BuildValue("(s#i)", PyImport_FrozenModules[i].code,
abs(PyImport_FrozenModules[i].size),
is_package);
}
++i;
}
return Py_BuildValue("");
}
/*
* Call this function to return a list of the existing frozen module
* names.
*/
static PyObject *
py_get_frozen_module_names(PyObject *self, PyObject *args) {
int i;
PyObject *list;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
list = PyList_New(0);
i = 0;
while (PyImport_FrozenModules[i].name != NULL) {
PyObject *name = PyString_FromString(PyImport_FrozenModules[i].name);
PyList_Append(list, name);
Py_DECREF(name);
++i;
}
return list;
}
/* Initializes the Python module with our functions. */
DLLEXPORT void initextend_frozen() {
static PyMethodDef extend_frozen_methods[] = {
{ "extend", py_extend_frozen_modules, METH_VARARGS,
"Adds new frozen modules at runtime." },
{ "is_frozen_module", py_is_frozen_module, METH_VARARGS,
"Returns true if the named module is a frozen module." },
{ "get_frozen_module_code", py_get_frozen_module_code, METH_VARARGS,
"Returns the code string associated with the named module." },
{ "get_frozen_module_names", py_get_frozen_module_names, METH_VARARGS,
"Returns a list of frozen module names." },
{ NULL, NULL, 0, NULL } /* Sentinel */
};
Py_InitModule("extend_frozen", extend_frozen_methods);
}