From 418bd89a5f81cf757a7581b8df96af0237e3a8ad Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Mon, 7 Oct 2024 16:33:03 +0900 Subject: [PATCH 1/6] Use JRuby implementation for TruffleRuby Fix GH-145 lib/fiddle/truffleruby.rb uses lib/fiddle/jruby.rb. lib/fiddle/jruby.rb uses FFI API. So we can use it for TruffleRuby too. --- .github/workflows/ci.yml | 2 ++ ext/fiddle/extconf.rb | 2 +- fiddle.gemspec | 1 + lib/fiddle/jruby.rb | 11 +++++++++++ lib/fiddle/truffleruby.rb | 1 + test/fiddle/test_closure.rb | 6 ++++++ test/fiddle/test_fiddle.rb | 11 +++++++++++ test/fiddle/test_func.rb | 4 ++++ test/fiddle/test_function.rb | 13 +++++++++++++ test/fiddle/test_handle.rb | 15 +++++++++++++++ test/fiddle/test_import.rb | 3 +++ test/fiddle/test_pointer.rb | 15 +++++++++++++++ 12 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 lib/fiddle/truffleruby.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe0d68bf..972bcce5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - '3.2' - debug - jruby + - truffleruby include: - { os: windows-latest , ruby: mingw } - { os: windows-latest , ruby: mswin } @@ -33,6 +34,7 @@ jobs: - { os: macos-14 , ruby: '2.5' } - { os: windows-latest , ruby: '3.0' } - { os: windows-latest , ruby: debug } + - { os: windows-latest , ruby: truffleruby } steps: - uses: actions/checkout@v4 diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb index 6b0ea753..a4a7e7e1 100644 --- a/ext/fiddle/extconf.rb +++ b/ext/fiddle/extconf.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'mkmf' -if RUBY_ENGINE == "jruby" +unless RUBY_ENGINE == "ruby" File.write('Makefile', dummy_makefile("").join) return end diff --git a/fiddle.gemspec b/fiddle.gemspec index 65de7a30..89c2c851 100644 --- a/fiddle.gemspec +++ b/fiddle.gemspec @@ -44,6 +44,7 @@ Gem::Specification.new do |spec| "lib/fiddle/pack.rb", "lib/fiddle/ruby.rb", "lib/fiddle/struct.rb", + "lib/fiddle/truffleruby.rb", "lib/fiddle/types.rb", "lib/fiddle/value.rb", "lib/fiddle/version.rb", diff --git a/lib/fiddle/jruby.rb b/lib/fiddle/jruby.rb index 9ad61d4d..2f1648ca 100644 --- a/lib/fiddle/jruby.rb +++ b/lib/fiddle/jruby.rb @@ -158,6 +158,17 @@ def call(*args, &block); end args[i] = Fiddle::JRuby.__ffi_type__(args[i]) end + else + args.size.times do |i| + arg = args[i] + next unless arg.respond_to?(:to_ptr) + loop do + arg = arg.to_ptr + break if arg.is_a?(FFI::Pointer) + break unless arg.respond_to?(:to_ptr) + end + args[i] = arg + end end result = @function.call(*args, &block) result = Pointer.new(result) if result.is_a?(FFI::Pointer) diff --git a/lib/fiddle/truffleruby.rb b/lib/fiddle/truffleruby.rb new file mode 100644 index 00000000..d2be1474 --- /dev/null +++ b/lib/fiddle/truffleruby.rb @@ -0,0 +1 @@ +require_relative 'jruby' diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb index 787a9b63..8f927b7d 100644 --- a/test/fiddle/test_closure.rb +++ b/test/fiddle/test_closure.rb @@ -6,6 +6,12 @@ module Fiddle class TestClosure < Fiddle::TestCase + def setup + if RUBY_ENGINE == "truffleruby" + omit("FFI::Function don't accept #call-able object with TruffleRuby") + end + end + def teardown super # We can't use ObjectSpace with JRuby. diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb index b247ae15..e1ed1b5e 100644 --- a/test/fiddle/test_fiddle.rb +++ b/test/fiddle/test_fiddle.rb @@ -9,6 +9,9 @@ def test_nil_true_etc if RUBY_ENGINE == "jruby" omit("Fiddle::Q* aren't supported with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Q* aren't supported with TruffleRuby") + end assert_equal Fiddle::Qtrue, Fiddle.dlwrap(true) assert_equal Fiddle::Qfalse, Fiddle.dlwrap(false) @@ -30,6 +33,10 @@ def test_dlopen_linker_script_input_linux if Dir.glob("/usr/lib/*/libncurses.so").empty? omit("libncurses.so is needed") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + end + # libncurses.so uses INPUT() on Debian GNU/Linux # $ cat /usr/lib/x86_64-linux-gnu/libncurses.so # INPUT(libncurses.so.6 -ltinfo) @@ -44,6 +51,10 @@ def test_dlopen_linker_script_input_linux def test_dlopen_linker_script_group_linux omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + end + # libc.so uses GROUP() on Debian GNU/Linux # $ cat /usr/lib/x86_64-linux-gnu/libc.so # /* GNU ld script diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb index 5d69cc5f..7ed88a45 100644 --- a/test/fiddle/test_func.rb +++ b/test/fiddle/test_func.rb @@ -64,6 +64,10 @@ def test_strtod end def test_qsort1 + if RUBY_ENGINE == "truffleruby" + omit("TruffleRuby's FFI::Function don't accept #call-able object") + end + closure_class = Class.new(Closure) do def call(x, y) Pointer.new(x)[0] <=> Pointer.new(y)[0] diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb index b6ae8c14..61b786d3 100644 --- a/test/fiddle/test_function.rb +++ b/test/fiddle/test_function.rb @@ -41,6 +41,9 @@ def test_need_gvl? if RUBY_ENGINE == "jruby" omit("rb_str_dup() doesn't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("rb_str_dup() doesn't work with TruffleRuby") + end libruby = Fiddle.dlopen(nil) rb_str_dup = Function.new(libruby['rb_str_dup'], @@ -91,6 +94,10 @@ def test_call end def test_argument_count + if RUBY_ENGINE == "truffleruby" + omit("TruffleRuby's FFI::Function don't accept #call-able object") + end + closure_class = Class.new(Closure) do def call one 10 + one @@ -112,6 +119,9 @@ def test_last_error if RUBY_ENGINE == "jruby" omit("Fiddle.last_error doesn't work with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle.last_error doesn't work with TruffleRuby") + end func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) @@ -219,6 +229,9 @@ def test_no_memory_leak if RUBY_ENGINE == "jruby" omit("rb_obj_frozen_p() doesn't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("memory leak detection is fragile with TruffleRuby") + end if respond_to?(:assert_nothing_leaked_memory) rb_obj_frozen_p_symbol = Fiddle.dlopen(nil)["rb_obj_frozen_p"] diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb index c952a7ef..bdef9e8f 100644 --- a/test/fiddle/test_handle.rb +++ b/test/fiddle/test_handle.rb @@ -12,6 +12,9 @@ def test_to_i if RUBY_ENGINE == "jruby" omit("Fiddle::Handle#to_i is unavailable with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle#to_i is unavailable with TruffleRuby") + end handle = Fiddle::Handle.new(LIBC_SO) assert_kind_of Integer, handle.to_i @@ -21,6 +24,9 @@ def test_to_ptr if RUBY_ENGINE == "jruby" omit("Fiddle::Handle#to_i is unavailable with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle#to_i is unavailable with TruffleRuby") + end handle = Fiddle::Handle.new(LIBC_SO) ptr = handle.to_ptr @@ -37,6 +43,9 @@ def test_static_sym if RUBY_ENGINE == "jruby" omit("We can't assume static symbols with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("We can't assume static symbols with TruffleRuby") + end begin # Linux / Darwin / FreeBSD @@ -136,6 +145,9 @@ def test_file_name if RUBY_ENGINE == "jruby" omit("Fiddle::Handle#file_name doesn't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + end file_name = Handle.new(LIBC_SO).file_name if file_name @@ -158,6 +170,9 @@ def test_NEXT if RUBY_ENGINE == "jruby" omit("Fiddle::Handle::NEXT doesn't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Handle::NEXT doesn't exist in TruffleRuby") + end begin # Linux / Darwin diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 26190325..2ef3edd7 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -157,6 +157,9 @@ def test_io() if RUBY_ENGINE == "jruby" omit("BUILD_RUBY_PLATFORM doesn't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("BUILD_RUBY_PLATFORM doesn't exist in TruffleRuby") + end if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf) return diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index f17c8338..56c2f4ea 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -14,6 +14,9 @@ def test_can_read_write_memory if RUBY_ENGINE == "jruby" omit("Fiddle::Pointer.{read,write} don't exist in JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Pointer.{read,write} don't exist in TruffleRuby") + end # Allocate some memory Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |pointer| @@ -116,6 +119,9 @@ def test_inspect if RUBY_ENGINE == "jruby" omit("Fiddle::Pointer#inspect is incompatible on JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Pointer#inspect is incompatible on TruffleRuby") + end ptr = Pointer.new(0) inspect = ptr.inspect @@ -135,6 +141,9 @@ def test_to_ptr_io if RUBY_ENGINE == "jruby" omit("Fiddle::Pointer.to_ptr(IO) isn't supported with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle::Pointer.to_ptr(IO) isn't supported with TruffleRuby") + end Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf| File.open(__FILE__, 'r') do |f| @@ -186,6 +195,9 @@ def test_ref_ptr if RUBY_ENGINE == "jruby" omit("Fiddle.dlwrap([]) isn't supported with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle.dlwrap([]) isn't supported with TruffleRuby") + end ary = [0,1,2,4,5] addr = Pointer.new(dlwrap(ary)) @@ -198,6 +210,9 @@ def test_to_value if RUBY_ENGINE == "jruby" omit("Fiddle.dlwrap([]) isn't supported with JRuby") end + if RUBY_ENGINE == "truffleruby" + omit("Fiddle.dlwrap([]) isn't supported with TruffleRuby") + end ary = [0,1,2,4,5] addr = Pointer.new(dlwrap(ary)) From 0631d46cddb4dc64ff449ddc62524c6767e9b66b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 8 Oct 2024 23:27:42 +0200 Subject: [PATCH 2/6] Simplify coercion logic in Fiddle::Function#call * The issue is Fiddle structs have #to_ptr returning a CStructEntity and not a FFI::Pointer. So both Fiddle and FFI use #to_ptr and we need explicit coercion. --- lib/fiddle/jruby.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/fiddle/jruby.rb b/lib/fiddle/jruby.rb index 2f1648ca..c23d63cd 100644 --- a/lib/fiddle/jruby.rb +++ b/lib/fiddle/jruby.rb @@ -149,7 +149,7 @@ def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil) end end - def call(*args, &block); + def call(*args, &block) if @function.is_a?(FFI::VariadicInvoker) n_fixed_args = @args.size - 1 n_fixed_args.step(args.size - 1, 2) do |i| @@ -159,15 +159,15 @@ def call(*args, &block); args[i] = Fiddle::JRuby.__ffi_type__(args[i]) end else - args.size.times do |i| - arg = args[i] - next unless arg.respond_to?(:to_ptr) - loop do - arg = arg.to_ptr - break if arg.is_a?(FFI::Pointer) - break unless arg.respond_to?(:to_ptr) + args.map! do |arg| + if arg.respond_to?(:to_ptr) + begin + arg = arg.to_ptr + end until arg.is_a?(FFI::Pointer) || !arg.respond_to?(:to_ptr) + arg + else + arg end - args[i] = arg end end result = @function.call(*args, &block) From fc3be9ce3da7f343aa02420fe313ce6456201f2f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 9 Oct 2024 00:45:34 +0200 Subject: [PATCH 3/6] Fix TestFunc#test_qsort1 to not permanently mutate all "9341" string literals --- test/fiddle/test_func.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb index 7ed88a45..677abaab 100644 --- a/test/fiddle/test_func.rb +++ b/test/fiddle/test_func.rb @@ -78,16 +78,20 @@ def call(x, y) qsort = Function.new(@libc['qsort'], [TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP], TYPE_VOID) - buff = "9341" + untouched = "9341" + buff = +"9341" qsort.call(buff, buff.size, 1, callback) assert_equal("1349", buff) bug4929 = '[ruby-core:37395]' - buff = "9341" + buff = +"9341" under_gc_stress do qsort.call(buff, buff.size, 1, callback) end assert_equal("1349", buff, bug4929) + + # Ensure the test didn't mutate String literals + assert_equal("93" + "41", untouched) end ensure # We can't use ObjectSpace with JRuby. From 41c21f5887af6133ed5ee347ac6143cbc396c382 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 8 Oct 2024 23:51:15 +0200 Subject: [PATCH 4/6] Fix Fiddle::Closure on TruffleRuby * TruffleRuby requires a Proc or Method as the callable. --- lib/fiddle/jruby.rb | 14 ++++++++------ test/fiddle/test_closure.rb | 15 +++++++-------- test/fiddle/test_func.rb | 4 ---- test/fiddle/test_function.rb | 4 ---- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/fiddle/jruby.rb b/lib/fiddle/jruby.rb index c23d63cd..70f14d63 100644 --- a/lib/fiddle/jruby.rb +++ b/lib/fiddle/jruby.rb @@ -185,15 +185,17 @@ def initialize(ret, args, abi = Function::DEFAULT) if ffi_args.size == 1 && ffi_args[0] == FFI::Type::Builtin::VOID ffi_args = [] end - @function = FFI::Function.new( - Fiddle::JRuby.__ffi_type__(@ctype), - ffi_args, - self, - :convention => abi - ) + return_type = Fiddle::JRuby.__ffi_type__(@ctype) + raise "#{self.class} must implement #call" unless respond_to?(:call) + callable = method(:call) + @function = FFI::Function.new(return_type, ffi_args, callable, convention: abi) @freed = false end + def to_ptr + @function + end + def to_i @function.to_i end diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb index 8f927b7d..acc02842 100644 --- a/test/fiddle/test_closure.rb +++ b/test/fiddle/test_closure.rb @@ -6,12 +6,6 @@ module Fiddle class TestClosure < Fiddle::TestCase - def setup - if RUBY_ENGINE == "truffleruby" - omit("FFI::Function don't accept #call-able object with TruffleRuby") - end - end - def teardown super # We can't use ObjectSpace with JRuby. @@ -64,7 +58,7 @@ def call thing end def test_const_string - if RUBY_ENGINE == "jruby" + if RUBY_ENGINE == "jruby" or RUBY_ENGINE == "truffleruby" omit("Closure with :const_string works but " + "Function with :const_string doesn't work with JRuby") end @@ -125,9 +119,14 @@ def test_memsize_ruby_dev_42480 end require 'objspace' + closure_class = Class.new(Closure) do + def call + 10 + end + end n = 10000 n.times do - Closure.create(:int, [:void]) do |closure| + closure_class.create(:int, [:void]) do |closure| ObjectSpace.memsize_of(closure) end end diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb index 677abaab..4e76523d 100644 --- a/test/fiddle/test_func.rb +++ b/test/fiddle/test_func.rb @@ -64,10 +64,6 @@ def test_strtod end def test_qsort1 - if RUBY_ENGINE == "truffleruby" - omit("TruffleRuby's FFI::Function don't accept #call-able object") - end - closure_class = Class.new(Closure) do def call(x, y) Pointer.new(x)[0] <=> Pointer.new(y)[0] diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb index 61b786d3..c2beaa90 100644 --- a/test/fiddle/test_function.rb +++ b/test/fiddle/test_function.rb @@ -94,10 +94,6 @@ def test_call end def test_argument_count - if RUBY_ENGINE == "truffleruby" - omit("TruffleRuby's FFI::Function don't accept #call-able object") - end - closure_class = Class.new(Closure) do def call one 10 + one From bf06273aa5e56aa118388439c7ed08aef2e7d2f2 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 9 Oct 2024 00:00:18 +0200 Subject: [PATCH 5/6] Rename the FFI backend since it is no longer JRuby-specific --- fiddle.gemspec | 4 +- lib/fiddle.rb | 6 +- lib/fiddle/{jruby.rb => ffi_backend.rb} | 120 ++++++++++++------------ lib/fiddle/ruby.rb | 1 - lib/fiddle/truffleruby.rb | 1 - test/fiddle/helper.rb | 4 + test/fiddle/test_closure.rb | 4 +- test/fiddle/test_fiddle.rb | 15 ++- test/fiddle/test_function.rb | 7 +- test/fiddle/test_handle.rb | 35 ++----- test/fiddle/test_import.rb | 7 +- test/fiddle/test_pointer.rb | 35 ++----- 12 files changed, 102 insertions(+), 137 deletions(-) rename lib/fiddle/{jruby.rb => ffi_backend.rb} (75%) delete mode 100644 lib/fiddle/ruby.rb delete mode 100644 lib/fiddle/truffleruby.rb diff --git a/fiddle.gemspec b/fiddle.gemspec index 89c2c851..0092f520 100644 --- a/fiddle.gemspec +++ b/fiddle.gemspec @@ -38,13 +38,11 @@ Gem::Specification.new do |spec| "lib/fiddle.rb", "lib/fiddle/closure.rb", "lib/fiddle/cparser.rb", + "lib/fiddle/ffi_backend.rb", "lib/fiddle/function.rb", "lib/fiddle/import.rb", - "lib/fiddle/jruby.rb", "lib/fiddle/pack.rb", - "lib/fiddle/ruby.rb", "lib/fiddle/struct.rb", - "lib/fiddle/truffleruby.rb", "lib/fiddle/types.rb", "lib/fiddle/value.rb", "lib/fiddle/version.rb", diff --git a/lib/fiddle.rb b/lib/fiddle.rb index b1c6cd08..6718b012 100644 --- a/lib/fiddle.rb +++ b/lib/fiddle.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true -require "fiddle/#{RUBY_ENGINE}" +if RUBY_ENGINE == 'ruby' + require 'fiddle.so' +else + require 'fiddle/ffi_backend' +end require 'fiddle/closure' require 'fiddle/function' require 'fiddle/version' diff --git a/lib/fiddle/jruby.rb b/lib/fiddle/ffi_backend.rb similarity index 75% rename from lib/fiddle/jruby.rb rename to lib/fiddle/ffi_backend.rb index 70f14d63..0c7c3797 100644 --- a/lib/fiddle/jruby.rb +++ b/lib/fiddle/ffi_backend.rb @@ -1,4 +1,4 @@ -# This is part of JRuby's FFI-based fiddle implementation. +# This is based on JRuby's FFI-based fiddle implementation. require 'ffi' @@ -74,7 +74,7 @@ module Types WINDOWS = FFI::Platform.windows? - module JRuby + module FFIBackend FFITypes = { 'c' => FFI::Type::INT8, 'h' => FFI::Type::INT16, @@ -104,16 +104,16 @@ module JRuby Types::VARIADIC => FFI::Type::Builtin::VARARGS, } - def self.__ffi_type__(dl_type) - if dl_type.is_a?(Symbol) - dl_type = Types.const_get(dl_type.to_s.upcase) + def self.to_ffi_type(fiddle_type) + if fiddle_type.is_a?(Symbol) + fiddle_type = Types.const_get(fiddle_type.to_s.upcase) end - if !dl_type.is_a?(Integer) && dl_type.respond_to?(:to_int) - dl_type = dl_type.to_int + if !fiddle_type.is_a?(Integer) && fiddle_type.respond_to?(:to_int) + fiddle_type = fiddle_type.to_int end - ffi_type = FFITypes[dl_type] - ffi_type = FFITypes[-dl_type] if ffi_type.nil? && dl_type.is_a?(Integer) && dl_type < 0 - raise TypeError.new("cannot convert #{dl_type} to ffi") unless ffi_type + ffi_type = FFITypes[fiddle_type] + ffi_type = FFITypes[-fiddle_type] if ffi_type.nil? && fiddle_type.is_a?(Integer) && fiddle_type < 0 + raise TypeError.new("cannot convert #{fiddle_type} to ffi") unless ffi_type ffi_type end end @@ -133,8 +133,8 @@ def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil) @ptr, @args, @return_type, @abi = ptr, args, return_type, abi raise TypeError.new "invalid argument types" unless args.is_a?(Array) - ffi_return_type = Fiddle::JRuby::__ffi_type__(@return_type) - ffi_args = @args.map { |t| Fiddle::JRuby.__ffi_type__(t) } + ffi_return_type = Fiddle::FFIBackend.to_ffi_type(@return_type) + ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) } pointer = FFI::Pointer.new(ptr.to_i) options = {convention: @abi} if ffi_args.last == FFI::Type::Builtin::VARARGS @@ -156,7 +156,7 @@ def call(*args, &block) if args[i] == :const_string || args[i] == Types::CONST_STRING args[i + 1] = String.try_convert(args[i + 1]) || args[i + 1] end - args[i] = Fiddle::JRuby.__ffi_type__(args[i]) + args[i] = Fiddle::FFIBackend.to_ffi_type(args[i]) end else args.map! do |arg| @@ -181,11 +181,11 @@ def initialize(ret, args, abi = Function::DEFAULT) raise TypeError.new "invalid argument types" unless args.is_a?(Array) @ctype, @args = ret, args - ffi_args = @args.map { |t| Fiddle::JRuby.__ffi_type__(t) } + ffi_args = @args.map { |t| Fiddle::FFIBackend.to_ffi_type(t) } if ffi_args.size == 1 && ffi_args[0] == FFI::Type::Builtin::VOID ffi_args = [] end - return_type = Fiddle::JRuby.__ffi_type__(@ctype) + return_type = Fiddle::FFIBackend.to_ffi_type(@ctype) raise "#{self.class} must implement #call" unless respond_to?(:call) callable = method(:call) @function = FFI::Function.new(return_type, ffi_args, callable, convention: abi) @@ -563,51 +563,51 @@ def cleared? RUBY_FREE = Fiddle::Pointer::LibC::FREE.address NULL = Fiddle::Pointer.new(0) - ALIGN_VOIDP = Fiddle::JRuby::FFITypes[Types::VOIDP].alignment - ALIGN_CHAR = Fiddle::JRuby::FFITypes[Types::CHAR].alignment - ALIGN_SHORT = Fiddle::JRuby::FFITypes[Types::SHORT].alignment - ALIGN_INT = Fiddle::JRuby::FFITypes[Types::INT].alignment - ALIGN_LONG = Fiddle::JRuby::FFITypes[Types::LONG].alignment - ALIGN_LONG_LONG = Fiddle::JRuby::FFITypes[Types::LONG_LONG].alignment - ALIGN_INT8_T = Fiddle::JRuby::FFITypes[Types::INT8_T].alignment - ALIGN_INT16_T = Fiddle::JRuby::FFITypes[Types::INT16_T].alignment - ALIGN_INT32_T = Fiddle::JRuby::FFITypes[Types::INT32_T].alignment - ALIGN_INT64_T = Fiddle::JRuby::FFITypes[Types::INT64_T].alignment - ALIGN_FLOAT = Fiddle::JRuby::FFITypes[Types::FLOAT].alignment - ALIGN_DOUBLE = Fiddle::JRuby::FFITypes[Types::DOUBLE].alignment - ALIGN_BOOL = Fiddle::JRuby::FFITypes[Types::BOOL].alignment - ALIGN_SIZE_T = Fiddle::JRuby::FFITypes[Types::SIZE_T].alignment + ALIGN_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].alignment + ALIGN_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].alignment + ALIGN_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].alignment + ALIGN_INT = Fiddle::FFIBackend::FFITypes[Types::INT].alignment + ALIGN_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].alignment + ALIGN_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].alignment + ALIGN_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].alignment + ALIGN_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].alignment + ALIGN_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].alignment + ALIGN_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].alignment + ALIGN_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].alignment + ALIGN_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].alignment + ALIGN_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].alignment + ALIGN_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].alignment ALIGN_SSIZE_T = ALIGN_SIZE_T - ALIGN_PTRDIFF_T = Fiddle::JRuby::FFITypes[Types::PTRDIFF_T].alignment - ALIGN_INTPTR_T = Fiddle::JRuby::FFITypes[Types::INTPTR_T].alignment - ALIGN_UINTPTR_T = Fiddle::JRuby::FFITypes[Types::UINTPTR_T].alignment - - SIZEOF_VOIDP = Fiddle::JRuby::FFITypes[Types::VOIDP].size - SIZEOF_CHAR = Fiddle::JRuby::FFITypes[Types::CHAR].size - SIZEOF_UCHAR = Fiddle::JRuby::FFITypes[Types::UCHAR].size - SIZEOF_SHORT = Fiddle::JRuby::FFITypes[Types::SHORT].size - SIZEOF_USHORT = Fiddle::JRuby::FFITypes[Types::USHORT].size - SIZEOF_INT = Fiddle::JRuby::FFITypes[Types::INT].size - SIZEOF_UINT = Fiddle::JRuby::FFITypes[Types::UINT].size - SIZEOF_LONG = Fiddle::JRuby::FFITypes[Types::LONG].size - SIZEOF_ULONG = Fiddle::JRuby::FFITypes[Types::ULONG].size - SIZEOF_LONG_LONG = Fiddle::JRuby::FFITypes[Types::LONG_LONG].size - SIZEOF_ULONG_LONG = Fiddle::JRuby::FFITypes[Types::ULONG_LONG].size - SIZEOF_INT8_T = Fiddle::JRuby::FFITypes[Types::INT8_T].size - SIZEOF_UINT8_T = Fiddle::JRuby::FFITypes[Types::UINT8_T].size - SIZEOF_INT16_T = Fiddle::JRuby::FFITypes[Types::INT16_T].size - SIZEOF_UINT16_T = Fiddle::JRuby::FFITypes[Types::UINT16_T].size - SIZEOF_INT32_T = Fiddle::JRuby::FFITypes[Types::INT32_T].size - SIZEOF_UINT32_T = Fiddle::JRuby::FFITypes[Types::UINT32_T].size - SIZEOF_INT64_T = Fiddle::JRuby::FFITypes[Types::INT64_T].size - SIZEOF_UINT64_T = Fiddle::JRuby::FFITypes[Types::UINT64_T].size - SIZEOF_FLOAT = Fiddle::JRuby::FFITypes[Types::FLOAT].size - SIZEOF_DOUBLE = Fiddle::JRuby::FFITypes[Types::DOUBLE].size - SIZEOF_BOOL = Fiddle::JRuby::FFITypes[Types::BOOL].size - SIZEOF_SIZE_T = Fiddle::JRuby::FFITypes[Types::SIZE_T].size + ALIGN_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].alignment + ALIGN_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].alignment + ALIGN_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].alignment + + SIZEOF_VOIDP = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size + SIZEOF_CHAR = Fiddle::FFIBackend::FFITypes[Types::CHAR].size + SIZEOF_UCHAR = Fiddle::FFIBackend::FFITypes[Types::UCHAR].size + SIZEOF_SHORT = Fiddle::FFIBackend::FFITypes[Types::SHORT].size + SIZEOF_USHORT = Fiddle::FFIBackend::FFITypes[Types::USHORT].size + SIZEOF_INT = Fiddle::FFIBackend::FFITypes[Types::INT].size + SIZEOF_UINT = Fiddle::FFIBackend::FFITypes[Types::UINT].size + SIZEOF_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG].size + SIZEOF_ULONG = Fiddle::FFIBackend::FFITypes[Types::ULONG].size + SIZEOF_LONG_LONG = Fiddle::FFIBackend::FFITypes[Types::LONG_LONG].size + SIZEOF_ULONG_LONG = Fiddle::FFIBackend::FFITypes[Types::ULONG_LONG].size + SIZEOF_INT8_T = Fiddle::FFIBackend::FFITypes[Types::INT8_T].size + SIZEOF_UINT8_T = Fiddle::FFIBackend::FFITypes[Types::UINT8_T].size + SIZEOF_INT16_T = Fiddle::FFIBackend::FFITypes[Types::INT16_T].size + SIZEOF_UINT16_T = Fiddle::FFIBackend::FFITypes[Types::UINT16_T].size + SIZEOF_INT32_T = Fiddle::FFIBackend::FFITypes[Types::INT32_T].size + SIZEOF_UINT32_T = Fiddle::FFIBackend::FFITypes[Types::UINT32_T].size + SIZEOF_INT64_T = Fiddle::FFIBackend::FFITypes[Types::INT64_T].size + SIZEOF_UINT64_T = Fiddle::FFIBackend::FFITypes[Types::UINT64_T].size + SIZEOF_FLOAT = Fiddle::FFIBackend::FFITypes[Types::FLOAT].size + SIZEOF_DOUBLE = Fiddle::FFIBackend::FFITypes[Types::DOUBLE].size + SIZEOF_BOOL = Fiddle::FFIBackend::FFITypes[Types::BOOL].size + SIZEOF_SIZE_T = Fiddle::FFIBackend::FFITypes[Types::SIZE_T].size SIZEOF_SSIZE_T = SIZEOF_SIZE_T - SIZEOF_PTRDIFF_T = Fiddle::JRuby::FFITypes[Types::PTRDIFF_T].size - SIZEOF_INTPTR_T = Fiddle::JRuby::FFITypes[Types::INTPTR_T].size - SIZEOF_UINTPTR_T = Fiddle::JRuby::FFITypes[Types::UINTPTR_T].size - SIZEOF_CONST_STRING = Fiddle::JRuby::FFITypes[Types::VOIDP].size + SIZEOF_PTRDIFF_T = Fiddle::FFIBackend::FFITypes[Types::PTRDIFF_T].size + SIZEOF_INTPTR_T = Fiddle::FFIBackend::FFITypes[Types::INTPTR_T].size + SIZEOF_UINTPTR_T = Fiddle::FFIBackend::FFITypes[Types::UINTPTR_T].size + SIZEOF_CONST_STRING = Fiddle::FFIBackend::FFITypes[Types::VOIDP].size end diff --git a/lib/fiddle/ruby.rb b/lib/fiddle/ruby.rb deleted file mode 100644 index ee5a9a93..00000000 --- a/lib/fiddle/ruby.rb +++ /dev/null @@ -1 +0,0 @@ -require "fiddle.so" diff --git a/lib/fiddle/truffleruby.rb b/lib/fiddle/truffleruby.rb deleted file mode 100644 index d2be1474..00000000 --- a/lib/fiddle/truffleruby.rb +++ /dev/null @@ -1 +0,0 @@ -require_relative 'jruby' diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb index eae39a6e..241426d7 100644 --- a/test/fiddle/helper.rb +++ b/test/fiddle/helper.rb @@ -183,6 +183,10 @@ def teardown end end + def ffi_backend? + RUBY_ENGINE != 'ruby' + end + def under_gc_stress stress, GC.stress = GC.stress, true yield diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb index acc02842..12194190 100644 --- a/test/fiddle/test_closure.rb +++ b/test/fiddle/test_closure.rb @@ -58,9 +58,9 @@ def call thing end def test_const_string - if RUBY_ENGINE == "jruby" or RUBY_ENGINE == "truffleruby" + if ffi_backend? omit("Closure with :const_string works but " + - "Function with :const_string doesn't work with JRuby") + "Function with :const_string doesn't work with FFI backend") end closure_class = Class.new(Closure) do diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb index e1ed1b5e..28ae9221 100644 --- a/test/fiddle/test_fiddle.rb +++ b/test/fiddle/test_fiddle.rb @@ -6,11 +6,8 @@ class TestFiddle < Fiddle::TestCase def test_nil_true_etc - if RUBY_ENGINE == "jruby" - omit("Fiddle::Q* aren't supported with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Q* aren't supported with TruffleRuby") + if ffi_backend? + omit("Fiddle::Q* aren't supported with FFI backend") end assert_equal Fiddle::Qtrue, Fiddle.dlwrap(true) @@ -33,8 +30,8 @@ def test_dlopen_linker_script_input_linux if Dir.glob("/usr/lib/*/libncurses.so").empty? omit("libncurses.so is needed") end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle#file_name doesn't exist in FFI backend") end # libncurses.so uses INPUT() on Debian GNU/Linux @@ -51,8 +48,8 @@ def test_dlopen_linker_script_input_linux def test_dlopen_linker_script_group_linux omit("This is only for Linux") unless RUBY_PLATFORM.match?("linux") - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle#file_name doesn't exist in FFI backend") end # libc.so uses GROUP() on Debian GNU/Linux diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb index c2beaa90..2822723f 100644 --- a/test/fiddle/test_function.rb +++ b/test/fiddle/test_function.rb @@ -112,11 +112,8 @@ def call one end def test_last_error - if RUBY_ENGINE == "jruby" - omit("Fiddle.last_error doesn't work with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle.last_error doesn't work with TruffleRuby") + if ffi_backend? + omit("Fiddle.last_error doesn't work with FFI backend") end func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb index bdef9e8f..2b555dac 100644 --- a/test/fiddle/test_handle.rb +++ b/test/fiddle/test_handle.rb @@ -9,11 +9,8 @@ class TestHandle < TestCase include Fiddle def test_to_i - if RUBY_ENGINE == "jruby" - omit("Fiddle::Handle#to_i is unavailable with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle#to_i is unavailable with TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle#to_i is unavailable with FFI backend") end handle = Fiddle::Handle.new(LIBC_SO) @@ -21,11 +18,8 @@ def test_to_i end def test_to_ptr - if RUBY_ENGINE == "jruby" - omit("Fiddle::Handle#to_i is unavailable with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle#to_i is unavailable with TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle#to_i is unavailable with FFI backend") end handle = Fiddle::Handle.new(LIBC_SO) @@ -40,11 +34,8 @@ def test_static_sym_unknown end def test_static_sym - if RUBY_ENGINE == "jruby" - omit("We can't assume static symbols with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("We can't assume static symbols with TruffleRuby") + if ffi_backend? + omit("We can't assume static symbols with FFI backend") end begin @@ -142,11 +133,8 @@ def test_disable_close end def test_file_name - if RUBY_ENGINE == "jruby" - omit("Fiddle::Handle#file_name doesn't exist in JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle#file_name doesn't exist in TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle#file_name doesn't exist in FFI backend") end file_name = Handle.new(LIBC_SO).file_name @@ -167,11 +155,8 @@ def test_file_name end def test_NEXT - if RUBY_ENGINE == "jruby" - omit("Fiddle::Handle::NEXT doesn't exist in JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Handle::NEXT doesn't exist in TruffleRuby") + if ffi_backend? + omit("Fiddle::Handle::NEXT doesn't exist in FFI backend") end begin diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 2ef3edd7..79b65d97 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -154,11 +154,8 @@ def test_unsigned_result() end def test_io() - if RUBY_ENGINE == "jruby" - omit("BUILD_RUBY_PLATFORM doesn't exist in JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("BUILD_RUBY_PLATFORM doesn't exist in TruffleRuby") + if ffi_backend? + omit("BUILD_RUBY_PLATFORM doesn't exist in FFI backend") end if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf) diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index 56c2f4ea..fb83b867 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -11,11 +11,8 @@ def dlwrap arg end def test_can_read_write_memory - if RUBY_ENGINE == "jruby" - omit("Fiddle::Pointer.{read,write} don't exist in JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Pointer.{read,write} don't exist in TruffleRuby") + if ffi_backend? + omit("Fiddle::Pointer.{read,write} don't exist in FFI backend") end # Allocate some memory @@ -116,11 +113,8 @@ def test_plus end def test_inspect - if RUBY_ENGINE == "jruby" - omit("Fiddle::Pointer#inspect is incompatible on JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Pointer#inspect is incompatible on TruffleRuby") + if ffi_backend? + omit("Fiddle::Pointer#inspect is incompatible with FFI backend") end ptr = Pointer.new(0) @@ -138,11 +132,8 @@ def test_to_ptr_string end def test_to_ptr_io - if RUBY_ENGINE == "jruby" - omit("Fiddle::Pointer.to_ptr(IO) isn't supported with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle::Pointer.to_ptr(IO) isn't supported with TruffleRuby") + if ffi_backend? + omit("Fiddle::Pointer.to_ptr(IO) isn't supported with FFI backend") end Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf| @@ -192,11 +183,8 @@ def test_cmp end def test_ref_ptr - if RUBY_ENGINE == "jruby" - omit("Fiddle.dlwrap([]) isn't supported with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle.dlwrap([]) isn't supported with TruffleRuby") + if ffi_backend? + omit("Fiddle.dlwrap([]) isn't supported with FFI backend") end ary = [0,1,2,4,5] @@ -207,11 +195,8 @@ def test_ref_ptr end def test_to_value - if RUBY_ENGINE == "jruby" - omit("Fiddle.dlwrap([]) isn't supported with JRuby") - end - if RUBY_ENGINE == "truffleruby" - omit("Fiddle.dlwrap([]) isn't supported with TruffleRuby") + if ffi_backend? + omit("Fiddle.dlwrap([]) isn't supported with FFI backend") end ary = [0,1,2,4,5] From 3e11fad84b58eadf44a749bae494182ced816f07 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 9 Oct 2024 01:06:57 +0200 Subject: [PATCH 6/6] Fix return in ensure hiding test errors --- test/fiddle/test_func.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb index 4e76523d..ca503f92 100644 --- a/test/fiddle/test_func.rb +++ b/test/fiddle/test_func.rb @@ -64,6 +64,10 @@ def test_strtod end def test_qsort1 + if RUBY_ENGINE == "jruby" + omit("The untouched sanity check is broken on JRuby: https://github.com/jruby/jruby/issues/8365") + end + closure_class = Class.new(Closure) do def call(x, y) Pointer.new(x)[0] <=> Pointer.new(y)[0] @@ -91,14 +95,15 @@ def call(x, y) end ensure # We can't use ObjectSpace with JRuby. - return if RUBY_ENGINE == "jruby" - # Ensure freeing all closures. - # See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 . - not_freed_closures = [] - ObjectSpace.each_object(Fiddle::Closure) do |closure| - not_freed_closures << closure unless closure.freed? + unless RUBY_ENGINE == "jruby" + # Ensure freeing all closures. + # See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 . + not_freed_closures = [] + ObjectSpace.each_object(Fiddle::Closure) do |closure| + not_freed_closures << closure unless closure.freed? + end + assert_equal([], not_freed_closures) end - assert_equal([], not_freed_closures) end def test_snprintf