gir_ffi 0.0.3 → 0.0.4

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.
Files changed (52) hide show
  1. data/DESIGN.rdoc +9 -0
  2. data/History.txt +11 -0
  3. data/TODO.rdoc +6 -9
  4. data/examples/01_empty_window.rb +1 -1
  5. data/examples/02_hello_world.rb +4 -4
  6. data/examples/03_upgraded_hello_world.rb +7 -8
  7. data/lib/gir_ffi.rb +0 -4
  8. data/lib/gir_ffi/arg_helper.rb +27 -2
  9. data/lib/gir_ffi/builder.rb +13 -86
  10. data/lib/gir_ffi/builder_helper.rb +2 -15
  11. data/lib/gir_ffi/class_base.rb +51 -0
  12. data/lib/gir_ffi/class_builder.rb +178 -52
  13. data/lib/gir_ffi/function_definition_builder.rb +46 -9
  14. data/lib/gir_ffi/g_object.rb +37 -0
  15. data/lib/gir_ffi/i_arg_info.rb +27 -9
  16. data/lib/gir_ffi/i_base_info.rb +12 -4
  17. data/lib/gir_ffi/i_callable_info.rb +15 -5
  18. data/lib/gir_ffi/i_enum_info.rb +9 -3
  19. data/lib/gir_ffi/i_field_info.rb +12 -4
  20. data/lib/gir_ffi/i_function_info.rb +24 -8
  21. data/lib/gir_ffi/i_object_info.rb +63 -21
  22. data/lib/gir_ffi/i_registered_type_info.rb +11 -0
  23. data/lib/gir_ffi/i_repository.rb +2 -2
  24. data/lib/gir_ffi/i_signal_info.rb +1 -1
  25. data/lib/gir_ffi/i_struct_info.rb +24 -8
  26. data/lib/gir_ffi/i_type_info.rb +24 -8
  27. data/lib/gir_ffi/i_union_info.rb +15 -0
  28. data/lib/gir_ffi/i_value_info.rb +3 -1
  29. data/lib/gir_ffi/i_vfunc_info.rb +12 -1
  30. data/lib/gir_ffi/lib.rb +23 -1
  31. data/lib/gir_ffi/lib_c.rb +1 -1
  32. data/lib/gir_ffi/module_base.rb +19 -0
  33. data/lib/gir_ffi/module_builder.rb +67 -20
  34. data/lib/gir_ffi/overrides/gobject.rb +174 -0
  35. data/lib/gir_ffi/overrides/gtk.rb +26 -9
  36. data/test/builder_test.rb +57 -37
  37. data/test/{base_test.rb → class_base_test.rb} +3 -3
  38. data/test/class_builder_test.rb +48 -6
  39. data/test/everything_test.rb +285 -36
  40. data/test/function_definition_builder_test.rb +11 -9
  41. data/test/g_object_test.rb +22 -0
  42. data/test/gobject_overrides_test.rb +216 -0
  43. data/test/i_object_info_test.rb +21 -0
  44. data/test/module_builder_test.rb +54 -0
  45. data/test/test_helper.rb +6 -4
  46. metadata +18 -14
  47. data/lib/gir_ffi/base.rb +0 -25
  48. data/lib/gir_ffi/constructor_definition_builder.rb +0 -20
  49. data/lib/gir_ffi/g_type.rb +0 -14
  50. data/lib/gir_ffi/method_missing_definition_builder.rb +0 -59
  51. data/test/constructor_definition_builder_test.rb +0 -19
  52. data/test/g_type_test.rb +0 -22
data/lib/gir_ffi/lib_c.rb CHANGED
@@ -4,7 +4,7 @@ module GirFFI
4
4
  module LibC
5
5
  extend FFI::Library
6
6
  ffi_lib FFI::Library::LIBC
7
-
7
+
8
8
  attach_function :malloc, [:size_t], :pointer
9
9
  attach_function :free, [:pointer], :void
10
10
  end
@@ -0,0 +1,19 @@
1
+ module GirFFI
2
+ module ModuleBase
3
+ def method_missing method, *arguments, &block
4
+ result = gir_ffi_builder.setup_function method.to_s
5
+ return super unless result
6
+ self.send method, *arguments, &block
7
+ end
8
+
9
+ def const_missing classname
10
+ klass = gir_ffi_builder.build_class classname.to_s
11
+ return super if klass.nil?
12
+ klass
13
+ end
14
+
15
+ def gir_ffi_builder
16
+ self.const_get :GIR_FFI_BUILDER
17
+ end
18
+ end
19
+ end
@@ -1,9 +1,11 @@
1
1
  require 'gir_ffi/builder_helper'
