diff --git a/DockerfileJupyter b/DockerfileJupyter index 1fc031d01415..cab96b81cebe 100644 --- a/DockerfileJupyter +++ b/DockerfileJupyter @@ -47,6 +47,12 @@ RUN chmod -R 777 ${WORK} RUN pip install clr-loader +# Work around for https://github.com/pythonnet/clr-loader/issues/8 +COPY fix_ffi_mono.py /opt/miniconda3/lib/python3.6/site-packages/clr_loader/ffi/mono.py +COPY fix_mono.py /opt/miniconda3/lib/python3.6/site-packages/clr_loader/mono.py +ENV LD_LIBRARY_PATH=/lib/ +RUN ln -s /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libc.so + EXPOSE 8888 WORKDIR $WORK diff --git a/Research/fix_ffi_mono.py b/Research/fix_ffi_mono.py new file mode 100644 index 000000000000..6719f3437e27 --- /dev/null +++ b/Research/fix_ffi_mono.py @@ -0,0 +1,30 @@ +# flake8: noqa + +cdef = [] + +cdef.append( + """ +typedef struct _MonoDomain MonoDomain; +typedef struct _MonoAssembly MonoAssembly; +typedef struct _MonoImage MonoImage; +typedef struct _MonoMethodDesc MonoMethodDesc; +typedef struct _MonoMethod MonoMethod; +typedef struct _MonoObject MonoObject; + +MonoDomain* mono_jit_init(const char *root_domain_name); +void mono_jit_cleanup(MonoDomain *domain); +MonoAssembly* mono_domain_assembly_open(MonoDomain *domain, const char *name); +MonoImage* mono_assembly_get_image(MonoAssembly *assembly); + +void mono_config_parse(const char* path); +void mono_domain_set_config(MonoDomain *domain, const char *base_dir, const char *config_file_name); + +MonoMethodDesc* mono_method_desc_new(const char* name, bool include_namespace); +MonoMethod* mono_method_desc_search_in_image(MonoMethodDesc *method_desc, MonoImage *image); +void mono_method_desc_free(MonoMethodDesc *method_desc); + +MonoObject* mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc); + +void* mono_object_unbox(MonoObject *object); +""" +) \ No newline at end of file diff --git a/Research/fix_mono.py b/Research/fix_mono.py new file mode 100644 index 000000000000..ca8c88ab196a --- /dev/null +++ b/Research/fix_mono.py @@ -0,0 +1,112 @@ +import atexit + +from .ffi import load_mono, ffi + + +__all__ = ["Mono"] + + +_MONO = None +_ROOT_DOMAIN = None + + +class Mono: + def __init__(self, libmono, domain=None, config_file=None): + self._assemblies = {} + + initialize(config_file=config_file, libmono=libmono) + + if domain is None: + self._domain = _ROOT_DOMAIN + else: + raise NotImplementedError + + def get_callable(self, assembly_path, typename, function): + assembly = self._assemblies.get(assembly_path) + if not assembly: + assembly = _MONO.mono_domain_assembly_open( + self._domain, assembly_path.encode("utf8") + ) + _check_result(assembly, f"Unable to load assembly {assembly_path}") + self._assemblies[assembly_path] = assembly + + image = _MONO.mono_assembly_get_image(assembly) + _check_result(image, "Unable to load image from assembly") + + desc = MethodDesc(typename, function) + method = desc.search(image) + _check_result( + method, f"Could not find method {typename}.{function} in assembly" + ) + + return MonoMethod(method) + + +class MethodDesc: + def __init__(self, typename, function): + self._desc = f"{typename}:{function}" + self._ptr = _MONO.mono_method_desc_new( + self._desc.encode("utf8"), 1 # include_namespace + ) + + def search(self, image): + return _MONO.mono_method_desc_search_in_image(self._ptr, image) + + def __del__(self): + if _MONO: + _MONO.mono_method_desc_free(self._ptr) + + +class MonoMethod: + def __init__(self, ptr): + self._ptr = ptr + + def __call__(self, ptr, size): + exception = ffi.new("MonoObject**") + params = ffi.new("void*[2]") + + # Keep these alive until the function is called by assigning them locally + ptr_ptr = ffi.new("void**", ptr) + size_ptr = ffi.new("int32_t*", size) + + params[0] = ptr_ptr + params[1] = size_ptr + + res = _MONO.mono_runtime_invoke(self._ptr, ffi.NULL, params, exception) + _check_result(res, "Failed to call method") + + unboxed = ffi.cast("int32_t*", _MONO.mono_object_unbox(res)) + _check_result(unboxed, "Failed to convert result to int") + + return unboxed[0] + + +def initialize(config_file: str, libmono: str) -> None: + global _MONO, _ROOT_DOMAIN + if _MONO is None: + _MONO = load_mono(libmono) + + if config_file is None: + config_bytes = ffi.NULL + else: + config_bytes = config_file.encode("utf8") + + _ROOT_DOMAIN = _MONO.mono_jit_init(b"clr_loader") + + _MONO.mono_domain_set_config(_ROOT_DOMAIN, b"/etc/mono/", b"config"); + + _check_result(_ROOT_DOMAIN, "Failed to initialize Mono") + atexit.register(_release) + + +def _release(): + global _MONO, _ROOT_DOMAIN + if _ROOT_DOMAIN is not None and _MONO is not None: + _MONO.mono_jit_cleanup(_ROOT_DOMAIN) + _MONO = None + _ROOT_DOMAIN = None + + +def _check_result(res, msg): + if res == ffi.NULL or not res: + raise RuntimeError(msg) \ No newline at end of file