wrenpp/src/wren/vm.cpp

258 lines
7.7 KiB
C++

#include "vm.hpp"
#include "configuration.hpp"
#include "dynafunc_maker.hpp"
#include <wren.hpp>
#include <cassert>
#include <stdexcept>
namespace wren {
namespace {
[[gnu::const]]
ErrorType to_error_type (WrenErrorType wet) {
switch (wet) {
case WREN_ERROR_COMPILE: return ErrorType::Compile;
case WREN_ERROR_RUNTIME: return ErrorType::Runtime;
case WREN_ERROR_STACK_TRACE: return ErrorType::StackTrace;
default: return ErrorType::Runtime;
};
}
void write_fn (WrenVM* wvm, const char* text) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->write_fn and cb->config_obj and cb->owner);
cb->write_fn(*cb->config_obj, cb->owner, text);
}
void error_fn (WrenVM* wvm, WrenErrorType type, const char* module, int line, const char* message) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->error_fn and cb->config_obj and cb->owner);
cb->error_fn(*cb->config_obj, cb->owner, to_error_type(type), module, line, message);
}
const char* resolve_module_fn (WrenVM* wvm, const char* importer, const char* name) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->resolve_module_fn and cb->config_obj and cb->owner);
return cb->resolve_module_fn(*cb->config_obj, cb->owner, importer, name);
}
char* load_module_fn (WrenVM* wvm, const char* name) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->load_module_fn and cb->config_obj and cb->owner);
return cb->load_module_fn(*cb->config_obj, cb->owner, name);
}
WrenForeignMethodFn foreign_method_fn (WrenVM* wvm, const char* module, const char* class_name, bool is_static, const char* signature) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->foreign_method_fn and cb->config_obj and cb->owner);
foreign_method_t func = cb->foreign_method_fn(*cb->config_obj, cb->owner, module, class_name, is_static, signature);
DynafuncMaker* const dfm = cb->dynafunc;
assert(dfm);
auto retval = reinterpret_cast<WrenForeignMethodFn>(dfm->make(cb->owner, func));
return retval;
}
WrenForeignClassMethods foreign_class_fn (WrenVM* wvm, const char* module, const char* class_name) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->foreign_class_fn and cb->config_obj and cb->owner);
foreign_class_t funcs = cb->foreign_class_fn(*cb->config_obj, cb->owner, module, class_name);
DynafuncMaker* const dfm = cb->dynafunc;
assert(dfm);
WrenForeignClassMethods retval;
retval.allocate = reinterpret_cast<WrenForeignMethodFn>(dfm->make(cb->owner, funcs.allocate));
retval.finalize = funcs.finalize;
return retval;
}
void throw_if_err (WrenInterpretResult res, const char* module_name) {
using std::runtime_error;
using std::string;
switch (res) {
case WREN_RESULT_COMPILE_ERROR:
throw runtime_error(string("Compilation of ") + module_name + " has failed");
case WREN_RESULT_RUNTIME_ERROR:
throw runtime_error(string("A runtime error occurred in ") + module_name);
case WREN_RESULT_SUCCESS:
break;
}
}
} //unnamed namespace
struct VM::LocalData {
explicit LocalData (const detail::Callbacks& cb) :
callbacks(cb),
wvm(nullptr)
{
callbacks.dynafunc = &dynafunc;
}
~LocalData() noexcept {
if (wvm)
wrenFreeVM(wvm);
#if !defined(NDEBUG)
wvm = nullptr;
#endif
}
detail::Callbacks callbacks;
DynafuncMaker dynafunc;
WrenVM* wvm;
};
VM::VM (Configuration* conf, const detail::Callbacks& cb) :
m_local(std::make_unique<LocalData>(cb))
{
WrenConfiguration wconf;
wrenInitConfiguration(&wconf);
wconf.userData = static_cast<void*>(&m_local->callbacks);
wconf.initialHeapSize = conf->initial_heap_size();
wconf.minHeapSize = conf->min_heap_size();
wconf.heapGrowthPercent = conf->heap_growth_percent();
if (cb.write_fn)
wconf.writeFn = &write_fn;
if (cb.error_fn)
wconf.errorFn = &error_fn;
if (cb.reallocate_fn)
wconf.reallocateFn = cb.reallocate_fn;
if (cb.resolve_module_fn)
wconf.resolveModuleFn = &resolve_module_fn;
if (cb.load_module_fn)
wconf.loadModuleFn = &load_module_fn;
if (cb.foreign_method_fn)
wconf.bindForeignMethodFn = &foreign_method_fn;
if (cb.foreign_class_fn)
wconf.bindForeignClassFn = &foreign_class_fn;
m_local->wvm = wrenNewVM(&wconf);
if (not m_local->wvm)
throw std::runtime_error("Failed to initialize Wren VM");
}
VM::~VM() noexcept = default;
DynafuncMaker* VM::dynafunc_maker() {
return &m_local->dynafunc;
}
void VM::interpret (const char* module_name, const char* script) {
using std::string;
using std::runtime_error;
const WrenInterpretResult res = wrenInterpret(m_local->wvm, module_name, script);
throw_if_err(res, module_name);
}
void VM::call (const Handle& method) {
const WrenInterpretResult res = wrenCall(m_local->wvm, method);
throw_if_err(res, "[wrenCall()]");
}
void VM::release_handle (Handle& handle) noexcept {
if (handle)
wrenReleaseHandle(m_local->wvm, handle);
}
bool VM::slot_bool (int slot_num) {
assert(SlotType::Bool == slot_type(slot_num));
return wrenGetSlotBool(m_local->wvm, slot_num);
}
const char* VM::slot_string (int slot_num) {
assert(SlotType::String == slot_type(slot_num));
return wrenGetSlotString(m_local->wvm, slot_num);
}
double VM::slot_double (int slot_num) {
assert(SlotType::Num == slot_type(slot_num));
return wrenGetSlotDouble(m_local->wvm, slot_num);
}
std::pair<const char*, int> VM::slot_bytes (int slot_num) {
assert(SlotType::String == slot_type(slot_num));
int length;
const char* const data = wrenGetSlotBytes(m_local->wvm, slot_num, &length);
return {data, length};
}
void* VM::slot_foreign (int slot_num) {
assert(SlotType::Foreign == slot_type(slot_num));
return wrenGetSlotForeign(m_local->wvm, slot_num);
}
void VM::set_slot_string (int slot_num, const char* value) {
wrenSetSlotString(m_local->wvm, slot_num, value);
}
void VM::set_slot_double (int slot_num, double value) {
wrenSetSlotDouble(m_local->wvm, slot_num, value);
}
void VM::set_slot_bool (int slot_num, bool value) {
wrenSetSlotBool(m_local->wvm, slot_num, value);
}
void VM::set_slot_null (int slot_num) {
wrenSetSlotNull(m_local->wvm, slot_num);
}
void VM::set_slot_bytes (int slot_num, const char* bytes, std::size_t length) {
wrenSetSlotBytes(m_local->wvm, slot_num, bytes, length);
}
void* VM::set_slot_new_foreign (int slot_num, int class_slot, std::size_t size) {
return wrenSetSlotNewForeign(m_local->wvm, slot_num, class_slot, size);
}
void VM::ensure_slots (int num_slots) {
wrenEnsureSlots(m_local->wvm, num_slots);
}
int VM::slot_count() {
return wrenGetSlotCount(m_local->wvm);
}
void VM::variable(const char* module, const char* name, int slot) {
wrenGetVariable(m_local->wvm, module, name, slot);
}
void VM::set_slot_handle (const Handle& handle, int slot) {
wrenSetSlotHandle(m_local->wvm, slot, handle);
}
SlotType VM::slot_type(int slot_num) {
const WrenType tp = wrenGetSlotType(m_local->wvm, slot_num);
switch (tp) {
case WREN_TYPE_NUM: return SlotType::Num;
case WREN_TYPE_BOOL: return SlotType::Bool;
case WREN_TYPE_LIST: return SlotType::List;
case WREN_TYPE_NULL: return SlotType::Null;
case WREN_TYPE_STRING: return SlotType::String;
case WREN_TYPE_FOREIGN: return SlotType::Foreign;
case WREN_TYPE_UNKNOWN: return SlotType::Unknown;
};
assert(false);
return SlotType::Unknown;
}
Handle VM::slot_handle(int slot_num) {
return {this, wrenGetSlotHandle(m_local->wvm, slot_num)};
}
Handle VM::make_call_handle(const char* signature) {
return {this, wrenMakeCallHandle(m_local->wvm, signature)};
}
} //namespace wren