2
+ require 'gir_ffi/module_base'
2
3
 
3
4
  module GirFFI
4
5
  # Builds a module based on information found in the introspection
5
6
  # repository.
6
7
  class ModuleBuilder
8
+ include BuilderHelper
7
9
 
8
10
  def initialize namespace
9
11
  @namespace = namespace
@@ -13,47 +15,92 @@ module GirFFI
13
15
  build_module
14
16
  end
15
17
 
18
+ def setup_function method
19
+ go = function_introspection_data method.to_s
20
+
21
+ return false if go.nil?
22
+
23
+ modul = build_module
24
+ lib = modul.const_get(:Lib)
25
+
26
+ Builder.attach_ffi_function lib, go
27
+
28
+ meta = (class << modul; self; end)
29
+ meta.class_eval function_definition(go, lib)
30
+
31
+ true
32
+ end
33
+
34
+ def build_class classname
35
+ Builder.build_class @namespace, classname.to_s
36
+ end
37
+
16
38
  private
17
39
 
18
40
  def build_module
19
- IRepository.default.require @namespace, nil
20
- setup_module
21
- setup_lib_for_ffi
22
- unless @module.respond_to? :method_missing
23
- @module.class_eval module_method_missing_definition
24
- @module.class_eval const_missing_definition
41
+ unless defined? @module
42
+ instantiate_module
43
+ setup_lib_for_ffi
44
+ setup_module unless already_set_up
25
45
  end
26
46
  @module
27
47
  end
28
48
 
49
+ def instantiate_module
50
+ @module = get_or_define_module ::Object, @namespace.to_s
51
+ end
52
+
29
53
  def setup_module
30
- @module = BuilderHelper.get_or_define_module ::Object, @namespace.to_s
54
+ @module.extend ModuleBase
55
+ @module.const_set :GIR_FFI_BUILDER, self
56
+ begin
57
+ require "gir_ffi/overrides/#{@namespace.downcase}"
58
+ @module.class_eval "include GirFFI::Overrides::#{@namespace}"
59
+ rescue LoadError
60
+ end
61
+ end
62
+
63
+ def already_set_up
64
+ @module.respond_to? :method_missing
31
65
  end
32
66
 
33
67
  def setup_lib_for_ffi
34
- @lib = BuilderHelper.get_or_define_module @module, :Lib
68
+ @lib = get_or_define_module @module, :Lib
35
69
 
36
70
  unless (class << @lib; self.include? FFI::Library; end)
37
71
  @lib.extend FFI::Library
38
- libs = IRepository.default.shared_library(@namespace).split(/,/)
72
+ libs = gir.shared_library(@namespace).split(/,/)
39
73
  @lib.ffi_lib(*libs)
40
74
  end
41
75
 
42
- BuilderHelper.optionally_define_constant(@lib, :CALLBACKS) { [] }
76
+ optionally_define_constant(@lib, :CALLBACKS) { [] }
43
77
  end
44
78
 
45
- def module_method_missing_definition
46
- ModuleMethodMissingDefinitionBuilder.new(@lib, @namespace).generate
79
+ def function_introspection_data function
80
+ info = gir.find_by_name @namespace, function.to_s
81
+
82
+ if info.type == :function
83
+ info
84
+ else
85
+ nil
86
+ end
47
87
  end
48
88
 
49
- def const_missing_definition
50
- return <<-CODE
51
- def self.const_missing classname
52
- info = IRepository.default.find_by_name "#{@namespace}", classname.to_s
53
- return super if info.nil?
54
- return GirFFI::Builder.build_class "#{@namespace}", classname.to_s
55
- end
56
- CODE
89
+ def function_definition info, libmodule
90
+ FunctionDefinitionBuilder.new(info, libmodule).generate
57
91
  end
92
+
93
+ def gir
94
+ unless defined? @gir
95
+ @gir = IRepository.default
96
+ @gir.require @namespace, nil
97
+ end
98
+ @gir
99
+ end
100
+
101
+ def get_or_define_module parent, name
102
+ optionally_define_constant(parent, name) { Module.new }
103
+ end
104
+
58
105
  end
59
106
  end
