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.
- data/.gitignore +2 -1
- data/LICENSE +2 -2
- data/README.md +85 -0
- data/lib/virtualbox/com.rb +6 -0
- data/lib/virtualbox/com/model/4.2.rb +28 -2
- data/lib/virtualbox/com/util.rb +91 -76
- data/lib/virtualbox/com/version.rb +1 -1
- data/lib/virtualbox/com/xpcomc-ffi.rb +0 -20
- data/lib/virtualbox/com/xpcomc-ffi/binding.rb +16 -3
- data/lib/virtualbox/com/xpcomc-ffi/implementer.rb +9 -2
- data/lib/virtualbox/com/xpcomc-ffi/lib.rb +1 -0
- data/lib/virtualbox/com/xpcomc-ffi/model-types.rb +9 -8
- data/lib/virtualbox/com/xpcomc-ffi/sig.rb +205 -253
- data/lib/virtualbox/com/xpcomc-ffi/xpcomc-vbox.rb +18 -2
- data/scripts/xidl-conv.rb +4 -3
- data/virtualbox-com.gemspec +2 -2
- metadata +6 -5
- data/Readme.md +0 -59
- data/lib/virtualbox/com/model/4.2-gen.rb +0 -2720
@@ -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 = :
|
6
|
-
INT32 = :
|
7
|
-
INT64 = :
|
7
|
+
INT16 = :int16
|
8
|
+
INT32 = :int32
|
9
|
+
INT64 = :int64
|
8
10
|
UINT8 = :uint8
|
9
|
-
UINT16 = :
|
10
|
-
UINT32 = :
|
11
|
-
UINT64 = :
|
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.
|
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
|
-
|
12
|
-
# function
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
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,
|
167
|
-
|
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
|
-
|
211
|
-
|
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 <<
|
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(
|
130
|
+
results << pointer_for_type(type)
|
218
131
|
end
|
219
|
-
|
132
|
+
when :in
|
133
|
+
if type.is_a?(Array)
|
220
134
|
# Array argument
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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 <<
|
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
|
-
|
286
|
-
|
287
|
-
|
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
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
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(
|
255
|
+
Model.create(name, ptr)
|
299
256
|
end
|
300
257
|
|
301
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
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
|
-
|
319
|
-
|
320
|
-
|
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
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
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
|