/* ** sandbox.h ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** mkxp 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 2 of the License, or ** (at your option) any later version. ** ** mkxp 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 mkxp. If not, see . */ #ifndef MKXPZ_SANDBOX_H #define MKXPZ_SANDBOX_H #include #include #include #include "types.h" #define SANDBOX_COROUTINE(name, definition) struct name : boost::asio::coroutine { inline name(struct mkxp_sandbox::binding_base &bind) {} definition }; #define SANDBOX_AWAIT(coroutine, ...) \ do { \ { \ struct mkxp_sandbox::bindings::stack_frame_guard frame = mkxp_sandbox::sb()->bind(); \ frame()(__VA_ARGS__); \ if (frame().is_complete()) break; \ } \ BOOST_ASIO_CORO_YIELD; \ } while (1) #define SANDBOX_AWAIT_AND_SET(variable, coroutine, ...) \ do { \ { \ struct mkxp_sandbox::bindings::stack_frame_guard frame = mkxp_sandbox::sb()->bind(); \ auto ret = frame()(__VA_ARGS__); \ if (frame().is_complete()) { \ variable = ret; \ break; \ } \ } \ BOOST_ASIO_CORO_YIELD; \ } while (1) #define SANDBOX_YIELD \ do { \ sb()._begin_yield(); \ BOOST_ASIO_CORO_YIELD; \ sb()._end_yield(); \ } while (0) #define SANDBOX_VALUE_TO_BOOL(value) ((value) != SANDBOX_FALSE && (value) != SANDBOX_NIL) #define SANDBOX_BOOL_TO_VALUE(boolean) ((boolean) ? SANDBOX_TRUE : SANDBOX_FALSE) namespace mkxp_sandbox { struct sandbox; } namespace mkxp_retro { extern boost::optional sandbox; } namespace mkxp_sandbox { struct sandbox { private: std::shared_ptr ruby; std::unique_ptr wasi; boost::optional bindings; bool yielding; usize sandbox_malloc(usize size); void sandbox_free(usize ptr); public: bool transitioning; inline struct mkxp_sandbox::bindings &operator*() noexcept { return *bindings; } inline struct mkxp_sandbox::bindings *operator->() noexcept { return &*bindings; } sandbox(); ~sandbox(); // Internal utility method of the `SANDBOX_YIELD` macro. inline void _begin_yield() { yielding = true; w2c_ruby_asyncify_start_unwind(ruby.get(), ruby->w2c_mkxp_sandbox_async_buf); } // Internal utility method of the `SANDBOX_YIELD` macro. inline void _end_yield() { w2c_ruby_asyncify_stop_rewind(ruby.get()); } // Executes the given coroutine as the top-level coroutine. Don't call this from inside of another coroutine; use `sb()->bind()` instead. // Returns whether or not the coroutine completed execution. template inline bool run() { if (yielding) { w2c_ruby_asyncify_start_rewind(ruby.get(), ruby->w2c_mkxp_sandbox_async_buf); yielding = false; } for (;;) { { struct mkxp_sandbox::bindings::stack_frame_guard frame = bindings->bind(); frame()(); if (yielding || frame().is_complete()) break; } w2c_ruby_mkxp_sandbox_yield(ruby.get()); } return !yielding; } }; inline struct sandbox &sb() noexcept { return *mkxp_retro::sandbox; } } #endif // MKXPZ_SANDBOX_H