From 3550148378bafb23b752abbc018ef3f508f6787b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 11:13:09 +0100 Subject: [PATCH 1/4] Fix usage of rb_struct_initialize() to pass an Array of members values and not a Hash * rb_struct_initialize() does not accept a Hash, and it's very brittle to pass `[{...}]` and to rely on that C function using rb_keyword_given_p(). It basically worked accidentally, by having **members in the caller of the caller. Such logic when Struct#initialize is defined in Ruby (as in TruffleRuby) is basically impossible to implement, because it's incorrectly treating positional arguments as keyword arguments. * rb_struct_initialize() is used in CRuby to set members of Data instances in marshal.c (there is no rb_data_initialize() yet). There, the code passes an Array of members values for Data (and for Struct which are not `keyword_init: true`): https://github.com/ruby/ruby/blob/48c7f349f68846e10d60ae77ad299a38ee014479/marshal.c#L2150-L2176 So we should do the same in psych. * Rename to init_data since it's only used for Data. * See https://github.com/ruby/psych/pull/692#discussion_r2483947279. --- .github/workflows/test.yml | 2 +- ext/psych/psych_to_ruby.c | 9 +++------ lib/psych/visitors/to_ruby.rb | 3 ++- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad70dfad..f1ca6f54 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }} os: [ ubuntu-latest, macos-latest, windows-latest ] include: - # jruby is broken with "undefined method 'init_struct'" + # jruby is broken with "undefined method 'init_data'" # https://github.com/ruby/psych/actions/runs/15434465445/job/43438083198?pr=734 - { os: windows-latest, ruby: jruby-head } - { os: macos-latest, ruby: jruby-head } diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index d473a5f8..53a03827 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -24,12 +24,9 @@ static VALUE path2class(VALUE self, VALUE path) return rb_path_to_class(path); } -static VALUE init_struct(VALUE self, VALUE data, VALUE attrs) +static VALUE init_data(VALUE self, VALUE data, VALUE values) { - VALUE args = rb_ary_new2(1); - rb_ary_push(args, attrs); - rb_struct_initialize(data, args); - + rb_struct_initialize(data, values); return data; } @@ -42,7 +39,7 @@ void Init_psych_to_ruby(void) VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor); - rb_define_private_method(cPsychVisitorsToRuby, "init_struct", init_struct, 2); + rb_define_private_method(cPsychVisitorsToRuby, "init_data", init_data, 2); rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); rb_define_private_method(class_loader, "path2class", path2class, 1); } diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb index 580a74e9..2814ce1a 100644 --- a/lib/psych/visitors/to_ruby.rb +++ b/lib/psych/visitors/to_ruby.rb @@ -219,7 +219,8 @@ def visit_Psych_Nodes_Mapping o revive_data_members(members, o) end data ||= allocate_anon_data(o, members) - init_struct(data, **members) + values = data.members.map { |m| members[m] } + init_data(data, values) data.freeze data From d7110e30d062eb07bf7433796df0d30f0d6f22c8 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 11:46:01 +0100 Subject: [PATCH 2/4] Pin power_assert to v2 for Ruby 2.7 (v3 requires 3.1+) * See https://github.com/ruby/power_assert/pull/61 * Same approach as https://github.com/ruby/irb/pull/1135 --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 6185df02..19673236 100644 --- a/Gemfile +++ b/Gemfile @@ -7,4 +7,5 @@ group :development do gem 'ruby-maven', :platforms => :jruby gem 'test-unit' gem 'test-unit-ruby-core', ">= 1.0.7" + gem 'power_assert', '~> 2.0' if RUBY_VERSION < '3.0' # https://github.com/ruby/power_assert/pull/61 end From dbabe7aac65553c7d3710a9377d9d5ec04aa7262 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 12:01:21 +0100 Subject: [PATCH 3/4] Properly set the message of Exceptions on TruffleRuby * From https://github.com/truffleruby/truffleruby/commit/1f81db82d2969ff7c5de0dacdecb38252664f42c --- ext/psych/psych_to_ruby.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index 53a03827..0132b2c9 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -10,7 +10,11 @@ static VALUE build_exception(VALUE self, VALUE klass, VALUE mesg) { VALUE e = rb_obj_alloc(klass); +#ifdef TRUFFLERUBY + rb_exc_set_message(e, mesg); +#else rb_iv_set(e, "mesg", mesg); +#endif return e; } From c16b4e6007c42d79ff0106985474fe1a3fbf483f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 5 Nov 2025 12:03:25 +0100 Subject: [PATCH 4/4] Add TruffleRuby in CI --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1ca6f54..e372feee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,6 +31,9 @@ jobs: - { os: windows-latest, ruby: jruby-head } - { os: macos-latest, ruby: jruby-head } - { os: ubuntu-latest, ruby: jruby-head } + # Needs truffleruby-head for rb_struct_initialize() (which truffleruby 25.0 does not have) + - { os: ubuntu-latest, ruby: truffleruby-head } + - { os: macos-latest, ruby: truffleruby-head } - { os: windows-latest, ruby: ucrt } - { os: windows-latest, ruby: mingw } - { os: windows-latest, ruby: mswin }