gir_ffi 0.0.6 → 0.0.7

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