virtualbox-com 0.9.6 → 0.9.7

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