258 lines
7.7 KiB
C++
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
|