Skip to content

Commit

Permalink
Use C compiler to preprocess Ruby headers before passing to Ctags
Browse files Browse the repository at this point in the history
Ctags by itself misses a few functions defined in the Ruby API.
  • Loading branch information
white-axe committed Jan 5, 2025
1 parent 19cafa3 commit 1439b31
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 32 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/autobuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ jobs:
- uses: actions/cache@v4
with:
path: |
retro/build
key: retro-phase1-${{ hashFiles('retro/Makefile', 'retro/extra-ruby-bindings.h') }}
retro/build/retro-phase1
key: retro-phase1-${{ hashFiles('retro/Makefile', 'retro/extra-ruby-bindings.h', 'retro/sandbox-bindgen.rb') }}

- name: Install apt dependencies
run: |
Expand Down
10 changes: 7 additions & 3 deletions retro/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ clean:
clean-ruby-bindings:
rm -f $(OUTDIR)/mkxp-sandbox-bindgen.cpp; \
rm -f $(OUTDIR)/mkxp-sandbox-bindgen.h; \
rm -f $(LIBDIR)/tags
rm -f $(LIBDIR)/tags.c; \
rm -f $(LIBDIR)/tags; \

$(OUTDIR)/libretro.h:
mkdir -p $(OUTDIR); \
Expand Down Expand Up @@ -112,8 +113,11 @@ $(OUTDIR)/mkxp-sandbox-bindgen.cpp $(OUTDIR)/mkxp-sandbox-bindgen.h: $(LIBDIR)/t
mv mkxp-sandbox-bindgen.h $(OUTDIR); \
mv mkxp-sandbox-bindgen.cpp $(OUTDIR)

$(LIBDIR)/tags: $(LIBDIR)/include/ruby-$(RUBY_VERSION).0
$(CTAGS) -R --fields=S --kinds-c=p --kinds-c++=p -o $(LIBDIR)/tags $(LIBDIR)/include/ruby-$(RUBY_VERSION).0
$(LIBDIR)/tags: $(LIBDIR)/tags.c
$(CTAGS) -R --fields=S --kinds-c=p --kinds-c++=p -o $(LIBDIR)/tags $(LIBDIR)/tags.c

$(LIBDIR)/tags.c: $(RUBY) $(LIBDIR)/include/ruby-$(RUBY_VERSION).0
echo '#include <ruby.h>' | $(CC) -E -I$(LIBDIR)/include/ruby-$(RUBY_VERSION).0 -I$(LIBDIR)/include/ruby-$(RUBY_VERSION).0/$(shell $(RUBY) -e 'puts RUBY_PLATFORM') -o $(LIBDIR)/tags.c -

$(RUBY) $(LIBDIR)/include/ruby-$(RUBY_VERSION).0: $(DOWNLOADS)/baseruby/Makefile
cd $(DOWNLOADS)/baseruby; \
Expand Down
67 changes: 41 additions & 26 deletions retro/sandbox-bindgen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@
]

