This commit is contained in:
Benedikt Reinartz 2019-10-29 11:14:36 +01:00
Родитель 83321c495d
Коммит 09f258da07
4 изменённых файлов: 209 добавлений и 4 удалений

74
Example.ipynb Normal file
Просмотреть файл

@ -0,0 +1,74 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"# os.environ[\"COREHOST_TRACE\"] = \"1\"\n",
"# os.environ[\"COREHOST_TRACE_VERBOSITY\"] = \"4\"\n",
"# os.environ[\"MONO_LOG_LEVEL\"] = \"debug\"\n",
"\n",
"from clr_loader import hostfxr, mono\n",
"from clr_loader.ffi import ffi\n",
"\n",
"core = hostfxr.HostFxr(\"example/out/example.runtimeconfig.json\")\n",
"mono = mono.Mono()\n",
"\n",
"func_core = core.get_callable(\"example/out/example.dll\", \"example.Class1, example\", \"Test\")\n",
"func_mono = mono.get_callable(\"example/out/example.dll\", \"example.Class1\", \"Test\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"func_core(ffi.NULL, 123)\n",
"func_mono(ffi.NULL, 124)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

Просмотреть файл

@ -5,6 +5,8 @@ import cffi
from . import coreclr, hostfxr, mono
__all__ = ["ffi", "load_coreclr", "load_hostfxr", "load_mono"]
ffi = cffi.FFI()
for cdef in coreclr.cdef + hostfxr.cdef + mono.cdef:
@ -12,15 +14,14 @@ for cdef in coreclr.cdef + hostfxr.cdef + mono.cdef:
def load_coreclr(runtime):
dll_name = get_dll_name("coreclr")
dll_name = _get_dll_name("coreclr")
dll_path = os.path.join(runtime.path, dll_name)
return ffi.dlopen(dll_path)
def load_hostfxr(dotnet_root):
hostfxr_version = "3.0.0"
hostfxr_name = get_dll_name("hostfxr")
hostfxr_name = _get_dll_name("hostfxr")
hostfxr_path = os.path.join(
dotnet_root, "host", "fxr", hostfxr_version, hostfxr_name
)
@ -28,7 +29,18 @@ def load_hostfxr(dotnet_root):
return ffi.dlopen(hostfxr_path)
def get_dll_name(name):
def load_mono(path=None, gc=None):
if path is None:
from ctypes.util import find_library
path = find_library(f"mono{gc or ''}-2.0")
if path is None:
raise RuntimeError("Could not find libmono")
return ffi.dlopen(path)
def _get_dll_name(name):
import sys
if sys.platform == "win32":

Просмотреть файл

@ -1,2 +1,24 @@
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);
MonoAssembly* mono_domain_assembly_open(MonoDomain *domain, const char *name);
MonoImage* mono_assembly_get_image(MonoAssembly *assembly);
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);
"""
)

Просмотреть файл

@ -0,0 +1,97 @@
import atexit
from .ffi import load_mono, ffi
__all__ = ["Mono"]
_MONO = None
_ROOT_DOMAIN = None
class Mono:
def __init__(self, domain=None):
self._assemblies = {}
initialize()
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(self._domain, 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, domain, ptr):
self._ptr = ptr
def __call__(self, ptr, size):
exception = ffi.new("MonoObject**")
params = ffi.new("void*[2]")
params[0] = ffi.new("void**", ptr)
params[1] = ffi.new("int*", size)
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(path=None, gc=None):
global _MONO, _ROOT_DOMAIN
if _MONO is None:
_MONO = load_mono(path=path, gc=gc)
_ROOT_DOMAIN = _MONO.mono_jit_init(b"clr_loader")
_check_result(_ROOT_DOMAIN, "Failed to initialize Mono")
atexit.register(_release)
def _release():
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)