lama_byterun/byterun/src/module_manager.cpp

267 lines
8.5 KiB
C++

#include <functional>
#include <iostream>
extern "C" {
#include "module_manager.h"
#include "runtime_externs.h"
#include "stack.h"
#include "utils.h"
}
#include "analyzer.hpp"
#include "parser.hpp"
#include <filesystem>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
struct ModSymbolPos {
uint32_t mod_id;
size_t offset;
};
struct Module {
std::string name;
Bytefile *bf;
};
struct ModuleManager {
std::unordered_map<std::string, uint32_t> loaded_modules;
std::unordered_map<std::string, ModSymbolPos> public_symbols_mods;
std::vector<Module> modules;
std::vector<std::filesystem::path> search_paths;
};
static ModuleManager manager;
uint32_t mod_add_impl(Bytefile *bf, bool do_verification,
std::optional<const char *> name = std::nullopt) {
#ifdef DEBUG_VERSION
std::cerr << "- add module (impl) '" << std::string{name ? *name : ""}
<< "'\n";
#endif
uint32_t id = manager.modules.size();
manager.modules.push_back({.name = name ? *name : "", .bf = bf});
for (size_t i = 0; i < bf->public_symbols_number; ++i) {
const char *public_name = get_public_name_safe(bf, i);
size_t public_offset = get_public_offset_safe(bf, i);
if (strcmp(public_name, "main") == 0) {
bf->main_offset = public_offset;
} else if (!manager.public_symbols_mods
.insert(
{public_name, {.mod_id = id, .offset = public_offset}})
.second) {
failure("public symbol '%s' loaded more then once\n",
get_public_name_safe(bf, i));
}
}
if (name) {
manager.loaded_modules.insert({*name, id});
}
if (do_verification) {
analyze(id);
}
return id;
}
uint32_t path_mod_load(const char *name, std::filesystem::path &&path,
bool do_verification) {
#ifdef DEBUG_VERSION
std::cerr << "- module path load '" << name << "'\n";
#endif
Bytefile *module = read_file(path.c_str());
return mod_add_impl(module, do_verification, name);
}
extern "C" {
void mod_add_search_path(const char *path) {
manager.search_paths.emplace_back(path);
}
const char *mod_get_name(uint32_t id) {
if (id > manager.modules.size()) {
failure("module id is out of range\n");
}
return manager.modules[id].name.c_str();
}
Bytefile *mod_get(uint32_t id) {
if (id > manager.modules.size()) {
failure("module id is out of range\n");
}
return manager.modules[id].bf;
}
int32_t find_mod_loaded(const char *name) {
auto it = manager.loaded_modules.find(name);
// module already loaded
if (it != manager.loaded_modules.end()) {
return it->second;
}
return -1;
}
int32_t mod_load(const char *name, bool do_verification) {
std::string full_name = std::string{name} + ".bc";
auto it = manager.loaded_modules.find(name);
// module already loaded
if (it != manager.loaded_modules.end()) {
return it->second;
}
if (std::filesystem::exists(full_name)) {
return path_mod_load(name, full_name, do_verification);
}
for (const auto &dir_path : manager.search_paths) {
auto path = dir_path / full_name;
if (std::filesystem::exists(path)) {
return path_mod_load(name, std::move(path), do_verification);
}
}
return -1;
}
uint32_t mod_add(Bytefile *module, bool do_verification) {
#ifdef DEBUG_VERSION
std::cerr << "- add module, no name\n";
#endif
return mod_add_impl(module, do_verification);
}
ModSearchResult mod_search_pub_symbol(const char *name) {
auto it = manager.public_symbols_mods.find(name);
if (it == manager.public_symbols_mods.end()) {
return {.symbol_offset = 0, .mod_id = 0, .mod_file = NULL};
}
return {
.symbol_offset = it->second.offset,
.mod_id = it->second.mod_id,
.mod_file = mod_get(it->second.mod_id),
};
}
struct StdFunc {
void (*ptr)();
size_t args_count;
bool is_args = false; // one var for all args
bool is_vararg = false;
};
bool run_stdlib_func(const char *name, size_t args_count) {
static const std::unordered_map<std::string, StdFunc> std_func = {
{"Luppercase", {.ptr = (void (*)()) & Luppercase, .args_count = 1}},
{"Llowercase", {.ptr = (void (*)()) & Llowercase, .args_count = 1}},
{"Lassert",
{.ptr = (void (*)()) & Lassert, .args_count = 2, .is_vararg = true}},
{"Lstring",
{.ptr = (void (*)()) & Lstring, .args_count = 1, .is_args = true}},
{"Llength", {.ptr = (void (*)()) & Llength, .args_count = 1}},
{"LstringInt", {.ptr = (void (*)()) & LstringInt, .args_count = 1}},
{"Lread", {.ptr = (void (*)()) & Lread, .args_count = 0}},
{"Lwrite", {.ptr = (void (*)()) & Lwrite, .args_count = 1}},
{"LmakeArray", {.ptr = (void (*)()) & LmakeArray, .args_count = 1}},
{"LmakeString", {.ptr = (void (*)()) & LmakeString, .args_count = 1}},
{"Lstringcat",
{.ptr = (void (*)()) & Lstringcat, .args_count = 1, .is_args = true}},
{"LmatchSubString",
{.ptr = (void (*)()) & LmatchSubString, .args_count = 3}},
{"Lsprintf",
{.ptr = (void (*)()) & Lsprintf, .args_count = 1, .is_vararg = true}},
{"Lsubstring",
{.ptr = (void (*)()) & Lsubstring, .args_count = 3, .is_args = true}},
{"Li__Infix_4343",
{.ptr = (void (*)()) & Li__Infix_4343,
.args_count = 2,
.is_args = true}}, // ++
{"Lclone",
{.ptr = (void (*)()) & Lclone, .args_count = 1, .is_args = true}},
{"Lhash", {.ptr = (void (*)()) & Lhash, .args_count = 1}},
{"LtagHash", {.ptr = (void (*)()) & LtagHash, .args_count = 1}},
{"Lcompare", {.ptr = (void (*)()) & Lcompare, .args_count = 2}},
{"LflatCompare", {.ptr = (void (*)()) & LflatCompare, .args_count = 2}},
{"Lfst", {.ptr = (void (*)()) & Lfst, .args_count = 1}},
{"Lsnd", {.ptr = (void (*)()) & Lsnd, .args_count = 1}},
{"Lhd", {.ptr = (void (*)()) & Lhd, .args_count = 1}},
{"Ltl", {.ptr = (void (*)()) & Ltl, .args_count = 1}},
{"LreadLine", {.ptr = (void (*)()) & LreadLine, .args_count = 0}},
{"Lprintf",
{.ptr = (void (*)()) & Lprintf, .args_count = 1, .is_vararg = true}},
{"Lfopen", {.ptr = (void (*)()) & Lfopen, .args_count = 2}},
{"Lfclose", {.ptr = (void (*)()) & Lfclose, .args_count = 1}},
{"Lfread", {.ptr = (void (*)()) & Lfread, .args_count = 1}},
{"Lfwrite", {.ptr = (void (*)()) & Lfwrite, .args_count = 2}},
{"Lfexists", {.ptr = (void (*)()) & Lfexists, .args_count = 1}},
{"Lfprintf",
{.ptr = (void (*)()) & Lfprintf, .args_count = 2, .is_vararg = true}},
{"Lregexp", {.ptr = (void (*)()) & Lregexp, .args_count = 1}},
{"LregexpMatch", {.ptr = (void (*)()) & LregexpMatch, .args_count = 3}},
{"Lfailure",
{.ptr = (void (*)()) & Lfailure, .args_count = 1, .is_vararg = true}},
{"Lsystem", {.ptr = (void (*)()) & Lsystem, .args_count = 1}},
{"LgetEnv", {.ptr = (void (*)()) & LgetEnv, .args_count = 1}},
{"Lrandom", {.ptr = (void (*)()) & Lrandom, .args_count = 1}},
{"Ltime", {.ptr = (void (*)()) & Ltime, .args_count = 0}},
};
// some functions do use on args pointer
const auto it = std_func.find(name);
if (it == std_func.end()) {
return false;
}
// TODO: move to bytecode verifier
if ((!it->second.is_vararg && it->second.args_count != args_count) ||
it->second.args_count > args_count) {
failure("RUNTIME ERROR: stdlib function <%s> argument count <%zu> is not "
"expected (expected is <%s%zu>)\n",
name, it->second.args_count, it->second.is_vararg ? ">=" : "=",
args_count);
}
// TODO: work with varargs
if (it->second.is_vararg) {
failure("vararg stdlib functions are not supported yet");
}
if (it->second.is_args) {
void *ret = ((void *(*)(aint *))it->second.ptr)((aint *)s_peek());
s_popn(it->second.args_count);
s_push(ret);
} else {
// TODO: check if arg order is not reversed
switch (it->second.args_count) {
case 0: {
s_push(((void *(*)())it->second.ptr)());
} break;
case 1: {
void *arg1 = s_pop();
s_push(((void *(*)(void *))it->second.ptr)(arg1));
} break;
case 2: {
void *arg1 = s_pop();
void *arg2 = s_pop();
s_push(((void *(*)(void *, void *))it->second.ptr)(arg1, arg2));
} break;
case 3: {
void *arg1 = s_pop();
void *arg2 = s_pop();
void *arg3 = s_pop();
s_push(((void *(*)(void *, void *, void *))it->second.ptr)(arg1, arg2,
arg3));
} break;
default:
failure("too many std function args (> 3)");
}
}
return true;
}
} // extern "C"