#pragma once #include "../common/std.hpp" #include "../common/UsefulException.hpp" #include "../common/ougge_format.hpp" #include #include #include #include #include namespace Mono { typedef i8 SByte; typedef u8 Byte; typedef i16 Short; typedef u16 UShort; typedef i32 Int; typedef u32 UInt; typedef i64 Long; typedef u64 ULong; typedef f32 Float; typedef f64 Double; typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true}; typedef char16_t Char; typedef MonoString* String; /// @warning MonoObject can be moved in memory by GC in any time and raw pointer will be invalid. /// Use ObjectHandle where it is possible. typedef MonoObject* Object; typedef void Void; template MonoClass* getClass(); template void* valueToVoidPtr(T& v){ return &v; } template<> inline void* valueToVoidPtr(Object& v){ return v; } template<> inline void* valueToVoidPtr(String& v){ return v; } template T valueFromMonoObject(MonoObject* o){ void* result_value_ptr = mono_object_unbox(o); if(result_value_ptr == nullptr) throw UsefulException("can't unbox method value"); return *((T*)result_value_ptr); } template<> inline Object valueFromMonoObject(MonoObject* o){ return o; } template<> inline String valueFromMonoObject(MonoObject* o){ return (String)((void*)o); } void getMethodSignatureTypes(MonoMethod* mono_method, MonoType** return_type, std::vector& argument_types); /// searches for method `name` in `target_class` with return type `return_class` and argument types `arg_classes` /// @return found method or nullptr MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name, MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size); template class Method; template class Method { MonoMethod* method_ptr; public: Method() { method_ptr = nullptr; } /// all types must implement getClass() Method(MonoClass* target_class, const std::string& name){ static MonoClass* arg_classes[] { getClass()... }; static MonoClass* return_class { getClass() }; method_ptr = tryGetMonoMethod(target_class, name, return_class, arg_classes, sizeof...(ArgTypes)); if(method_ptr == nullptr){ throw UsefulException(ougge_format("can't get method '%s' from class '%s'", name.c_str(), mono_class_get_name(target_class))); } } // ReturnT not is void template std::enable_if_t::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const { if(method_ptr == nullptr) throw UsefulException("method_ptr is null"); void* arg_array[] = { valueToVoidPtr(args)..., nullptr }; MonoObject* ex = nullptr; MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex); if(ex){ //TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h mono_print_unhandled_exception(ex); throw UsefulException("Some C# exception occured"); } return valueFromMonoObject(result); }; // ReturnT is void template std::enable_if_t::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const { if(method_ptr == nullptr) throw UsefulException("method_ptr is null"); void* arg_array[] = { valueToVoidPtr(args)..., nullptr }; MonoObject* ex = nullptr; mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex); if(ex){ //TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h mono_print_unhandled_exception(ex); throw UsefulException("Some C# exception occured"); } }; }; class Assembly { MonoAssembly* ptr; MonoImage* image; public: Assembly(MonoAssembly* ptr); Assembly(const Assembly&) = delete; MonoClass* getClass(const std::string& name_space, const std::string& name); MonoAssembly* getMonoAssembly() const { return ptr; } MonoImage* getMonoImage() const { return image; } }; ///LINUX: `config.xml`, `mscorelib.dll`, `libmono-native.so` in `mono-libs` directory /// ///WINDOWS: `config.xml`, `mscorelib.dll` in `mono-libs` directory class RuntimeJIT { MonoDomain* domain; public: RuntimeJIT(const std::string& domain_name = "OuggeDomain"); RuntimeJIT(const RuntimeJIT&) = delete; ~RuntimeJIT(); inline MonoDomain* getDomain() { return domain; } std::shared_ptr loadAssembly(const std::string& name); inline Object createObject(MonoClass* klass) { return mono_object_new(domain, klass); } inline String createString(const std::string& v) { return mono_string_new_len(domain, v.c_str(), v.size()); } inline String createString(const char* v) { return mono_string_new(domain, v); } }; /// @brief ObjectHandle can be used to store reliable reference to MonoObject. /// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid. struct ObjectHandle { u32 gc_handle; inline ObjectHandle() : gc_handle(0) {} inline ObjectHandle(Object obj) { gc_handle = mono_gchandle_new(obj, false); } /// implicitly create new ObjectHandle instead inline ObjectHandle(const ObjectHandle& o) = delete; inline ObjectHandle(ObjectHandle&& o) { gc_handle = o.gc_handle; o.gc_handle = 0; } inline ObjectHandle& operator=(ObjectHandle&& o) { gc_handle = o.gc_handle; o.gc_handle = 0; return *this; } inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); } inline Object getObject() const { return mono_gchandle_get_target(gc_handle); } }; }