wrenpp/src/vm.cpp
King_DuckZ eadd25b827 Foreign function callbacks now receive a ModuleAndName parameter too.
This commit breaks the ARM64 version, I will fix it next.

Lots going on here. DynafuncMaker got updated to store strings
to back the ModuleAndName objects that get hardcoded in the
assembly glue function.
ModuleAndName is not a typedef to a tuple anymore, because I
discovered that tuples suck. They get always pushed on the stack
when passed as parameter, instead the new implementation gets
passed into 2 registers being it a standard layout type.

dhandy::bt::string got updated so it can be used as a literal
value for non-type template parameters, which allowed for a
really easy to use `wren::MN<>` helper. Code now fully requires
c++20.
2022-05-17 01:06:36 +02:00

407 lines
12 KiB
C++

/* Copyright 2020-2022, Michele Santullo
* This file is part of wrenpp.
*
* Wrenpp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Wrenpp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wrenpp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wrenpp/vm.hpp"
#include "wrenpp/configuration.hpp"
#include "wrenpp/callback_manager.hpp"
#include "wrenpp/class_manager.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 load_module_complete_fn (WrenVM* wvm, const char* name, struct WrenLoadModuleResult result);
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);
const char* const sane_module = (module ? module : "");
cb->error_fn(*cb->config_obj, cb->owner, to_error_type(type), sane_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);
}
void* reallocate_fn (void* memory, size_t size, void* user_data) {
auto cb = static_cast<detail::Callbacks*>(user_data);
assert(cb);
assert(cb->reallocate_fn);
return cb->reallocate_fn(memory, size);
}
WrenLoadModuleResult load_module_fn (WrenVM* wvm, const char* name) {
const auto user_data = wrenGetUserData(wvm);
auto cb = static_cast<detail::Callbacks*>(user_data);
assert(cb);
assert(cb->load_module_fn and cb->config_obj and cb->owner);
char* const source = cb->load_module_fn(*cb->config_obj, cb->owner, name);
return ::WrenLoadModuleResult{
.source = source,
.onComplete = (cb->load_module_complete_fn ? &load_module_complete_fn : nullptr),
.userData = nullptr
};
}
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);
if (func) {
DynafuncMaker* const dfm = cb->dynafunc;
assert(dfm);
//dfm->make creates a function that takes one WrenVM* parameter
//which it disregards; then it calls func passing cb->owner to
//it and a ModuleAndName object populated with the values given
//here. This should rarely change, but please double check if
//this is important to you as this comment might become
//outdated.
auto retval = reinterpret_cast<WrenForeignMethodFn>(dfm->make(
cb->owner,
func,
module,
class_name
));
return retval;
}
else {
return nullptr;
}
}
void load_module_complete_fn (WrenVM* wvm, const char* name, struct WrenLoadModuleResult result) {
auto cb = static_cast<detail::Callbacks*>(wrenGetUserData(wvm));
assert(cb);
assert(cb->load_module_complete_fn and cb->config_obj and cb->owner);
//Documentation says result is the same object we returned from
//load_module_fn. It should then be safe to const_cast source,
//since it wasn't const when we got it there
char* const non_const_buff = const_cast<char*>(result.source);
(*cb->load_module_complete_fn)(*cb->config_obj, cb->owner, name, non_const_buff);
}
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);
WrenForeignClassMethods retval;
if (funcs.allocate) {
DynafuncMaker* const dfm = cb->dynafunc;
assert(dfm);
retval.allocate = reinterpret_cast<WrenForeignMethodFn>(dfm->make(
cb->owner,
funcs.allocate,
module,
class_name
));
}
else {
retval.allocate = nullptr;
}
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
}
CallbackManager callback_manager;
ClassManager class_manager;
detail::Callbacks callbacks;
DynafuncMaker dynafunc;
WrenVM* wvm;
void* user_data;
std::uint32_t user_data_type;
};
VM::VM (Configuration* conf, const detail::Callbacks& cb, void* user_data, std::uint32_t user_data_type) :
m_local(std::make_unique<LocalData>(cb))
{
this->reset(conf);
m_local->user_data = user_data;
m_local->user_data_type = user_data_type;
}
VM::~VM() noexcept = default;
DynafuncMaker* VM::dynafunc_maker() {
return &m_local->dynafunc;
}
std::uint32_t VM::user_data_type() const {
return m_local->user_data_type;
}
void* VM::void_user_data() {
return m_local->user_data;
}
void VM::set_user_data (void* user_data, std::uint32_t user_data_type) {
m_local->user_data = user_data;
m_local->user_data_type = user_data_type;
}
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);
}
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);
}
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_user_data (std::nullptr_t) {
m_local->user_data = nullptr;
m_local->user_data_type = detail::type_id<std::nullptr_t>();
}
bool VM::has_user_data() const noexcept {
return nullptr != m_local->user_data;
}
bool VM::has_module(const char* module) const noexcept {
return wrenHasModule(m_local->wvm, module);
}
bool VM::has_variable(const char* module, const char* name) const noexcept {
return wrenHasVariable(m_local->wvm, module, name);
}
void VM::abort_fiber(int slot_num) {
wrenAbortFiber(m_local->wvm, slot_num);
}
std::size_t VM::dynafunc_byte_size() const {
return m_local->dynafunc.total_memory();
}
void VM::reset (Configuration* conf) {
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();
auto& cb = m_local->callbacks;
if (cb.write_fn)
wconf.writeFn = &write_fn;
if (cb.error_fn)
wconf.errorFn = &error_fn;
if (cb.reallocate_fn)
wconf.reallocateFn = &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;
if (m_local->wvm)
wrenFreeVM(m_local->wvm);
m_local->wvm = wrenNewVM(&wconf);
if (not m_local->wvm)
throw std::runtime_error("Failed to initialize Wren VM");
m_local->dynafunc.clear();
}
CallbackManager& VM::callback_manager() {
return m_local->callback_manager;
}
ClassManager& VM::class_manager() {
return m_local->class_manager;
}
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) noexcept {
assert(slot_count() >= 1);
wrenGetVariable(m_local->wvm, module, name, slot);
}
void VM::variable_or_throw(const char* module, const char* name, int slot) {
using std::string;
using std::runtime_error;
if (not has_module(module))
throw runtime_error(string{"Module "} + module + " is unknown");
if (not has_variable(module, name))
throw runtime_error(string{"Variable "} + module + "::" + name + " not found");
variable(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;
case WREN_TYPE_MAP: return SlotType::Map;
};
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