initial commit

This commit is contained in:
2026-06-08 01:14:26 +05:00
commit 30724de4d8
19 changed files with 781 additions and 0 deletions

198
src/main.c Normal file
View File

@@ -0,0 +1,198 @@
#include "tlibc/tlibc.h"
#include "tlibc/filesystem.h"
#include "tlibtoml.h"
#include <mono/jit/jit.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/class.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/object.h>
#include <mono/utils/mono-logger.h>
static const cstr APPHOST_CONFIG_PATH = "mono-apphost.toml";
static const cstr DOMAIN_NAME = "mono-apphost";
typedef struct ApphostConfig {
char* main_assembly_path;
char* mono_libs_dir;
// error, critical, warning, message, info, debug (default=error)
NULLABLE(char*) trace_level;
// see dotnet-runtime/src/mono/mono/utils/mono-logger.c
NULLABLE(char*) trace_mask;
//TODO: bool use_llvm
} ApphostConfig;
void ApphostConfig_free(ApphostConfig* conf){
if(!conf)
return;
free(conf->main_assembly_path);
free(conf->mono_libs_dir);
free(conf->trace_level);
free(conf->trace_mask);
free(conf);
}
Result(ApphostConfig*) ApphostConfig_load_filename(cstr _conf_path_raw){
Deferral(32);
ApphostConfig* conf = (ApphostConfig*)malloc(sizeof(ApphostConfig));
zeroStruct(conf);
bool success = false;
Defer(if(!success) ApphostConfig_free(conf));
str conf_path = str_copy(str_from_cstr(_conf_path_raw));
Defer(str_destroy(conf_path));
path_fix_separators(&conf_path);
try(TomlTable* config_top, p, toml_load_filename(conf_path.data));
Defer(TomlTable_free(config_top));
// main_assembly_path
try(str* main_assembly_path, p, TomlTable_get_str(config_top, STR("main_assembly_path")));
if(main_assembly_path->len == 0){
Return RESULT_ERROR_LITERAL("main_assembly_path: value is empty string")
}
path_fix_separators(main_assembly_path);
if(!file_exists(main_assembly_path->data)){
char* err = sprintf_malloc(
"main_assembly_path: file '%s' doesn't exist",
main_assembly_path->data);
return RESULT_ERROR_HEAP(err);
}
conf->main_assembly_path = str_copy(*main_assembly_path).data;
// mono_libs_dir
try(str* mono_libs_dir, p, TomlTable_get_str(config_top, STR("mono_libs_dir")));
if(mono_libs_dir->len == 0){
Return RESULT_ERROR_LITERAL("mono_libs_dir: value is empty string");
}
path_fix_separators(mono_libs_dir);
if(!dir_exists(mono_libs_dir->data)){
char* err = sprintf_malloc(
"mono_libs_dir: directory '%s' doesn't exist",
mono_libs_dir->data);
Return RESULT_ERROR_HEAP(err);
}
char* corelib_dll_path = strcat_malloc(mono_libs_dir->data, path_seps, "System.Private.CoreLib.dll");
Defer(free(corelib_dll_path));
char* corelib_dll_so_path = strcat_malloc(corelib_dll_path, ".so");
Defer(free(corelib_dll_so_path));
if(!file_exists(corelib_dll_path) && !file_exists(corelib_dll_so_path)){
char* err = sprintf_malloc(
"mono_libs_dir: can't find System.Private.CoreLib.dll in '%s'",
mono_libs_dir->data);
Return RESULT_ERROR_HEAP(err);
}
conf->mono_libs_dir = str_copy(*mono_libs_dir).data;
// trace_level
NULLABLE(TomlValue*) trace_level = TomlTable_tryGet(config_top, STR("trace_level"));
if(trace_level){
if(trace_level->s->len != 0)
conf->trace_level = str_copy(*trace_level->s).data;
}
// trace_mask
NULLABLE(TomlValue*) trace_mask = TomlTable_tryGet(config_top, STR("trace_mask"));
if(trace_mask){
if(trace_mask->s->len != 0)
conf->trace_mask = str_copy(*trace_mask->s).data;
}
success = true;
Return RESULT_VALUE(p, conf);
}
void trace_print_handler(const char *message, mono_bool is_stdout){
FILE* stream = is_stdout ? stdout : stderr;
fprintf(stream, "[MonoPrint]: %s\n", message);
}
void trace_log_handler(const char *log_domain, const char *log_level, const char *message,
mono_bool fatal, void *user_data)
{
FILE* stream = stderr;
fprintf(stream, "[%s/%s]: %s\n", log_domain, log_level, message);
}
Result(MonoDomain*) init_runtime(ApphostConfig* conf){
Deferral(16);
// configure logging
mono_trace_set_print_handler(trace_print_handler);
mono_trace_set_log_handler(trace_log_handler, NULL);
if(conf->trace_level)
mono_trace_set_level_string(conf->trace_level);
if(conf->trace_mask)
mono_trace_set_mask_string(conf->trace_mask);
// set dll search path
mono_set_assemblies_path(conf->mono_libs_dir);
// code for compatibility with old mono
mono_set_dirs(conf->mono_libs_dir, conf->mono_libs_dir);
char* mono_config_xml_path = strcat_malloc(conf->mono_libs_dir, path_seps, "config.xml");
Defer(free(mono_config_xml_path));
mono_config_parse(mono_config_xml_path);
// init mono runtime and create domain
MonoDomain* domain = mono_jit_init(DOMAIN_NAME);
if(domain == NULL){
Return RESULT_ERROR_LITERAL("can't initialize mono jit domain");
}
Return RESULT_VALUE(p, domain);
}
Result(MonoAssembly*) load_assembly(MonoDomain* domain, cstr name){
Deferral(1);
MonoAssembly* assembly = mono_domain_assembly_open(domain, name);
if(assembly == NULL){
char* err = sprintf_malloc("can't load assembly '%s'", name);
Return RESULT_ERROR_HEAP(err);
}
Return RESULT_VALUE(p, assembly);
}
Result(i32) try_main(i32 argc, cstr* argv){
Deferral(8);
try(ApphostConfig* conf, p, ApphostConfig_load_filename(APPHOST_CONFIG_PATH));
Defer(ApphostConfig_free(conf));
try(MonoDomain* domain, p, init_runtime(conf))
// mono_jit_cleanup sometimes leads to segfault.
// I guess nobody really tested this thing.
Defer(mono_jit_cleanup(domain));
try(MonoAssembly* assembly, p, load_assembly(domain, conf->main_assembly_path))
i32 ret = mono_jit_exec(domain, assembly, argc, (char**)(argv));
Return RESULT_VALUE(i, ret);
}
i32 main(i32 argc, cstr* argv){
Deferral(4);
try_fatal_void(tlibc_init());
Defer(tlibc_deinit());
try_fatal_void(tlibtoml_init());
Defer(tlibtoml_deinit());
try_fatal(i32 ret, i, try_main(argc, argv));
if(ret != 0){
printfe("program exited with non-zero code: %i\n", ret);
}
Return ret;
}