virtualbox-com 0.9.6 → 0.9.7

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.
@@ -73,6 +73,7 @@ module Lib
73
73
  # Load the interface description
74
74
  require "virtualbox/com/model/#{version}"
75
75
 
76
+ #
76
77
  @@virtualbox = Model.create(:VirtualBox, virtualbox_ptr)
77
78
  @@session = Model.create(:Session, session_ptr)
78
79
 
@@ -1,15 +1,16 @@
1
1
  module VirtualBox
2
2
  module COM
3
- WSTRING = :unicode_string
3
+ WSTRING = :unicode_string # \
4
+ BOOL = :boolean # | They are not FFI types
5
+ OCTET = :octet # /
4
6
  INT8 = :int8
5
- INT16 = :short
6
- INT32 = :int
7
- INT64 = :long
7
+ INT16 = :int16
8
+ INT32 = :int32
9
+ INT64 = :int64
8
10
  UINT8 = :uint8
9
- UINT16 = :ushort
10
- UINT32 = :uint
11
- UINT64 = :ulong
12
- BOOL = :char
11
+ UINT16 = :uint16
12
+ UINT32 = :uint32
13
+ UINT64 = :uint64
13
14
  end
14
15
  end
15
16
 
@@ -2,52 +2,48 @@ module VirtualBox
2
2
  module COM
3
3
  module XPCOMC
4
4
 
5
+ # Signatures are a list of type and direction (way: :in, :out)
6
+ # The type supported are simple types which resolved to Symbol
7
+ # or array of simple types
8
+ #
9
+ # These types need to be "converted" to be interpreted corretly
10
+ # in the contexts of:
11
+ # - callback
12
+ # - writing arguments
13
+ # - reading arguments
5
14
  class Sig
15
+ # Store and normalize signatures
6
16
  def initialize(sig)
7
- @sig = sig.dup.freeze
17
+ @sig = sig.map {|item|
18
+ if item.is_a?(Array) && item[0] == :out
19
+ then [ item[1], :out ]
20
+ else [ item, :in ]
21
+ end
22
+ }.freeze.each{|type, way|
23
+ # Sanity check: only `[type]` or `type`
24
+ if type.is_a?(Array) && type.length != 1
25
+ raise ArgumentError, "only arrays of simple type are supported"
26
+ end
27
+ }
8
28
  end
9
29
 
10
30
 
11
- # Converts a function spec from {AbstractInterface} to an FFI
12
- # function spec. This handles custom types (unicode strings,
13
- # arrays, and out-parameters) and will return a perfectly valid
14
- # array ready to be passed into `callback`.
15
- #
16
- # @param [Array] spec The function spec
17
- # @return [Array]
18
- def to_ffi
19
- spec = @sig.map do |item|
20
- if item.is_a?(Array) && item[0] == :out
21
- if item[1].is_a?(Array)
22
- # The out is an array of items, so we add in
23
- # two pointers: one for size and one for the array
24
- [ :pointer, :pointer ]
25
- else
26
- # A regular out parameter is just a single pointer
27
- :pointer
28
- end
29
- elsif item.is_a?(Array) && item.length == 1
30
- # The parameter is an array of somethings
31
- [ UINT32, :pointer ]
32
- elsif item == WSTRING
33
- # Unicode strings are simply pointers
34
- :pointer
35
- elsif item.to_s[0,1] == item.to_s[0,1].upcase
36
- # Try to get the class from the interfaces
37
- Model.get(item).to_ffi
38
- else
39
- # Unknown items are simply passed as-is, hopefully FFI
40
- # will catch any problems
41
- item
31
+
32
+ # Converts a function signature to an FFI function spec.
33
+ # This handles custom types (unicode strings, arrays, and
34
+ # out-parameters) and will return a perfectly valid array
35
+ # ready to be passed into `callback`.
36
+ def to_ffi_callback
37
+ @sig.map {|type, way| is_array = type.is_a?(Array)
38
+ case way
39
+ when :out then is_array ? [:pointer, :pointer] : :pointer
40
+ when :in then is_array ? [:uint32, :pointer] : mangling(type)[0]
42
41
  end
43
- end
44
-
45
- # Prepend a :pointer to represent the `this` parameter required
46
- # for the FFI parameter lists
47
- spec.unshift(:pointer).flatten
42
+ }.unshift(:pointer).flatten # Add `this` element
48
43
  end
