Permalink
Browse files

FFI::Library.attach_variable

  • Loading branch information...
1 parent c1061d7 commit 65342414f91741d8ef7ce1f13126fc742ca8568d @DanielVartanov committed Feb 19, 2012
View
@@ -106,6 +106,36 @@ def attach_function(name, a3, a4, a5=nil)
ffi_function_missing cname, mname, args, ret
end
+ def attach_variable(mname, cname, type)
+ ffi_libraries.each do |lib|
+ if pointer = lib.find_symbol(cname.to_s)
+ container_class = Class.new(FFI::Struct)
+ container_class.layout :gvar, type
+ container = container_class.new(pointer)
+
+ define_gvar(mname, container)
+
+ return pointer
+ end
+ end
+ raise FFI::NotFoundError, "Unable to find '#{cname}'"
+ end
+
+ def define_gvar(mname, container)
+ mixin = Module.new do
+ define_method(mname) do
+ container[:gvar]
+ end
+
+ define_method("#{mname}=") do |value|
+ container[:gvar] = value
+ end
+ end
+
+ extend mixin
@evanphx

evanphx Feb 23, 2012

Creating a module and then extending the current module with it is unnecessary. Just call define_method on self since self is a Module.

+ end
+ private :define_gvar
+
# Generic error method attached in place of missing foreign functions
# during kernel loading. See kernel/delta/ffi.rb for a version that raises
# immediately if the foreign function is unavaiblable.
@@ -0,0 +1,104 @@
+require File.expand_path('../../spec_helper', __FILE__)
+
+describe FFI::Library, "#attach_variable" do
+ extend FFISpecs::LibraryHelper
+
+ it "should raise an exception if a variable does not exist" do
+ lambda {
+ extended_module do
+ attach_variable :nonexistent, "nonexistent", :char
+ end
+ }.should raise_error(FFI::NotFoundError)
+ end
+
+ [ 0, 127, -128, -1 ].each do |i|
+ it "should correctly operate with :char variable" do
+ gvar_test("s8", :char, i)
+ end
+ end
+
+ [ 0, 0x7f, 0x80, 0xff ].each do |i|
+ it "should correctly operate with :uchar variable" do
+ gvar_test("u8", :uchar, i)
+ end
+ end
+
+ [ 0, 0x7fff, -0x8000, -1 ].each do |i|
+ it "should correctly operate with :short variable" do
+ gvar_test("s16", :short, i)
+ end
+ end
+
+ [ 0, 0x7fff, 0x8000, 0xffff ].each do |i|
+ it "should correctly operate with :ushort variable" do
+ gvar_test("u16", :ushort, i)
+ end
+ end
+
+ [ 0, 0x7fffffff, -0x80000000, -1 ].each do |i|
+ it "should correctly operate with :int variable" do
+ gvar_test("s32", :int, i)
+ end
+ end
+
+ [ 0, 0x7fffffff, 0x80000000, 0xffffffff ].each do |i|
+ it "should correctly operate with :uint variable" do
+ gvar_test("u32", :uint, i)
+ end
+ end
+
+ [ 0, 0x7fffffffffffffff, -0x8000000000000000, -1 ].each do |i|
+ it "should correctly operate with :long_long variable" do
+ gvar_test("s64", :long_long, i)
+ end
+ end
+
+ [ 0, 0x7fffffffffffffff, 0x8000000000000000, 0xffffffffffffffff ].each do |i|
+ it "should correctly operate with :ulong_long variable" do
+ gvar_test("u64", :ulong_long, i)
+ end
+ end
+
+ if FFI::Platform::LONG_SIZE == 32
+ [ 0, 0x7fffffff, -0x80000000, -1 ].each do |i|
+ it "should correctly operate with :long variable" do
+ gvar_test("long", :long, i)
+ end
+ end
+
+ [ 0, 0x7fffffff, 0x80000000, 0xffffffff ].each do |i|
+ it "should correctly operate with :ulong variable" do
+ gvar_test("ulong", :ulong, i)
+ end
+ end
+ else
+ [ 0, 0x7fffffffffffffff, -0x8000000000000000, -1 ].each do |i|
+ it "should correctly operate with :long variable" do
+ gvar_test("long", :long, i)
+ end
+ end
+
+ [ 0, 0x7fffffffffffffff, 0x8000000000000000, 0xffffffffffffffff ].each do |i|
+ it "should correctly operate with :ulong variable" do
+ gvar_test("ulong", :ulong, i)
+ end
+ end
+ end
+
+ def gvar_lib(name, type)
+ extended_module FFISpecs::LIBRARY do
+ attach_variable :gvar, "gvar_#{name}", type
+ attach_function :get, "gvar_#{name}_get", [], type
+ attach_function :set, "gvar_#{name}_set", [ type ], :void
+ end
+ end
+
+ def gvar_test(name, type, val)
+ lib = gvar_lib(name, type)
+ lib.set(val)
+ lib.gvar.should == val
+ lib.set(0)
+ lib.gvar = val
+ lib.get.should == val
+ end
+end
@@ -0,0 +1,6 @@
+__DIR__ = File.dirname(__FILE__)
+
+require File.join(__DIR__, '..', '..', 'spec_helper')
+require 'ffi'
+require File.join(__DIR__, '..', '..', 'ruby', 'optional', 'ffi', 'support', 'ffi_specs')
+require File.join(__DIR__, 'support', 'library_helper')
@@ -0,0 +1,9 @@
+module FFISpecs::LibraryHelper
+ def extended_module(*libs, &block)
+ Module.new do
+ extend FFI::Library
+ ffi_lib(*libs) unless libs.empty?
+ module_eval(&block)
+ end
+ end
+end
@@ -7,31 +7,5 @@
end
end
-module FFISpecs
- include FFI
-
- LongSize = FFI::Platform::LONG_SIZE / 8
-
- FIXTURE_DIR = File.expand_path("../fixtures", __FILE__)
- LIBRARY = File.join(FIXTURE_DIR, "build/libtest/libtest.#{FFI::Platform::LIBSUFFIX}")
-
- def self.need_to_compile_fixtures?
- !File.exist?(LIBRARY) or Dir.glob(File.join(FIXTURE_DIR, "*.c")).any? { |f| File.mtime(f) > File.mtime(LIBRARY) }
- end
-
- if need_to_compile_fixtures?
- puts "[!] Compiling Ruby-FFI fixtures"
- Dir.chdir(File.dirname(FIXTURE_DIR)) do
- unless system("make -f fixtures/GNUmakefile")
- raise "Failed to compile Ruby-FFI fixtures"
- end
- end
- end
-
- module LibTest
- extend FFI::Library
- ffi_lib LIBRARY
- end
-end
-
+require File.join(File.dirname(__FILE__), 'support', 'ffi_specs')
require File.join(FFISpecs::FIXTURE_DIR, 'classes')
@@ -0,0 +1,26 @@
+module FFISpecs
+ include FFI
+
+ LongSize = FFI::Platform::LONG_SIZE / 8
+
+ FIXTURE_DIR = File.expand_path("../../fixtures", __FILE__)
+ LIBRARY = File.join(FIXTURE_DIR, "build/libtest/libtest.#{FFI::Platform::LIBSUFFIX}")
+
+ def self.need_to_compile_fixtures?
+ !File.exist?(LIBRARY) or Dir.glob(File.join(FIXTURE_DIR, "*.c")).any? { |f| File.mtime(f) > File.mtime(LIBRARY) }
+ end
+
+ if need_to_compile_fixtures?
+ puts "[!] Compiling Ruby-FFI fixtures"
+ Dir.chdir(File.dirname(FIXTURE_DIR)) do
+ unless system("make -f fixtures/GNUmakefile")
+ raise "Failed to compile Ruby-FFI fixtures"
+ end
+ end
+ end
+
+ module LibTest
+ extend FFI::Library
+ ffi_lib LIBRARY
+ end
+end
@@ -0,0 +1 @@
+fails:FFI::Library#attach_variable should correctly operate with :ushort variable
@@ -0,0 +1 @@
+fails:FFI::Library#attach_variable should correctly operate with :ushort variable

1 comment on commit 6534241

The changes to spec/core need to be in a separate commit.

Please sign in to comment.