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.
This commit is contained in:
2026-09-08 23:12:52 +01:00
parent f5ec5516fa
commit 118934a393
4 changed files with 34 additions and 22 deletions
+1 -3
View File
@@ -14,6 +14,7 @@
namespace bed { namespace bed {
struct BEd { struct BEd {
internal::scripting::RubyState mrb;
internal::trie::Trie<internal::functions::Function> functions; internal::trie::Trie<internal::functions::Function> functions;
internal::functions::Function no_op; internal::functions::Function no_op;
internal::functions::Function eof_op; internal::functions::Function eof_op;
@@ -24,9 +25,6 @@ struct BEd {
internal::vase::AppendStorage append{"/tmp"}; internal::vase::AppendStorage append{"/tmp"};
std::unordered_map<std::string, internal::buffer::Buffer *> buffers; std::unordered_map<std::string, internal::buffer::Buffer *> buffers;
mrb_state *mrb = nullptr;
int arena;
bool help_mode = false; bool help_mode = false;
bool prompt_mode = true; bool prompt_mode = true;
std::function<std::string(BEd &)> prompt = nullptr; std::function<std::string(BEd &)> prompt = nullptr;
+24
View File
@@ -4,6 +4,30 @@
#include "pch.h" #include "pch.h"
namespace bed::internal::scripting { 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 { struct Block {
mrb_state *mrb = nullptr; mrb_state *mrb = nullptr;
mrb_value proc = mrb_nil_value(); mrb_value proc = mrb_nil_value();
+1 -12
View File
@@ -2,7 +2,7 @@
namespace bed { namespace bed {
BEd::BEd(std::vector<std::string> args) BEd::BEd(std::vector<std::string> args)
: theme(internal::theme::Theme::default_theme()), io(*this) { : mrb(this), theme(internal::theme::Theme::default_theme()), io(*this) {
std::string prompt_ = ""; std::string prompt_ = "";
std::string file = ""; std::string file = "";
bool suppress = false; bool suppress = false;
@@ -42,11 +42,6 @@ BEd::BEd(std::vector<std::string> args)
&& strcmp(colorterm, "24bit") != 0) && strcmp(colorterm, "24bit") != 0)
color = false; color = false;
color_mode = color; 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_posix(*this);
internal::functions::Function::register_extented(*this); internal::functions::Function::register_extented(*this);
internal::functions::Suffix::register_suffixes(*this); internal::functions::Suffix::register_suffixes(*this);
@@ -70,12 +65,6 @@ BEd::~BEd() {
delete buffer; delete buffer;
for (auto &[_, lang] : languages) for (auto &[_, lang] : languages)
delete lang; delete lang;
mrb->ud = nullptr;
if (mrb) {
mrb_gc_arena_restore(mrb, arena);
mrb_full_gc(mrb);
mrb_close(mrb);
}
} }
void BEd::print_help() { void BEd::print_help() {
+8 -7
View File
@@ -105,17 +105,18 @@ static mrb_value mrb_bed_unregister(mrb_state *mrb, mrb_value) {
} }
void register_basic(BEd &ctx) { void register_basic(BEd &ctx) {
auto mrb = ctx.mrb.state;
auto *bed_error = auto *bed_error =
mrb_define_class(ctx.mrb, "EdError", mrb_exc_get_id(ctx.mrb, MRB_ERROR_SYM(RuntimeError))); mrb_define_class(mrb, "EdError", mrb_exc_get_id(mrb, MRB_ERROR_SYM(RuntimeError)));
mrb_define_class(ctx.mrb, "FatalError", bed_error); mrb_define_class(mrb, "FatalError", bed_error);
mrb_define_method(ctx.mrb, ctx.mrb->kernel_module, "exit", mrb_bed_exit, MRB_ARGS_NONE()); mrb_define_method(mrb, 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(mrb, 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(mrb, 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_method(mrb, mrb->kernel_module, "unregister", mrb_bed_unregister, MRB_ARGS_REQ(1));
} }
void run(BEd &ctx, const std::string &str) { 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()); mrb_load_nstring(mrb, str.data(), str.size());
if (!mrb->exc) if (!mrb->exc)
return; return;