49
44
 
50
45
 
46
+ # Prepare arguments by converting them to C structures
51
47
  def prepare_args(args=[])
52
48
  args = args.dup
53
49
 
@@ -57,284 +53,240 @@ class Sig
57
53
  end
58
54
 
59
55
 
60
-
61
-
62
- # Takes a spec and a formal parameter list and returns the output from
56
+ # Takes arguments list and returns the output from
63
57
  # a function, properly dereferencing any output pointers.
64
- #
65
- # @param [Array] specs The parameter spec for the function
66
- # @param [Array] formal The formal parameter list
67
- def retrieve_values(formal)
68
- return_values = []
69
- i = 0
70
- @sig.each do |spec|
71
- # Output parameters are all we care about
72
- if spec.is_a?(Array) && spec[0] == :out
73
- if spec[1].is_a?(Array)
74
- # We are dealing with formal[i] and formal[i+1]
75
- # here, where the first has the size and the
76
- # second has the contents
77
- return_values << dereference_pointer_array(formal[i+1], spec[1][0], dereference_pointer(formal[i], UINT32))
78
-
79
- # Skip 2: size param + pointer
80
- i += 2
81
- else
82
- return_values << dereference_pointer(formal[i], spec[1])
83
-
84
- # Skip 1: Pointer
85
- i += 1
58
+ def read_values(args)
59
+ values, i = [], 0
60
+ @sig.each do |type, way|
61
+ # Array of type: [ type ]
62
+ # Skip 2: size + pointer to array
63
+ if type.is_a?(Array)
64
+ if way == :out
65
+ size, type = args[i].read_uint32, type[0]
66
+ values << if type == OCTET
67
+ then read_binary_blob(args[i+1], size)
68
+ else get_array_by_type(args[i+1], type, size)
69
+ end
86
70
  end
87
- elsif spec.is_a?(Array) && spec.length == 1
88
- # This is an array argument, meaning it takes two
89
- # arguments: one for size and one is a pointer to the
90
- # array items. Therefore, skip two items.
91
- i += 2
71
+ i += 2
72
+
73
+ # Simple type : type
74
+ # Skip 1: pointer
92
75
  else
76
+ if way == :out
77
+ values << read_by_type(args[i], type)
78
+ end
93
79
  i += 1
94
80
  end
95
81
  end
96
82
 
97
- if return_values.empty? then nil
98
- elsif return_values.length == 1 then return_values.first
99
- else return_values
83
+ case values.size when 0 then nil
84
+ when 1 then values.first
85
+ else values
100
86
  end
101
87
  end
102
88
 
103
89
 
104
90
 
91
+ #--[ Private ]---------------------------------------------------------
92
+ private
105
93
 
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
- # Dereferences a pointer with a given type into a proper Ruby object.
117
- # If the type is a standard primitive of Ruby-FFI, it simply calls the
118
- # proper `get_*` method on the pointer. Otherwise, it calls a
119
- # `read_*` on the Binding class.
120
- #
121
- # @param [FFI::MemoryPointer] pointer
122
- # @param [Symbol] type The type of the pointer
123
- # @return [Object] The value of the dereferenced pointer
124
- def dereference_pointer(pointer, type)
125
- c_type, inferred_type = infer_type(type)
126
- method = :"read_#{inferred_type}"
127
-
128
- if pointer.respond_to?(method)
129
- result = pointer.send(method)
130
- result = result != 0 if type == BOOL
131
- result
132
- elsif respond_to?(method)
133
- send(method, pointer, type)
134
- else
135
- raise "don't know how to dereference pointer of type #{type}"
94
+ def mangling(type)
95
+ if type == WSTRING then [ :pointer, type ]
96
+ elsif type == BOOL then [ :int, type ]
97
+ elsif model = Model.fetch(type)
98
+ if model <= COM::AbstractInterface then [ :pointer, :interface ]
99
+ elsif model <= COM::AbstractEnum then [ :uint32, :enum ]
100
+ end
101
+ else [ type, type ]
136
102
  end
