Import JRuby implementation (#147)
Fix GH-104 lib/fiddle/jruby.rb is based on https://github.com/jruby/jruby/blob/master/lib/ruby/stdlib/fiddle/jruby.rb . Here are changes for it: * Move `Fiddle::TYPE_*` to `Fiddle::Types::*` * Add `Fiddle::Types::VARIADIC` * Add `Fiddle::Types::CONST_STRING` * Add `Fiddle::Types::BOOL` * Add `Fiddle::Types::INT8_T` * Add `Fiddle::Types::UINT8_T` * Add `Fiddle::Types::INT16_T` * Add `Fiddle::Types::UINT16_T` * Add `Fiddle::Types::INT32_T` * Add `Fiddle::Types::UINT32_T` * Add `Fiddle::Types::INT64_T` * Add `Fiddle::Types::UINT64_T` * Add more `Fiddle::ALIGN_*` for the above new `Fiddle::Types::*` * Add more `Fiddle::SIZEOF_*` for the above new `Fiddle::Types::*` * Add support for specifying type as not only `Fiddle::Types::*` but also `Symbol` like `:int` * Add support for variable size arguments in `Fiddle::Function` * Add `Fiddle::Closure#free` * Add `Fiddle::Closure#freed?` * Add `Fiddle::Error` as base the error class * Add `Fiddle::Pointer#call_free` and stop using `FFI::AutoPointer` in `Fiddle::Pointer` * Add support for `Fiddle::Pointer.malloc {}` `Fiddle::Pointer` * Add support for `Fiddle::Pointer#free=` * Add `Fiddle::Pointer#freed?` * Use binary string not C string for `Fiddle::Pointer#[]` * Add `Fiddle::Handle.sym_defined?` * Add `Fiddle::Handle#sym_defined?` * Add `Fiddle::Handle::DEFAULT` * Add `Fiddle::ClearedReferenceError` * Add no-op `Fiddle::Pinned` Some features are still "not implemented". So there are some "omit"s for JRuby in tests.
This commit is contained in:
parent
a392ee1437
commit
a47f153d9d
Notes:
git
2024-10-10 01:54:48 +00:00
@ -1,6 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
require 'mkmf'
|
require 'mkmf'
|
||||||
|
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
File.write('Makefile', dummy_makefile("").join)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# :stopdoc:
|
# :stopdoc:
|
||||||
|
|
||||||
def gcc?
|
def gcc?
|
||||||
|
@ -40,7 +40,9 @@ Gem::Specification.new do |spec|
|
|||||||
"lib/fiddle/cparser.rb",
|
"lib/fiddle/cparser.rb",
|
||||||
"lib/fiddle/function.rb",
|
"lib/fiddle/function.rb",
|
||||||
"lib/fiddle/import.rb",
|
"lib/fiddle/import.rb",
|
||||||
|
"lib/fiddle/jruby.rb",
|
||||||
"lib/fiddle/pack.rb",
|
"lib/fiddle/pack.rb",
|
||||||
|
"lib/fiddle/ruby.rb",
|
||||||
"lib/fiddle/struct.rb",
|
"lib/fiddle/struct.rb",
|
||||||
"lib/fiddle/types.rb",
|
"lib/fiddle/types.rb",
|
||||||
"lib/fiddle/value.rb",
|
"lib/fiddle/value.rb",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'fiddle.so'
|
require "fiddle/#{RUBY_ENGINE}"
|
||||||
require 'fiddle/closure'
|
require 'fiddle/closure'
|
||||||
require 'fiddle/function'
|
require 'fiddle/function'
|
||||||
require 'fiddle/version'
|
require 'fiddle/version'
|
||||||
@ -10,36 +10,63 @@ module Fiddle
|
|||||||
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
|
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
|
||||||
# if none
|
# if none
|
||||||
def self.win32_last_error
|
def self.win32_last_error
|
||||||
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
|
if RUBY_ENGINE == 'jruby'
|
||||||
|
errno = FFI.errno
|
||||||
|
errno == 0 ? nil : errno
|
||||||
|
else
|
||||||
|
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the last win32 +Error+ of the current executing +Thread+ to +error+
|
# Sets the last win32 +Error+ of the current executing +Thread+ to +error+
|
||||||
def self.win32_last_error= error
|
def self.win32_last_error= error
|
||||||
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
|
if RUBY_ENGINE == 'jruby'
|
||||||
|
FFI.errno = error || 0
|
||||||
|
else
|
||||||
|
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the last win32 socket +Error+ of the current executing
|
# Returns the last win32 socket +Error+ of the current executing
|
||||||
# +Thread+ or nil if none
|
# +Thread+ or nil if none
|
||||||
def self.win32_last_socket_error
|
def self.win32_last_socket_error
|
||||||
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
|
if RUBY_ENGINE == 'jruby'
|
||||||
|
errno = FFI.errno
|
||||||
|
errno == 0 ? nil : errno
|
||||||
|
else
|
||||||
|
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the last win32 socket +Error+ of the current executing
|
# Sets the last win32 socket +Error+ of the current executing
|
||||||
# +Thread+ to +error+
|
# +Thread+ to +error+
|
||||||
def self.win32_last_socket_error= error
|
def self.win32_last_socket_error= error
|
||||||
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
|
if RUBY_ENGINE == 'jruby'
|
||||||
|
FFI.errno = error || 0
|
||||||
|
else
|
||||||
|
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the last +Error+ of the current executing +Thread+ or nil if none
|
# Returns the last +Error+ of the current executing +Thread+ or nil if none
|
||||||
def self.last_error
|
def self.last_error
|
||||||
Thread.current[:__FIDDLE_LAST_ERROR__]
|
if RUBY_ENGINE == 'jruby'
|
||||||
|
errno = FFI.errno
|
||||||
|
errno == 0 ? nil : errno
|
||||||
|
else
|
||||||
|
Thread.current[:__FIDDLE_LAST_ERROR__]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets the last +Error+ of the current executing +Thread+ to +error+
|
# Sets the last +Error+ of the current executing +Thread+ to +error+
|
||||||
def self.last_error= error
|
def self.last_error= error
|
||||||
Thread.current[:__DL2_LAST_ERROR__] = error
|
if RUBY_ENGINE == 'jruby'
|
||||||
Thread.current[:__FIDDLE_LAST_ERROR__] = error
|
FFI.errno = error || 0
|
||||||
|
else
|
||||||
|
Thread.current[:__DL2_LAST_ERROR__] = error
|
||||||
|
Thread.current[:__FIDDLE_LAST_ERROR__] = error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# call-seq: dlopen(library) => Fiddle::Handle
|
# call-seq: dlopen(library) => Fiddle::Handle
|
||||||
|
600
ext/fiddle/lib/fiddle/jruby.rb
Normal file
600
ext/fiddle/lib/fiddle/jruby.rb
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
# This is part of JRuby's FFI-based fiddle implementation.
|
||||||
|
|
||||||
|
require 'ffi'
|
||||||
|
|
||||||
|
module Fiddle
|
||||||
|
def self.malloc(size)
|
||||||
|
Fiddle::Pointer.malloc(size)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.free(ptr)
|
||||||
|
Fiddle::Pointer::LibC::FREE.call(ptr)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dlwrap(val)
|
||||||
|
Pointer.to_ptr(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
module Types
|
||||||
|
VOID = 0
|
||||||
|
VOIDP = 1
|
||||||
|
CHAR = 2
|
||||||
|
UCHAR = -CHAR
|
||||||
|
SHORT = 3
|
||||||
|
USHORT = -SHORT
|
||||||
|
INT = 4
|
||||||
|
UINT = -INT
|
||||||
|
LONG = 5
|
||||||
|
ULONG = -LONG
|
||||||
|
LONG_LONG = 6
|
||||||
|
ULONG_LONG = -LONG_LONG
|
||||||
|
FLOAT = 7
|
||||||
|
DOUBLE = 8
|
||||||
|
VARIADIC = 9
|
||||||
|
CONST_STRING = 10
|
||||||
|
BOOL = 11
|
||||||
|
INT8_T = CHAR
|
||||||
|
UINT8_T = UCHAR
|
||||||
|
if FFI::Type::Builtin::SHORT.size == 2
|
||||||
|
INT16_T = SHORT
|
||||||
|
UINT16_T = USHORT
|
||||||
|
elsif FFI::Type::Builtin::INT.size == 2
|
||||||
|
INT16_T = INT
|
||||||
|
UINT16_T = UINT
|
||||||
|
end
|
||||||
|
if FFI::Type::Builtin::SHORT.size == 4
|
||||||
|
INT32_T = SHORT
|
||||||
|
UINT32_T = USHORT
|
||||||
|
elsif FFI::Type::Builtin::INT.size == 4
|
||||||
|
INT32_T = INT
|
||||||
|
UINT32_T = UINT
|
||||||
|
elsif FFI::Type::Builtin::LONG.size == 4
|
||||||
|
INT32_T = LONG
|
||||||
|
UINT32_T = ULONG
|
||||||
|
end
|
||||||
|
if FFI::Type::Builtin::INT.size == 8
|
||||||
|
INT64_T = INT
|
||||||
|
UINT64_T = UINT
|
||||||
|
elsif FFI::Type::Builtin::LONG.size == 8
|
||||||
|
INT64_T = LONG
|
||||||
|
UINT64_T = ULONG
|
||||||
|
else
|
||||||
|
INT64_T = LONG_LONG
|
||||||
|
UINT64_T = ULONG_LONG
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: platform specific values
|
||||||
|
SSIZE_T = INT64_T
|
||||||
|
SIZE_T = -SSIZE_T
|
||||||
|
PTRDIFF_T = SSIZE_T
|
||||||
|
INTPTR_T = INT64_T
|
||||||
|
UINTPTR_T = -INTPTR_T
|
||||||
|
end
|
||||||
|
|
||||||
|
WINDOWS = FFI::Platform.windows?
|
||||||
|
|
||||||
|
module JRuby
|
||||||
|
FFITypes = {
|
||||||
|
'c' => FFI::Type::INT8,
|
||||||
|
'h' => FFI::Type::INT16,
|
||||||
|
'i' => FFI::Type::INT32,
|
||||||
|
'l' => FFI::Type::LONG,
|
||||||
|
'f' => FFI::Type::FLOAT32,
|
||||||
|
'd' => FFI::Type::FLOAT64,
|
||||||
|
'p' => FFI::Type::POINTER,
|
||||||
|
's' => FFI::Type::STRING,
|
||||||
|
|
||||||
|
Types::VOID => FFI::Type::Builtin::VOID,
|
||||||
|
Types::VOIDP => FFI::Type::Builtin::POINTER,
|
||||||
|
Types::CHAR => FFI::Type::Builtin::CHAR,
|
||||||
|
Types::UCHAR => FFI::Type::Builtin::UCHAR,
|
||||||
|
Types::SHORT => FFI::Type::Builtin::SHORT,
|
||||||
|
Types::USHORT => FFI::Type::Builtin::USHORT,
|
||||||
|
Types::INT => FFI::Type::Builtin::INT,
|
||||||
|
Types::UINT => FFI::Type::Builtin::UINT,
|
||||||
|
Types::LONG => FFI::Type::Builtin::LONG,
|
||||||
|
Types::ULONG => FFI::Type::Builtin::ULONG,
|
||||||
|
Types::LONG_LONG => FFI::Type::Builtin::LONG_LONG,
|
||||||
|
Types::ULONG_LONG => FFI::Type::Builtin::ULONG_LONG,
|
||||||
|
Types::FLOAT => FFI::Type::Builtin::FLOAT,
|
||||||
|
Types::DOUBLE => FFI::Type::Builtin::DOUBLE,
|
||||||
|
Types::BOOL => FFI::Type::Builtin::BOOL,
|
||||||
|
Types::CONST_STRING => FFI::Type::Builtin::POINTER,
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
if !dl_type.is_a?(Integer) && dl_type.respond_to?(:to_int)
|
||||||
|
dl_type = dl_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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Function
|
||||||
|
DEFAULT = "default"
|
||||||
|
STDCALL = "stdcall"
|
||||||
|
|
||||||
|
def initialize(ptr, args, return_type, abi = DEFAULT, kwargs = nil)
|
||||||
|
if kwargs.nil?
|
||||||
|
if abi.kind_of? Hash
|
||||||
|
kwargs = abi
|
||||||
|
abi = DEFAULT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@name = kwargs[:name] if kwargs.kind_of? Hash
|
||||||
|
@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) }
|
||||||
|
pointer = FFI::Pointer.new(ptr.to_i)
|
||||||
|
options = {convention: @abi}
|
||||||
|
if ffi_args.last == FFI::Type::Builtin::VARARGS
|
||||||
|
@function = FFI::VariadicInvoker.new(
|
||||||
|
pointer,
|
||||||
|
ffi_args,
|
||||||
|
ffi_return_type,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
else
|
||||||
|
@function = FFI::Function.new(ffi_return_type, ffi_args, pointer, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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|
|
||||||
|
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])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result = @function.call(*args, &block)
|
||||||
|
result = Pointer.new(result) if result.is_a?(FFI::Pointer)
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Closure
|
||||||
|
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) }
|
||||||
|
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
|
||||||
|
)
|
||||||
|
@freed = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
@function.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def free
|
||||||
|
return if @freed
|
||||||
|
@function.free
|
||||||
|
@freed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def freed?
|
||||||
|
@freed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Error < StandardError; end
|
||||||
|
class DLError < Error; end
|
||||||
|
class ClearedReferenceError < Error; end
|
||||||
|
|
||||||
|
class Pointer
|
||||||
|
attr_reader :ffi_ptr
|
||||||
|
extend FFI::DataConverter
|
||||||
|
native_type FFI::Type::Builtin::POINTER
|
||||||
|
|
||||||
|
def self.to_native(value, ctx)
|
||||||
|
if value.is_a?(Pointer)
|
||||||
|
value.ffi_ptr
|
||||||
|
|
||||||
|
elsif value.is_a?(Integer)
|
||||||
|
FFI::Pointer.new(value)
|
||||||
|
|
||||||
|
elsif value.is_a?(String)
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_native(value, ctx)
|
||||||
|
self.new(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.to_ptr(value)
|
||||||
|
if value.is_a?(String)
|
||||||
|
cptr = Pointer.malloc(value.bytesize)
|
||||||
|
cptr.ffi_ptr.put_string(0, value)
|
||||||
|
cptr
|
||||||
|
|
||||||
|
elsif value.is_a?(Array)
|
||||||
|
raise NotImplementedError, "array ptr"
|
||||||
|
|
||||||
|
elsif value.respond_to?(:to_ptr)
|
||||||
|
ptr = value.to_ptr
|
||||||
|
case ptr
|
||||||
|
when Pointer
|
||||||
|
ptr
|
||||||
|
when FFI::Pointer
|
||||||
|
Pointer.new(ptr)
|
||||||
|
else
|
||||||
|
raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{ptr.class}")
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
Pointer.new(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
alias [] to_ptr
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(*args, value)
|
||||||
|
if args.size == 2
|
||||||
|
if value.is_a?(Integer)
|
||||||
|
value = self.class.new(value)
|
||||||
|
end
|
||||||
|
if value.is_a?(Fiddle::Pointer)
|
||||||
|
value = value.to_str(args[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
@ffi_ptr.put_bytes(args[0], value, 0, args[1])
|
||||||
|
elsif args.size == 1
|
||||||
|
if value.is_a?(Fiddle::Pointer)
|
||||||
|
value = value.to_str(args[0] + 1)
|
||||||
|
else
|
||||||
|
value = value.chr
|
||||||
|
end
|
||||||
|
|
||||||
|
@ffi_ptr.put_bytes(args[0], value, 0, 1)
|
||||||
|
end
|
||||||
|
rescue FFI::NullPointerError
|
||||||
|
raise DLError.new("NULL pointer access")
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(addr, size = nil, free = nil)
|
||||||
|
ptr = if addr.is_a?(FFI::Pointer)
|
||||||
|
addr
|
||||||
|
|
||||||
|
elsif addr.is_a?(Integer)
|
||||||
|
FFI::Pointer.new(addr)
|
||||||
|
|
||||||
|
elsif addr.respond_to?(:to_ptr)
|
||||||
|
fiddle_ptr = addr.to_ptr
|
||||||
|
if fiddle_ptr.is_a?(Pointer)
|
||||||
|
fiddle_ptr.ffi_ptr
|
||||||
|
elsif fiddle_ptr.is_a?(FFI::AutoPointer)
|
||||||
|
addr.ffi_ptr
|
||||||
|
elsif fiddle_ptr.is_a?(FFI::Pointer)
|
||||||
|
fiddle_ptr
|
||||||
|
else
|
||||||
|
raise DLError.new("to_ptr should return a Fiddle::Pointer object, was #{fiddle_ptr.class}")
|
||||||
|
end
|
||||||
|
elsif addr.is_a?(IO)
|
||||||
|
raise NotImplementedError, "IO ptr isn't supported"
|
||||||
|
end
|
||||||
|
|
||||||
|
@size = size ? size : ptr.size
|
||||||
|
@free = free
|
||||||
|
@ffi_ptr = ptr
|
||||||
|
@freed = false
|
||||||
|
end
|
||||||
|
|
||||||
|
module LibC
|
||||||
|
extend FFI::Library
|
||||||
|
ffi_lib FFI::Library::LIBC
|
||||||
|
MALLOC = attach_function :malloc, [ :size_t ], :pointer
|
||||||
|
REALLOC = attach_function :realloc, [ :pointer, :size_t ], :pointer
|
||||||
|
FREE = attach_function :free, [ :pointer ], :void
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.malloc(size, free = nil)
|
||||||
|
if block_given? and free.nil?
|
||||||
|
message = "a free function must be supplied to #{self}.malloc " +
|
||||||
|
"when it is called with a block"
|
||||||
|
raise ArgumentError, message
|
||||||
|
end
|
||||||
|
|
||||||
|
pointer = new(LibC.malloc(size), size, free)
|
||||||
|
if block_given?
|
||||||
|
begin
|
||||||
|
yield(pointer)
|
||||||
|
ensure
|
||||||
|
pointer.call_free
|
||||||
|
end
|
||||||
|
else
|
||||||
|
pointer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def null?
|
||||||
|
@ffi_ptr.null?
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ptr
|
||||||
|
@ffi_ptr
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
defined?(@layout) ? @layout.size : @size
|
||||||
|
end
|
||||||
|
|
||||||
|
def free
|
||||||
|
@free
|
||||||
|
end
|
||||||
|
|
||||||
|
def free=(free)
|
||||||
|
@free = free
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_free
|
||||||
|
return if @free.nil?
|
||||||
|
return if @freed
|
||||||
|
if @free == RUBY_FREE
|
||||||
|
LibC::FREE.call(@ffi_ptr)
|
||||||
|
else
|
||||||
|
@free.call(@ffi_ptr)
|
||||||
|
end
|
||||||
|
@freed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def freed?
|
||||||
|
@freed
|
||||||
|
end
|
||||||
|
|
||||||
|
def size=(size)
|
||||||
|
@size = size
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](index, length = nil)
|
||||||
|
if length
|
||||||
|
ffi_ptr.get_bytes(index, length)
|
||||||
|
else
|
||||||
|
ffi_ptr.get_char(index)
|
||||||
|
end
|
||||||
|
rescue FFI::NullPointerError
|
||||||
|
raise DLError.new("NULL pointer dereference")
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
ffi_ptr.to_i
|
||||||
|
end
|
||||||
|
alias to_int to_i
|
||||||
|
|
||||||
|
# without \0
|
||||||
|
def to_s(len = nil)
|
||||||
|
if len
|
||||||
|
ffi_ptr.get_string(0, len)
|
||||||
|
else
|
||||||
|
ffi_ptr.get_string(0)
|
||||||
|
end
|
||||||
|
rescue FFI::NullPointerError
|
||||||
|
raise DLError.new("NULL pointer access")
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_str(len = nil)
|
||||||
|
if len
|
||||||
|
ffi_ptr.read_string(len)
|
||||||
|
else
|
||||||
|
ffi_ptr.read_string(@size)
|
||||||
|
end
|
||||||
|
rescue FFI::NullPointerError
|
||||||
|
raise DLError.new("NULL pointer access")
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_value
|
||||||
|
raise NotImplementedError, "to_value isn't supported"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name} ptr=#{to_i.to_s(16)} size=#{@size} free=#{@free.inspect}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def +(delta)
|
||||||
|
self.class.new(to_i + delta, @size - delta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def -(delta)
|
||||||
|
self.class.new(to_i - delta, @size + delta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
return unless other.is_a?(Pointer)
|
||||||
|
diff = self.to_i - other.to_i
|
||||||
|
return 0 if diff == 0
|
||||||
|
diff > 0 ? 1 : -1
|
||||||
|
end
|
||||||
|
|
||||||
|
def eql?(other)
|
||||||
|
return unless other.is_a?(Pointer)
|
||||||
|
self.to_i == other.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
eql?(other)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ptr
|
||||||
|
Pointer.new(ffi_ptr.get_pointer(0))
|
||||||
|
end
|
||||||
|
|
||||||
|
def +@
|
||||||
|
ptr
|
||||||
|
end
|
||||||
|
|
||||||
|
def -@
|
||||||
|
ref
|
||||||
|
end
|
||||||
|
|
||||||
|
def ref
|
||||||
|
cptr = Pointer.malloc(FFI::Type::POINTER.size, RUBY_FREE)
|
||||||
|
cptr.ffi_ptr.put_pointer(0, ffi_ptr)
|
||||||
|
cptr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Handle
|
||||||
|
RTLD_GLOBAL = FFI::DynamicLibrary::RTLD_GLOBAL
|
||||||
|
RTLD_LAZY = FFI::DynamicLibrary::RTLD_LAZY
|
||||||
|
RTLD_NOW = FFI::DynamicLibrary::RTLD_NOW
|
||||||
|
|
||||||
|
def initialize(libname = nil, flags = RTLD_LAZY | RTLD_GLOBAL)
|
||||||
|
@lib = FFI::DynamicLibrary.open(libname, flags) rescue LoadError
|
||||||
|
raise DLError.new("Could not open #{libname}") unless @lib
|
||||||
|
|
||||||
|
@open = true
|
||||||
|
|
||||||
|
begin
|
||||||
|
yield(self)
|
||||||
|
ensure
|
||||||
|
self.close
|
||||||
|
end if block_given?
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
raise DLError.new("closed handle") unless @open
|
||||||
|
@open = false
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sym(func)
|
||||||
|
DEFAULT.sym(func)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sym(func)
|
||||||
|
raise TypeError.new("invalid function name") unless func.is_a?(String)
|
||||||
|
raise DLError.new("closed handle") unless @open
|
||||||
|
address = @lib.find_function(func)
|
||||||
|
raise DLError.new("unknown symbol #{func}") if address.nil? || address.null?
|
||||||
|
address.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sym_defined?(func)
|
||||||
|
DEFAULT.sym_defined?(func)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sym_defined?(func)
|
||||||
|
raise TypeError.new("invalid function name") unless func.is_a?(String)
|
||||||
|
raise DLError.new("closed handle") unless @open
|
||||||
|
address = @lib.find_function(func)
|
||||||
|
!address.nil? && !address.null?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.[](func)
|
||||||
|
self.sym(func)
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](func)
|
||||||
|
sym(func)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable_close
|
||||||
|
@enable_close = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def close_enabled?
|
||||||
|
@enable_close
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable_close
|
||||||
|
@enable_close = false
|
||||||
|
end
|
||||||
|
|
||||||
|
DEFAULT = new
|
||||||
|
end
|
||||||
|
|
||||||
|
class Pinned
|
||||||
|
def initialize(object)
|
||||||
|
@object = object
|
||||||
|
end
|
||||||
|
|
||||||
|
def ref
|
||||||
|
if @object.nil?
|
||||||
|
raise ClearedReferenceError, "`ref` called on a cleared object"
|
||||||
|
end
|
||||||
|
@object
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
@object = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleared?
|
||||||
|
@object.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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_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
|
||||||
|
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
|
||||||
|
end
|
@ -41,6 +41,12 @@ module Fiddle
|
|||||||
when SIZEOF_LONG
|
when SIZEOF_LONG
|
||||||
PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_ULONG]
|
PACK_MAP[TYPE_BOOL] = PACK_MAP[TYPE_ULONG]
|
||||||
end
|
end
|
||||||
|
if RUBY_ENGINE == "jruby" and WINDOWS and [0].pack("l!").size == 8
|
||||||
|
# JRuby's 'l!' pack string doesn't use 32-bit on Windows.
|
||||||
|
# See https://github.com/jruby/jruby/issues/8357 for details
|
||||||
|
PACK_MAP[TYPE_LONG] = PACK_MAP[TYPE_INT]
|
||||||
|
PACK_MAP[TYPE_ULONG] = PACK_MAP[TYPE_UINT]
|
||||||
|
end
|
||||||
|
|
||||||
SIZE_MAP = {
|
SIZE_MAP = {
|
||||||
TYPE_VOIDP => SIZEOF_VOIDP,
|
TYPE_VOIDP => SIZEOF_VOIDP,
|
||||||
|
1
ext/fiddle/lib/fiddle/ruby.rb
Normal file
1
ext/fiddle/lib/fiddle/ruby.rb
Normal file
@ -0,0 +1 @@
|
|||||||
|
require "fiddle.so"
|
@ -290,15 +290,28 @@ module Fiddle
|
|||||||
# Allocates a C struct with the +types+ provided.
|
# Allocates a C struct with the +types+ provided.
|
||||||
#
|
#
|
||||||
# See Fiddle::Pointer.malloc for memory management issues.
|
# See Fiddle::Pointer.malloc for memory management issues.
|
||||||
def CStructEntity.malloc(types, func = nil, size = size(types), &block)
|
def CStructEntity.malloc(types, func = nil, size = size(types))
|
||||||
|
if block_given? and func.nil?
|
||||||
|
message = "a free function must be supplied to #{self}.malloc " +
|
||||||
|
"when it is called with a block"
|
||||||
|
raise ArgumentError, message
|
||||||
|
end
|
||||||
|
|
||||||
|
pointer = Pointer.malloc(size)
|
||||||
|
begin
|
||||||
|
struct = new(pointer, types, func)
|
||||||
|
rescue
|
||||||
|
pointer.free = func
|
||||||
|
pointer.call_free
|
||||||
|
raise
|
||||||
|
end
|
||||||
if block_given?
|
if block_given?
|
||||||
super(size, func) do |struct|
|
begin
|
||||||
struct.set_ctypes types
|
yield(struct)
|
||||||
yield struct
|
ensure
|
||||||
|
struct.call_free
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
struct = super(size, func)
|
|
||||||
struct.set_ctypes types
|
|
||||||
struct
|
struct
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -505,6 +518,14 @@ module Fiddle
|
|||||||
def to_s() # :nodoc:
|
def to_s() # :nodoc:
|
||||||
super(@size)
|
super(@size)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def +(delta)
|
||||||
|
Pointer.new(to_i + delta, @size - delta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def -(delta)
|
||||||
|
Pointer.new(to_i - delta, @size + delta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# A pointer to a C union
|
# A pointer to a C union
|
||||||
|
@ -4,11 +4,19 @@ require 'rbconfig/sizeof'
|
|||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'fiddle'
|
require 'fiddle'
|
||||||
|
|
||||||
|
puts("Fiddle::VERSION: #{Fiddle::VERSION}")
|
||||||
|
|
||||||
# FIXME: this is stolen from DL and needs to be refactored.
|
# FIXME: this is stolen from DL and needs to be refactored.
|
||||||
|
|
||||||
libc_so = libm_so = nil
|
libc_so = libm_so = nil
|
||||||
|
|
||||||
case RUBY_PLATFORM
|
if RUBY_ENGINE == "jruby"
|
||||||
|
# "jruby ... [x86_64-linux]" -> "x86_64-linux"
|
||||||
|
ruby_platform = RUBY_DESCRIPTION.split(" ").last[1..-2]
|
||||||
|
else
|
||||||
|
ruby_platform = RUBY_PLATFORM
|
||||||
|
end
|
||||||
|
case ruby_platform
|
||||||
when /cygwin/
|
when /cygwin/
|
||||||
libc_so = "cygwin1.dll"
|
libc_so = "cygwin1.dll"
|
||||||
libm_so = "cygwin1.dll"
|
libm_so = "cygwin1.dll"
|
||||||
@ -147,6 +155,7 @@ unless rigid_path
|
|||||||
end
|
end
|
||||||
|
|
||||||
if !libc_so || !libm_so
|
if !libc_so || !libm_so
|
||||||
|
require "envutil"
|
||||||
ruby = EnvUtil.rubybin
|
ruby = EnvUtil.rubybin
|
||||||
# When the ruby binary is 32-bit and the host is 64-bit,
|
# When the ruby binary is 32-bit and the host is 64-bit,
|
||||||
# `ldd ruby` outputs "not a dynamic executable" message.
|
# `ldd ruby` outputs "not a dynamic executable" message.
|
||||||
|
@ -8,6 +8,8 @@ module Fiddle
|
|||||||
class TestClosure < Fiddle::TestCase
|
class TestClosure < Fiddle::TestCase
|
||||||
def teardown
|
def teardown
|
||||||
super
|
super
|
||||||
|
# We can't use ObjectSpace with JRuby.
|
||||||
|
return if RUBY_ENGINE == "jruby"
|
||||||
# Ensure freeing all closures.
|
# Ensure freeing all closures.
|
||||||
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
||||||
not_freed_closures = []
|
not_freed_closures = []
|
||||||
@ -31,19 +33,6 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_type_symbol
|
|
||||||
Closure.create(:int, [:void]) do |closure|
|
|
||||||
assert_equal([
|
|
||||||
TYPE_INT,
|
|
||||||
[TYPE_VOID],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
closure.instance_variable_get(:@ctype),
|
|
||||||
closure.instance_variable_get(:@args),
|
|
||||||
])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_call
|
def test_call
|
||||||
closure_class = Class.new(Closure) do
|
closure_class = Class.new(Closure) do
|
||||||
def call
|
def call
|
||||||
@ -69,6 +58,11 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_const_string
|
def test_const_string
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Closure with :const_string works but " +
|
||||||
|
"Function with :const_string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
closure_class = Class.new(Closure) do
|
closure_class = Class.new(Closure) do
|
||||||
def call(string)
|
def call(string)
|
||||||
@return_string = "Hello! #{string}"
|
@return_string = "Hello! #{string}"
|
||||||
@ -94,7 +88,12 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_free
|
def test_free
|
||||||
Closure.create(:int, [:void]) do |closure|
|
closure_class = Class.new(Closure) do
|
||||||
|
def call
|
||||||
|
10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
closure_class.create(:int, [:void]) do |closure|
|
||||||
assert(!closure.freed?)
|
assert(!closure.freed?)
|
||||||
closure.free
|
closure.free
|
||||||
assert(closure.freed?)
|
assert(closure.freed?)
|
||||||
@ -115,6 +114,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_memsize_ruby_dev_42480
|
def test_memsize_ruby_dev_42480
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("We can't use ObjectSpace with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
require 'objspace'
|
require 'objspace'
|
||||||
n = 10000
|
n = 10000
|
||||||
n.times do
|
n.times do
|
||||||
|
@ -6,6 +6,10 @@ end
|
|||||||
|
|
||||||
class TestFiddle < Fiddle::TestCase
|
class TestFiddle < Fiddle::TestCase
|
||||||
def test_nil_true_etc
|
def test_nil_true_etc
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Q* aren't supported with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal Fiddle::Qtrue, Fiddle.dlwrap(true)
|
assert_equal Fiddle::Qtrue, Fiddle.dlwrap(true)
|
||||||
assert_equal Fiddle::Qfalse, Fiddle.dlwrap(false)
|
assert_equal Fiddle::Qfalse, Fiddle.dlwrap(false)
|
||||||
assert_equal Fiddle::Qnil, Fiddle.dlwrap(nil)
|
assert_equal Fiddle::Qnil, Fiddle.dlwrap(nil)
|
||||||
|
@ -26,6 +26,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_string
|
def test_string
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Function that returns string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
under_gc_stress do
|
under_gc_stress do
|
||||||
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||||
buff = +"000"
|
buff = +"000"
|
||||||
@ -82,6 +86,8 @@ module Fiddle
|
|||||||
assert_equal("1349", buff, bug4929)
|
assert_equal("1349", buff, bug4929)
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
|
# We can't use ObjectSpace with JRuby.
|
||||||
|
return if RUBY_ENGINE == "jruby"
|
||||||
# Ensure freeing all closures.
|
# Ensure freeing all closures.
|
||||||
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
||||||
not_freed_closures = []
|
not_freed_closures = []
|
||||||
@ -113,37 +119,36 @@ module Fiddle
|
|||||||
:variadic,
|
:variadic,
|
||||||
],
|
],
|
||||||
:int)
|
:int)
|
||||||
output_buffer = " " * 1024
|
Pointer.malloc(1024, Fiddle::RUBY_FREE) do |output|
|
||||||
output = Pointer[output_buffer]
|
written = snprintf.call(output,
|
||||||
|
output.size,
|
||||||
|
"int: %d, string: %.*s, const string: %s\n",
|
||||||
|
:int, -29,
|
||||||
|
:int, 4,
|
||||||
|
:voidp, "Hello",
|
||||||
|
:const_string, "World")
|
||||||
|
assert_equal("int: -29, string: Hell, const string: World\n",
|
||||||
|
output[0, written])
|
||||||
|
|
||||||
written = snprintf.call(output,
|
string_like_class = Class.new do
|
||||||
output.size,
|
def initialize(string)
|
||||||
"int: %d, string: %.*s, const string: %s\n",
|
@string = string
|
||||||
:int, -29,
|
end
|
||||||
:int, 4,
|
|
||||||
:voidp, "Hello",
|
|
||||||
:const_string, "World")
|
|
||||||
assert_equal("int: -29, string: Hell, const string: World\n",
|
|
||||||
output_buffer[0, written])
|
|
||||||
|
|
||||||
string_like_class = Class.new do
|
def to_str
|
||||||
def initialize(string)
|
@string
|
||||||
@string = string
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def to_str
|
|
||||||
@string
|
|
||||||
end
|
end
|
||||||
|
written = snprintf.call(output,
|
||||||
|
output.size,
|
||||||
|
"string: %.*s, const string: %s, uint: %u\n",
|
||||||
|
:int, 2,
|
||||||
|
:voidp, "Hello",
|
||||||
|
:const_string, string_like_class.new("World"),
|
||||||
|
:int, 29)
|
||||||
|
assert_equal("string: He, const string: World, uint: 29\n",
|
||||||
|
output[0, written])
|
||||||
end
|
end
|
||||||
written = snprintf.call(output,
|
|
||||||
output.size,
|
|
||||||
"string: %.*s, const string: %s, uint: %u\n",
|
|
||||||
:int, 2,
|
|
||||||
:voidp, "Hello",
|
|
||||||
:const_string, string_like_class.new("World"),
|
|
||||||
:int, 29)
|
|
||||||
assert_equal("string: He, const string: World, uint: 29\n",
|
|
||||||
output_buffer[0, written])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_rb_memory_view_available_p
|
def test_rb_memory_view_available_p
|
||||||
|
@ -16,6 +16,8 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
|
# We can't use ObjectSpace with JRuby.
|
||||||
|
return if RUBY_ENGINE == "jruby"
|
||||||
# Ensure freeing all closures.
|
# Ensure freeing all closures.
|
||||||
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
# See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 .
|
||||||
not_freed_closures = []
|
not_freed_closures = []
|
||||||
@ -36,6 +38,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_need_gvl?
|
def test_need_gvl?
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("rb_str_dup() doesn't exit in JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
libruby = Fiddle.dlopen(nil)
|
libruby = Fiddle.dlopen(nil)
|
||||||
rb_str_dup = Function.new(libruby['rb_str_dup'],
|
rb_str_dup = Function.new(libruby['rb_str_dup'],
|
||||||
[:voidp],
|
[:voidp],
|
||||||
@ -103,6 +109,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_last_error
|
def test_last_error
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle.last_error doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||||
|
|
||||||
assert_nil Fiddle.last_error
|
assert_nil Fiddle.last_error
|
||||||
@ -135,6 +145,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_strcpy
|
def test_strcpy
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Function that returns string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||||
buff = +"000"
|
buff = +"000"
|
||||||
str = f.call(buff, "123")
|
str = f.call(buff, "123")
|
||||||
@ -149,6 +163,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_function_as_proc
|
def test_function_as_proc
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Function that returns string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||||
buff, str = call_proc("123", &f)
|
buff, str = call_proc("123", &f)
|
||||||
assert_equal("123", buff)
|
assert_equal("123", buff)
|
||||||
@ -156,6 +174,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_function_as_method
|
def test_function_as_method
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Function that returns string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
|
||||||
klass = Class.new do
|
klass = Class.new do
|
||||||
define_singleton_method(:strcpy, &f)
|
define_singleton_method(:strcpy, &f)
|
||||||
@ -194,6 +216,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_no_memory_leak
|
def test_no_memory_leak
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("rb_obj_frozen_p() doesn't exist in JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
if respond_to?(:assert_nothing_leaked_memory)
|
if respond_to?(:assert_nothing_leaked_memory)
|
||||||
rb_obj_frozen_p_symbol = Fiddle.dlopen(nil)["rb_obj_frozen_p"]
|
rb_obj_frozen_p_symbol = Fiddle.dlopen(nil)["rb_obj_frozen_p"]
|
||||||
rb_obj_frozen_p = Fiddle::Function.new(rb_obj_frozen_p_symbol,
|
rb_obj_frozen_p = Fiddle::Function.new(rb_obj_frozen_p_symbol,
|
||||||
|
@ -9,11 +9,19 @@ module Fiddle
|
|||||||
include Fiddle
|
include Fiddle
|
||||||
|
|
||||||
def test_to_i
|
def test_to_i
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Handle#to_i is unavailable with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
handle = Fiddle::Handle.new(LIBC_SO)
|
handle = Fiddle::Handle.new(LIBC_SO)
|
||||||
assert_kind_of Integer, handle.to_i
|
assert_kind_of Integer, handle.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_to_ptr
|
def test_to_ptr
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Handle#to_i is unavailable with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
handle = Fiddle::Handle.new(LIBC_SO)
|
handle = Fiddle::Handle.new(LIBC_SO)
|
||||||
ptr = handle.to_ptr
|
ptr = handle.to_ptr
|
||||||
assert_equal ptr.to_i, handle.to_i
|
assert_equal ptr.to_i, handle.to_i
|
||||||
@ -26,6 +34,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_static_sym
|
def test_static_sym
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("We can't assume static symbols with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# Linux / Darwin / FreeBSD
|
# Linux / Darwin / FreeBSD
|
||||||
refute_nil Fiddle::Handle.sym('dlopen')
|
refute_nil Fiddle::Handle.sym('dlopen')
|
||||||
@ -90,6 +102,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_initialize_noargs
|
def test_initialize_noargs
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("rb_str_new() doesn't exist in JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
handle = Handle.new
|
handle = Handle.new
|
||||||
refute_nil handle['rb_str_new']
|
refute_nil handle['rb_str_new']
|
||||||
end
|
end
|
||||||
@ -117,6 +133,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_file_name
|
def test_file_name
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Handle::NEXT doesn't exist with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
file_name = Handle.new(LIBC_SO).file_name
|
file_name = Handle.new(LIBC_SO).file_name
|
||||||
if file_name
|
if file_name
|
||||||
assert_kind_of String, file_name
|
assert_kind_of String, file_name
|
||||||
@ -135,6 +155,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_NEXT
|
def test_NEXT
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Handle::NEXT doesn't exist with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# Linux / Darwin
|
# Linux / Darwin
|
||||||
#
|
#
|
||||||
@ -173,9 +197,13 @@ module Fiddle
|
|||||||
end unless /mswin|mingw/ =~ RUBY_PLATFORM
|
end unless /mswin|mingw/ =~ RUBY_PLATFORM
|
||||||
|
|
||||||
def test_DEFAULT
|
def test_DEFAULT
|
||||||
|
if Fiddle::WINDOWS
|
||||||
|
omit("Fiddle::Handle::DEFAULT doesn't have malloc() on Windows")
|
||||||
|
end
|
||||||
|
|
||||||
handle = Handle::DEFAULT
|
handle = Handle::DEFAULT
|
||||||
refute_nil handle['malloc']
|
refute_nil handle['malloc']
|
||||||
end unless /mswin|mingw/ =~ RUBY_PLATFORM
|
end
|
||||||
|
|
||||||
def test_dlerror
|
def test_dlerror
|
||||||
# FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
|
# FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
|
||||||
|
@ -149,11 +149,15 @@ module Fiddle
|
|||||||
def test_unsigned_result()
|
def test_unsigned_result()
|
||||||
d = (2 ** 31) + 1
|
d = (2 ** 31) + 1
|
||||||
|
|
||||||
r = LIBC.strtoul(d.to_s, 0, 0)
|
r = LIBC.strtoul(d.to_s, nil, 0)
|
||||||
assert_equal(d, r)
|
assert_equal(d, r)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_io()
|
def test_io()
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("BUILD_RUBY_PLATFORM doesn't exist in JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf)
|
if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -329,11 +333,12 @@ module Fiddle
|
|||||||
|
|
||||||
def test_struct_nested_struct_replace_array_element_hash()
|
def test_struct_nested_struct_replace_array_element_hash()
|
||||||
LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
|
LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s|
|
||||||
|
s.vertices[0] = nil
|
||||||
s.vertices[0] = {
|
s.vertices[0] = {
|
||||||
position: {
|
position: {
|
||||||
x: 10,
|
x: 10,
|
||||||
y: 100,
|
y: 100,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
assert_equal({
|
assert_equal({
|
||||||
"position" => {
|
"position" => {
|
||||||
@ -450,7 +455,7 @@ module Fiddle
|
|||||||
s.buff = "012345\377"
|
s.buff = "012345\377"
|
||||||
assert_equal([0,1,2,3,4], s.num)
|
assert_equal([0,1,2,3,4], s.num)
|
||||||
assert_equal(?a.ord, s.c)
|
assert_equal(?a.ord, s.c)
|
||||||
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
|
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,"\xFF".ord], s.buff)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -467,6 +472,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_strcpy()
|
def test_strcpy()
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Function that returns string doesn't work with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
buff = +"000"
|
buff = +"000"
|
||||||
str = LIBC.strcpy(buff, "123")
|
str = LIBC.strcpy(buff, "123")
|
||||||
assert_equal("123", buff)
|
assert_equal("123", buff)
|
||||||
|
@ -11,19 +11,22 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_can_read_write_memory
|
def test_can_read_write_memory
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Pointer.{read,write} don't exist in JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
# Allocate some memory
|
# Allocate some memory
|
||||||
address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP)
|
Fiddle::Pointer.malloc(Fiddle::SIZEOF_VOIDP, Fiddle::RUBY_FREE) do |pointer|
|
||||||
|
address = pointer.to_i
|
||||||
|
bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a.pack("C*")
|
||||||
|
|
||||||
bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a.pack("C*")
|
# Write to the memory
|
||||||
|
Fiddle::Pointer.write(address, bytes_to_write)
|
||||||
|
|
||||||
# Write to the memory
|
# Read the bytes out again
|
||||||
Fiddle::Pointer.write(address, bytes_to_write)
|
bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP)
|
||||||
|
assert_equal bytes_to_write, bytes
|
||||||
# Read the bytes out again
|
end
|
||||||
bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP)
|
|
||||||
assert_equal bytes_to_write, bytes
|
|
||||||
ensure
|
|
||||||
Fiddle.free address
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_cptr_to_int
|
def test_cptr_to_int
|
||||||
@ -110,6 +113,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_inspect
|
def test_inspect
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Pointer#inspect is incompatible on JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
ptr = Pointer.new(0)
|
ptr = Pointer.new(0)
|
||||||
inspect = ptr.inspect
|
inspect = ptr.inspect
|
||||||
assert_match(/size=#{ptr.size}/, inspect)
|
assert_match(/size=#{ptr.size}/, inspect)
|
||||||
@ -125,6 +132,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_to_ptr_io
|
def test_to_ptr_io
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle::Pointer.to_ptr(IO) isn't supported with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf|
|
Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf|
|
||||||
File.open(__FILE__, 'r') do |f|
|
File.open(__FILE__, 'r') do |f|
|
||||||
ptr = Pointer.to_ptr f
|
ptr = Pointer.to_ptr f
|
||||||
@ -172,6 +183,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_ref_ptr
|
def test_ref_ptr
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle.dlwrap([]) isn't supported with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
ary = [0,1,2,4,5]
|
ary = [0,1,2,4,5]
|
||||||
addr = Pointer.new(dlwrap(ary))
|
addr = Pointer.new(dlwrap(ary))
|
||||||
assert_equal addr.to_i, addr.ref.ptr.to_i
|
assert_equal addr.to_i, addr.ref.ptr.to_i
|
||||||
@ -180,6 +195,10 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_to_value
|
def test_to_value
|
||||||
|
if RUBY_ENGINE == "jruby"
|
||||||
|
omit("Fiddle.dlwrap([]) isn't supported with JRuby")
|
||||||
|
end
|
||||||
|
|
||||||
ary = [0,1,2,4,5]
|
ary = [0,1,2,4,5]
|
||||||
addr = Pointer.new(dlwrap(ary))
|
addr = Pointer.new(dlwrap(ary))
|
||||||
assert_equal ary, addr.to_value
|
assert_equal ary, addr.to_value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user