Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define Fiddle.last_error family and Fiddle.dlopen statically #172

Merged
merged 1 commit into from
Jan 31, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 69 additions & 76 deletions lib/fiddle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,98 +10,72 @@
require 'fiddle/version'

module Fiddle
if WINDOWS
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
# if none
def self.win32_last_error
if RUBY_ENGINE == 'jruby'
errno = FFI.errno
errno == 0 ? nil : errno
else
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
end
case RUBY_ENGINE
when 'jruby'
def self.last_error
FFI.errno.nonzero?
end

# Sets the last win32 +Error+ of the current executing +Thread+ to +error+
def self.win32_last_error= error
if RUBY_ENGINE == 'jruby'
FFI.errno = error || 0
else
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
end
end

# Returns the last win32 socket +Error+ of the current executing
# +Thread+ or nil if none
def self.win32_last_socket_error
if RUBY_ENGINE == 'jruby'
errno = FFI.errno
errno == 0 ? nil : errno
else
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
end
def self.last_error= error
FFI.errno = error || 0
end

# Sets the last win32 socket +Error+ of the current executing
# +Thread+ to +error+
def self.win32_last_socket_error= error
if RUBY_ENGINE == 'jruby'
FFI.errno = error || 0
else
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
if WINDOWS
class << self
alias win32_last_error last_error
alias win32_last_error= last_error=
alias win32_last_socket_error last_error
alias win32_last_socket_error= last_error=
end
end
end

# Returns the last +Error+ of the current executing +Thread+ or nil if none
def self.last_error
if RUBY_ENGINE == 'jruby'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmh, actually FFI.last_error{,=} should also use FFI.errno on TruffleRuby, it seems somehow I missed this, maybe because there are no tests for this?

Orthogonal to this PR, so feel free to merge this first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah it wasn't caught because of

def test_last_error
if ffi_backend?
omit("Fiddle.last_error doesn't work with FFI backend")
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=> #173

errno = FFI.errno
errno == 0 ? nil : errno
else
else
# Returns the last +Error+ of the current executing +Thread+ or nil if none
def self.last_error
Thread.current[:__FIDDLE_LAST_ERROR__]
end
end

# Sets the last +Error+ of the current executing +Thread+ to +error+
def self.last_error= error
if RUBY_ENGINE == 'jruby'
FFI.errno = error || 0
else
# Sets the last +Error+ of the current executing +Thread+ to +error+
def self.last_error= error
Thread.current[:__DL2_LAST_ERROR__] = error
Thread.current[:__FIDDLE_LAST_ERROR__] = error
end

if WINDOWS
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
# if none
def self.win32_last_error
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
end

# Sets the last win32 +Error+ of the current executing +Thread+ to +error+
def self.win32_last_error= error
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
end

# Returns the last win32 socket +Error+ of the current executing
# +Thread+ or nil if none
def self.win32_last_socket_error
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
end

# Sets the last win32 socket +Error+ of the current executing
# +Thread+ to +error+
def self.win32_last_socket_error= error
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
end
end
end

# call-seq: dlopen(library) => Fiddle::Handle
#
# Creates a new handler that opens +library+, and returns an instance of
# Fiddle::Handle.
#
# If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which
# is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
#
# lib = Fiddle.dlopen(nil)
#
# The default is dependent on OS, and provide a handle for all libraries
# already loaded. For example, in most cases you can use this to access
# +libc+ functions, or ruby functions like +rb_str_new+.
#
# See Fiddle::Handle.new for more.
def dlopen library
begin
case RUBY_PLATFORM
when /linux/
def dlopen library
Fiddle::Handle.new(library)
rescue DLError => error
case RUBY_PLATFORM
when /linux/
case error.message
when /\A(\/.+?): (?:invalid ELF header|file too short)/
# This may be a linker script:
# https://sourceware.org/binutils/docs/ld.html#Scripts
path = $1
else
raise
end
case error.message
when /\A(\/.+?): (?:invalid ELF header|file too short)/
# This may be a linker script:
# https://sourceware.org/binutils/docs/ld.html#Scripts
path = $1
else
raise
end
Expand All @@ -123,6 +97,25 @@ def dlopen library
# Not found
raise
end
else
# call-seq: dlopen(library) => Fiddle::Handle
#
# Creates a new handler that opens +library+, and returns an instance of
# Fiddle::Handle.
#
# If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which
# is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
#
# lib = Fiddle.dlopen(nil)
#
# The default is dependent on OS, and provide a handle for all libraries
# already loaded. For example, in most cases you can use this to access
# +libc+ functions, or ruby functions like +rb_str_new+.
#
# See Fiddle::Handle.new for more.
def dlopen library
Fiddle::Handle.new(library)
end
end
module_function :dlopen

Expand Down