refactor(furvm): improve functions

Define native functions inside the module instead of the function
itself (native functions hold only the key to the definition), introduce
private functions, add parameter count to function and fix some bugs.

Closes: #20
Closes: #25
This commit is contained in:
2026-06-28 12:37:36 +02:00
parent 55932f6113
commit 8702298cd2
6 changed files with 157 additions and 54 deletions
+21 -14
View File
@@ -9,18 +9,31 @@
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace furvm {
void executor::push_frame(const mod_h& mod, function function) {
mod_h modInst = mod;
while (function.type() == function_t::Import) {
function = *m_context->module_at(function.imp().mod)->function_at(function.imp().function);
modInst = m_context->module_at(function.imp().mod);
function = *modInst->function_at(function.imp().function);
}
std::vector<thing_h> args;
args.reserve(function.param_count());
for (size_t i = 0; i < function.param_count(); ++i)
args.push_back(pop_thing());
switch (function.type()) {
case function_t::Normal: {
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size() });
m_frames.emplace((struct executor::frame){ mod, function.position(), m_stack.size(), std::move(args) });
} break;
case function_t::Native: {
m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), std::move(args) });
modInst->get_native_function(function.name())(*this);
m_frames.pop();
} break;
case function_t::Native:
default: throw std::runtime_error("unexpected function type");
}
}
@@ -54,13 +67,13 @@ thing_h executor::thing() const {
void executor::store_thing(variable_t variable, const thing_h& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = thing;
}
void executor::store_thing(variable_t variable, thing_h&& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if (frame.variables.size() <= variable) frame.variables.resize(variable + 1);
frame.variables[variable] = std::move(thing);
}
@@ -160,21 +173,15 @@ void executor::step() {
function_id funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
const function_h& function = frame.mod->function_at(funcId);
switch (function->type()) {
case function_t::Normal:
case function_t::Import: push_frame(frame.mod, *function); break;
case function_t::Native: function->native()(*this); break;
}
push_frame(frame.mod, *frame.mod->function_at(funcId));
} break;
case instruction_t::Jump: {
frame.position += frame.mod->byte(frame.position++);
frame.position += ((std::int8_t)frame.mod->byte(frame.position)) + 1;
} break;
case instruction_t::JumpNotZero: {
byte offset = frame.mod->byte(frame.position++);
auto cond = pop_thing();
if (cond->int32() != 0) frame.position += offset;
if (cond->int32() != 0) frame.position += (std::int8_t)offset;
} break;
case instruction_t::Return: {
pop_frame();
+24 -11
View File
@@ -1,14 +1,21 @@
#include "furvm/function.hpp"
#include "furvm/furvm.hpp"
#include <utility>
namespace furvm {
function::function(const mod_h& mod, const function_h& function)
: m_type(function_t::Import), m_paramCount(0), m_value(import_function{ mod.id(), function.id() }) {}
function::~function() {
switch (m_type) {
case function_t::Normal:
case function_t::Native:
default: break;
case function_t::Native: {
m_value.native.~native_function();
} break;
case function_t::Import: {
m_value.imp.~import_function();
} break;
@@ -16,7 +23,7 @@ function::~function() {
}
function::function(function&& other) noexcept
: m_name(std::move(other.m_name)), m_type(other.m_type) {
: m_name(std::move(other.m_name)), m_type(other.m_type), m_paramCount(other.m_paramCount) {
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
@@ -25,8 +32,9 @@ function::function(function&& other) noexcept
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
case function_t::Import: {
m_value.imp = std::move(other.m_value.imp);
new (&m_value.imp) import_function(std::move(other.m_value.imp));
} break;
default: break;
}
other.m_value.position = 0;
}
@@ -34,8 +42,9 @@ function::function(function&& other) noexcept
function& function::operator=(function&& other) noexcept {
if (this == &other) return *this;
m_name = std::move(other.m_name);
m_type = other.m_type;
m_name = std::move(other.m_name);
m_type = other.m_type;
m_paramCount = other.m_paramCount;
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
@@ -44,8 +53,9 @@ function& function::operator=(function&& other) noexcept {
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
case function_t::Import: {
m_value.imp = std::move(other.m_value.imp);
new (&m_value.imp) import_function(std::move(other.m_value.imp));
} break;
default: break;
}
other.m_value.position = 0;
@@ -53,7 +63,7 @@ function& function::operator=(function&& other) noexcept {
}
function::function(const function& other)
: m_name(other.m_name), m_type(other.m_type) {
: m_name(other.m_name), m_type(other.m_type), m_paramCount(other.m_paramCount) {
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
@@ -62,16 +72,18 @@ function::function(const function& other)
new (&m_value.native) native_function(other.m_value.native);
} break;
case function_t::Import: {
m_value.imp = other.m_value.imp;
new (&m_value.imp) import_function(other.m_value.imp);
} break;
default: break;
}
}
function& function::operator=(const function& other) {
if (this == &other) return *this;
m_name = other.m_name;
m_type = other.m_type;
m_name = other.m_name;
m_type = other.m_type;
m_paramCount = other.m_paramCount;
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
@@ -80,8 +92,9 @@ function& function::operator=(const function& other) {
new (&m_value.native) native_function(other.m_value.native);
} break;
case function_t::Import: {
m_value.imp = other.m_value.imp;
new (&m_value.imp) import_function(other.m_value.imp);
} break;
default: break;
}
return *this;
+16 -5
View File
@@ -13,22 +13,33 @@ static constexpr std::array<furvm::byte, 8> s_bytecode = {
67,
furvm::byte(furvm::instruction_t::Clone),
furvm::byte(furvm::instruction_t::Add),
furvm::byte(furvm::instruction_t::Call),
1,
0,
furvm::byte(furvm::instruction_t::Return),
};
int main(void) {
auto context = std::make_shared<furvm::context>();
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end());
furvm::function_h mainFunc = mainModule->emplace_function("main", 0);
furvm::mod_h furlangModule = context->emplace_module("furlang");
furvm::function_h printFunc = furlangModule->emplace_function("print", 1, "print");
furlangModule->set_native_function("print", [](furvm::executor& executor) {
furvm::thing_h thing = executor.load_thing(0);
switch (thing->type()) {
case furvm::thing_t::Int32: {
std::cout << thing->int32() << '\n';
} break;
}
});
mainModule->serialize(std::cout);
furvm::mod_h mainModule = context->emplace_module("main", s_bytecode.begin(), s_bytecode.end());
furvm::function_h mainFunc = mainModule->emplace_function("main", 0, 0);
mainModule->emplace_function(furlangModule, printFunc).dispatch();
furvm::executor_h executor = context->emplace_executor(context);
executor->push_frame(mainModule, *mainFunc);
static constexpr std::size_t FPC = 3; // Frames per collection
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step();
}
+4 -3
View File
@@ -12,9 +12,10 @@ std::ostream& mod::serialize(std::ostream& os) const {
detail::serialize(os, std::uint32_t(0)); // version
detail::serialize(os, function_id(m_functionMap.size()));
for (const auto& [name, id] : m_functionMap) {
detail::serialize(os, name);
function_h func = m_functions.at(id);
for (auto* pair : m_functions) {
function_h func = { pair };
bool isPublic = m_publicFunctions.find(func->name()) != m_publicFunctions.end();
detail::serialize(os, isPublic ? func->name() : ""); // private functions have empty names
detail::serialize(os, std::uint8_t(func->type()));
switch (func->type()) {
case function_t::Normal: {