137
103
  end
138
-
139
- # Dereferences an array out of a pointer into an array of proper Ruby
140
- # objects.
141
- #
142
- # @param [FFI::MemoryPointer] pointer
143
- # @param [Symbol] type The type of the pointer
144
- # @param [Fixnum] length The length of the array
145
- # @return [Array<Object>]
146
- def dereference_pointer_array(pointer, type, length)
147
- return [] if length == 0
148
-
149
- c_type, inferred_type = infer_type(type)
150
- method = :"get_array_of_#{inferred_type}"
151
-
152
- array_pointer = pointer.get_pointer(0)
153
104
 
154
- if array_pointer.respond_to?(method)
155
- array_pointer.send(method, 0, length)
156
- elsif respond_to?(method)
157
- send(method, array_pointer, type, length)
158
- else
159
- raise "don't know how to dereference array of type #{type}"
160
- end
161
- end
162
105
 
163
106
 
164
107
 
165
108
  def pointer_for_type(type)
166
- c_type, type = infer_type(type)
167
- pointer = ::FFI::MemoryPointer.new(c_type)
109
+ c_type, = mangling(type)
110
+ ::FFI::MemoryPointer.new(c_type)
168
111
  end
169
112
 
170
- # Gives the C type and inferred type of a parameter type. Quite confusing
171
- # since the terminology is not consistent, but hopefully these examples
172
- # will help:
173
- #
174
- # type => [pointer_type, internal_type]
175
- # :int => [:int, :int]
176
- # :MyStruct => [:pointer, :struct]
177
- # :unicode_string => [:pointer, :unicode_string]
178
- #
179
- def infer_type(type)
180
- c_type = type
181
-
182
- begin
183
- if type == WSTRING
184
- # Handle strings as pointer types
185
- c_type = :pointer
186
- else
187
- # Try to get the class from the interfaces
188
- interface = Model.get(type)
189
-
190
- c_type = :pointer
191
-
192
- # Depending on the class type, we're either dealing with an
193
- # interface or an enum
194
- if interface.superclass == COM::AbstractInterface
195
- c_type, type = :pointer, :interface
196
- elsif interface.superclass == COM::AbstractEnum
197
- c_type, type = :uint32, :enum
198
- end
199
- end
200
- rescue ModelNotFoundException
201
- # Do nothing
202
- end
203
-
204
- [c_type, type]
205
- end
206
113
 
207
114
 
208
115
  # Converts a single type and args list to the proper formal args list
209
116
  def single_type_to_arg(args, item, results)
210
- if item.is_a?(Array) && item[0] == :out
211
- if item[1].is_a?(Array)
117
+ type, way = item
118
+
119
+ case way
120
+ when :out
121
+ if type.is_a?(Array)
212
122
  # For arrays we need two pointers: one for size, and
213
123
  # one for the actual array
214
124
  results << pointer_for_type(UINT32)
215
- results << pointer_for_type(item[1][0])
125
+ results << if type[0] == OCTET
126
+ then pointer_for_type(:pointer)
127
+ else pointer_for_type(type[0])
128
+ end
216
129
  else
217
- results << pointer_for_type(item[1])
130
+ results << pointer_for_type(type)
218
131
  end
219
- elsif item.is_a?(Array) && item.length == 1
132
+ when :in
133
+ if type.is_a?(Array)
220
134
  # Array argument
