mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-26 08:43:44 +02:00
Turn all sandbox bindings into coroutines
We can't have them as normal functions because reentrant calls into the Ruby API don't work if you do that, i.e. calling into the Ruby API and that calls mkxp-z's bindings and that calls back into the Ruby API.
This commit is contained in:
parent
c4cb891e37
commit
c4d5d8af97
8 changed files with 216 additions and 119 deletions
|
@ -35,6 +35,12 @@ if get_option('retro') == true
|
||||||
|
|
||||||
cmake = import('cmake')
|
cmake = import('cmake')
|
||||||
|
|
||||||
|
boost_options = cmake.subproject_options()
|
||||||
|
boost_options.add_cmake_defines({
|
||||||
|
'CMAKE_POSITION_INDEPENDENT_CODE': true,
|
||||||
|
'BUILD_TESTING': false,
|
||||||
|
})
|
||||||
|
|
||||||
zlib_options = cmake.subproject_options()
|
zlib_options = cmake.subproject_options()
|
||||||
zlib_options.add_cmake_defines({
|
zlib_options.add_cmake_defines({
|
||||||
'CMAKE_POSITION_INDEPENDENT_CODE': true,
|
'CMAKE_POSITION_INDEPENDENT_CODE': true,
|
||||||
|
@ -106,6 +112,7 @@ if get_option('retro') == true
|
||||||
library(
|
library(
|
||||||
'retro-' + meson.project_name(),
|
'retro-' + meson.project_name(),
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
cmake.subproject('boost_asio', options: boost_options).dependency('boost_asio'),
|
||||||
cmake.subproject('zlib', options: zlib_options).dependency('zlibstatic'),
|
cmake.subproject('zlib', options: zlib_options).dependency('zlibstatic'),
|
||||||
cmake.subproject('bzip2', options: bzip2_options).dependency('bz2_static'),
|
cmake.subproject('bzip2', options: bzip2_options).dependency('bz2_static'),
|
||||||
cmake.subproject('liblzma', options: lzma_options).dependency('liblzma'),
|
cmake.subproject('liblzma', options: lzma_options).dependency('liblzma'),
|
||||||
|
|
|
@ -86,7 +86,7 @@ $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby.h $(OUTDIR)/mkxp-retro-ruby/mkxp-retro
|
||||||
mkdir -p $(OUTDIR)/mkxp-retro-ruby
|
mkdir -p $(OUTDIR)/mkxp-retro-ruby
|
||||||
$(WASM2C) $(LIBDIR)/ruby.wasm -n ruby --num-outputs=8 -o $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby.c
|
$(WASM2C) $(LIBDIR)/ruby.wasm -n ruby --num-outputs=8 -o $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby.c
|
||||||
|
|
||||||
$(LIBDIR)/ruby.wasm $(OUTDIR)/mkxp-retro-dist.zip.c &: $(DOWNLOADS)/ruby/Makefile
|
$(LIBDIR)/ruby.wasm $(OUTDIR)/mkxp-retro-dist.zip.c &: $(DOWNLOADS)/ruby/Makefile extra-ruby-bindings.h
|
||||||
mkdir -p $(OUTDIR)
|
mkdir -p $(OUTDIR)
|
||||||
cd $(DOWNLOADS)/ruby && $(MAKE) install DESTDIR=$(OUTDIR)
|
cd $(DOWNLOADS)/ruby && $(MAKE) install DESTDIR=$(OUTDIR)
|
||||||
mv $(OUTDIR)/mkxp-retro-dist/bin/ruby $(LIBDIR)/ruby.wasm
|
mv $(OUTDIR)/mkxp-retro-dist/bin/ruby $(LIBDIR)/ruby.wasm
|
||||||
|
@ -105,7 +105,7 @@ $(LIBDIR)/tags: $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config.h
|
||||||
echo '#include <ruby.h>' | $(WASI_CC) -E -I$(DOWNLOADS)/ruby/include -I$(DOWNLOADS)/ruby/.ext/include/$(TARGET) -o $(LIBDIR)/tags.c -
|
echo '#include <ruby.h>' | $(WASI_CC) -E -I$(DOWNLOADS)/ruby/include -I$(DOWNLOADS)/ruby/.ext/include/$(TARGET) -o $(LIBDIR)/tags.c -
|
||||||
$(CTAGS) -R --fields=S --kinds-c=p --kinds-c++=p -o $(LIBDIR)/tags $(LIBDIR)/tags.c
|
$(CTAGS) -R --fields=S --kinds-c=p --kinds-c++=p -o $(LIBDIR)/tags $(LIBDIR)/tags.c
|
||||||
|
|
||||||
$(DOWNLOADS)/ruby/Makefile $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config.h &: $(DOWNLOADS)/ruby/configure $(RUBY) extra-ruby-bindings.h $(LIBDIR)/usr/local/lib/libyaml.a $(LIBDIR)/usr/local/lib/libz.a $(LIBDIR)/usr/local/lib/libssl.a
|
$(DOWNLOADS)/ruby/Makefile $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config.h &: $(DOWNLOADS)/ruby/configure $(RUBY) $(LIBDIR)/usr/local/lib/libyaml.a $(LIBDIR)/usr/local/lib/libz.a $(LIBDIR)/usr/local/lib/libssl.a
|
||||||
cd $(DOWNLOADS)/ruby && ./configure \
|
cd $(DOWNLOADS)/ruby && ./configure \
|
||||||
--prefix=/mkxp-retro-dist \
|
--prefix=/mkxp-retro-dist \
|
||||||
--host $(TARGET) \
|
--host $(TARGET) \
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
/* This file contains bindings that expose low-level functionality of the Ruby VM to the outside of the sandbox it's running in. They can be called from sandbox.cpp. */
|
/* This file contains bindings that expose low-level functionality of the Ruby VM to the outside of the sandbox it's running in. They can be called from sandbox.cpp. */
|
||||||
|
|
||||||
#ifndef MKXPZ_SANDBOX_EXTRA_RUBY_BINDINGS_H
|
#ifndef SANDBOX_EXTRA_RUBY_BINDINGS_H
|
||||||
#define MKXPZ_SANDBOX_EXTRA_RUBY_BINDINGS_H
|
#define SANDBOX_EXTRA_RUBY_BINDINGS_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -31,43 +31,49 @@
|
||||||
#include "wasm/machine.h"
|
#include "wasm/machine.h"
|
||||||
#include "wasm/setjmp.h"
|
#include "wasm/setjmp.h"
|
||||||
|
|
||||||
#define MKXPZ_SANDBOX_API __attribute__((__visibility__("default")))
|
#define MKXP_SANDBOX_API __attribute__((__visibility__("default")))
|
||||||
|
|
||||||
/* This function should be called immediately after initializing the sandbox to perform initialization, before calling any other functions. */
|
/* This function should be called immediately after initializing the sandbox to perform initialization, before calling any other functions. */
|
||||||
MKXPZ_SANDBOX_API void mkxp_sandbox_init(void) {
|
MKXP_SANDBOX_API void mkxp_sandbox_init(void) {
|
||||||
void __wasm_call_ctors(void); /* Defined by wasi-libc from the WASI SDK */
|
void __wasm_call_ctors(void); /* Defined by wasi-libc from the WASI SDK */
|
||||||
__wasm_call_ctors();
|
__wasm_call_ctors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function should be called immediately before deinitializing the sandbox. */
|
/* This function should be called immediately before deinitializing the sandbox. */
|
||||||
MKXPZ_SANDBOX_API void mkxp_sandbox_deinit(void) {
|
MKXP_SANDBOX_API void mkxp_sandbox_deinit(void) {
|
||||||
void __wasm_call_dtors(void); /* Defined by wasi-libc from the WASI SDK */
|
void __wasm_call_dtors(void); /* Defined by wasi-libc from the WASI SDK */
|
||||||
__wasm_call_dtors();
|
__wasm_call_dtors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exposes the `malloc()` function. */
|
/* Exposes the `malloc()` function. */
|
||||||
MKXPZ_SANDBOX_API void *mkxp_sandbox_malloc(size_t size) {
|
MKXP_SANDBOX_API void *mkxp_sandbox_malloc(size_t size) {
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exposes the `free()` function. */
|
/* Exposes the `free()` function. */
|
||||||
MKXPZ_SANDBOX_API void mkxp_sandbox_free(void *ptr) {
|
MKXP_SANDBOX_API void mkxp_sandbox_free(void *ptr) {
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ruby's `rb_`/`ruby_` functions may return early before they're actually finished running.
|
||||||
|
* You can use `mkxp_sandbox_complete()` to check if the most recent call to a `rb_`/`ruby_` function finished.
|
||||||
|
* If `mkxp_sandbox_complete()` returns false, the `rb_`/`ruby_` function is not done executing yet and needs to be called again with the same arguments. */
|
||||||
|
MKXP_SANDBOX_API bool mkxp_sandbox_complete(void) {
|
||||||
|
extern void *rb_asyncify_unwind_buf; /* Defined in wasm/setjmp.c in Ruby source code */
|
||||||
|
return rb_asyncify_unwind_buf == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function drives Ruby's asynchronous runtime. It's based on the `rb_wasm_rt_start()` function from wasm/runtime.c in the Ruby source code.
|
/* This function drives Ruby's asynchronous runtime. It's based on the `rb_wasm_rt_start()` function from wasm/runtime.c in the Ruby source code.
|
||||||
* After calling any function that starts with `rb_` or `ruby_` other than `ruby_sysinit()`, you need to call `mkxp_sandbox_yield()`.
|
* After calling any function that starts with `rb_` or `ruby_` other than `ruby_sysinit()`, you need to call `mkxp_sandbox_yield()`.
|
||||||
* If `mkxp_sandbox_yield()` returns false, you may proceed as usual.
|
* If `mkxp_sandbox_yield()` returns false, you may proceed as usual.
|
||||||
* However, if it returns true, then you need to call the `rb_`/`ruby_` function again with the same arguments
|
* However, if it returns true, then you need to call the `rb_`/`ruby_` function again with the same arguments
|
||||||
* and then call `mkxp_sandbox_yield()` again, and repeat until `mkxp_sandbox_yield()` returns false. */
|
* and then call `mkxp_sandbox_yield()` again, and repeat until `mkxp_sandbox_yield()` returns false. */
|
||||||
MKXPZ_SANDBOX_API bool mkxp_sandbox_yield(void) {
|
MKXP_SANDBOX_API bool mkxp_sandbox_yield(void) {
|
||||||
static void (*fiber_entry_point)(void *, void *) = NULL;
|
static void (*fiber_entry_point)(void *, void *) = NULL;
|
||||||
static bool new_fiber_started = false;
|
static bool new_fiber_started = false;
|
||||||
static void *arg0;
|
static void *arg0;
|
||||||
static void *arg1;
|
static void *arg1;
|
||||||
|
|
||||||
extern void *rb_asyncify_unwind_buf; /* Defined in wasm/setjmp.c in Ruby source code */
|
|
||||||
|
|
||||||
void *asyncify_buf;
|
void *asyncify_buf;
|
||||||
bool unwound = false;
|
bool unwound = false;
|
||||||
|
|
||||||
|
@ -82,7 +88,7 @@ MKXPZ_SANDBOX_API bool mkxp_sandbox_yield(void) {
|
||||||
unwound = true;
|
unwound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rb_asyncify_unwind_buf == NULL) {
|
if (mkxp_sandbox_complete()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,4 +119,4 @@ MKXPZ_SANDBOX_API bool mkxp_sandbox_yield(void) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MKXPZ_SANDBOX_EXTRA_RUBY_BINDINGS_H */
|
#endif /* SANDBOX_EXTRA_RUBY_BINDINGS_H */
|
||||||
|
|
|
@ -34,8 +34,7 @@ MALLOC_FUNC = 'mkxp_sandbox_malloc'
|
||||||
# The name of the `free()` binding defined in extra-ruby-bindings.h
|
# The name of the `free()` binding defined in extra-ruby-bindings.h
|
||||||
FREE_FUNC = 'mkxp_sandbox_free'
|
FREE_FUNC = 'mkxp_sandbox_free'
|
||||||
|
|
||||||
# The name of the function defined in extra-ruby-bindings.h that yields to Ruby's asynchronous runtime
|
COMPLETE_FUNC = 'mkxp_sandbox_complete'
|
||||||
YIELD_FUNC = 'mkxp_sandbox_yield'
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ ARG_HANDLERS = {
|
||||||
'const char *' => {
|
'const char *' => {
|
||||||
keep: true,
|
keep: true,
|
||||||
buf_size: 'std::strlen(ARG) + 1',
|
buf_size: 'std::strlen(ARG) + 1',
|
||||||
serialize: "std::strcpy((char *)(module_instance->w2c_memory.data + BUF), ARG);\n",
|
serialize: "std::strcpy((char *)(bind.instance->w2c_memory.data + BUF), ARG);\n",
|
||||||
},
|
},
|
||||||
'const VALUE *' => {
|
'const VALUE *' => {
|
||||||
keep: true,
|
keep: true,
|
||||||
|
@ -64,7 +63,7 @@ ARG_HANDLERS = {
|
||||||
buf_size: 'PREV_ARG * sizeof(VALUE)',
|
buf_size: 'PREV_ARG * sizeof(VALUE)',
|
||||||
serialize: <<~HEREDOC
|
serialize: <<~HEREDOC
|
||||||
for (int i = 0; i < PREV_ARG; ++i) {
|
for (int i = 0; i < PREV_ARG; ++i) {
|
||||||
((VALUE *)(module_instance->w2c_memory.data + BUF))[i] = SERIALIZE_PTR(ARG[i]);
|
((VALUE *)(bind.instance->w2c_memory.data + BUF))[i] = SERIALIZE_PTR(ARG[i]);
|
||||||
}
|
}
|
||||||
HEREDOC
|
HEREDOC
|
||||||
},
|
},
|
||||||
|
@ -156,6 +155,7 @@ HEADER_START = <<~HEREDOC
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <boost/asio/coroutine.hpp>
|
||||||
#{MODULE_INCLUDE}
|
#{MODULE_INCLUDE}
|
||||||
#include "src/sandbox/types.h"
|
#include "src/sandbox/types.h"
|
||||||
|
|
||||||
|
@ -171,9 +171,9 @@ HEADER_START = <<~HEREDOC
|
||||||
struct SandboxBind {
|
struct SandboxBind {
|
||||||
private:
|
private:
|
||||||
wasm_ptr_t next_func_ptr;
|
wasm_ptr_t next_func_ptr;
|
||||||
std::shared_ptr<struct w2c_#{MODULE_NAME}> module_instance;
|
std::shared_ptr<struct w2c_#{MODULE_NAME}> instance;
|
||||||
wasm_ptr_t _sbindgen_malloc(wasm_ptr_t);
|
wasm_ptr_t sbindgen_malloc(wasm_ptr_t);
|
||||||
wasm_ptr_t _sbindgen_create_func_ptr();
|
wasm_ptr_t sbindgen_create_func_ptr();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SandboxBind(std::shared_ptr<struct w2c_#{MODULE_NAME}>);
|
SandboxBind(std::shared_ptr<struct w2c_#{MODULE_NAME}>);
|
||||||
|
@ -181,7 +181,6 @@ HEADER_START = <<~HEREDOC
|
||||||
HEREDOC
|
HEREDOC
|
||||||
|
|
||||||
HEADER_END = <<~HEREDOC
|
HEADER_END = <<~HEREDOC
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MKXP_SANDBOX_BINDGEN_H
|
#endif // MKXP_SANDBOX_BINDGEN_H
|
||||||
HEREDOC
|
HEREDOC
|
||||||
|
@ -211,27 +210,28 @@ PRELUDE = <<~HEREDOC
|
||||||
// Autogenerated by sandbox-bindgen.rb. Don't manually modify this file - modify sandbox-bindgen.rb instead!
|
// Autogenerated by sandbox-bindgen.rb. Don't manually modify this file - modify sandbox-bindgen.rb instead!
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
#include <boost/asio/yield.hpp>
|
||||||
#include "mkxp-sandbox-bindgen.h"
|
#include "mkxp-sandbox-bindgen.h"
|
||||||
|
|
||||||
#if WABT_BIG_ENDIAN
|
#if WABT_BIG_ENDIAN
|
||||||
#define SERIALIZE_32(value) __builtin_bswap32(value)
|
#define SERIALIZE_32(value) __builtin_bswap32(value)
|
||||||
#define SERIALIZE_64(value) __builtin_bswap64(value)
|
#define SERIALIZE_64(value) __builtin_bswap64(value)
|
||||||
#else
|
#else
|
||||||
#define SERIALIZE_32(value) (value)
|
#define SERIALIZE_32(value) (value)
|
||||||
#define SERIALIZE_64(value) (value)
|
#define SERIALIZE_64(value) (value)
|
||||||
#endif // WABT_BIG_ENDIAN
|
#endif
|
||||||
#define SERIALIZE_PTR(value) SERIALIZE_#{MEMORY64 ? '64' : '32'}(value)
|
#define SERIALIZE_PTR(value) SERIALIZE_#{MEMORY64 ? '64' : '32'}(value)
|
||||||
|
|
||||||
|
|
||||||
SandboxBind::SandboxBind(std::shared_ptr<struct w2c_#{MODULE_NAME}> m) : next_func_ptr(m->w2c_T0.size), module_instance(m) {}
|
SandboxBind::SandboxBind(std::shared_ptr<struct w2c_#{MODULE_NAME}> m) : next_func_ptr(-1), instance(m) {}
|
||||||
|
|
||||||
|
|
||||||
wasm_ptr_t SandboxBind::_sbindgen_malloc(wasm_size_t size) {
|
wasm_ptr_t SandboxBind::sbindgen_malloc(wasm_size_t size) {
|
||||||
wasm_ptr_t buf = w2c_#{MODULE_NAME}_#{MALLOC_FUNC}(module_instance.get(), size);
|
wasm_ptr_t buf = w2c_#{MODULE_NAME}_#{MALLOC_FUNC}(instance.get(), size);
|
||||||
|
|
||||||
// Verify that the entire allocated buffer is in valid memory
|
// Verify that the entire allocated buffer is in valid memory
|
||||||
wasm_ptr_t buf_end;
|
wasm_ptr_t buf_end;
|
||||||
if (buf == 0 || __builtin_add_overflow(buf, size, &buf_end) || buf_end >= module_instance->w2c_memory.size) {
|
if (buf == 0 || __builtin_add_overflow(buf, size, &buf_end) || buf_end >= instance->w2c_memory.size) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,30 +239,34 @@ PRELUDE = <<~HEREDOC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wasm_ptr_t SandboxBind::_sbindgen_create_func_ptr() {
|
wasm_ptr_t SandboxBind::sbindgen_create_func_ptr() {
|
||||||
if (next_func_ptr < module_instance->w2c_T0.max_size) {
|
if (next_func_ptr == (wasm_ptr_t)-1) {
|
||||||
|
next_func_ptr = instance->w2c_T0.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_func_ptr < instance->w2c_T0.max_size) {
|
||||||
return next_func_ptr++;
|
return next_func_ptr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that an integer overflow won't occur if we double the max size of the funcref table
|
// Make sure that an integer overflow won't occur if we double the max size of the funcref table
|
||||||
wasm_size_t new_max_size;
|
wasm_size_t new_max_size;
|
||||||
if (__builtin_add_overflow(module_instance->w2c_T0.max_size, module_instance->w2c_T0.max_size, &new_max_size)) {
|
if (__builtin_add_overflow(instance->w2c_T0.max_size, instance->w2c_T0.max_size, &new_max_size)) {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double the max size of the funcref table
|
// Double the max size of the funcref table
|
||||||
wasm_size_t old_max_size = module_instance->w2c_T0.max_size;
|
wasm_size_t old_max_size = instance->w2c_T0.max_size;
|
||||||
module_instance->w2c_T0.max_size = new_max_size;
|
instance->w2c_T0.max_size = new_max_size;
|
||||||
|
|
||||||
// Double the size of the funcref table buffer
|
// Double the size of the funcref table buffer
|
||||||
if (wasm_rt_grow_funcref_table(&module_instance->w2c_T0, old_max_size, wasm_rt_funcref_t {
|
if (wasm_rt_grow_funcref_table(&instance->w2c_T0, old_max_size, wasm_rt_funcref_t {
|
||||||
.func_type = wasm2c_ruby_get_func_type(0, 0),
|
.func_type = wasm2c_ruby_get_func_type(0, 0),
|
||||||
.func = NULL,
|
.func = NULL,
|
||||||
.func_tailcallee = {.fn = NULL},
|
.func_tailcallee = {.fn = NULL},
|
||||||
.module_instance = module_instance.get(),
|
.module_instance = instance.get(),
|
||||||
}) != old_max_size) {
|
}) != old_max_size) {
|
||||||
module_instance->w2c_T0.max_size = old_max_size;
|
instance->w2c_T0.max_size = old_max_size;
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return next_func_ptr++;
|
return next_func_ptr++;
|
||||||
|
@ -275,7 +279,8 @@ HEREDOC
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
declarations = []
|
declarations = []
|
||||||
bindings = []
|
coroutines = []
|
||||||
|
func_names = []
|
||||||
|
|
||||||
File.readlines('tags', chomp: true).each do |line|
|
File.readlines('tags', chomp: true).each do |line|
|
||||||
line = line.split("\t")
|
line = line.split("\t")
|
||||||
|
@ -300,7 +305,8 @@ File.readlines('tags', chomp: true).each do |line|
|
||||||
args = args.gsub('VALUE,VALUE', '$').split(',').map { |arg| arg.gsub('$', 'VALUE,VALUE') }.map { |arg| arg == '...' ? '...' : arg.match?(/\(\* \w+\)/) ? arg.gsub(/\(\* \w+\)/, '(*)') : arg.rpartition(' ')[0].strip }
|
args = args.gsub('VALUE,VALUE', '$').split(',').map { |arg| arg.gsub('$', 'VALUE,VALUE') }.map { |arg| arg == '...' ? '...' : arg.match?(/\(\* \w+\)/) ? arg.gsub(/\(\* \w+\)/, '(*)') : arg.rpartition(' ')[0].strip }
|
||||||
next unless (0...args.length).all? { |i| args[i] == '...' || (ARG_HANDLERS.include?(args[i]) && (ARG_HANDLERS[args[i]][:condition].nil? || ARG_HANDLERS[args[i]][:condition].call(func_name, args, i))) }
|
next unless (0...args.length).all? { |i| args[i] == '...' || (ARG_HANDLERS.include?(args[i]) && (ARG_HANDLERS[args[i]][:condition].nil? || ARG_HANDLERS[args[i]][:condition].call(func_name, args, i))) }
|
||||||
|
|
||||||
binding = ''
|
coroutine_initializer = ''
|
||||||
|
destructor = []
|
||||||
transformed_args = Set[]
|
transformed_args = Set[]
|
||||||
buffers = []
|
buffers = []
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -310,129 +316,176 @@ File.readlines('tags', chomp: true).each do |line|
|
||||||
|
|
||||||
# Generate bindings for converting the arguments
|
# Generate bindings for converting the arguments
|
||||||
if !handler[:func_ptr_args].nil? || handler[:anyargs]
|
if !handler[:func_ptr_args].nil? || handler[:anyargs]
|
||||||
binding += "wasm_ptr_t v#{i} = _sbindgen_create_func_ptr();\n"
|
coroutine_initializer += <<~HEREDOC
|
||||||
binding += "if (v#{i} == (wasm_ptr_t)-1) {\n"
|
f#{i} = bind.sbindgen_create_func_ptr();
|
||||||
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
|
if (f#{i} == 0) {
|
||||||
binding += " throw SandboxOutOfMemoryException();\n"
|
|
||||||
binding += "}\n"
|
|
||||||
if handler[:anyargs]
|
|
||||||
binding += <<~HEREDOC
|
|
||||||
module_instance->w2c_T0.data[v#{i}] = wasm_rt_funcref_t {
|
|
||||||
.func_type = wasm2c_#{MODULE_NAME}_get_func_type(a#{args.length - 1} == -1 ? 3 : a#{args.length - 1} == -2 ? 2 : a#{args.length - 1} + 1, 1, #{([:size] * 16).map { |type| FUNC_TYPE_TABLE[type] }.join(', ')}),
|
|
||||||
.func = (wasm_rt_function_ptr_t)a#{i},
|
|
||||||
.func_tailcallee = {.fn = NULL},
|
|
||||||
.module_instance = module_instance.get(),
|
|
||||||
};
|
|
||||||
HEREDOC
|
|
||||||
else
|
|
||||||
binding += <<~HEREDOC
|
|
||||||
module_instance->w2c_T0.data[v#{i}] = wasm_rt_funcref_t {
|
|
||||||
.func_type = wasm2c_#{MODULE_NAME}_get_func_type(#{handler[:func_ptr_args].length}, #{handler[:func_ptr_rets].length}#{handler[:func_ptr_args].empty? && handler[:func_ptr_rets].empty? ? '' : ', ' + (handler[:func_ptr_args] + handler[:func_ptr_rets]).map { |type| FUNC_TYPE_TABLE[type] }.join(', ')}),
|
|
||||||
.func = (wasm_rt_function_ptr_t)a#{i},
|
|
||||||
.func_tailcallee = {.fn = NULL},
|
|
||||||
.module_instance = module_instance.get(),
|
|
||||||
};
|
|
||||||
HEREDOC
|
|
||||||
end
|
|
||||||
binding += "\n"
|
|
||||||
transformed_args.add(i)
|
|
||||||
elsif !handler[:buf_size].nil?
|
|
||||||
binding += <<~HEREDOC
|
|
||||||
wasm_ptr_t v#{i} = _sbindgen_malloc(#{handler[:buf_size].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}")});
|
|
||||||
if (v#{i} == 0) {
|
|
||||||
HEREDOC
|
HEREDOC
|
||||||
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
|
buffers.reverse_each { |buf| coroutine_initializer += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(bind.instance.get(), #{buf});\n" }
|
||||||
binding += <<~HEREDOC
|
coroutine_initializer += <<~HEREDOC
|
||||||
throw SandboxOutOfMemoryException();
|
throw SandboxOutOfMemoryException();
|
||||||
}
|
}
|
||||||
HEREDOC
|
HEREDOC
|
||||||
binding += handler[:serialize].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}").gsub('BUF', "v#{i}")
|
if handler[:anyargs]
|
||||||
binding += "\n"
|
coroutine_initializer += <<~HEREDOC
|
||||||
|
bind.instance->w2c_T0.data[f#{i}] = wasm_rt_funcref_t {
|
||||||
|
.func_type = wasm2c_#{MODULE_NAME}_get_func_type(a#{args.length - 1} == -1 ? 3 : a#{args.length - 1} == -2 ? 2 : a#{args.length - 1} + 1, 1, #{([:size] * 16).map { |type| FUNC_TYPE_TABLE[type] }.join(', ')}),
|
||||||
|
.func = (wasm_rt_function_ptr_t)a#{i},
|
||||||
|
.func_tailcallee = {.fn = NULL},
|
||||||
|
.module_instance = bind.instance.get(),
|
||||||
|
};
|
||||||
|
HEREDOC
|
||||||
|
else
|
||||||
|
coroutine_initializer += <<~HEREDOC
|
||||||
|
bind.instance->w2c_T0.data[f#{i}] = wasm_rt_funcref_t {
|
||||||
|
.func_type = wasm2c_#{MODULE_NAME}_get_func_type(#{handler[:func_ptr_args].length}, #{handler[:func_ptr_rets].length}#{handler[:func_ptr_args].empty? && handler[:func_ptr_rets].empty? ? '' : ', ' + (handler[:func_ptr_args] + handler[:func_ptr_rets]).map { |type| FUNC_TYPE_TABLE[type] }.join(', ')}),
|
||||||
|
.func = (wasm_rt_function_ptr_t)a#{i},
|
||||||
|
.func_tailcallee = {.fn = NULL},
|
||||||
|
.module_instance = bind.instance.get(),
|
||||||
|
};
|
||||||
|
HEREDOC
|
||||||
|
end
|
||||||
|
coroutine_initializer += "\n"
|
||||||
transformed_args.add(i)
|
transformed_args.add(i)
|
||||||
buffers.append("v#{i}")
|
elsif !handler[:buf_size].nil?
|
||||||
|
coroutine_initializer += <<~HEREDOC
|
||||||
|
f#{i} = bind.sbindgen_malloc(#{handler[:buf_size].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}")});
|
||||||
|
if (f#{i} == 0) {
|
||||||
|
HEREDOC
|
||||||
|
buffers.reverse_each { |buf| coroutine_initializer += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(bind.instance.get(), #{buf});\n" }
|
||||||
|
coroutine_initializer += <<~HEREDOC
|
||||||
|
throw SandboxOutOfMemoryException();
|
||||||
|
}
|
||||||
|
HEREDOC
|
||||||
|
coroutine_initializer += handler[:serialize].gsub('PREV_ARG', "a#{i - 1}").gsub('ARG', "a#{i}").gsub('BUF', "f#{i}")
|
||||||
|
coroutine_initializer += "\n"
|
||||||
|
transformed_args.add(i)
|
||||||
|
buffers.append("f#{i}")
|
||||||
end
|
end
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
coroutine_vars = []
|
||||||
|
|
||||||
# If this is a varargs function, manually generate bindings for getting the varargs based on the function name
|
# If this is a varargs function, manually generate bindings for getting the varargs based on the function name
|
||||||
if !args.empty? && args[-1] == '...'
|
if !args.empty? && args[-1] == '...'
|
||||||
case func_name
|
case func_name
|
||||||
when 'rb_funcall'
|
when 'rb_funcall'
|
||||||
binding += <<~HEREDOC
|
coroutine_initializer += <<~HEREDOC
|
||||||
wasm_ptr_t v = _sbindgen_malloc(a#{args.length - 2} * sizeof(VALUE));
|
f#{args.length - 1} = bind.sbindgen_malloc(a#{args.length - 2} * sizeof(VALUE));
|
||||||
if (v == 0) {
|
if (f#{args.length - 1} == 0) {
|
||||||
HEREDOC
|
HEREDOC
|
||||||
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
|
buffers.reverse_each { |buf| coroutine_initializer += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(bind.instance.get(), #{buf});\n" }
|
||||||
binding += <<~HEREDOC
|
coroutine_initializer += <<~HEREDOC
|
||||||
throw SandboxOutOfMemoryException();
|
throw SandboxOutOfMemoryException();
|
||||||
}
|
}
|
||||||
std::va_list a;
|
std::va_list a;
|
||||||
va_start(a, a#{args.length - 2});
|
va_start(a, a#{args.length - 2});
|
||||||
for (long i = 0; i < a#{args.length - 2}; ++i) {
|
for (long i = 0; i < a#{args.length - 2}; ++i) {
|
||||||
((VALUE *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
|
((VALUE *)(bind.instance->w2c_memory.data + f#{args.length - 1}))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
|
||||||
}
|
}
|
||||||
va_end(a);
|
va_end(a);
|
||||||
HEREDOC
|
HEREDOC
|
||||||
binding += "\n"
|
coroutine_initializer += "\n"
|
||||||
buffers.append('v')
|
buffers.append("f#{args.length - 1}")
|
||||||
when 'rb_rescue2'
|
when 'rb_rescue2'
|
||||||
binding += <<~HEREDOC
|
coroutine_vars.append('wasm_size_t n')
|
||||||
|
coroutine_initializer += <<~HEREDOC
|
||||||
std::va_list a, b;
|
std::va_list a, b;
|
||||||
va_start(a, a#{args.length - 2});
|
va_start(a, a#{args.length - 2});
|
||||||
va_copy(b, a);
|
va_copy(b, a);
|
||||||
wasm_size_t n = 0;
|
n = 0;
|
||||||
do ++n; while (va_arg(b, VALUE));
|
do ++n; while (va_arg(b, VALUE));
|
||||||
va_end(b);
|
va_end(b);
|
||||||
wasm_ptr_t v = _sbindgen_malloc(n * sizeof(VALUE));
|
f#{args.length - 1} = bind.sbindgen_malloc(n * sizeof(VALUE));
|
||||||
if (v == 0) {
|
if (f#{args.length - 1} == 0) {
|
||||||
va_end(a);
|
va_end(a);
|
||||||
HEREDOC
|
HEREDOC
|
||||||
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
|
buffers.reverse_each { |buf| coroutine_initializer += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(bind.instance.get(), #{buf});\n" }
|
||||||
binding += <<~HEREDOC
|
coroutine_initializer += <<~HEREDOC
|
||||||
throw SandboxOutOfMemoryException();
|
throw SandboxOutOfMemoryException();
|
||||||
}
|
}
|
||||||
for (wasm_size_t i = 0; i < n; ++i) {
|
for (wasm_size_t i = 0; i < n; ++i) {
|
||||||
((VALUE *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
|
((VALUE *)(bind.instance->w2c_memory.data + f#{args.length - 1}))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
|
||||||
}
|
}
|
||||||
HEREDOC
|
HEREDOC
|
||||||
binding += "\n"
|
coroutine_initializer += "\n"
|
||||||
buffers.append('v')
|
buffers.append("f#{args.length - 1}")
|
||||||
else
|
else
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generate bindings for running the actual function
|
|
||||||
handler = RET_HANDLERS[ret]
|
handler = RET_HANDLERS[ret]
|
||||||
if handler[:primitive] != :void
|
|
||||||
binding += <<~HEREDOC
|
fields = (0...args.length).filter_map do |i|
|
||||||
#{!RET_HANDLERS[ret][:keep] ? VAR_TYPE_TABLE[RET_HANDLERS[ret][:primitive]] : ret} r;
|
(args[i] == '...' || transformed_args.include?(i)) && "wasm_ptr_t f#{i}"
|
||||||
do r = w2c_#{MODULE_NAME}_#{func_name}(#{(['module_instance.get()'] + (0...args.length).map { |i| args[i] == '...' ? 'v' : transformed_args.include?(i) ? "v#{i}" : "a#{i}" }).join(', ')}); while (w2c_#{MODULE_NAME}_#{YIELD_FUNC}(module_instance.get()));
|
|
||||||
HEREDOC
|
|
||||||
else
|
|
||||||
binding += "do w2c_#{MODULE_NAME}_#{func_name}(#{(['module_instance.get()'] + (0...args.length).map { |i| args[i] == '...' ? 'v' : transformed_args.include?(i) ? "v#{i}" : "a#{i}" }).join(', ')}); while (w2c_#{MODULE_NAME}_#{YIELD_FUNC}(module_instance.get()));\n"
|
|
||||||
end
|
|
||||||
buffers.reverse_each { |buf| binding += "w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
|
|
||||||
if handler[:primitive] != :void
|
|
||||||
binding += "return r;\n"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
declarations.append("#{!RET_HANDLERS[ret][:keep] ? VAR_TYPE_TABLE[RET_HANDLERS[ret][:primitive]] : ret} #{func_name}(#{(0...args.length).map { |i| args[i] == '...' ? '...' : !ARG_HANDLERS[args[i]][:declaration].nil? ? ARG_HANDLERS[args[i]][:declaration] : !ARG_HANDLERS[args[i]][:keep] ? VAR_TYPE_TABLE[ARG_HANDLERS[args[i]][:primitive]] : args[i] }.join(', ')});\n")
|
coroutine_ret = !RET_HANDLERS[ret][:keep] ? VAR_TYPE_TABLE[RET_HANDLERS[ret][:primitive]] : ret;
|
||||||
bindings.append("#{!RET_HANDLERS[ret][:keep] ? VAR_TYPE_TABLE[RET_HANDLERS[ret][:primitive]] : ret} SandboxBind::#{func_name}(#{(0...args.length).map { |i| args[i] == '...' ? '...' : !ARG_HANDLERS[args[i]][:formatter].nil? ? ARG_HANDLERS[args[i]][:formatter].call("a#{i}") : !ARG_HANDLERS[args[i]][:keep] ? "#{VAR_TYPE_TABLE[ARG_HANDLERS[args[i]][:primitive]]} a#{i}" : "#{args[i]} a#{i}" }.join(', ')}) {\n#{binding.split("\n").map { |line| " #{line}".rstrip() }.join("\n")}\n}\n")
|
|
||||||
|
coroutine_vars.append("#{coroutine_ret} r") if handler[:primitive] != :void
|
||||||
|
|
||||||
|
coroutine_args = ['SandboxBind &bind']
|
||||||
|
coroutine_args.append((0...args.length).map do |i|
|
||||||
|
args[i] == '...' ? '...'
|
||||||
|
: !ARG_HANDLERS[args[i]][:formatter].nil? ? ARG_HANDLERS[args[i]][:formatter].call("a#{i}")
|
||||||
|
: !ARG_HANDLERS[args[i]][:keep] ? "#{VAR_TYPE_TABLE[ARG_HANDLERS[args[i]][:primitive]]} a#{i}"
|
||||||
|
: "#{args[i]} a#{i}"
|
||||||
|
end)
|
||||||
|
|
||||||
|
declaration_args = ['SandboxBind &']
|
||||||
|
declaration_args.append((0...args.length).map do |i|
|
||||||
|
args[i] == '...' ? '...'
|
||||||
|
: !ARG_HANDLERS[args[i]][:formatter].nil? ? ARG_HANDLERS[args[i]][:formatter].call('')
|
||||||
|
: !ARG_HANDLERS[args[i]][:keep] ? "#{VAR_TYPE_TABLE[ARG_HANDLERS[args[i]][:primitive]]}"
|
||||||
|
: "#{args[i]}"
|
||||||
|
end)
|
||||||
|
|
||||||
|
coroutine_inner = <<~HEREDOC
|
||||||
|
#{handler[:primitive] == :void ? '' : 'r = '}w2c_#{MODULE_NAME}_#{func_name}(#{(['bind.instance.get()'] + (0...args.length).map { |i| args[i] == '...' || transformed_args.include?(i) ? "f#{i}" : "a#{i}" }).join(', ')});
|
||||||
|
if (w2c_#{MODULE_NAME}_#{COMPLETE_FUNC}(bind.instance.get())) break;
|
||||||
|
yield;
|
||||||
|
HEREDOC
|
||||||
|
|
||||||
|
coroutine_finalizer = (0...buffers.length).map { |i| "w2c_#{MODULE_NAME}_#{FREE_FUNC}(bind.instance.get(), #{buffers[buffers.length - 1 - i]});" }
|
||||||
|
|
||||||
|
coroutine_definition = <<~HEREDOC
|
||||||
|
#{coroutine_ret} #{func_name}::operator()(#{coroutine_args.join(', ')}) {#{coroutine_vars.empty? ? '' : (coroutine_vars.map { |var| "\n #{var} = 0;" }.join + "\n")}
|
||||||
|
reenter (this) {
|
||||||
|
#{coroutine_initializer.empty? ? '' : (coroutine_initializer.split("\n").map { |line| " #{line}" }.join("\n") + "\n\n")} for (;;) {
|
||||||
|
#{coroutine_inner.split("\n").map { |line| " #{line}" }.join("\n")}
|
||||||
|
}#{coroutine_finalizer.empty? ? '' : ("\n\n" + coroutine_finalizer.map { |line| " #{line}" }.join("\n"))}
|
||||||
|
}#{handler[:primitive] == :void ? '' : "\n\n return r;"}
|
||||||
|
}
|
||||||
|
HEREDOC
|
||||||
|
|
||||||
|
coroutine_declaration = <<~HEREDOC
|
||||||
|
struct #{func_name} : boost::asio::coroutine {
|
||||||
|
#{coroutine_ret} operator()(#{declaration_args.join(', ')});
|
||||||
|
#{fields.empty? ? '' : (" private:\n" + fields.map { |field| " #{field};\n" }.join)}};
|
||||||
|
HEREDOC
|
||||||
|
|
||||||
|
func_names.append(func_name)
|
||||||
|
coroutines.append(coroutine_definition)
|
||||||
|
declarations.append(coroutine_declaration)
|
||||||
end
|
end
|
||||||
|
|
||||||
File.open('mkxp-sandbox-bindgen.h', 'w') do |file|
|
File.open('mkxp-sandbox-bindgen.h', 'w') do |file|
|
||||||
file.write(HEADER_START)
|
file.write(HEADER_START)
|
||||||
|
for func_name in func_names
|
||||||
|
file.write(" friend struct #{func_name};\n")
|
||||||
|
end
|
||||||
|
file.write("};\n")
|
||||||
for declaration in declarations
|
for declaration in declarations
|
||||||
file.write(' ' + declaration)
|
file.write("\n" + declaration)
|
||||||
end
|
end
|
||||||
file.write(HEADER_END)
|
file.write(HEADER_END)
|
||||||
end
|
end
|
||||||
File.open('mkxp-sandbox-bindgen.cpp', 'w') do |file|
|
File.open('mkxp-sandbox-bindgen.cpp', 'w') do |file|
|
||||||
file.write(PRELUDE)
|
file.write(PRELUDE)
|
||||||
for binding in bindings
|
for coroutine in coroutines
|
||||||
file.write("\n\n")
|
file.write("\n\n")
|
||||||
file.write(binding)
|
file.write(coroutine)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
36
src/core.cpp
36
src/core.cpp
|
@ -24,9 +24,13 @@
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <boost/asio/coroutine.hpp>
|
||||||
|
#include <boost/asio/yield.hpp>
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "sandbox/sandbox.h"
|
#include "sandbox/sandbox.h"
|
||||||
|
|
||||||
|
#define AWAIT(coroutine, ...) do { coroutine(__VA_ARGS__); if (coroutine.is_complete()) break; yield; } while (1)
|
||||||
|
|
||||||
using namespace mkxp_retro;
|
using namespace mkxp_retro;
|
||||||
|
|
||||||
static void fallback_log(enum retro_log_level level, const char *fmt, ...) {
|
static void fallback_log(enum retro_log_level level, const char *fmt, ...) {
|
||||||
|
@ -46,19 +50,37 @@ static VALUE my_cpp_func(w2c_ruby *ruby, int32_t argc, wasm_ptr_t argv, VALUE se
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool init_sandbox() {
|
static bool init_sandbox() {
|
||||||
|
struct runtime : boost::asio::coroutine {
|
||||||
|
struct rb_eval_string eval;
|
||||||
|
struct rb_define_global_function define;
|
||||||
|
|
||||||
|
void operator()() {
|
||||||
|
reenter (this) {
|
||||||
|
AWAIT(eval, sandbox->bind, "puts 'Hello, World!'");
|
||||||
|
|
||||||
|
eval = rb_eval_string();
|
||||||
|
AWAIT(eval, sandbox->bind, "require 'zlib'; p Zlib::Deflate::deflate('hello')");
|
||||||
|
|
||||||
|
AWAIT(define, sandbox->bind, "my_cpp_func", (VALUE (*)(void *, ANYARGS))my_cpp_func, -1);
|
||||||
|
|
||||||
|
eval = rb_eval_string();
|
||||||
|
AWAIT(eval, sandbox->bind, "my_cpp_func(1, nil, 3, 'this is a string', :symbol, 2)");
|
||||||
|
|
||||||
|
eval = rb_eval_string();
|
||||||
|
AWAIT(eval, sandbox->bind, "p Dir.glob '/mkxp-retro-game/*'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
sandbox.reset();
|
sandbox.reset();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sandbox.reset(new Sandbox(game_path));
|
sandbox.reset(new Sandbox(game_path));
|
||||||
|
|
||||||
sandbox->bind.rb_eval_string("puts 'Hello, World!'");
|
struct runtime runtime;
|
||||||
|
|
||||||
sandbox->bind.rb_eval_string("require 'zlib'; p Zlib::Deflate::deflate('hello')");
|
// TODO: Replace this loop with a stackful executor, otherwise you won't be able to call into the Ruby API from inside of a C/C++ function that is itself called from inside of Ruby.
|
||||||
|
do runtime(); while (w2c_ruby_mkxp_sandbox_yield(&sandbox->module_instance()));
|
||||||
sandbox->bind.rb_define_global_function("my_cpp_func", (VALUE (*)(void *, ANYARGS))my_cpp_func, -1);
|
|
||||||
sandbox->bind.rb_eval_string("my_cpp_func(1, nil, 3, 'this is a string', :symbol, 2)");
|
|
||||||
|
|
||||||
sandbox->bind.rb_eval_string("p Dir.glob '/mkxp-retro-game/*'");
|
|
||||||
} catch (SandboxException) {
|
} catch (SandboxException) {
|
||||||
log_printf(RETRO_LOG_ERROR, "Failed to initialize Ruby\n");
|
log_printf(RETRO_LOG_ERROR, "Failed to initialize Ruby\n");
|
||||||
sandbox.reset();
|
sandbox.reset();
|
||||||
|
|
|
@ -145,3 +145,7 @@ Sandbox::~Sandbox() {
|
||||||
wasm2c_ruby_free(RB);
|
wasm2c_ruby_free(RB);
|
||||||
wasm_rt_free();
|
wasm_rt_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w2c_ruby &Sandbox::module_instance() {
|
||||||
|
return *ruby;
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct Sandbox {
|
||||||
SandboxBind bind;
|
SandboxBind bind;
|
||||||
Sandbox(const char *game_path);
|
Sandbox(const char *game_path);
|
||||||
~Sandbox();
|
~Sandbox();
|
||||||
|
struct w2c_ruby &module_instance();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MKXPZ_SANDBOX_H
|
#endif // MKXPZ_SANDBOX_H
|
||||||
|
|
4
subprojects/boost_asio.wrap
Normal file
4
subprojects/boost_asio.wrap
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[wrap-git]
|
||||||
|
url = https://github.com/boostorg/asio
|
||||||
|
revision = boost-1.87.0
|
||||||
|
depth = 1
|
Loading…
Add table
Reference in a new issue