gir_ffi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|