Skip to content

Commit

Permalink
Fix mono running in research
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin-Molinero committed Mar 10, 2021
1 parent 6c7353d commit d42a009
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
6 changes: 6 additions & 0 deletions DockerfileJupyter
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
30 changes: 30 additions & 0 deletions Research/fix_ffi_mono.py
Original file line number Diff line number Diff line change
@@ -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);
"""
)
112 changes: 112 additions & 0 deletions Research/fix_mono.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit d42a009

Please sign in to comment.