Skip to content

Commit 0becddc

Browse files
authored
Fix Fiddle::Pointer#ref behavior for FFI backend (#185)
Fixes #182 by making the pointer address dynamic. ```ruby require "fiddle" ptr = Fiddle::Pointer["hello"] ptr.ref[0, Fiddle::SIZEOF_VOIDP] = Fiddle::Pointer["world"].ref p ptr.to_s ``` The code above previously returned `"hello"` with the FFI backend.
1 parent 47be73d commit 0becddc

File tree

2 files changed

+27
-11
lines changed

2 files changed

+27
-11
lines changed

lib/fiddle/ffi_backend.rb

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ class Error < StandardError; end
218218
class DLError < Error; end
219219
class ClearedReferenceError < Error; end
220220

221+
# Pointer isn't thread safe for now
221222
class Pointer
222-
attr_reader :ffi_ptr
223223
extend FFI::DataConverter
224224
native_type FFI::Type::Builtin::POINTER
225225

@@ -285,15 +285,15 @@ def []=(*args, value)
285285
value = value.to_str(args[1])
286286
end
287287

288-
@ffi_ptr.put_bytes(args[0], value, 0, args[1])
288+
ffi_ptr.put_bytes(args[0], value, 0, args[1])
289289
elsif args.size == 1
290290
if value.is_a?(Fiddle::Pointer)
291291
value = value.to_str(args[0] + 1)
292292
else
293293
value = value.chr
294294
end
295295

296-
@ffi_ptr.put_bytes(args[0], value, 0, 1)
296+
ffi_ptr.put_bytes(args[0], value, 0, 1)
297297
end
298298
rescue FFI::NullPointerError
299299
raise DLError.new("NULL pointer access")
@@ -332,9 +332,14 @@ def initialize(addr, size = nil, free = nil)
332332
end
333333
@free = free
334334
@ffi_ptr = ptr
335+
@addr_ptr = nil
335336
@freed = false
336337
end
337338

339+
def ffi_ptr
340+
@addr_ptr ? @addr_ptr.get_pointer(0) : @ffi_ptr
341+
end
342+
338343
module LibC
339344
extend FFI::Library
340345
ffi_lib FFI::Library::LIBC
@@ -363,11 +368,11 @@ def self.malloc(size, free = nil)
363368
end
364369

365370
def null?
366-
@ffi_ptr.null?
371+
ffi_ptr.null?
367372
end
368373

369374
def to_ptr
370-
@ffi_ptr
375+
ffi_ptr
371376
end
372377

373378
def size
@@ -386,9 +391,9 @@ def call_free
386391
return if @free.nil?
387392
return if @freed
388393
if @free == RUBY_FREE
389-
LibC::FREE.call(@ffi_ptr)
394+
LibC::FREE.call(ffi_ptr)
390395
else
391-
@free.call(@ffi_ptr)
396+
@free.call(ffi_ptr)
392397
end
393398
@freed = true
394399
end
@@ -495,10 +500,8 @@ def -@
495500
end
496501

497502
def ref
498-
cptr = Pointer.malloc(FFI::Type::POINTER.size, RUBY_FREE)
499-
cptr.ffi_ptr.put_pointer(0, ffi_ptr)
500-
cptr.size = 0
501-
cptr
503+
@addr_ptr ||= FFI::MemoryPointer.new(:pointer).put_pointer(0, @ffi_ptr)
504+
Pointer.new(@addr_ptr, 0)
502505
end
503506
end
504507

test/fiddle/test_pointer.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,19 @@ def test_cmp
193193
assert_nil(ptr <=> 10, '10 should not be comparable')
194194
end
195195

196+
def test_ref
197+
ptr = Fiddle::Pointer["hello"]
198+
ref = ptr.ref
199+
assert_equal(0, ref.size)
200+
assert_nil(ref.free)
201+
assert_equal(ptr, ref.ptr)
202+
203+
ptr2 = Fiddle::Pointer["world"]
204+
ptr.ref[0, Fiddle::SIZEOF_VOIDP] = ptr2.ref
205+
assert_equal("world", ptr.to_s)
206+
assert_equal(ptr.to_i, ptr2.to_i)
207+
end
208+
196209
def test_ref_ptr
197210
if ffi_backend?
198211
omit("Fiddle.dlwrap([]) isn't supported with FFI backend")

0 commit comments

Comments
 (0)