/* Copyright 2020, 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 . */ #include "wrenpp/vm.hpp" #include "wrenpp/configuration.hpp" #include "dynafunc_maker.hpp" #include #include #include 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(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(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(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(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(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); auto retval = reinterpret_cast(dfm->make(cb->owner, func)); return retval; } else { return nullptr; } } WrenForeignClassMethods foreign_class_fn (WrenVM* wvm, const char* module, const char* class_name) { auto cb = static_cast(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(dfm->make(cb->owner, funcs.allocate)); } 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 } 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(cb)) { WrenConfiguration wconf; wrenInitConfiguration(&wconf); wconf.userData = static_cast(&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"); 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 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(); } bool VM::has_user_data() const { return nullptr != m_local->user_data; } 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