1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-06 01:19:41 +00:00
openmw-tes3mp/apps/openmw-mp/Script/LangMono/LangMono.cpp

373 lines
9.9 KiB
C++
Raw Normal View History

2018-12-08 22:40:02 +00:00
//
// Created by koncord on 08.12.18.
//
#include <cstdarg>
#include <mono/metadata/appdomain.h>
2018-12-22 21:07:11 +00:00
#include <mono/metadata/object-forward.h>
2018-12-08 22:40:02 +00:00
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
2018-12-22 21:07:11 +00:00
#include <mono/metadata/mono-config.h>
2018-12-08 22:40:02 +00:00
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
2018-12-22 21:07:11 +00:00
#include <apps/openmw-mp/Script/API/TimerAPI.hpp>
2018-12-08 22:40:02 +00:00
#include "LangMono.hpp"
static MonoDomain *domain = nullptr; // shared domain
2018-12-22 21:07:11 +00:00
std::string monoStringToStdString(MonoString *monoString)
{
char *utf8 = mono_string_to_utf8(monoString);
std::string str = utf8;
mono_free(utf8);
return str;
}
template<typename T>
T unbox(MonoObject *obj)
{
return *(T *) mono_object_unbox(obj);
}
boost::any monoObjectToAny(MonoObject *obj)
{
MonoClass *klass = mono_object_get_class(obj);
MonoType *rawType = mono_class_get_type(klass);
switch ((MonoTypeEnum) mono_type_get_type(rawType))
{
case MONO_TYPE_END:
case MONO_TYPE_VOID:
break;
case MONO_TYPE_BOOLEAN:
return (bool) unbox<MonoBoolean>(obj);
case MONO_TYPE_CHAR:
return unbox<uint16_t>(obj);
case MONO_TYPE_I1:
return unbox<int8_t>(obj);
case MONO_TYPE_U1:
return unbox<uint8_t>(obj);
case MONO_TYPE_I2:
return unbox<int16_t>(obj);
case MONO_TYPE_U2:
return unbox<uint16_t>(obj);
case MONO_TYPE_I4:
return unbox<int32_t>(obj);
case MONO_TYPE_U4:
return unbox<uint32_t>(obj);
case MONO_TYPE_I8:
return unbox<int64_t>(obj);
case MONO_TYPE_U8:
return unbox<uint64_t>(obj);
case MONO_TYPE_R4:
return unbox<float>(obj);
case MONO_TYPE_R8:
return unbox<double>(obj);
case MONO_TYPE_STRING:
return monoStringToStdString((MonoString *) obj);
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY:
{
MonoArrayType *arrayType = mono_type_get_array_type(rawType);
}
default:
return boost::any();
}
return boost::any();
}
int LangMono::CreateTimerEx(MonoObject *delegate, long msec, MonoString *monoStr, MonoArray *monoArgs)
{
size_t argsLength = mono_array_length(monoArgs);
std::vector<boost::any> params (argsLength);
try
{
for(size_t i = 0; i < argsLength; ++i)
params[i] = monoObjectToAny(mono_array_get(monoArgs, MonoObject*, i));
char *types = mono_string_to_utf8(monoStr);
int id = mwmp::TimerAPI::CreateTimerMono(delegate, msec, types, params);
mono_free(types);
return id;
}
catch (...)
{
return -1;
}
}
void LangMono::MakePublic(MonoObject *delegate, const char *name) noexcept
{
}
MonoObject *LangMono::CallPublic(const char *name, MonoArray *args)
{
return nullptr;
}
2018-12-08 22:40:02 +00:00
lib_t LangMono::GetInterface()
{
return nullptr;
}
LangMono::LangMono()
{
instance = new MonoInstance;
Init();
}
LangMono::LangMono(MonoInstance *instance) : instance(instance)
{
Init();
}
LangMono::~LangMono()
{
delete instance;
}
void LangMono::Init()
{
if (domain != nullptr)
return;
domain = mono_jit_init("TES3MP Mono VM"); // will leak :P
2018-12-22 21:07:11 +00:00
mono_add_internal_call("TES3MPSharp.TES3MP::CreateTimerEx", (void*) &LangMono::CreateTimerEx);
mono_add_internal_call("TES3MPSharp.TES3MP::MakePublic", (void*) &LangMono::MakePublic);
mono_add_internal_call("TES3MPSharp.TES3MP::CallPublic", (void*) &LangMono::CallPublic);
2018-12-08 22:40:02 +00:00
}
std::vector<MonoClass *> getInstanceClassList(MonoImage *image, const std::string &parentName)
{
std::vector<MonoClass *> classes;
const MonoTableInfo *tableInfo = mono_image_get_table_info(image, MONO_TABLE_TYPEDEF);
int rows = mono_table_info_get_rows(tableInfo);
for (int i = 0; i < rows; i++)
{
MonoClass *klass = nullptr;
uint32_t cols[MONO_TYPEDEF_SIZE];
mono_metadata_decode_row(tableInfo, i, cols, MONO_TYPEDEF_SIZE);
const char *name = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAME]);
const char *nameSpace = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAMESPACE]);
klass = mono_class_from_name(image, nameSpace, name);
if (klass == nullptr)
continue;
MonoClass *parent = mono_class_get_parent(klass);
if (parent != nullptr && parentName == mono_class_get_name(parent))
classes.push_back(klass);
}
return classes;
}
void LangMono::LoadProgram(const char *filename)
{
2018-12-09 06:59:32 +00:00
MonoAssembly *assembly = mono_domain_assembly_open(domain, filename);
if(!assembly)
throw std::runtime_error("Cannot load: " + std::string(filename));
instance->assembly = assembly;
2018-12-08 22:40:02 +00:00
instance->image = mono_assembly_get_image(instance->assembly);
std::vector<MonoClass *> list = getInstanceClassList(instance->image, "Instance");
for (auto &&klass : list)
{
instance->object = mono_object_new(domain, klass);
mono_runtime_object_init(instance->object); // call default ctor
instance->klass = klass;
}
}
int LangMono::FreeProgram()
{
for(auto &&method : methodsCache)
mono_free_method(method.second);
return 0;
}
bool LangMono::IsCallbackPresent(const char *name)
{
return true;
}
boost::any LangMono::Call(const char *name, const char *argl, int buf, ...)
{
va_list vargs;
va_start(vargs, buf);
int n_args = (int) (strlen(argl));
2018-12-22 21:07:11 +00:00
std::vector<void *> vec(n_args);
2018-12-08 22:40:02 +00:00
for (int index = 0; index < n_args; index++)
{
switch (argl[index])
{
case 'i':
{
auto val = va_arg(vargs, unsigned int);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 'q':
{
auto val = va_arg(vargs, signed int);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 'l':
{
auto val = va_arg(vargs, unsigned long long);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 'w':
{
auto val = va_arg(vargs, signed long long);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 'f':
{
auto val = va_arg(vargs, double);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 'p':
{
auto val = va_arg(vargs, void*);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
case 's':
{
2018-12-22 21:07:11 +00:00
vec[index] = (mono_string_new(mono_domain_get(), va_arg(vargs, const char*)));
2018-12-08 22:40:02 +00:00
break;
}
case 'b':
{
auto val = va_arg(vargs, int);
2018-12-22 21:07:11 +00:00
vec[index] = (void *) &val;
2018-12-08 22:40:02 +00:00
break;
}
default:
throw std::runtime_error("Mono call: Unknown argument identifier " + argl[index]);
}
}
va_end(vargs);
MonoMethod *method;
auto it = methodsCache.find({name, n_args});
if (it != methodsCache.end())
{
method = it->second;
} else
{
method = mono_class_get_method_from_name(instance->klass, name, n_args);
methodsCache[{name, n_args}] = method;
}
2018-12-22 21:07:11 +00:00
if (method == nullptr)
return boost::any();
2018-12-08 22:40:02 +00:00
MonoObject *ret = mono_runtime_invoke(method, instance->object, vec.data(), nullptr);
if (ret)
return boost::any(mono_object_unbox(ret));
else
return boost::any();
}
boost::any LangMono::Call(const char *name, const char *argl, const std::vector<boost::any> &args)
{
int n_args = args.size();
std::vector<void *> vec(n_args);
for (int index = 0; index < n_args; index++)
{
switch (argl[index])
{
case 'i':
{
auto val = boost::any_cast<unsigned int>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 'q':
{
auto val = boost::any_cast<signed int>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 'l':
{
auto val = boost::any_cast<unsigned long long>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 'w':
{
auto val = boost::any_cast<signed long long>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 'f':
{
auto val = boost::any_cast<double>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 'p':
{
auto val = boost::any_cast<void *>(args.at(index));
vec[index] = ((void *) &val);
break;
}
case 's':
{
auto val = mono_string_new(mono_domain_get(), boost::any_cast<const char *>(args.at(index)));
vec[index] = (val);
break;
}
case 'b':
{
auto val = boost::any_cast<int>(args.at(index));
vec[index] = ((void *) &val);
break;
}
default:
throw std::runtime_error("Mono call: Unknown argument identifier " + argl[index]);
}
}
MonoMethod *method;
auto it = methodsCache.find({name, n_args});
if (it != methodsCache.end())
{
method = it->second;
}
else
{
method = mono_class_get_method_from_name(instance->klass, name, n_args);
methodsCache[{name, n_args}] = method;
}
MonoObject *ret = mono_runtime_invoke(method, instance->object, vec.data(), nullptr);
if (ret)
return boost::any(mono_object_unbox(ret));
else
return boost::any();
}