gir_ffi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/DESIGN.rdoc +54 -0
- data/History.txt +3 -0
- data/README.rdoc +59 -0
- data/Rakefile +21 -0
- data/TODO.rdoc +40 -0
- data/examples/01_empty_window.rb +15 -0
- data/examples/02_hello_world.rb +30 -0
- data/examples/03_upgraded_hello_world.rb +45 -0
- data/examples/demo_ffi_inherited_layout.rb +21 -0
- data/examples/demo_ffi_nested_struct.rb +17 -0
- data/examples/demo_ffi_safe_inherited_layout.rb +43 -0
- data/examples/hard_coded.rb +144 -0
- data/lib/gir_ffi.rb +47 -0
- data/lib/gir_ffi/allocation_helper.rb +12 -0
- data/lib/gir_ffi/arg_helper.rb +77 -0
- data/lib/gir_ffi/base.rb +23 -0
- data/lib/gir_ffi/builder.rb +159 -0
- data/lib/gir_ffi/builder_helper.rb +32 -0
- data/lib/gir_ffi/class_base.rb +11 -0
- data/lib/gir_ffi/class_builder.rb +116 -0
- data/lib/gir_ffi/constructor_definition_builder.rb +20 -0
- data/lib/gir_ffi/function_definition_builder.rb +148 -0
- data/lib/gir_ffi/g_error.rb +8 -0
- data/lib/gir_ffi/g_type.rb +14 -0
- data/lib/gir_ffi/i_arg_info.rb +16 -0
- data/lib/gir_ffi/i_base_info.rb +45 -0
- data/lib/gir_ffi/i_callable_info.rb +18 -0
- data/lib/gir_ffi/i_callback_info.rb +7 -0
- data/lib/gir_ffi/i_constant_info.rb +6 -0
- data/lib/gir_ffi/i_enum_info.rb +13 -0
- data/lib/gir_ffi/i_field_info.rb +10 -0
- data/lib/gir_ffi/i_flags_info.rb +5 -0
- data/lib/gir_ffi/i_function_info.rb +16 -0
- data/lib/gir_ffi/i_interface_info.rb +7 -0
- data/lib/gir_ffi/i_object_info.rb +50 -0
- data/lib/gir_ffi/i_property_info.rb +7 -0
- data/lib/gir_ffi/i_registered_type_info.rb +8 -0
- data/lib/gir_ffi/i_repository.rb +108 -0
- data/lib/gir_ffi/i_signal_info.rb +7 -0
- data/lib/gir_ffi/i_struct_info.rb +22 -0
- data/lib/gir_ffi/i_type_info.rb +25 -0
- data/lib/gir_ffi/i_union_info.rb +7 -0
- data/lib/gir_ffi/i_value_info.rb +8 -0
- data/lib/gir_ffi/i_vfunc_info.rb +7 -0
- data/lib/gir_ffi/lib.rb +174 -0
- data/lib/gir_ffi/lib_c.rb +11 -0
- data/lib/gir_ffi/method_missing_definition_builder.rb +62 -0
- data/lib/gir_ffi/module_builder.rb +66 -0
- data/lib/gir_ffi/overrides/gtk.rb +12 -0
- data/lib/gir_ffi/version.rb +4 -0
- data/tasks/bones.rake +87 -0
- data/tasks/notes.rake +134 -0
- data/tasks/post_load.rake +25 -0
- data/tasks/setup.rb +138 -0
- data/tasks/test.rake +22 -0
- data/test/arg_helper_test.rb +112 -0
- data/test/builder_test.rb +328 -0
- data/test/constructor_definition_builder_test.rb +19 -0
- data/test/function_definition_builder_test.rb +60 -0
- data/test/g_type_test.rb +22 -0
- data/test/girffi_test.rb +11 -0
- data/test/gtk_overrides_test.rb +22 -0
- data/test/i_repository_test.rb +54 -0
- data/test/test_helper.rb +39 -0
- metadata +174 -0
data/lib/gir_ffi.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module GirFFI
|
3
|
+
|
4
|
+
# :stopdoc:
|
5
|
+
VERSION = '0.0.1'
|
6
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
7
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
8
|
+
# :startdoc:
|
9
|
+
|
10
|
+
# Returns the version string for the library.
|
11
|
+
#
|
12
|
+
def self.version
|
13
|
+
VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the library path for the module. If any arguments are given,
|
17
|
+
# they will be joined to the end of the libray path using
|
18
|
+
# <tt>File.join</tt>.
|
19
|
+
#
|
20
|
+
def self.libpath( *args )
|
21
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the lpath for the module. If any arguments are given,
|
25
|
+
# they will be joined to the end of the path using
|
26
|
+
# <tt>File.join</tt>.
|
27
|
+
#
|
28
|
+
def self.path( *args )
|
29
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'gir_ffi/i_repository'
|
35
|
+
require 'gir_ffi/builder'
|
36
|
+
|
37
|
+
module GirFFI
|
38
|
+
def self.setup module_name
|
39
|
+
module_name = module_name.to_s
|
40
|
+
GirFFI::Builder.build_module module_name
|
41
|
+
begin
|
42
|
+
require "gir_ffi/overrides/#{module_name.downcase}"
|
43
|
+
rescue LoadError
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
# EOF
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'gir_ffi/allocation_helper'
|
2
|
+
|
3
|
+
module GirFFI
|
4
|
+
module ArgHelper
|
5
|
+
def self.object_to_inptr obj
|
6
|
+
return obj.to_ptr if obj.respond_to? :to_ptr
|
7
|
+
return nil if obj.nil?
|
8
|
+
FFI::Pointer.new(obj.object_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.int_to_inoutptr val
|
12
|
+
ptr = AllocationHelper.safe_malloc FFI.type_size(:int)
|
13
|
+
ptr.write_int val
|
14
|
+
return ptr
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.string_array_to_inoutptr ary
|
18
|
+
return nil if ary.nil?
|
19
|
+
|
20
|
+
ptrs = ary.map {|str|
|
21
|
+
len = str.bytesize
|
22
|
+
AllocationHelper.safe_malloc(len + 1).write_string(str).put_char(len, 0)
|
23
|
+
}
|
24
|
+
|
25
|
+
block = AllocationHelper.safe_malloc FFI.type_size(:pointer) * ptrs.length
|
26
|
+
block.write_array_of_pointer ptrs
|
27
|
+
|
28
|
+
argv = AllocationHelper.safe_malloc FFI.type_size(:pointer)
|
29
|
+
argv.write_pointer block
|
30
|
+
argv
|
31
|
+
end
|
32
|
+
|
33
|
+
# Converts an outptr to an int, then frees the outptr.
|
34
|
+
def self.outptr_to_int ptr
|
35
|
+
value = ptr.read_int
|
36
|
+
LibC.free ptr
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
# Converts an outptr to a string array, then frees the outptr.
|
41
|
+
def self.outptr_to_string_array ptr, size
|
42
|
+
return nil if ptr.nil?
|
43
|
+
|
44
|
+
block = ptr.read_pointer
|
45
|
+
LibC.free ptr
|
46
|
+
|
47
|
+
return nil if block.null?
|
48
|
+
|
49
|
+
ptrs = block.read_array_of_pointer(size)
|
50
|
+
LibC.free block
|
51
|
+
|
52
|
+
ptrs.map { |p| p.null? ? nil : (str = p.read_string; LibC.free p; str) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.mapped_callback_args prc=nil, &block
|
56
|
+
return prc if FFI::Function === prc
|
57
|
+
if prc.nil?
|
58
|
+
return nil if block.nil?
|
59
|
+
prc = block
|
60
|
+
end
|
61
|
+
return Proc.new do |*args|
|
62
|
+
mapped = args.map {|arg|
|
63
|
+
if FFI::Pointer === arg
|
64
|
+
begin
|
65
|
+
ObjectSpace._id2ref arg.address
|
66
|
+
rescue RangeError
|
67
|
+
arg
|
68
|
+
end
|
69
|
+
else
|
70
|
+
arg
|
71
|
+
end
|
72
|
+
}
|
73
|
+
prc.call(*mapped)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/gir_ffi/base.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module GirFFI
|
3
|
+
class Base
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :@struct, :[], :[]=, :to_ptr
|
6
|
+
|
7
|
+
def initialize(ptr=nil)
|
8
|
+
@struct = ptr.nil? ?
|
9
|
+
self.ffi_structure.new :
|
10
|
+
self.ffi_structure.new(ptr)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ffi_structure
|
14
|
+
self.class.ffi_structure
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def ffi_structure
|
19
|
+
self.const_get(:Struct)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'gir_ffi/class_base'
|
2
|
+
require 'gir_ffi/arg_helper'
|
3
|
+
require 'gir_ffi/function_definition_builder'
|
4
|
+
require 'gir_ffi/constructor_definition_builder'
|
5
|
+
require 'gir_ffi/method_missing_definition_builder'
|
6
|
+
require 'gir_ffi/base'
|
7
|
+
require 'gir_ffi/class_builder'
|
8
|
+
require 'gir_ffi/module_builder'
|
9
|
+
require 'gir_ffi/builder_helper'
|
10
|
+
|
11
|
+
module GirFFI
|
12
|
+
# Builds modules and classes based on information found in the
|
13
|
+
# introspection repository. Call its build_module and build_class methods
|
14
|
+
# to create the modules and classes used in your program.
|
15
|
+
module Builder
|
16
|
+
def self.build_class namespace, classname, box=nil
|
17
|
+
ClassBuilder.new(namespace, classname, box).generate
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.build_module namespace, box=nil
|
21
|
+
ModuleBuilder.new(namespace, box).generate
|
22
|
+
end
|
23
|
+
|
24
|
+
# TODO: Make better interface
|
25
|
+
def self.setup_method namespace, classname, lib, modul, klass, method
|
26
|
+
go = method_introspection_data namespace, classname, method.to_s
|
27
|
+
|
28
|
+
setup_function_or_method klass, modul, lib, go
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: Make better interface
|
32
|
+
def self.setup_function namespace, lib, modul, method
|
33
|
+
go = function_introspection_data namespace, method.to_s
|
34
|
+
|
35
|
+
setup_function_or_method modul, modul, lib, go
|
36
|
+
end
|
37
|
+
|
38
|
+
# All methods below will be made private at the end.
|
39
|
+
|
40
|
+
def self.function_definition info, libmodule
|
41
|
+
if info.constructor?
|
42
|
+
fdbuilder = ConstructorDefinitionBuilder.new info, libmodule
|
43
|
+
else
|
44
|
+
fdbuilder = FunctionDefinitionBuilder.new info, libmodule
|
45
|
+
end
|
46
|
+
fdbuilder.generate
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.function_introspection_data namespace, function
|
50
|
+
gir = IRepository.default
|
51
|
+
return gir.find_by_name namespace, function.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.method_introspection_data namespace, object, method
|
55
|
+
gir = IRepository.default
|
56
|
+
objectinfo = gir.find_by_name namespace, object.to_s
|
57
|
+
return objectinfo.find_method method
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.attach_ffi_function modul, lib, info, box
|
61
|
+
sym = info.symbol
|
62
|
+
argtypes = ffi_function_argument_types modul, lib, info, box
|
63
|
+
rt = ffi_function_return_type modul, lib, info, box
|
64
|
+
|
65
|
+
lib.attach_function sym, argtypes, rt
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.ffi_function_argument_types modul, lib, info, box
|
69
|
+
types = info.args.map do |a|
|
70
|
+
iarginfo_to_ffitype modul, lib, a, box
|
71
|
+
end
|
72
|
+
if info.type == :function
|
73
|
+
types.unshift :pointer if info.method?
|
74
|
+
end
|
75
|
+
types
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.ffi_function_return_type modul, lib, info, box
|
79
|
+
itypeinfo_to_ffitype modul, lib, info.return_type, box
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.define_ffi_types modul, lib, info, box
|
83
|
+
info.args.each do |arg|
|
84
|
+
type = iarginfo_to_ffitype modul, lib, arg, box
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.itypeinfo_to_ffitype modul, lib, info, box
|
89
|
+
if info.pointer?
|
90
|
+
return :string if info.tag == :utf8
|
91
|
+
return :pointer
|
92
|
+
end
|
93
|
+
case info.tag
|
94
|
+
when :interface
|
95
|
+
interface = info.interface
|
96
|
+
case interface.type
|
97
|
+
when :object, :struct, :flags, :enum
|
98
|
+
return build_class interface.namespace, interface.name, box
|
99
|
+
when :callback
|
100
|
+
return build_callback modul, lib, interface, box
|
101
|
+
else
|
102
|
+
raise NotImplementedError
|
103
|
+
end
|
104
|
+
when :boolean
|
105
|
+
return :bool
|
106
|
+
else
|
107
|
+
return info.tag
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.iarginfo_to_ffitype modul, lib, info, box
|
112
|
+
return :pointer if info.direction == :inout
|
113
|
+
return itypeinfo_to_ffitype modul, lib, info.type, box
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.build_callback modul, lib, interface, box
|
117
|
+
sym = interface.name.to_sym
|
118
|
+
|
119
|
+
# FIXME: Rescue is ugly here.
|
120
|
+
ft = lib.find_type sym rescue nil
|
121
|
+
if ft.nil?
|
122
|
+
args = ffi_function_argument_types modul, lib, interface, box
|
123
|
+
ret = ffi_function_return_type modul, lib, interface, box
|
124
|
+
lib.callback sym, args, ret
|
125
|
+
end
|
126
|
+
sym
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.setup_function_or_method klass, modul, lib, go
|
130
|
+
return false if go.nil?
|
131
|
+
return false if go.type != :function
|
132
|
+
|
133
|
+
box = get_box modul
|
134
|
+
|
135
|
+
define_ffi_types modul, lib, go, box
|
136
|
+
attach_ffi_function modul, lib, go, box
|
137
|
+
|
138
|
+
(class << klass; self; end).class_eval function_definition(go, lib)
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
# TODO: This is a weird way to get back the box.
|
143
|
+
def self.get_box modul
|
144
|
+
name = modul.to_s
|
145
|
+
if name =~ /::/
|
146
|
+
return Kernel.const_get(name.split('::')[0])
|
147
|
+
else
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Set up method access.
|
153
|
+
(self.public_methods - Module.public_methods).each do |m|
|
154
|
+
private_class_method m.to_sym
|
155
|
+
end
|
156
|
+
public_class_method :build_module, :build_class, :setup_method, :setup_function, :setup_function_or_method
|
157
|
+
public_class_method :itypeinfo_to_ffitype
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module GirFFI
|
2
|
+
module BuilderHelper
|
3
|
+
def self.const_defined_for parent, name
|
4
|
+
if RUBY_VERSION < "1.9"
|
5
|
+
parent.const_defined? name
|
6
|
+
else
|
7
|
+
parent.const_defined? name, false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.optionally_define_constant parent, name
|
12
|
+
unless const_defined_for parent, name
|
13
|
+
parent.const_set name, yield
|
14
|
+
end
|
15
|
+
parent.const_get name
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_or_define_module parent, name
|
19
|
+
optionally_define_constant(parent, name) { Module.new }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_or_define_class namespace, name, parent
|
23
|
+
BuilderHelper.optionally_define_constant namespace, name do
|
24
|
+
if parent.nil?
|
25
|
+
klass = Class.new
|
26
|
+
else
|
27
|
+
klass = Class.new parent
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module GirFFI
|
2
|
+
# Builds a class based on information found in the introspection
|
3
|
+
# repository.
|
4
|
+
class ClassBuilder
|
5
|
+
def initialize namespace, classname, box
|
6
|
+
@namespace = namespace
|
7
|
+
@classname = classname
|
8
|
+
@box = box
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate
|
12
|
+
build_class
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_class
|
18
|
+
get_gir_info
|
19
|
+
instantiate_module
|
20
|
+
case @info.type
|
21
|
+
when :object, :struct
|
22
|
+
instantiate_class
|
23
|
+
setup_class unless already_set_up
|
24
|
+
when :enum, :flags
|
25
|
+
@klass = BuilderHelper.optionally_define_constant @module, @classname do
|
26
|
+
vals = @info.values.map {|v| [v.name.to_sym, v.value]}.flatten
|
27
|
+
@lib.enum(@classname.to_sym, vals)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@klass
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_gir_info
|
34
|
+
gir = IRepository.default
|
35
|
+
gir.require @namespace, nil
|
36
|
+
|
37
|
+
@info = gir.find_by_name @namespace, @classname
|
38
|
+
raise "Class #{@classname} not found in namespace #{@namespace}" if @info.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_superclass
|
42
|
+
@parent = @info.type == :object ? @info.parent : nil
|
43
|
+
if @parent
|
44
|
+
@superclass = Builder.build_class @parent.namespace, @parent.name, @box
|
45
|
+
else
|
46
|
+
@superclass = GirFFI::Base
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def instantiate_module
|
51
|
+
@module = Builder.build_module @namespace, @box
|
52
|
+
@lib = @module.const_get :Lib
|
53
|
+
end
|
54
|
+
|
55
|
+
def instantiate_class
|
56
|
+
get_superclass
|
57
|
+
@klass = BuilderHelper.get_or_define_class @module, @classname, @superclass
|
58
|
+
@structklass = BuilderHelper.get_or_define_class @klass, :Struct, FFI::Struct
|
59
|
+
end
|
60
|
+
|
61
|
+
def setup_class
|
62
|
+
setup_method_missing
|
63
|
+
setup_base
|
64
|
+
setup_layout
|
65
|
+
setup_constructor
|
66
|
+
end
|
67
|
+
|
68
|
+
def setup_base
|
69
|
+
return if @parent
|
70
|
+
class << @klass
|
71
|
+
self.class_eval { alias_method :_real_new, :new }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup_constructor
|
76
|
+
ctor = @info.find_method 'new'
|
77
|
+
if not ctor.nil? and ctor.constructor?
|
78
|
+
Builder.setup_function_or_method @klass, @module, @lib, ctor
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def setup_method_missing
|
83
|
+
@klass.class_eval instance_method_missing_definition
|
84
|
+
@klass.class_eval class_method_missing_definition
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup_layout
|
88
|
+
layoutspec = []
|
89
|
+
@info.fields.each do |f|
|
90
|
+
layoutspec << f.name.to_sym
|
91
|
+
|
92
|
+
ffitype = Builder.itypeinfo_to_ffitype @module, @lib, f.type, @box
|
93
|
+
if ffitype.kind_of?(Class) and BuilderHelper.const_defined_for ffitype, :Struct
|
94
|
+
ffitype = ffitype.const_get :Struct
|
95
|
+
end
|
96
|
+
|
97
|
+
layoutspec << ffitype
|
98
|
+
|
99
|
+
layoutspec << f.offset
|
100
|
+
end
|
101
|
+
@structklass.class_eval { layout(*layoutspec) }
|
102
|
+
end
|
103
|
+
|
104
|
+
def instance_method_missing_definition
|
105
|
+
InstanceMethodMissingDefinitionBuilder.new(@lib, @module, @namespace, @classname).generate
|
106
|
+
end
|
107
|
+
|
108
|
+
def class_method_missing_definition
|
109
|
+
ClassMethodMissingDefinitionBuilder.new(@lib, @module, @namespace, @classname).generate
|
110
|
+
end
|
111
|
+
|
112
|
+
def already_set_up
|
113
|
+
@klass.instance_methods(false).map(&:to_sym).include? :method_missing
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|