From 118934a3931c876f52c9f4452247572612b89509 Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Tue, 8 Sep 2026 23:12:52 +0100 Subject: [PATCH] Make mruby raii cleanup, and fix cleanup bug. - Functions were cleaned up after mruby, which was wrong. - Becuase functions could have stored a reference to a Block which refers to mruby. - And so it would crash on release builds at exit if custom functions were used. --- include/bed.h | 4 +--- include/internal/scripting/ruby.h | 24 ++++++++++++++++++++++++ src/bed/bed.cc | 13 +------------ src/internal/scripting/ruby.cc | 15 ++++++++------- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/include/bed.h b/include/bed.h index a211388..4c04832 100644 --- a/include/bed.h +++ b/include/bed.h @@ -14,6 +14,7 @@ namespace bed { struct BEd { + internal::scripting::RubyState mrb; internal::trie::Trie functions; internal::functions::Function no_op; internal::functions::Function eof_op; @@ -24,9 +25,6 @@ struct BEd { internal::vase::AppendStorage append{"/tmp"}; std::unordered_map buffers; - mrb_state *mrb = nullptr; - int arena; - bool help_mode = false; bool prompt_mode = true; std::function prompt = nullptr; diff --git a/include/internal/scripting/ruby.h b/include/internal/scripting/ruby.h index 5030bc4..d8a4fd1 100644 --- a/include/internal/scripting/ruby.h +++ b/include/internal/scripting/ruby.h @@ -4,6 +4,30 @@ #include "pch.h" namespace bed::internal::scripting { +struct RubyState { + mrb_state *state = nullptr; + int arena = 0; + + explicit RubyState(BEd *ctx) : state(mrb_open()) { + if (!state) + throw fatal_error("Failed to initialize mruby.", 1); + state->ud = ctx; + arena = mrb_gc_arena_save(state); + } + + ~RubyState() { + state->ud = nullptr; + if (state) { + mrb_gc_arena_restore(state, arena); + mrb_full_gc(state); + mrb_close(state); + } + } + + RubyState(const RubyState &) = delete; + RubyState &operator=(const RubyState &) = delete; +}; + struct Block { mrb_state *mrb = nullptr; mrb_value proc = mrb_nil_value(); diff --git a/src/bed/bed.cc b/src/bed/bed.cc index 37f660e..850b62f 100644 --- a/src/bed/bed.cc +++ b/src/bed/bed.cc @@ -2,7 +2,7 @@ namespace bed { BEd::BEd(std::vector args) - : theme(internal::theme::Theme::default_theme()), io(*this) { + : mrb(this), theme(internal::theme::Theme::default_theme()), io(*this) { std::string prompt_ = ""; std::string file = ""; bool suppress = false; @@ -42,11 +42,6 @@ BEd::BEd(std::vector args) && strcmp(colorterm, "24bit") != 0) color = false; color_mode = color; - mrb = mrb_open(); - if (!mrb) - throw fatal_error("Failed to initialize mruby.", 1); - mrb->ud = this; - arena = mrb_gc_arena_save(mrb); internal::functions::Function::register_posix(*this); internal::functions::Function::register_extented(*this); internal::functions::Suffix::register_suffixes(*this); @@ -70,12 +65,6 @@ BEd::~BEd() { delete buffer; for (auto &[_, lang] : languages) delete lang; - mrb->ud = nullptr; - if (mrb) { - mrb_gc_arena_restore(mrb, arena); - mrb_full_gc(mrb); - mrb_close(mrb); - } } void BEd::print_help() { diff --git a/src/internal/scripting/ruby.cc b/src/internal/scripting/ruby.cc index 92e41ce..e45ef39 100644 --- a/src/internal/scripting/ruby.cc +++ b/src/internal/scripting/ruby.cc @@ -105,17 +105,18 @@ static mrb_value mrb_bed_unregister(mrb_state *mrb, mrb_value) { } void register_basic(BEd &ctx) { + auto mrb = ctx.mrb.state; auto *bed_error = - mrb_define_class(ctx.mrb, "EdError", mrb_exc_get_id(ctx.mrb, MRB_ERROR_SYM(RuntimeError))); - mrb_define_class(ctx.mrb, "FatalError", bed_error); - mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE()); - mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "handle", mrb_bed_handle, MRB_ARGS_REQ(1)); - mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "register", mrb_bed_register, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1) | MRB_ARGS_BLOCK()); - mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1)); + mrb_define_class(mrb, "EdError", mrb_exc_get_id(mrb, MRB_ERROR_SYM(RuntimeError))); + mrb_define_class(mrb, "FatalError", bed_error); + mrb_define_method(mrb, mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE()); + mrb_define_method(mrb, mrb->kernel_module, "handle", mrb_bed_handle, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mrb->kernel_module, "register", mrb_bed_register, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1) | MRB_ARGS_BLOCK()); + mrb_define_method(mrb, mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1)); } void run(BEd &ctx, const std::string &str) { - mrb_state *mrb = ctx.mrb; + mrb_state *mrb = ctx.mrb.state; mrb_load_nstring(mrb, str.data(), str.size()); if (!mrb->exc) return;