@@ -0,0 +1,174 @@
1
+ module GirFFI
2
+ module Overrides
3
+ module GObject
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def type_from_instance_pointer inst_ptr
11
+ base = ::GObject::TypeInstance.new inst_ptr
12
+ kls = ::GObject::TypeClass.new(base[:g_class])
13
+ kls[:g_type]
14
+ end
15
+
16
+ def type_from_instance instance
17
+ type_from_instance_pointer instance.to_ptr
18
+ end
19
+
20
+ def wrap_in_g_value val
21
+ gvalue = ::GObject::Value.new
22
+ case val
23
+ when true, false
24
+ gvalue.init ::GObject.type_from_name("gboolean")
25
+ gvalue.set_boolean val
26
+ end
27
+ gvalue
28
+ end
29
+
30
+ def unwrap_g_value gvalue
31
+ gtype = gvalue[:g_type]
32
+ gtypename = ::GObject.type_name gtype
33
+ case gtypename
34
+ when "gboolean"
35
+ gvalue.get_boolean
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # FIXME: This is a private helper function. Move elsewhere?
42
+ def signal_callback_args sig, klass, &block
43
+ return Proc.new do |*args|
44
+ mapped = Helper.cast_back_signal_arguments sig, klass, *args
45
+ block.call(*mapped)
46
+ end
47
+ end
48
+
49
+ def signal_emit object, signal, *args
50
+ type = type_from_instance object
51
+
52
+ id = signal_lookup signal, type
53
+
54
+ val = ::GObject::Value.new
55
+ val.init type
56
+ val.set_instance object
57
+
58
+ q = ::GObject::SignalQuery.new
59
+ signal_query id, q
60
+
61
+ use_ret = (q[:return_type] != ::GObject.type_from_name("void"))
62
+ if use_ret
63
+ rval = ::GObject::Value.new
64
+ rval.init q[:return_type]
65
+ end
66
+
67
+ arr = Helper.signal_arguments_to_gvalue_array signal, object, *args
68
+
69
+ signal_emitv arr[:values], id, 0, rval
70
+
71
+ if use_ret
72
+ rval
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ def signal_connect object, signal, data=nil, &block
79
+ sig = object.class.gir_ffi_builder.find_signal signal
80
+ if sig.nil?
81
+ raise "Signal #{signal} is invalid for #{object}"
82
+ end
83
+
84
+ rettype = GirFFI::Builder.itypeinfo_to_ffitype sig.return_type
85
+
86
+ argtypes = [:pointer] + sig.args.map {|a| :pointer} + [:pointer]
87
+
88
+ callback = FFI::Function.new rettype, argtypes,
89
+ &(signal_callback_args(sig, object.class, &block))
90
+
91
+ signal_connect_data object, signal, callback, data, nil, 0
92
+ end
93
+ end
94
+
95
+ module Helper
96
+ def self.signal_arguments_to_gvalue_array signal, instance, *rest
97
+ sig = instance.class.gir_ffi_builder.find_signal signal
98
+
99
+ arr = ::GObject::ValueArray.new sig.n_args+1
100
+
101
+ val = ::GObject::Value.new
102
+ val.init ::GObject.type_from_instance(instance)
103
+ val.set_instance instance
104
+ arr.append val
105
+ val.unset
106
+
107
+ sig.args.zip(rest).each do |a|
108
+ info, arg = *a
109
+ if info.type.tag == :interface
110
+ interface = info.type.interface
111
+
112
+ val = ::GObject::Value.new
113
+ val.init info.type.interface.g_type
114
+ case interface.type
115
+ when :struct
116
+ val.set_boxed arg
117
+ when :object
118
+ val.set_instance arg
119
+ else
120
+ raise NotImplementedError, interface.type
121
+ end
122
+
123
+ arr.append val
124
+
125
+ val.unset
126
+
127
+ else
128
+ raise NotImplementedError
129
+ end
130
+ end
131
+
132
+ arr
133
+ end
134
+
135
+ def self.cast_back_signal_arguments signalinfo, klass, *args
136
+ result = []
137
+
138
+ # Instance
139
+ instptr = args.shift
140
+ instance = klass.send :_real_new, instptr
141
+ result << instance
142
+
143
+ # Extra arguments
144
+ signalinfo.args.each do |info|
145
+ arg = args.shift
146
+ if info.type.tag == :interface
147
+ iface = info.type.interface
148
+ kls = GirFFI::Builder.build_class(iface.namespace, iface.name)
149
+ result << kls.send(:_real_new, arg)
150
+ else
151
+ result << arg
152
+ end
153
+ end
154
+
155
+ # User Data
156
+ arg = args.shift
157
+ arg = if FFI::Pointer === arg
158
+ begin
159
+ ObjectSpace._id2ref arg.address
160
+ rescue RangeError
161
+ arg
162
+ end
163
+ else
164
+ arg
165
+ end
166
+ result << arg
167
+
168
+ return result
169
+ end
170
+ end
171
+
172
+ end
173
+ end
174
+ end
@@ -1,12 +1,29 @@
1
- GirFFI::Builder.setup_function "Gtk", "init"
2
- module Gtk
3
- class << self
4
- alias _base_init init
5
- def init
6
- (my_len, my_args) = _base_init ARGV.length + 1, [$0, *ARGV]
7
- my_args.shift
8
- ARGV.replace my_args
1
+ module GirFFI
2
+ module Overrides
3
+ module Gtk
4
+
5
+ def self.included(base)
6
+ base.gir_ffi_builder.setup_function "init"
7
+ base.extend ClassMethods
8
+ base.class_eval do
9
+
10
+ class << self
11
+ alias init_without_auto_argv init
12
+ alias init init_with_auto_argv
13
+ end
14
+
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ def init_with_auto_argv
21
+ (my_len, my_args) = init_without_auto_argv ARGV.length + 1, [$0, *ARGV]
22
+ my_args.shift
23
+ ARGV.replace my_args
24
+ end
25
+
26
+ end
9
27
  end
