diff --git a/ext/rubydex/extconf.rb b/ext/rubydex/extconf.rb index c17f5de15..47c8c33ca 100644 --- a/ext/rubydex/extconf.rb +++ b/ext/rubydex/extconf.rb @@ -39,6 +39,12 @@ append_cflags("-Werror=unused-but-set-variable") append_cflags("-Werror=implicit-function-declaration") +# Compiles a minimal program to test if the `RUBY_TYPED_EMBEDDABLE` C constant exists ( Ruby 3.3 or newer). +# * If it exists, the generated makefile will set a preprocessor macro: `#define HAVE_CONST_RUBY_TYPED_EMBEDDABLE 1` +# * else: `#define HAVE_CONST_RUBY_TYPED_EMBEDDABLE 0` +# See https://ruby-doc.org/3.3.4/stdlibs/mkmf/MakeMakefile.html#method-i-have_const +have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") + # There's an error on Windows with function pointer types not matching. This has been fixed and backported in Ruby, but # it seems that RubyInstaller sometimes picks an older patch version on CI and it breaks compilation. This isn't # actually a problem, so we're ignoring it temporarily only on Windows diff --git a/ext/rubydex/graph.c b/ext/rubydex/graph.c index 149263399..4a16afaf0 100644 --- a/ext/rubydex/graph.c +++ b/ext/rubydex/graph.c @@ -5,6 +5,7 @@ #include "location.h" #include "reference.h" #include "ruby/internal/globals.h" +#include "ruby_compat.h" #include "rustbindings.h" #include "utils.h" @@ -44,8 +45,13 @@ static void graph_free(void *ptr) { // let the Rust side drop the Graph struct internally. rdx_graph_drop(ptr); +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + // The storage is embedded in the TypedData Ruby object itself. + // Don't free `ptr`, because the GC will do that for us. +#else // Free the TypeData Ruby object itself xfree(ptr); +#endif } } @@ -59,7 +65,7 @@ const rb_data_type_t graph_type = { }, .parent = NULL, .data = NULL, - .flags = RUBY_TYPED_FREE_IMMEDIATELY, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE, }; // Custom allocator for the Graph class. diff --git a/ext/rubydex/handle.h b/ext/rubydex/handle.h index 427608223..1f5a26f3e 100644 --- a/ext/rubydex/handle.h +++ b/ext/rubydex/handle.h @@ -1,7 +1,7 @@ #ifndef RUBYDEX_HANDLE_H #define RUBYDEX_HANDLE_H -#include "ruby.h" +#include "ruby_compat.h" typedef struct { VALUE graph_obj; // Ruby Graph object to keep it alive @@ -15,23 +15,29 @@ static void handle_mark(void *ptr) { } } +#ifndef HAVE_RUBY_TYPED_EMBEDDABLE static void handle_free(void *ptr) { if (ptr) { xfree(ptr); } } +#endif static const rb_data_type_t handle_type = { .wrap_struct_name = "RubydexHandle", .function = { .dmark = handle_mark, +#ifdef HAVE_RUBY_TYPED_EMBEDDABLE + .dfree = RUBY_DEFAULT_FREE, +#else .dfree = handle_free, +#endif .dsize = NULL, .dcompact = NULL, }, .parent = NULL, .data = NULL, - .flags = RUBY_TYPED_FREE_IMMEDIATELY, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE, }; static VALUE rdxr_handle_alloc(VALUE klass) { diff --git a/ext/rubydex/ruby_compat.h b/ext/rubydex/ruby_compat.h new file mode 100644 index 000000000..e0f5255a9 --- /dev/null +++ b/ext/rubydex/ruby_compat.h @@ -0,0 +1,17 @@ +#ifndef RUBYDEX_RUBY_COMPAT_H +#define RUBYDEX_RUBY_COMPAT_H + +#include "ruby.h" + +#ifdef RUBY_TYPED_EMBEDDABLE +# define HAVE_RUBY_TYPED_EMBEDDABLE 1 +#else +# ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE +# define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE +# define HAVE_RUBY_TYPED_EMBEDDABLE 1 +# else +# define RUBY_TYPED_EMBEDDABLE 0 +# endif +#endif + +#endif // RUBYDEX_RUBY_COMPAT_H