gir_ffi 0.0.6 → 0.0.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.
@@ -0,0 +1,552 @@
1
+ module GirFFI::Builder
2
+ # Abstract parent class of the argument building classes. These classes
3
+ # are used by Builder::Function to create the code that processes
4
+ # each argument before and after the actual function call.
5
+ class Argument
6
+ KEYWORDS = [
7
+ "alias", "and", "begin", "break", "case", "class", "def", "do",
8
+ "else", "elsif", "end", "ensure", "false", "for", "if", "in",
9
+ "module", "next", "nil", "not", "or", "redo", "rescue", "retry",
10
+ "return", "self", "super", "then", "true", "undef", "unless",
11
+ "until", "when", "while", "yield"
12
+ ]
13
+
14
+ attr_reader :callarg, :name, :retname
15
+
16
+ attr_accessor :length_arg, :length_arg_for
17
+
18
+ def initialize function_builder, arginfo=nil, libmodule=nil
19
+ @arginfo = arginfo
20
+ @inarg = nil
21
+ @callarg = nil
22
+ @retname = nil
23
+ @name = nil
24
+ @function_builder = function_builder
25
+ @libmodule = libmodule
26
+ @length_arg = nil
27
+ @length_arg_for = nil
28
+ end
29
+
30
+ def self.build function_builder, arginfo, libmodule
31
+ klass = case arginfo.direction
32
+ when :inout
33
+ InOutArgument
34
+ when :in
35
+ InArgument
36
+ when :out
37
+ OutArgument
38
+ else
39
+ raise ArgumentError
40
+ end
41
+ klass.build function_builder, arginfo, libmodule
42
+ end
43
+
44
+ def type
45
+ @arginfo.type
46
+ end
47
+
48
+ def safe name
49
+ if KEYWORDS.include? name
50
+ "#{name}_"
51
+ else
52
+ name
53
+ end
54
+ end
55
+
56
+ def inarg
57
+ @length_arg_for.nil? ? @inarg : nil
58
+ end
59
+
60
+ def retval
61
+ @length_arg_for.nil? ? @retname : nil
62
+ end
63
+
64
+ def pre
65
+ []
66
+ end
67
+
68
+ def post
69
+ []
70
+ end
71
+
72
+ def postpost
73
+ []
74
+ end
75
+ end
76
+
77
+ # Implements argument processing for arguments with direction :in.
78
+ class InArgument < Argument
79
+ def prepare
80
+ @name = safe(@arginfo.name)
81
+ @callarg = @function_builder.new_var
82
+ @inarg = @name
83
+ end
84
+
85
+ def self.build function_builder, arginfo, libmodule
86
+ type = arginfo.type
87
+ klass = case type.tag
88
+ when :interface
89
+ if type.interface.type == :callback
90
+ CallbackInArgument
91
+ else
92
+ RegularInArgument
93
+ end
94
+ when :void
95
+ VoidInArgument
96
+ when :array
97
+ ArrayInArgument
98
+ when :glist, :gslist
99
+ ListInArgument
100
+ when :utf8
101
+ Utf8InArgument
102
+ else
103
+ RegularInArgument
104
+ end
105
+ klass.new function_builder, arginfo, libmodule
106
+ end
107
+ end
108
+
109
+ # Implements argument processing for callback arguments with direction
110
+ # :in.
111
+ class CallbackInArgument < InArgument
112
+ def pre
113
+ iface = @arginfo.type.interface
114
+ [ "#{@callarg} = GirFFI::ArgHelper.wrap_in_callback_args_mapper \"#{iface.namespace}\", \"#{iface.name}\", #{@name}",
115
+ "::#{@libmodule}::CALLBACKS << #{@callarg}" ]
116
+ end
117
+ end
118
+
119
+ # Implements argument processing for void pointer arguments with
120
+ # direction :in.
121
+ class VoidInArgument < InArgument
122
+ def pre
123
+ [ "#{@callarg} = GirFFI::ArgHelper.object_to_inptr #{@name}" ]
124
+ end
125
+ end
126
+
127
+ # Implements argument processing for array arguments with direction :in.
128
+ class ArrayInArgument < InArgument
129
+ def subtype_tag
130
+ st = @arginfo.type.param_type(0)
131
+ t = st.tag
132
+ case t
133
+ when :GType : return :gtype
134
+ when :interface
135
+ raise NotImplementedError if st.pointer?
136
+ iface = st.interface
137
+ if iface.name == 'Value' and iface.namespace == 'GObject'
138
+ return :gvalue
139
+ else
140
+ raise NotImplementedError
141
+ end
142
+ else
143
+ return t
144
+ end
145
+ end
146
+
147
+ def post
148
+ unless @arginfo.ownership_transfer == :everything
149
+ if subtype_tag == :utf8
150
+ [ "GirFFI::ArgHelper.cleanup_ptr_ptr #{@callarg}" ]
151
+ else
152
+ [ "GirFFI::ArgHelper.cleanup_ptr #{@callarg}" ]
153
+ end
154
+ end
155
+ end
156
+
157
+ def pre
158
+ pr = []
159
+ size = type.array_fixed_size
160
+ if size > -1
161
+ pr << "GirFFI::ArgHelper.check_fixed_array_size #{size}, #{@name}, \"#{@name}\""
162
+ end
163
+ pr << "#{@callarg} = GirFFI::ArgHelper.#{subtype_tag}_array_to_inptr #{@name}"
164
+ pr
165
+ end
166
+ end
167
+
168
+ # Implements argument processing for gslist arguments with direction :in.
169
+ class ListInArgument < InArgument
170
+ def subtype_tag
171
+ @arginfo.type.param_type(0).tag
172
+ end
173
+
174
+ def type_tag
175
+ @arginfo.type.tag
176
+ end
177
+
178
+ def pre
179
+ if subtype_tag == :void
180
+ [ "#{@callarg} = #{@name}" ]
181
+ else
182
+ [ "#{@callarg} = GirFFI::ArgHelper.#{subtype_tag}_array_to_#{type_tag} #{@name}" ]
183
+ end
184
+ end
185
+ end
186
+
187
+ # Implements argument processing for UTF8 string arguments with direction
188
+ # :in.
189
+ class Utf8InArgument < InArgument
190
+ def pre
191
+ [ "#{@callarg} = GirFFI::ArgHelper.utf8_to_inptr #{@name}" ]
192
+ end
193
+
194
+ def post
195
+ # TODO: Write tests and enable this.
196
+ # [ "GirFFI::ArgHelper.cleanup_ptr #{@callarg}" ]
197
+ []
198
+ end
199
+ end
200
+
201
+ # Implements argument processing for arguments with direction :in whose
202
+ # type-specific processing is left to FFI (e.g., ints and floats, and
203
+ # objects that implement to_ptr.).
204
+ class RegularInArgument < InArgument
205
+ def pre
206
+ pr = []
207
+ if @length_arg_for
208
+ pr << "#{@name} = #{@length_arg_for.name}.nil? ? 0 : #{@length_arg_for.name}.length"
209
+ end
210
+ pr << "#{@callarg} = #{@name}"
211
+ pr
212
+ end
213
+ end
214
+
215
+ # Implements argument processing for arguments with direction :out.
216
+ class OutArgument < Argument
217
+ def prepare
218
+ @name = safe(@arginfo.name)
219
+ @callarg = @function_builder.new_var
220
+ @retname = @function_builder.new_var
221
+ end
222
+
223
+ def self.build function_builder, arginfo, libmodule
224
+ klass = case arginfo.type.tag
225
+ when :interface
226
+ InterfaceOutArgument
227
+ when :array
228
+ ArrayOutArgument
229
+ when :gslist
230
+ GSListOutArgument
231
+ else
232
+ RegularOutArgument
233
+ end
234
+ klass.new function_builder, arginfo, libmodule
235
+ end
236
+ end
237
+
238
+ # Implements argument processing for interface arguments with direction
239
+ # :out (structs, objects, etc.).
240
+ class InterfaceOutArgument < OutArgument
241
+ def klass
242
+ iface = @arginfo.type.interface
243
+ "#{iface.namespace}::#{iface.name}"
244
+ end
245
+
246
+ def pre
247
+ if @arginfo.caller_allocates?
248
+ [ "#{@callarg} = #{klass}.allocate" ]
249
+ else
250
+ [ "#{@callarg} = GirFFI::ArgHelper.pointer_outptr" ]
251
+ end
252
+ end
253
+
254
+ def post
255
+ if @arginfo.caller_allocates?
256
+ [ "#{@retname} = #{@callarg}" ]
257
+ else
258
+ [ "#{@retname} = #{klass}.wrap GirFFI::ArgHelper.outptr_to_pointer(#{@callarg})" ]
259
+ end
260
+ end
261
+ end
262
+
263
+ # Implements argument processing for array arguments with direction
264
+ # :out.
265
+ class ArrayOutArgument < OutArgument
266
+ def pre
267
+ [ "#{@callarg} = GirFFI::ArgHelper.pointer_outptr" ]
268
+ end
269
+
270
+ def postpost
271
+ type = @arginfo.type
272
+
273
+ size = if @length_arg
274
+ @length_arg.retname
275
+ else
276
+ type.array_fixed_size
277
+ end
278
+
279
+ tag = type.param_type(0).tag
280
+
281
+ pp = [ "#{@retname} = GirFFI::ArgHelper.outptr_to_#{tag}_array #{@callarg}, #{size}" ]
282
+
283
+ if @arginfo.ownership_transfer == :everything
284
+ if tag == :utf8
285
+ pp << "GirFFI::ArgHelper.cleanup_ptr_array_ptr #{@callarg}, #{size}"
286
+ else
287
+ pp << "GirFFI::ArgHelper.cleanup_ptr_ptr #{@callarg}"
288
+ end
289
+ end
290
+
291
+ pp
292
+ end
293
+ end
294
+
295
+ # Implements argument processing for gslist arguments with direction
296
+ # :out.
297
+ class GSListOutArgument < OutArgument
298
+ def pre
299
+ [ "#{@callarg} = GirFFI::ArgHelper.pointer_outptr" ]
300
+ end
301
+
302
+ def postpost
303
+ type = @arginfo.type
304
+
305
+ tag = type.param_type(0).tag
306
+
307
+ pp = [ "#{@retname} = GirFFI::ArgHelper.outgslist_to_#{tag}_array #{@callarg}" ]
308
+
309
+ pp
310
+ end
311
+ end
312
+ # Implements argument processing for arguments with direction
313
+ # :out that are neither arrays nor 'interfaces'.
314
+ class RegularOutArgument < OutArgument
315
+ def type_tag
316
+ @arginfo.type.tag
317
+ end
318
+
319
+ def post
320
+ pst = [ "#{@retname} = GirFFI::ArgHelper.outptr_to_#{type_tag} #{@callarg}" ]
321
+ if @arginfo.ownership_transfer == :everything
322
+ pst << "GirFFI::ArgHelper.cleanup_ptr #{@callarg}"
323
+ end
324
+ pst
325
+ end
326
+
327
+ def pre
328
+ [ "#{@callarg} = GirFFI::ArgHelper.#{type_tag}_outptr" ]
329
+ end
330
+ end
331
+
332
+ # Implements argument processing for arguments with direction :inout.
333
+ class InOutArgument < Argument
334
+ def prepare
335
+ @name = safe(@arginfo.name)
336
+ @callarg = @function_builder.new_var
337
+ @inarg = @name
338
+ @retname = @function_builder.new_var
339
+ end
340
+
341
+ def self.build function_builder, arginfo, libmodule
342
+ raise NotImplementedError unless arginfo.ownership_transfer == :everything
343
+
344
+ klass = case arginfo.type.tag
345
+ when :interface
346
+ raise NotImplementedError
347
+ when :array
348
+ ArrayInOutArgument
349
+ else
350
+ RegularInOutArgument
351
+ end
352
+
353
+ klass.new function_builder, arginfo, libmodule
354
+ end
355
+ end
356
+
357
+ # Implements argument processing for array arguments with direction
358
+ # :inout.
359
+ class ArrayInOutArgument < InOutArgument
360
+ def subtype_tag
361
+ @arginfo.type.param_type(0).tag
362
+ end
363
+
364
+ def pre
365
+ [ "#{@callarg} = GirFFI::ArgHelper.#{subtype_tag}_array_to_inoutptr #{@name}" ]
366
+ end
367
+
368
+ def post
369
+ tag = subtype_tag
370
+ size = @length_arg.retname
371
+ pst = [ "#{@retname} = GirFFI::ArgHelper.outptr_to_#{tag}_array #{@callarg}, #{size}" ]
372
+ if tag == :utf8
373
+ pst << "GirFFI::ArgHelper.cleanup_ptr_array_ptr #{@callarg}, #{size}"
374
+ else
375
+ pst << "GirFFI::ArgHelper.cleanup_ptr_ptr #{@callarg}"
376
+ end
377
+ pst
378
+ end
379
+ end
380
+
381
+ # Implements argument processing for arguments with direction
382
+ # :inout that are neither arrays nor 'interfaces'.
383
+ class RegularInOutArgument < InOutArgument
384
+ def type_tag
385
+ @arginfo.type.tag
386
+ end
387
+
388
+ def post
389
+ [ "#{@retname} = GirFFI::ArgHelper.outptr_to_#{type_tag} #{@callarg}",
390
+ "GirFFI::ArgHelper.cleanup_ptr #{@callarg}" ]
391
+ end
392
+
393
+ def pre
394
+ pr = []
395
+ if @length_arg_for
396
+ pr << "#{@name} = #{@length_arg_for.name}.length"
397
+ end
398
+ pr << "#{@callarg} = GirFFI::ArgHelper.#{type_tag}_to_inoutptr #{@name}"
399
+ pr
400
+ end
401
+ end
402
+
403
+ # Implements argument processing for return values.
404
+ class ReturnValue < Argument
405
+ attr_reader :cvar
406
+
407
+ def prepare
408
+ @cvar = @function_builder.new_var
409
+ @retname = @function_builder.new_var
410
+ end
411
+
412
+ def type
413
+ @arginfo.return_type
414
+ end
415
+
416
+ def self.build function_builder, arginfo
417
+ type = arginfo.return_type
418
+ klass = case type.tag
419
+ when :void
420
+ VoidReturnValue
421
+ when :interface
422
+ case type.interface.type
423
+ when :interface, :struct
424
+ InterfaceReturnValue
425
+ when :object
426
+ if arginfo.constructor?
427
+ ConstructorReturnValue
428
+ else
429
+ ObjectReturnValue
430
+ end
431
+ else
432
+ RegularReturnValue
433
+ end
434
+ when :array
435
+ ArrayReturnValue
436
+ when :glist, :gslist
437
+ ListReturnValue
438
+ else
439
+ RegularReturnValue
440
+ end
441
+ klass.new function_builder, arginfo, nil
442
+ end
443
+
444
+ def inarg
445
+ nil
446
+ end
447
+ end
448
+
449
+ # Null object to represent the case where no actual values is returned.
450
+ class VoidReturnValue < ReturnValue
451
+ def prepare; end
452
+ end
453
+
454
+ # Implements argument processing for interface return values (interfaces
455
+ # and structs, but not objects, which need special handling for
456
+ # polymorphism and constructors.
457
+ class InterfaceReturnValue < ReturnValue
458
+ def post
459
+ interface = @arginfo.return_type.interface
460
+ namespace = interface.namespace
461
+ name = interface.name
462
+
463
+ GirFFI::Builder.build_class interface
464
+ [ "#{@retname} = ::#{namespace}::#{name}.wrap(#{@cvar})" ]
465
+ end
466
+ end
467
+
468
+ # Implements argument processing for object return values.
469
+ class ObjectReturnValue < ReturnValue
470
+ def post
471
+ [ "#{@retname} = GirFFI::ArgHelper.object_pointer_to_object(#{@cvar})" ]
472
+ end
473
+ end
474
+
475
+ # Implements argument processing for object constructors.
476
+ class ConstructorReturnValue < ReturnValue
477
+ def post
478
+ classinfo = @arginfo.container
479
+ namespace = classinfo.namespace
480
+ name = classinfo.name
481
+
482
+ GirFFI::Builder.build_class classinfo
483
+ [ "#{@retname} = ::#{namespace}::#{name}.constructor_wrap(#{@cvar})" ]
484
+ end
485
+ end
486
+
487
+ # Implements argument processing for array return values.
488
+ class ArrayReturnValue < ReturnValue
489
+ def subtype_tag
490
+ @arginfo.return_type.param_type(0).tag
491
+ end
492
+
493
+ def post
494
+ type = @arginfo.return_type
495
+ size = type.array_fixed_size
496
+
497
+ if size <= 0
498
+ size = @length_arg.retname
499
+ end
500
+ [ "#{@retname} = GirFFI::ArgHelper.ptr_to_#{subtype_tag}_array #{@cvar}, #{size}" ]
501
+ end
502
+ end
503
+
504
+ # Implements argument processing for GSList return values.
505
+ class ListReturnValue < ReturnValue
506
+ # TODO: Extract to a module.
507
+ def subtype_tag
508
+ @arginfo.return_type.param_type(0).tag
509
+ end
510
+
511
+ def type_tag
512
+ @arginfo.return_type.tag
513
+ end
514
+
515
+ def post
516
+ if subtype_tag == :void
517
+ [ "#{@retname} = ::GLib::SList.wrap(#{@cvar})" ]
518
+ else
519
+ [ "#{@retname} = GirFFI::ArgHelper.#{type_tag}_to_#{subtype_tag}_array #{@cvar}" ]
520
+ end
521
+ end
522
+ end
523
+
524
+ # Implements argument processing for other return values.
525
+ class RegularReturnValue < ReturnValue
526
+ def retval
527
+ @cvar
528
+ end
529
+ end
530
+
531
+ # Implements argument processing for error handling arguments. These
532
+ # arguments are not part of the introspected signature, but their
533
+ # presence is indicated by the 'throws' attribute of the function.
534
+ class ErrorArgument < Argument
535
+ def prepare
536
+ @callarg = @function_builder.new_var
537
+ end
538
+
539
+ def pre
540
+ [ "#{@callarg} = FFI::MemoryPointer.new(:pointer).write_pointer nil" ]
541
+ end
542
+
543
+ def post
544
+ [ "GirFFI::ArgHelper.check_error(#{@callarg})" ]
545
+ end
546
+ end
547
+
548
+ # Argument builder that does nothing. Implements Null Object pattern.
549
+ class NullArgument < Argument
550
+ def prepare; end
551
+ end
552
+ end