Implement simple Mono support
This commit is contained in:
Родитель
83321c495d
Коммит
09f258da07
|
@ -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)
|
Загрузка…
Ссылка в новой задаче