221
- data = args.shift
222
-
223
- # First add the length of the array
224
- results << data.length
225
-
226
- # Create the array
227
- c_type, type = infer_type(item.first)
228
-
229
- # If its a regular type (int, bool, etc.) then just make it an
230
- # array of that
231
- if type != :interface
232
- # Build a pointer to an array of values
233
- result = ::FFI::MemoryPointer.new(c_type, data.length)
234
- adder = result.method("put_#{c_type}")
235
- data.each_with_index do |single, index|
236
- value = []
237
- single_type_to_arg([single], item[0], value)
238
- adder.call(index, value.first)
239
- end
240
-
241
- results << result
242
- else
243
- # Then convert the rest into a raw MemoryPointer
244
- array = ::FFI::MemoryPointer.new(:pointer, data.length)
245
- data.each_with_index do |datum, i|
246
- converted = []
247
- single_type_to_arg([datum], item.first, converted)
248
- array[i].put_pointer(0, converted.first)
135
+ data = args.shift
136
+
137
+ # First add the length of the array
138
+ results << data.length
139
+
140
+ # Create the array
141
+ c_type, type = mangling(type.first)
142
+
143
+ # If its a regular type (int, bool, etc.) then just make it an
144
+ # array of that
145
+ if type != :interface
146
+ # Build a pointer to an array of values
147
+ result = ::FFI::MemoryPointer.new(c_type, data.length)
148
+ adder = result.method("put_#{c_type}")
149
+ data.each_with_index do |single, index|
150
+ value = []
151
+ single_type_to_arg([single], type[0], value)
152
+ adder.call(index, value.first)
153
+ end
154
+
155
+ results << result
156
+ else
157
+ # Then convert the rest into a raw MemoryPointer
158
+ array = ::FFI::MemoryPointer.new(:pointer, data.length)
159
+ data.each_with_index do |datum, i|
160
+ converted = []
161
+ single_type_to_arg([datum], type.first, converted)
162
+ array[i].put_pointer(0, converted.first)
163
+ end
164
+
165
+ results << array
249
166
  end
167
+ elsif type == WSTRING
168
+ # We have to convert the arg to a unicode string
169
+ results << XPCOMC::Lib.xpcom.string_to_utf16(args.shift)
170
+ elsif type == BOOL
171
+ results << (args.shift ? 1 : 0)
172
+ elsif type.to_s[0,1] == type.to_s[0,1].upcase
173
+ # Try to get the class from the interfaces
174
+ interface = Model.get(type)
175
+ val = args.shift
250
176
 
251
- results << array
177
+ results << if interface.superclass == COM::AbstractInterface
178
+ # For interfaces, we need the COM object
179
+ val && val.implementer.object
180
+ elsif interface.superclass == COM::AbstractEnum
181
+ # For enums, we need the value of the enum
182
+ interface.index(val)
183
+ end
184
+ else
185
+ # Simply replace spec item with next item in args
186
+ # list
187
+ results << args.shift
252
188
  end
253
- elsif item == WSTRING
254
- # We have to convert the arg to a unicode string
255
- results << XPCOMC::Lib.xpcom.string_to_utf16(args.shift)
256
- elsif item == BOOL
257
- results << (args.shift ? 1 : 0)
258
- elsif item.to_s[0,1] == item.to_s[0,1].upcase
259
- # Try to get the class from the interfaces
260
- interface = Model.get(item)
261
- val = args.shift
262
-
263
- results << if interface.superclass == COM::AbstractInterface
264
- # For interfaces, get the instance, then
265
- # dig deep to get the pointer to the
266
- # VtblParent, which is what the API expects
267
- val && val.implementer.binding.object
268
- elsif interface.superclass == COM::AbstractEnum
269
- # For enums, we need the value of the enum
270
- interface.index(val)
271
- end
272
- else
273
- # Simply replace spec item with next item in args
274
- # list
275
- results << args.shift
276
189
  end
277
190
  end
278
191
 
279
192
 
280
193
 
281
194
 
195
+
196
+ def read_by_type(ptr, type)
197
+ # Retrieve information
198
+ _, reader = mangling(type)
199
+
200
+ # Try default pointer reader
201
+ method = :"read_#{reader}"
202
+ return ptr.send(method) if ptr.respond_to?(method)
203
+
204
+ # Custom reader
205
+ case reader
206
+ when :unicode_string then read_unicode_string(ptr)
207
+ when :boolean then read_boolean(ptr)
208
+ when :interface then read_interface(ptr, type)
209
+ when :enum then read_enum(ptr, type)
210
+ else raise "don't know how to dereference pointer of type #{type}"
211
+ end
212
+ end
213
+
214
+ def get_array_by_type(ptr, type, length)
215
+ # Shortcut
216
+ return [] if length == 0
282
217
 
