|
1 |
| -#include "pynetclr.h" |
| 1 | +// #define Py_LIMITED_API 0x03050000 |
| 2 | +#include <Python.h> |
2 | 3 |
|
3 |
| -/* List of functions defined in the module */ |
4 |
| -static PyMethodDef clr_methods[] = { |
5 |
| - {NULL, NULL, 0, NULL} /* Sentinel */ |
6 |
| -}; |
| 4 | +#include "stdlib.h" |
7 | 5 |
|
8 |
| -PyDoc_STRVAR(clr_module_doc, |
9 |
| - "clr facade module to initialize the CLR. It's later " |
10 |
| - "replaced by the real clr module. This module has a facade " |
11 |
| - "attribute to make it distinguishable from the real clr module." |
12 |
| -); |
| 6 | +#define MONO_VERSION "v4.0.30319.1" |
| 7 | +#define MONO_DOMAIN "Python" |
13 | 8 |
|
14 |
| -static PyNet_Args *pn_args; |
15 |
| -char **environ = NULL; |
16 |
| - |
17 |
| -#if PY_MAJOR_VERSION >= 3 |
18 |
| -static struct PyModuleDef clrdef = { |
19 |
| - PyModuleDef_HEAD_INIT, |
20 |
| - "clr", /* m_name */ |
21 |
| - clr_module_doc, /* m_doc */ |
22 |
| - -1, /* m_size */ |
23 |
| - clr_methods, /* m_methods */ |
24 |
| - NULL, /* m_reload */ |
25 |
| - NULL, /* m_traverse */ |
26 |
| - NULL, /* m_clear */ |
27 |
| - NULL, /* m_free */ |
28 |
| -}; |
| 9 | +#include <mono/jit/jit.h> |
| 10 | +#include <mono/metadata/environment.h> |
| 11 | +#include <mono/metadata/mono-config.h> |
| 12 | +#include <mono/metadata/debug-helpers.h> |
| 13 | +#include <mono/metadata/assembly.h> |
| 14 | + |
| 15 | +#ifndef _WIN32 |
| 16 | +#include "dirent.h" |
| 17 | +#include "dlfcn.h" |
| 18 | +#include "libgen.h" |
| 19 | +#include "alloca.h" |
29 | 20 | #endif
|
30 | 21 |
|
31 |
| -static PyObject *_initclr() |
| 22 | +typedef struct |
32 | 23 | {
|
33 |
| - PyObject *m; |
| 24 | + MonoDomain *domain; |
| 25 | + MonoAssembly *pr_assm; |
| 26 | + MonoMethod *shutdown; |
| 27 | + const char *pr_file; |
| 28 | + char *error; |
| 29 | + char *init_name; |
| 30 | + char *shutdown_name; |
| 31 | + PyObject *module; |
| 32 | +} PyNet_Args; |
34 | 33 |
|
35 |
| - /* Create the module and add the functions */ |
36 |
| -#if PY_MAJOR_VERSION >= 3 |
37 |
| - m = PyModule_Create(&clrdef); |
38 |
| -#else |
39 |
| - m = Py_InitModule3("clr", clr_methods, clr_module_doc); |
40 |
| -#endif |
41 |
| - if (m == NULL) |
42 |
| - return NULL; |
43 |
| - PyModule_AddObject(m, "facade", Py_True); |
44 |
| - Py_INCREF(Py_True); |
| 34 | +PyNet_Args *PyNet_Init(void); |
| 35 | +static PyNet_Args *pn_args; |
45 | 36 |
|
46 |
| - pn_args = PyNet_Init(1); |
| 37 | +PyMODINIT_FUNC |
| 38 | +PyInit_clr(void) |
| 39 | +{ |
| 40 | + pn_args = PyNet_Init(); |
47 | 41 | if (pn_args->error)
|
48 | 42 | {
|
49 | 43 | return NULL;
|
50 | 44 | }
|
51 | 45 |
|
52 |
| - if (NULL != pn_args->module) |
53 |
| - return pn_args->module; |
| 46 | + return pn_args->module; |
| 47 | +} |
| 48 | + |
| 49 | +void PyNet_Finalize(PyNet_Args *); |
| 50 | +void main_thread_handler(PyNet_Args *user_data); |
| 51 | + |
| 52 | +// initialize Mono and PythonNet |
| 53 | +PyNet_Args *PyNet_Init() |
| 54 | +{ |
| 55 | + PyObject *pn_module; |
| 56 | + PyObject *pn_path; |
| 57 | + PyNet_Args *pn_args; |
| 58 | + pn_args = (PyNet_Args *)malloc(sizeof(PyNet_Args)); |
| 59 | + |
| 60 | + pn_module = PyImport_ImportModule("pythonnet"); |
| 61 | + if (pn_module == NULL) |
| 62 | + { |
| 63 | + pn_args->error = "Failed to import pythonnet"; |
| 64 | + return pn_args; |
| 65 | + } |
| 66 | + |
| 67 | + pn_path = PyObject_CallMethod(pn_module, "get_assembly_path", NULL); |
| 68 | + if (pn_path == NULL) |
| 69 | + { |
| 70 | + Py_DecRef(pn_module); |
| 71 | + pn_args->error = "Failed to get assembly path"; |
| 72 | + return pn_args; |
| 73 | + } |
54 | 74 |
|
55 |
| - return m; |
| 75 | + pn_args->pr_file = PyUnicode_AsUTF8(pn_path); |
| 76 | + pn_args->error = NULL; |
| 77 | + pn_args->shutdown = NULL; |
| 78 | + pn_args->module = NULL; |
| 79 | + |
| 80 | +#ifdef __linux__ |
| 81 | + // Force preload libmono-2.0 as global. Without this, on some systems |
| 82 | + // symbols from libmono are not found by libmononative (which implements |
| 83 | + // some of the System.* namespaces). Since the only happened on Linux so |
| 84 | + // far, we hardcode the library name, load the symbols into the global |
| 85 | + // namespace and leak the handle. |
| 86 | + dlopen("libmono-2.0.so", RTLD_LAZY | RTLD_GLOBAL); |
| 87 | +#endif |
| 88 | + |
| 89 | + pn_args->init_name = "Python.Runtime:InitExt()"; |
| 90 | + pn_args->shutdown_name = "Python.Runtime:Shutdown()"; |
| 91 | + |
| 92 | + pn_args->domain = mono_jit_init_version(MONO_DOMAIN, MONO_VERSION); |
| 93 | + |
| 94 | + // XXX: Skip setting config for now, should be derived from pr_file |
| 95 | + // mono_domain_set_config(pn_args->domain, ".", "Python.Runtime.dll.config"); |
| 96 | + |
| 97 | + /* |
| 98 | + * Load the default Mono configuration file, this is needed |
| 99 | + * if you are planning on using the dllmaps defined on the |
| 100 | + * system configuration |
| 101 | + */ |
| 102 | + mono_config_parse(NULL); |
| 103 | + |
| 104 | + /* I can't use this call to run the main_thread_handler. The function |
| 105 | + * runs it in another thread but *this* thread holds the Python |
| 106 | + * import lock -> DEAD LOCK. |
| 107 | + * |
| 108 | + * mono_runtime_exec_managed_code(pn_args->domain, main_thread_handler, |
| 109 | + * pn_args); |
| 110 | + */ |
| 111 | + |
| 112 | + main_thread_handler(pn_args); |
| 113 | + |
| 114 | + if (pn_args->error != NULL) |
| 115 | + { |
| 116 | + PyErr_SetString(PyExc_ImportError, pn_args->error); |
| 117 | + } |
| 118 | + return pn_args; |
56 | 119 | }
|
57 | 120 |
|
58 |
| -#if PY_MAJOR_VERSION >= 3 |
59 |
| -PyMODINIT_FUNC |
60 |
| -PyInit_clr(void) |
| 121 | +char *PyNet_ExceptionToString(MonoObject *e); |
| 122 | + |
| 123 | +// Shuts down PythonNet and cleans up Mono |
| 124 | +void PyNet_Finalize(PyNet_Args *pn_args) |
61 | 125 | {
|
62 |
| - return _initclr(); |
| 126 | + MonoObject *exception = NULL; |
| 127 | + |
| 128 | + if (pn_args->shutdown) |
| 129 | + { |
| 130 | + mono_runtime_invoke(pn_args->shutdown, NULL, NULL, &exception); |
| 131 | + if (exception) |
| 132 | + { |
| 133 | + pn_args->error = PyNet_ExceptionToString(exception); |
| 134 | + } |
| 135 | + pn_args->shutdown = NULL; |
| 136 | + } |
| 137 | + |
| 138 | + if (pn_args->domain) |
| 139 | + { |
| 140 | + mono_jit_cleanup(pn_args->domain); |
| 141 | + pn_args->domain = NULL; |
| 142 | + } |
| 143 | + free(pn_args); |
63 | 144 | }
|
64 |
| -#else |
65 |
| -PyMODINIT_FUNC |
66 |
| -initclr(void) |
| 145 | + |
| 146 | +MonoMethod *getMethodFromClass(MonoClass *cls, char *name) |
67 | 147 | {
|
68 |
| - _initclr(); |
| 148 | + MonoMethodDesc *mdesc; |
| 149 | + MonoMethod *method; |
| 150 | + |
| 151 | + mdesc = mono_method_desc_new(name, 1); |
| 152 | + method = mono_method_desc_search_in_class(mdesc, cls); |
| 153 | + mono_method_desc_free(mdesc); |
| 154 | + |
| 155 | + return method; |
| 156 | +} |
| 157 | + |
| 158 | +void main_thread_handler(PyNet_Args *user_data) |
| 159 | +{ |
| 160 | + PyNet_Args *pn_args = user_data; |
| 161 | + MonoMethod *init; |
| 162 | + MonoImage *pr_image; |
| 163 | + MonoClass *pythonengine; |
| 164 | + MonoObject *exception = NULL; |
| 165 | + MonoObject *init_result; |
| 166 | + |
| 167 | + pn_args->pr_assm = mono_domain_assembly_open(pn_args->domain, pn_args->pr_file); |
| 168 | + if (!pn_args->pr_assm) |
| 169 | + { |
| 170 | + pn_args->error = "Unable to load assembly"; |
| 171 | + return; |
| 172 | + } |
| 173 | + |
| 174 | + pr_image = mono_assembly_get_image(pn_args->pr_assm); |
| 175 | + if (!pr_image) |
| 176 | + { |
| 177 | + pn_args->error = "Unable to get image"; |
| 178 | + return; |
| 179 | + } |
| 180 | + |
| 181 | + pythonengine = mono_class_from_name(pr_image, "Python.Runtime", "PythonEngine"); |
| 182 | + if (!pythonengine) |
| 183 | + { |
| 184 | + pn_args->error = "Unable to load class PythonEngine from Python.Runtime"; |
| 185 | + return; |
| 186 | + } |
| 187 | + |
| 188 | + init = getMethodFromClass(pythonengine, pn_args->init_name); |
| 189 | + if (!init) |
| 190 | + { |
| 191 | + pn_args->error = "Unable to fetch Init method from PythonEngine"; |
| 192 | + return; |
| 193 | + } |
| 194 | + |
| 195 | + pn_args->shutdown = getMethodFromClass(pythonengine, pn_args->shutdown_name); |
| 196 | + if (!pn_args->shutdown) |
| 197 | + { |
| 198 | + pn_args->error = "Unable to fetch shutdown method from PythonEngine"; |
| 199 | + return; |
| 200 | + } |
| 201 | + |
| 202 | + init_result = mono_runtime_invoke(init, NULL, NULL, &exception); |
| 203 | + if (exception) |
| 204 | + { |
| 205 | + pn_args->error = PyNet_ExceptionToString(exception); |
| 206 | + return; |
| 207 | + } |
| 208 | + |
| 209 | + pn_args->module = *(PyObject**)mono_object_unbox(init_result); |
| 210 | +} |
| 211 | + |
| 212 | +// Get string from a Mono exception |
| 213 | +char *PyNet_ExceptionToString(MonoObject *e) |
| 214 | +{ |
| 215 | + MonoMethodDesc *mdesc = mono_method_desc_new(":ToString()", 0 /*FALSE*/); |
| 216 | + MonoMethod *mmethod = mono_method_desc_search_in_class(mdesc, mono_get_object_class()); |
| 217 | + mono_method_desc_free(mdesc); |
| 218 | + |
| 219 | + mmethod = mono_object_get_virtual_method(e, mmethod); |
| 220 | + MonoString *monoString = (MonoString*) mono_runtime_invoke(mmethod, e, NULL, NULL); |
| 221 | + mono_runtime_invoke(mmethod, e, NULL, NULL); |
| 222 | + return mono_string_to_utf8(monoString); |
69 | 223 | }
|
70 |
| -#endif |
|
0 commit comments