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.
Files changed (65) hide show
  1. data/DESIGN.rdoc +54 -0
  2. data/History.txt +3 -0
  3. data/README.rdoc +59 -0
  4. data/Rakefile +21 -0
  5. data/TODO.rdoc +40 -0
  6. data/examples/01_empty_window.rb +15 -0
  7. data/examples/02_hello_world.rb +30 -0
  8. data/examples/03_upgraded_hello_world.rb +45 -0
  9. data/examples/demo_ffi_inherited_layout.rb +21 -0
  10. data/examples/demo_ffi_nested_struct.rb +17 -0
  11. data/examples/demo_ffi_safe_inherited_layout.rb +43 -0
  12. data/examples/hard_coded.rb +144 -0
  13. data/lib/gir_ffi.rb +47 -0
  14. data/lib/gir_ffi/allocation_helper.rb +12 -0
  15. data/lib/gir_ffi/arg_helper.rb +77 -0
  16. data/lib/gir_ffi/base.rb +23 -0
  17. data/lib/gir_ffi/builder.rb +159 -0
  18. data/lib/gir_ffi/builder_helper.rb +32 -0
  19. data/lib/gir_ffi/class_base.rb +11 -0
  20. data/lib/gir_ffi/class_builder.rb +116 -0
  21. data/lib/gir_ffi/constructor_definition_builder.rb +20 -0
  22. data/lib/gir_ffi/function_definition_builder.rb +148 -0
  23. data/lib/gir_ffi/g_error.rb +8 -0
  24. data/lib/gir_ffi/g_type.rb +14 -0
  25. data/lib/gir_ffi/i_arg_info.rb +16 -0
  26. data/lib/gir_ffi/i_base_info.rb +45 -0
  27. data/lib/gir_ffi/i_callable_info.rb +18 -0
  28. data/lib/gir_ffi/i_callback_info.rb +7 -0
  29. data/lib/gir_ffi/i_constant_info.rb +6 -0
  30. data/lib/gir_ffi/i_enum_info.rb +13 -0
  31. data/lib/gir_ffi/i_field_info.rb +10 -0
  32. data/lib/gir_ffi/i_flags_info.rb +5 -0
  33. data/lib/gir_ffi/i_function_info.rb +16 -0
  34. data/lib/gir_ffi/i_interface_info.rb +7 -0
  35. data/lib/gir_ffi/i_object_info.rb +50 -0
  36. data/lib/gir_ffi/i_property_info.rb +7 -0
  37. data/lib/gir_ffi/i_registered_type_info.rb +8 -0
  38. data/lib/gir_ffi/i_repository.rb +108 -0
  39. data/lib/gir_ffi/i_signal_info.rb +7 -0
  40. data/lib/gir_ffi/i_struct_info.rb +22 -0
  41. data/lib/gir_ffi/i_type_info.rb +25 -0
  42. data/lib/gir_ffi/i_union_info.rb +7 -0
  43. data/lib/gir_ffi/i_value_info.rb +8 -0
  44. data/lib/gir_ffi/i_vfunc_info.rb +7 -0
  45. data/lib/gir_ffi/lib.rb +174 -0
  46. data/lib/gir_ffi/lib_c.rb +11 -0
  47. data/lib/gir_ffi/method_missing_definition_builder.rb +62 -0
  48. data/lib/gir_ffi/module_builder.rb +66 -0
  49. data/lib/gir_ffi/overrides/gtk.rb +12 -0
  50. data/lib/gir_ffi/version.rb +4 -0
  51. data/tasks/bones.rake +87 -0
  52. data/tasks/notes.rake +134 -0
  53. data/tasks/post_load.rake +25 -0
  54. data/tasks/setup.rb +138 -0
  55. data/tasks/test.rake +22 -0
  56. data/test/arg_helper_test.rb +112 -0
  57. data/test/builder_test.rb +328 -0
  58. data/test/constructor_definition_builder_test.rb +19 -0
  59. data/test/function_definition_builder_test.rb +60 -0
  60. data/test/g_type_test.rb +22 -0
  61. data/test/girffi_test.rb +11 -0
  62. data/test/gtk_overrides_test.rb +22 -0
  63. data/test/i_repository_test.rb +54 -0
  64. data/test/test_helper.rb +39 -0
  65. metadata +174 -0
@@ -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,12 @@
1
+ require 'gir_ffi/lib_c'
2
+
3
+ module GirFFI
4
+ module AllocationHelper
5
+ def self.safe_malloc size
6
+ ptr = LibC.malloc size
7
+ raise NoMemoryError if ptr.null?
8
+ ptr
9
+ end
10
+ end
11
+ end
12
+
@@ -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
@@ -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,11 @@
1
+ module GirFFI
2
+ # Provides methods needed by all generated classes
3
+ module ClassBase
4
+ def initialize ptr
5
+ @gobj = ptr
6
+ end
7
+ def to_ptr
8
+ @gobj
9
+ end
10
+ end
11
+ 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