virtualbox-com 0.9.6

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.
@@ -0,0 +1,90 @@
1
+ require 'virtualbox/com/util'
2
+
3
+ module VirtualBox
4
+ module COM
5
+ module XPCOMC
6
+
7
+
8
+ module Lib
9
+ extend ::FFI::Library
10
+
11
+ # Constant with default library path and name
12
+ # according to running plateform
13
+ PATH = case Util.platform
14
+ when :mac
15
+ [ "/Applications/VirtualBox.app/Contents/MacOS",
16
+ "/Applications/MacPorts/VirtualBox.app/Contents/MacOS" ]
17
+ when :linux
18
+ [ "/opt/VirtualBox",
19
+ "/usr/lib/virtualbox",
20
+ "/usr/lib64/virtualbox" ]
21
+ when :solaris
22
+ [ "/opt/VirtualBox/amd64",
23
+ "/opt/VirtualBox/i386" ]
24
+ when :freebsd
25
+ [ "/usr/local/lib/virtualbox" ]
26
+ else
27
+ [ ]
28
+ end
29
+
30
+ NAME = case Util.platform
31
+ when :max then "VBoxXPCOMC.dylib"
32
+ else "VBoxXPCOMC.so"
33
+ end
34
+
35
+
36
+ def self.init
37
+ # Only once
38
+ return if respond_to?(:VBoxGetXPCOMCFunctions)
39
+
40
+ # Initialize lib and attach main function
41
+ ffi_lib_flags(:now, :local)
42
+ ffi_lib(ENV['VBOX_APP_HOME'] || PATH.map {|path| "#{path}/#{NAME}"})
43
+ attach_function :VBoxGetXPCOMCFunctions, [:uint], :pointer
44
+
45
+
46
+ # Get the pointer to the XPCOMC struct which contains the functions
47
+ # to initialize
48
+ xpcom_ptr = self.VBoxGetXPCOMCFunctions(XPCOMC::VERSION)
49
+ @@xpcom = XPCOMC::VBox.new(xpcom_ptr)
50
+
51
+
52
+ # Initializes the VirtualBox and Session interfaces.
53
+ # It goes through the various supported interfaces.
54
+ SUPPORTED_VERSIONS.each {|version, iids|
55
+ virtualbox_iid_str, session_iid_str = iids
56
+
57
+ # Setup the OUT pointers
58
+ virtualbox_ptr = ::FFI::MemoryPointer.new(:pointer)
59
+ session_ptr = ::FFI::MemoryPointer.new(:pointer)
60
+
61
+ # Call the initialization functions
62
+ @@xpcom[:pfnComInitialize].call(virtualbox_iid_str, virtualbox_ptr,
63
+ session_iid_str, session_ptr)
64
+
65
+ # Read the pointers from the results
66
+ virtualbox_ptr = virtualbox_ptr.read_pointer
67
+ session_ptr = session_ptr.read_pointer
68
+
69
+ # If either pointers are null it means that
70
+ # the initialization was not successful
71
+ next if virtualbox_ptr.null? || session_ptr.null?
72
+
73
+ # Load the interface description
74
+ require "virtualbox/com/model/#{version}"
75
+
76
+ @@virtualbox = Model.create(:VirtualBox, virtualbox_ptr)
77
+ @@session = Model.create(:Session, session_ptr)
78
+
79
+ break
80
+ }
81
+ end
82
+
83
+ def self.xpcom ; @@xpcom ; end
84
+ def self.virtualbox ; @@virtualbox ; end
85
+ def self.session ; @@session ; end
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ module VirtualBox
2
+ module COM
3
+ WSTRING = :unicode_string
4
+ INT8 = :int8
5
+ INT16 = :short
6
+ INT32 = :int
7
+ INT64 = :long
8
+ UINT8 = :uint8
9
+ UINT16 = :ushort
10
+ UINT32 = :uint
11
+ UINT64 = :ulong
12
+ BOOL = :char
13
+ end
14
+ end
15
+
@@ -0,0 +1,342 @@
1
+ module VirtualBox
2
+ module COM
3
+ module XPCOMC
4
+
5
+ class Sig
6
+ def initialize(sig)
7
+ @sig = sig.dup.freeze
8
+ end
9
+
10
+
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
42
+ 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
48
+ end
49
+
50
+
51
+ def prepare_args(args=[])
52
+ args = args.dup
53
+
54
+ results = @sig.inject([]) do |results, item|
55
+ single_type_to_arg(args, item, results)
56
+ end
57
+ end
58
+
59
+
60
+
61
+
62
+ # Takes a spec and a formal parameter list and returns the output from
63
+ # 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
86
+ 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
92
+ else
93
+ i += 1
94
+ end
95
+ end
96
+
97
+ if return_values.empty? then nil
98
+ elsif return_values.length == 1 then return_values.first
99
+ else return_values
100
+ end
101
+ end
102
+
103
+
104
+
105
+
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}"
136
+ end
137
+ 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
+
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
+
163
+
164
+
165
+ def pointer_for_type(type)
166
+ c_type, type = infer_type(type)
167
+ pointer = ::FFI::MemoryPointer.new(c_type)
168
+ end
169
+
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
+
207
+
208
+ # Converts a single type and args list to the proper formal args list
209
+ def single_type_to_arg(args, item, results)
210
+ if item.is_a?(Array) && item[0] == :out
211
+ if item[1].is_a?(Array)
212
+ # For arrays we need two pointers: one for size, and
213
+ # one for the actual array
214
+ results << pointer_for_type(UINT32)
215
+ results << pointer_for_type(item[1][0])
216
+ else
217
+ results << pointer_for_type(item[1])
218
+ end
219
+ elsif item.is_a?(Array) && item.length == 1
220
+ # 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)
249
+ end
250
+
251
+ results << array
252
+ 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
+ end
277
+ end
278
+
279
+
280
+
281
+
282
+
283
+
284
+
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) || ''
290
+ end
291
+
292
+ # Reads an interface from the pointer
293
+ #
294
+ # @return [::FFI::Struct]
295
+ def read_interface(ptr, original_type)
296
+ ptr = ptr.read_pointer
297
+ return nil if ptr.null?
298
+ Model.create(original_type, ptr)
299
+ end
300
+
301
+ # Reads an enum
302
+ #
303
+ # @return [Symbol]
304
+ def read_enum(ptr, original_type)
305
+ Model.get(original_type)[ptr.get_uint(0)]
306
+ end
307
+
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]
315
+ end
316
+ end
317
+
318
+ # Reads an array of structs from a pointer
319
+ #
320
+ # @return [Array<::FFI::Struct>]
321
+ def get_array_of_interface(ptr, type, length)
322
+ klass = Model.get(type)
323
+ ptr.get_array_of_pointer(0, length).map do |pointer|
324
+ klass.new(pointer)
325
+ end
326
+ 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)
334
+ end
335
+ end
336
+
337
+
338
+ end
339
+
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,58 @@
1
+ require_relative 'sig'
2
+
3
+ module VirtualBox
4
+ module COM
5
+ module XPCOMC
6
+
7
+ class Spec
8
+ attr_reader :name
9
+
10
+ def hide?
11
+ @opts[:hide]
12
+ end
13
+
14
+ class Function < Spec
15
+ def initialize(name, type, args, opts)
16
+ @name, @type, @args, @opts = name, type, args, opts
17
+ end
18
+
19
+ def signatures
20
+ { name => to_call }
21
+ end
22
+
23
+ def to_call
24
+ Sig.new( if @type.nil?
25
+ then @args
26
+ else @args + [ [ :out, @type ] ]
27
+ end)
28
+ end
29
+ end
30
+
31
+ class Property < Spec
32
+ attr_reader :getter, :setter
33
+
34
+ def initialize(name, type, opts)
35
+ @name, @type, @opts = name, type, opts
36
+ end
37
+
38
+ def signatures
39
+ r = {}
40
+ r[getter] = to_read
41
+ r[setter] = to_write unless self.readonly?
42
+ r
43
+ end
44
+
45
+ def readonly?
46
+ @opts[:readonly]
47
+ end
48
+
49
+ def getter ; :"get_#{@name}" ; end
50
+ def setter ; :"set_#{@name}" ; end
51
+ def to_read ; Sig.new([[:out, @type]]) ; end
52
+ def to_write ; Sig.new([ @type ]) ; end
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end