218
+ # Retrieve information
219
+ _, reader = mangling(type)
220
+ ary_ptr = ptr.read_pointer
221
+
222
+ # Try default pointer reader
223
+ method = :"get_array_of_#{reader}"
224
+ return ary_ptr.send(method, 0, length) if ary_ptr.respond_to?(method)
225
+
226
+ # Custom reader
227
+ case reader
228
+ when :unicode_string then get_array_of_unicode_string(ary_ptr, length)
229
+ when :boolean then get_array_of_boolean(ary_ptr, length)
230
+ when :interface then get_array_of_interface(ary_ptr, type, length)
231
+ when :enum then get_array_of_enum(ary_ptr, type, length)
232
+ else raise "don't know how to dereference array of type #{type}"
233
+ end
234
+ ensure
235
+ XPCOMC::Lib.xpcom.free(ary_ptr)
236
+ end
283
237
 
284
238
 
285
- # Reads a unicode string value from a pointer to that value.
286
- #
287
- # @return [String]
288
- def read_unicode_string(ptr, original_type=nil)
289
- XPCOMC::Lib.xpcom.utf16_to_string(ptr.read_pointer) || ''
239
+ ## Read_*
240
+ def read_binary_blob(ptr, size)
241
+ XPCOMC::Lib.xpcom.binary_to_string(ptr.read_pointer, size)
290
242
  end
243
+
244
+ def read_unicode_string(ptr)
245
+ XPCOMC::Lib.xpcom.utf16_to_string(ptr.read_pointer) || ''
246
+ end
291
247
 
292
- # Reads an interface from the pointer
293
- #
294
- # @return [::FFI::Struct]
295
- def read_interface(ptr, original_type)
248
+ def read_boolean(ptr)
249
+ ptr.read_int != 0
250
+ end
251
+
252
+ def read_interface(ptr, name)
296
253
  ptr = ptr.read_pointer
297
254
  return nil if ptr.null?
298
- Model.create(original_type, ptr)
255
+ Model.create(name, ptr)
299
256
  end
300
257
 
301
- # Reads an enum
302
- #
303
- # @return [Symbol]
304
- def read_enum(ptr, original_type)
305
- Model.get(original_type)[ptr.get_uint(0)]
258
+ def read_enum(ptr, name)
259
+ Model.get(name)[ptr.get_uint(0)]
306
260
  end
307
261
 
308
- # Reads an array of enums
309
- #
310
- # @return [Array<Symbol>]
311
- def get_array_of_enum(ptr, type, length)
312
- klass = Model.get(type)
313
- ptr.get_array_of_uint(0, length).map do |value|
314
- klass[value]
262
+
263
+ ## get_array_of_*
264
+ def get_array_of_unicode_string(ptr, length)
265
+ ptr.get_array_of_pointer(0, length).map do |pointer|
266
+ XPCOMC::Lib.xpcom.utf16_to_string(pointer)
315
267
  end
316
268
  end
317
-
318
- # Reads an array of structs from a pointer
319
- #
320
- # @return [Array<::FFI::Struct>]
269
+
270
+ def get_array_of_boolean(ptr, length)
271
+ ptr.get_array_of_int(0, length).map do |value|
272
+ value != 0
273
+ end
274
+ end
275
+
321
276
  def get_array_of_interface(ptr, type, length)
322
277
  klass = Model.get(type)
323
278
  ptr.get_array_of_pointer(0, length).map do |pointer|
324
279
  klass.new(pointer)
325
280
  end
326
281
  end
327
-
328
- # Reads an array of strings from a pointer
329
- #
330
- # @return [Array<String>]
331
- def get_array_of_unicode_string(ptr, type, length)
332
- ptr.get_array_of_pointer(0, length).map do |pointer|
333
- XPCOMC::Lib.xpcom.utf16_to_string(pointer)
282
+
283
+ def get_array_of_enum(ptr, type, length)
284
+ klass = Model.get(type)
285
+ ptr.get_array_of_uint(0, length).map do |value|
286
+ klass[value]
334
287
  end
335
288
  end
336
-
337
-
289
+
338
290
  end
339
291
 
340
292
  end