10
- private :_base_init
11
28
  end
12
29
  end
data/test/builder_test.rb CHANGED
@@ -10,11 +10,6 @@ class BuilderTest < Test::Unit::TestCase
10
10
  GirFFI::Builder.build_class 'GObject', 'Object'
11
11
  end
12
12
 
13
- should "create a method_missing method for the class" do
14
- ms = GObject::Object.instance_methods(false).map(&:to_sym)
15
- assert_contains ms, :method_missing
16
- end
17
-
18
13
  should "create a Lib module in the parent namespace ready to attach functions from gobject-2.0" do
19
14
  gir = GirFFI::IRepository.default
20
15
  expected = gir.shared_library 'GObject'
@@ -115,14 +110,6 @@ class BuilderTest < Test::Unit::TestCase
115
110
  assert_equal go2, @go
116
111
  end
117
112
 
118
- should "build correct definition of Gtk.main" do
119
- code = GirFFI::Builder.send :function_definition, @go, Lib
120
-
121
- expected = "def main\nLib.gtk_main\nend"
122
-
123
- assert_equal cws(expected), cws(code)
124
- end
125
-
126
113
  should "attach function to Whatever::Lib" do
127
114
  mod = Module.new
128
115
  mod.const_set :Lib, libmod = Module.new
@@ -142,12 +129,6 @@ class BuilderTest < Test::Unit::TestCase
142
129
  @go = get_function_introspection_data 'Gtk', 'init'
143
130
  end
144
131
 
145
- should "delegate definition to FunctionDefinitionBuilder" do
146
- code = GirFFI::Builder.send :function_definition, @go, Lib
147
- expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
148
- assert_equal cws(expected), cws(code)
149
- end
150
-
151
132
  should "have :pointer, :pointer as types of the arguments for the attached function" do
152
133
  # FIXME: Ideally, we attach the function and test that it requires
153
134
  # the correct argument types.
@@ -164,12 +145,6 @@ class BuilderTest < Test::Unit::TestCase
164
145
  @go = get_method_introspection_data 'Gtk', 'Widget', 'show'
165
146
  end
166
147
 
167
- should "delegate definition to FunctionDefinitionBuilder" do
168
- code = GirFFI::Builder.send :function_definition, @go, Lib
169
- expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
170
- assert_equal cws(expected), cws(code)
171
- end
172
-
173
148
  should "have :pointer as types of the arguments for the attached function" do
174
149
  assert_equal [:pointer], GirFFI::Builder.send(:ffi_function_argument_types, @go)
175
150
  end
@@ -183,19 +158,14 @@ class BuilderTest < Test::Unit::TestCase
183
158
  @go = get_function_introspection_data 'GObject', 'signal_connect_data'
184
159
  end
185
160
 
186
- should "delegate definition to FunctionDefinitionBuilder" do
187
- code = GirFFI::Builder.send :function_definition, @go, Lib
188
- expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
189
- assert_equal cws(expected), cws(code)
190
- end
191
-
192
161
  should "have the correct types of the arguments for the attached function" do
193
- assert_equal [:pointer, :string, :Callback, :pointer, :ClosureNotify, GObject::ConnectFlags],
194
- GirFFI::Builder.send(:ffi_function_argument_types, @go)
162
+ argtypes = GirFFI::Builder.send(:ffi_function_argument_types, @go)
163
+ assert_equal [:pointer, :string, GObject::Callback, :pointer, GObject::ClosureNotify, GObject::ConnectFlags],
164
+ argtypes
195
165
  end
196
166
 
197
167
  should "define ffi callback types :Callback and :ClosureNotify" do
198
- GirFFI::Builder.setup_function 'GObject', 'signal_connect_data'
168
+ GObject.gir_ffi_builder.setup_function 'signal_connect_data'
199
169
  cb = GObject::Lib.find_type :Callback
200
170
  cn = GObject::Lib.find_type :ClosureNotify
201
171
 
@@ -233,6 +203,35 @@ class BuilderTest < Test::Unit::TestCase
233
203
  end
234
204
  end
235
205
 
206
+ context "building GObject::TypeCValue" do
207
+ setup do
208
+ GirFFI::Builder.build_class 'GObject', 'TypeCValue'
209
+ end
210
+
211
+ should "set up the correct union members" do
212
+ assert_equal [:v_int, :v_long, :v_int64, :v_double, :v_pointer],
213
+ GObject::TypeCValue::Struct.members
214
+ end
215
+
216
+ should "set up union members with the correct offset" do
217
+ assert_equal [0, 0, 0, 0, 0],
218
+ GObject::TypeCValue::Struct.layout.fields.map(&:offset)
219
+ end
220
+
221
+ should "set up the inner class as derived from FFI::Union" do
222
+ assert_equal FFI::Union, GObject::TypeCValue::Struct.superclass
223
+ end
224
+ end
225
+
226
+ context "building GObject::ValueArray" do
227
+ should "use provided constructor if present" do
228
+ GirFFI::Builder.build_class 'GObject', 'ValueArray'
229
+ assert_nothing_raised {
230
+ GObject::ValueArray.new 2
231
+ }
232
+ end
233
+ end
234
+
236
235
  context "building Everything::TestBoxed" do
237
236
  setup do
238
237
  GirFFI::Builder.build_class 'Everything', 'TestBoxed'
@@ -245,22 +244,26 @@ class BuilderTest < Test::Unit::TestCase
245
244
 
246
245
  # TODO: Should not allow functions to be called as methods, etc.
247
246
 
248
- context "building the Everything module" do
247
+ context "built Everything module" do
249
248
  setup do
250
249
  cleanup_module :Everything
251
250
  GirFFI::Builder.build_module 'Everything'
252
251
  end
253
252
 
254
- should "create a method_missing method for the module" do
253
+ should "have a method_missing method" do
255
254
  ms = (Everything.public_methods - Module.public_methods).map(&:to_sym)
256
255
  assert_contains ms, :method_missing
257
256
  end
258
257
 
259
- should "cause the TestObj class to be autocreated" do
258
+ should "autocreate the TestObj class" do
260
259
  assert !Everything.const_defined?(:TestObj)
261
260
  assert_nothing_raised {Everything::TestObj}
262
261
  assert Everything.const_defined? :TestObj
263
262
  end
263
+
264
+ should "know its own module builder" do
265
+ assert GirFFI::ModuleBuilder === Everything.gir_ffi_builder
266
+ end
264
267
  end
265
268
 
266
269
  # TODO: Turn this into full test of instance method creation, including
@@ -290,6 +293,23 @@ class BuilderTest < Test::Unit::TestCase
290
293
  should "not have regular #new as a constructor" do
291
294
  assert_raises(NoMethodError) { Everything::TestObj.new }
292
295
  end
296
+
297
+ should "know its own GIR info" do
298
+ assert_equal 'TestObj', Everything::TestObj.gir_info.name
299
+ end
300
+
301
+ should "know its own class builder" do
302
+ assert GirFFI::ClassBuilder === Everything::TestObj.gir_ffi_builder
303
+ end
304
+
305
+ context "its #torture_signature_0 method" do
306
+ should "have the correct types of the arguments for the attached function" do
307
+ info = get_method_introspection_data 'Everything', 'TestObj',
308
+ 'torture_signature_0'
309
+ assert_equal [:pointer, :int, :pointer, :pointer, :string, :pointer, :uint],
310
+ GirFFI::Builder.send(:ffi_function_argument_types, info)
311
+ end
312
+ end
293
313
  end
294
314
 
295
315
  context "built Everything::TestSubObj" do