ARG_HANDLERS = {
'VALUE' => { keep: true, primitive: :ptr },
'ID' => { keep: true, primitive: :ptr },
'VALUE' => { keep: true, primitive: :size },
'ID' => { keep: true, primitive: :size },
'int' => { primitive: :s32 },
'unsigned int' => { primitive: :u32 },
'long' => { primitive: :size },
'unsigned long' => { primitive: :size },
'long long' => { primitive: :s64 },
'unsigned long long' => { primitive: :u64 },
'const char *' => {
keep: true,
buf_size: 'std::strlen(ARG) + 1',
Expand All @@ -57,44 +61,50 @@
'const VALUE *' => {
keep: true,
condition: lambda { |func_name, args, arg_index| arg_index > 0 && args[arg_index - 1] == 'int' }, # Only handle arguments of type `const VALUE *` if the previous argument is of type `int`
buf_size: 'PREV_ARG * sizeof(wasm_ptr_t)',
buf_size: 'PREV_ARG * sizeof(VALUE)',
serialize: <<~HEREDOC
for (int i = 0; i < PREV_ARG; ++i) {
((wasm_ptr_t *)(module_instance->w2c_memory.data + BUF))[i] = SERIALIZE_PTR(ARG[i]);
((VALUE *)(module_instance->w2c_memory.data + BUF))[i] = SERIALIZE_PTR(ARG[i]);
}
HEREDOC
},
'VALUE (*)(ANYARGS)' => {
'VALUE (*)()' => {
keep: true,
anyargs: true,
formatter: lambda { |name| "VALUE (*#{name})(void *, ANYARGS)" },
declaration: 'VALUE (*)(void *, ANYARGS)',
},
'VALUE (*)(VALUE)' => {
keep: true,
func_ptr_args: [:ptr],
func_ptr_rets: [:ptr],
func_ptr_args: [:size],
func_ptr_rets: [:size],
formatter: lambda { |name| "VALUE (*#{name})(void *, VALUE)" },
declaration: 'VALUE (*)(void *, VALUE)',
},
'VALUE (*)(VALUE,VALUE)' => {
keep: true,
func_ptr_args: [:ptr, :ptr],
func_ptr_rets: [:ptr],
func_ptr_args: [:size, :size],
func_ptr_rets: [:size],
formatter: lambda { |name| "VALUE (*#{name})(void *, VALUE, VALUE)" },
declaration: 'VALUE (*)(void *, VALUE, VALUE)',
},
}

RET_HANDLERS = {
'void' => { keep: true, primitive: :void },
'VALUE' => { keep: true, primitive: :ptr },
'ID' => { keep: true, primitive: :ptr },
'VALUE' => { keep: true, primitive: :size },
'ID' => { keep: true, primitive: :size },
'int' => { primitive: :s32 },
'unsigned int' => { primitive: :u32 },
'long' => { primitive: :size },
'unsigned long' => { primitive: :size },
'long long' => { primitive: :s64 },
'unsigned long long' => { primitive: :u64 },
}

VAR_TYPE_TABLE = {
ssize: 'wasm_ssize_t',
size: 'wasm_size_t',
ptr: 'wasm_ptr_t',
s32: 'int32_t',
u32: 'uint32_t',
Expand All @@ -105,7 +115,9 @@
}

FUNC_TYPE_TABLE = {
ptr: 'WASM_RT_I32',
ssize: MEMORY64 ? 'WASM_RT_I64' : 'WASM_RT_I32',
size: MEMORY64 ? 'WASM_RT_I64' : 'WASM_RT_I32',
ptr: MEMORY64 ? 'WASM_RT_I64' : 'WASM_RT_I32',
s32: 'WASM_RT_I32',
u32: 'WASM_RT_I32',
s64: 'WASM_RT_I64',
Expand Down Expand Up @@ -150,9 +162,11 @@
// Autogenerated by sandbox-bindgen.rb. Don't manually modify this file - modify sandbox-bindgen.rb instead!
#define ANYARGS ...
typedef uint#{MEMORY64 ? '64' : '32'}_t wasm_ptr_t;
typedef wasm_ptr_t VALUE;
typedef wasm_ptr_t ID;
typedef int#{MEMORY64 ? '64' : '32'}_t wasm_ssize_t;
typedef uint#{MEMORY64 ? '64' : '32'}_t wasm_size_t;
typedef wasm_size_t wasm_ptr_t;
typedef wasm_size_t VALUE;
typedef wasm_size_t ID;
struct SandboxBind {
private:
Expand Down Expand Up @@ -212,7 +226,7 @@
SandboxBind::SandboxBind(std::shared_ptr<struct w2c_#{MODULE_NAME}> m) : next_func_ptr(m->w2c_T0.size), module_instance(m) {}
wasm_ptr_t SandboxBind::_sbindgen_malloc(wasm_ptr_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);
// Verify that the entire allocated buffer is in valid memory
Expand All @@ -231,13 +245,13 @@
}
// Make sure that an integer overflow won't occur if we double the max size of the funcref table
wasm_ptr_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)) {
return -1;
}
// Double the max size of the funcref table
wasm_ptr_t old_max_size = module_instance->w2c_T0.max_size;
wasm_size_t old_max_size = module_instance->w2c_T0.max_size;
module_instance->w2c_T0.max_size = new_max_size;
// Double the size of the funcref table buffer
Expand Down Expand Up @@ -268,6 +282,7 @@

func_name = line[0]
next unless func_name.start_with?('rb_')
next if func_name.end_with?('_static')
next if IGNORED_FUNCTIONS.include?(func_name)

# Only bind functions whose return type matches one of the return types we have a handler for
Expand Down Expand Up @@ -303,7 +318,7 @@
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, #{([:ptr] * 16).map { |type| FUNC_TYPE_TABLE[type] }.join(', ')}),
.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(),
Expand Down Expand Up @@ -345,7 +360,7 @@
case func_name
when 'rb_funcall'
binding += <<~HEREDOC
wasm_ptr_t v = _sbindgen_malloc(a#{args.length - 2} * sizeof(wasm_ptr_t));
wasm_ptr_t v = _sbindgen_malloc(a#{args.length - 2} * sizeof(VALUE));
if (v == 0) {
HEREDOC
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
Expand All @@ -355,7 +370,7 @@
std::va_list a;
va_start(a, a#{args.length - 2});
for (long i = 0; i < a#{args.length - 2}; ++i) {
((wasm_ptr_t *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, wasm_ptr_t));
((VALUE *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
}
va_end(a);
HEREDOC
Expand All @@ -366,19 +381,19 @@
std::va_list a, b;
va_start(a, a#{args.length - 2});
va_copy(b, a);
wasm_ptr_t n = 0;
do ++n; while (va_arg(b, wasm_ptr_t));
wasm_size_t n = 0;
do ++n; while (va_arg(b, VALUE));
va_end(b);
wasm_ptr_t v = _sbindgen_malloc(n * sizeof(wasm_ptr_t));
wasm_ptr_t v = _sbindgen_malloc(n * sizeof(VALUE));
if (v == 0) {
va_end(a);
HEREDOC
buffers.reverse_each { |buf| binding += " w2c_#{MODULE_NAME}_#{FREE_FUNC}(module_instance.get(), #{buf});\n" }
binding += <<~HEREDOC
throw SandboxOutOfMemoryException();
}
for (wasm_ptr_t i = 0; i < n; ++i) {
((wasm_ptr_t *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, wasm_ptr_t));
for (wasm_size_t i = 0; i < n; ++i) {
((VALUE *)(module_instance->w2c_memory.data + v))[i] = SERIALIZE_PTR(va_arg(a, VALUE));
}
HEREDOC
binding += "\n"
Expand Down
1 change: 0 additions & 1 deletion src/sandbox/wasi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <sstream>
#include <zip.h>
#include "wasi.h"
#include "sandbox.h"
#include <mkxp-retro-ruby.h>
#include "../core.h"

Expand Down

0 comments on commit 1439b31

Please sign in to comment.