gir_ffi 0.0.1

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