Allow foreign_method_fn() in the configuration object.

Wren expects this function to return a pointer to a function
that takes a WrenVM*. This is bad news because that type is
wrapped inside wren::VM and client code doesn't know about it.
It would be better for client code to return a pointer to a
function that takes a VM* instead. In order to do the VM* to
WrenVM* translation I make a "handmade" closure, that is I
generate a function at runtime that contains the VM* hardcoded
into it. The same function also contains the hardcoded
address of the callback function client code wanted to use.
By doing this I can pass a different function pointer to Wren
for each foreign method, and that function knows how to forward
the call to the real client callback.
Currently only implemented for amd64 gnu/linux.
This commit is contained in:
King_DuckZ 2020-04-26 17:19:02 +02:00
commit 16c4b8cbe5
9 changed files with 187 additions and 4 deletions

View file

@ -1,5 +1,6 @@
#include "vm.hpp"
#include "configuration.hpp"
#include "dynafunc_maker.hpp"
#include <wren.hpp>
#include <cassert>
#include <stdexcept>
@ -43,13 +44,26 @@ namespace wren {
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;
}
} //unnamed namespace
struct VM::LocalData {
explicit LocalData (const detail::Callbacks& cb) :
callbacks(cb),
wvm(nullptr)
{}
{
callbacks.dynafunc = &dynafunc;
}
~LocalData() noexcept {
if (wvm)
wrenFreeVM(wvm);
@ -59,6 +73,7 @@ namespace wren {
}
detail::Callbacks callbacks;
DynafuncMaker dynafunc;
WrenVM* wvm;
};
@ -88,15 +103,20 @@ namespace wren {
if (cb.load_module_fn)
wconf.loadModuleFn = &load_module_fn;
if (cb.foreign_method_fn)
wconf.bindForeignMethodFn = &foreign_method_fn;
m_local->wvm = wrenNewVM(&wconf);
if (not m_local->wvm)
throw std::runtime_error("Failed to initialize Wren VM");
}
VM::VM (VM&& other) = default;
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;