virtualbox-com 0.9.6

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