wrenpp/include/wrenpp/vm.hpp
King_DuckZ 1fa6d62f17 Add support for registering foreign methods that return foreign objects
Still a bit work in progress but functioning
2022-05-18 18:48:18 +02:00

233 lines
8.4 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/>.
*/
#pragma once
#include "detail/has_method.hpp"
#include "detail/error_type.hpp"
#include "detail/StringCRC32.hpp"
#include "detail/wren_types.hpp"
#include "handle.hpp"
#include <memory>
#include <tuple>
#include <utility>
#if __cpp_concepts >= 201907
# include <concepts>
#endif
#include <cstdint>
#include <stdexcept>
namespace wren {
class Configuration;
class VM;
class DynafuncMaker;
class CallbackManager;
class ClassManager;
enum class SlotType {
Bool, Num, Foreign, List, Null, String, Unknown, Map
};
namespace detail {
struct Callbacks {
void (*write_fn)(Configuration&, VM*, wren_string_t) {nullptr};
void (*error_fn)(Configuration&, VM*, ErrorType, wren_string_t, int, wren_string_t) {nullptr};
void* (*reallocate_fn)(void*, std::size_t) {nullptr};
const char* (*resolve_module_fn)(Configuration&, VM*, wren_string_t, wren_string_t) {nullptr};
char* (*load_module_fn)(Configuration&, VM*, wren_string_t) {nullptr};
foreign_method_t (*foreign_method_fn)(Configuration&, VM*, wren_string_t, wren_string_t, bool, wren_string_t) {nullptr};
foreign_class_t (*foreign_class_fn)(Configuration&, VM*, wren_string_t, wren_string_t) {nullptr};
void (*load_module_complete_fn)(Configuration&, VM*, wren_string_t, char*) {nullptr};
Configuration* config_obj {nullptr};
VM* owner {nullptr};
DynafuncMaker* dynafunc {nullptr};
};
} //namespace detail
class VM {
public:
template <typename T, typename U> VM (T* conf, U* user_data);
template <typename T> VM (T* conf, std::nullptr_t);
VM (const VM&) = delete;
VM (VM&& other) = default;
~VM() noexcept;
VM& operator= (VM&&) = default;
void interpret (const char* module_name, const char* script);
void call (const Handle& method);
void release_handle (Handle& handle) noexcept;
void ensure_slots(int num_slots);
int slot_count();
void variable(const char* module, const char* name, int slot) noexcept;
void variable_or_throw(const char* module, const char* name, int slot);
void set_slot_handle(const Handle& handle, int slot);
SlotType slot_type(int slot_num);
Handle slot_handle(int slot_num);
Handle make_call_handle(const char* signature);
//slot setters
void set_slot_string (int slot_num, const char* value);
void set_slot_double (int slot_num, double value);
void set_slot_bool (int slot_num, bool value);
void set_slot_null (int slot_num);
void set_slot_bytes (int slot_num, const char* bytes, std::size_t length);
void* set_slot_new_foreign (int slot_num, int class_slot, std::size_t size);
//slot getters
const char* slot_string (int slot_num);
double slot_double (int slot_num);
bool slot_bool (int slot_num);
std::pair<const char*, int> slot_bytes (int slot_num);
void* slot_foreign (int slot_num);
template <typename U> U* user_data();
template <typename U> void set_user_data (U* user_data);
void set_user_data (std::nullptr_t);
bool has_user_data() const noexcept;
bool has_module (const char* module) const noexcept;
bool has_variable (const char* module, const char* name) const noexcept;
void abort_fiber(int slot_num);
std::size_t dynafunc_byte_size() const;
void reset (Configuration* conf);
CallbackManager& callback_manager();
const CallbackManager& callback_manager() const;
ClassManager& class_manager();
const ClassManager& class_manager() const;
private:
struct LocalData;
VM (Configuration* conf, const detail::Callbacks&, void* user_data, std::uint32_t user_data_type);
DynafuncMaker* dynafunc_maker();
std::uint32_t user_data_type() const;
void* void_user_data();
void set_user_data (void* user_data, std::uint32_t user_data_type);
template <typename T> detail::Callbacks to_callbacks (T& conf);
std::unique_ptr<LocalData> m_local;
};
namespace detail {
define_method_info(write_fn, write, void, VM*, wren_string_t);
define_method_info(error_fn, error, void, VM*, ErrorType, wren_string_t, int, wren_string_t);
define_method_info(reallocate_fn, reallocate, void*, void*, std::size_t);
define_method_info(resolve_module_fn, resolve_module, wren_string_t, wren_string_t, wren_string_t);
define_method_info(load_module_fn, load_module, char*, VM*, wren_string_t);
define_method_info(foreign_method_fn, foreign_method, foreign_method_t, VM*, wren_string_t, wren_string_t, bool, wren_string_t);
define_method_info(foreign_class_fn, foreign_class, foreign_class_t, VM*, wren_string_t, wren_string_t);
define_method_info(load_module_complete_fn, load_module_complete, void, VM*, wren_string_t, char*);
template <typename T, typename F> struct AnyFunctionWrap;
template <typename T, typename R, typename... Args>
struct AnyFunctionWrap<T, R(T::*)(Args...)> {
template <R(T::*M)(Args...)>
inline static R call(Configuration& obj, Args... args) {
return (reinterpret_cast<T&>(obj).*M)(args...);
}
};
template <typename T, typename R, typename... Args>
struct AnyFunctionWrap<T, R(*)(Args...)> {
template <R(*M)(Args...)>
inline static R call(Configuration&, Args... args) {
return (*M)(args...);
}
};
template <typename U>
[[gnu::const]]
inline constexpr std::uint32_t type_id() {
return duckcore::StringCRC32(__PRETTY_FUNCTION__);
}
} //namespace detail
template <typename T>
inline detail::Callbacks VM::to_callbacks (T& conf) {
using detail::method_reallocate;
detail::Callbacks ret;
ret.config_obj = &conf;
ret.owner = this;
ret.dynafunc = nullptr;
if constexpr (detail::method_write::exists<T>::value)
ret.write_fn = &detail::AnyFunctionWrap<T, decltype(&T::write_fn)>::template call<&T::write_fn>;
if constexpr (detail::method_error::exists<T>::value)
ret.error_fn = &detail::AnyFunctionWrap<T, decltype(&T::error_fn)>::template call<&T::error_fn>;
if constexpr (method_reallocate::exists<T>::value and method_reallocate::is_static<T>::value)
ret.reallocate_fn = &T::reallocate_fn;
static_assert(not method_reallocate::exists<T>::value or method_reallocate::is_static<T>::value, "Realloc function must be a static function");
if constexpr (detail::method_resolve_module::exists<T>::value)
ret.resolve_module_fn = &detail::AnyFunctionWrap<T, decltype(&T::resolve_module_fn)>::template call<&T::resolve_module_fn>;
if constexpr (detail::method_load_module::exists<T>::value)
ret.load_module_fn = &detail::AnyFunctionWrap<T, decltype(&T::load_module_fn)>::template call<&T::load_module_fn>;
if constexpr (detail::method_foreign_method::exists<T>::value)
ret.foreign_method_fn = &detail::AnyFunctionWrap<T, decltype(&T::foreign_method_fn)>::template call<&T::foreign_method_fn>;
if constexpr (detail::method_foreign_class::exists<T>::value)
ret.foreign_class_fn = &detail::AnyFunctionWrap<T, decltype(&T::foreign_class_fn)>::template call<&T::foreign_class_fn>;
if constexpr (detail::method_load_module_complete::exists<T>::value)
ret.load_module_complete_fn = &detail::AnyFunctionWrap<T, decltype(&T::load_module_complete_fn)>::template call<&T::load_module_complete_fn>;
return ret;
}
template <typename U>
inline U* VM::user_data() {
if (user_data_type() != detail::type_id<U>()) {
throw std::runtime_error("Wrong user data type requested");
}
return reinterpret_cast<U*>(void_user_data());
}
template <typename U>
inline void VM::set_user_data (U* user_data) {
set_user_data(reinterpret_cast<void*>(user_data), detail::type_id<U>());
}
template <typename T>
inline VM::VM (T* conf, std::nullptr_t) :
VM(
static_cast<Configuration*>(conf),
to_callbacks(*conf),
nullptr,
detail::type_id<std::nullptr_t>()
)
{
}
template <typename T, typename U>
inline VM::VM (T* conf, U* user_data) :
VM(
static_cast<Configuration*>(conf),
to_callbacks(*conf),
reinterpret_cast<void*>(user_data),
detail::type_id<U>()
)
{
}
} //namespace wren