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

FFI::Libary#find_type does not behave as expected #1044

Open
itspomf opened this issue Aug 31, 2023 · 1 comment
Open

FFI::Libary#find_type does not behave as expected #1044

itspomf opened this issue Aug 31, 2023 · 1 comment

Comments

@itspomf
Copy link

itspomf commented Aug 31, 2023

Brief

When passing the canonical name of a FFI::Pointer subclass as a type, FFI::Library fails to introspect the correct type and return FFI::Type::POINTER. Insofar it has been confirmed to impact FFI::Struct, but theoretically affects all parts of the gem.

Example code

A trivial example can be performed with the following:

require 'ffi'

# A subclass of MemoryPointer, itself a subclass of Pointer.
class SpecialReference < FFI::MemoryPointer

  # additional features would be put here ...
end

# A struct which uses a SpecialReference
class ExampleData < FFI::Struct
  layout :value, :uint32_t,
         :ref, SpecialReference # this will fail with TypeError
end

Expectation

When passing a subclass of Pointer, the expected output should be one of :pointer or Type::POINTER.

Reality

A TypeError is raised, declaring the type as invalid.

Example output

/var/lib/gems/3.1.0/gems/ffi-1.15.5/lib/ffi/types.rb:69:in `find_type': unable to resolve type 'SpecialReference' (TypeError)
from /var/lib/gems/3.1.0/gems/ffi-1.15.5/lib/ffi/struct.rb:278:in `find_type'
from /var/lib/gems/3.1.0/gems/ffi-1.15.5/lib/ffi/struct.rb:271:in `find_field_type'
from /var/lib/gems/3.1.0/gems/ffi-1.15.5/lib/ffi/struct.rb:311:in `array_layout'
from /var/lib/gems/3.1.0/gems/ffi-1.15.5/lib/ffi/struct.rb:217:in `layout'
from (irb):11:in `<class:ExampleData>'
from (irb):10:in `<main>'
...

Proposed solution

It appears that the current check used is inadequate, as it fails to occur as expected (see Relevant source code below). Replacing the line with the following appears to resolve the issue.

elsif t.ancestors.include?(Pointer) and not t.kind_of?(Struct)
  Type::POINTER

Relevant source code

See: library.rb:575, reproduced in full below.

# @param [DataConverter, Type, Struct, Symbol] t type to find
# @return [Type]
# Find a type definition.
def find_type(t)
  if t.kind_of?(Type)
    t

  elsif defined?(@ffi_typedefs) && @ffi_typedefs.has_key?(t)
    @ffi_typedefs[t]

  elsif t.is_a?(Class) && t < Struct
    Type::POINTER

  elsif t.is_a?(DataConverter)
    # Add a typedef so next time the converter is used, it hits the cache
    typedef Type::Mapped.new(t), t
   end || FFI.find_type(t)
end
@ParadoxV5
Copy link
Contributor

elsif t.ancestors.include?(Pointer) and not t.kind_of?(Struct)
  Type::POINTER
  • This will raise undefined method `ancestors' (NoMethodError) for non-Modules.
    • Replace it with t.is_a?(Class) && t < Pointer like the current code.
      • This can merge with the elsif t.is_a?(Class) && t < Struct branch.
    • Alternativelyt.is_a? Pointer.singleton_class
  • Struct is not a subclass of Pointer, so the and not t.kind_of?(Struct) part will always pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants