gir_ffi 0.0.3 → 0.0.4

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