qt 0.1.9 → 0.2.1

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.
@@ -4,12 +4,52 @@ def unsupported_cpp_type?(type_name)
4
4
  type_name.include?('<') || type_name.include?('>') || type_name.include?('[') || type_name.include?('(')
5
5
  end
6
6
 
7
- def map_cpp_pointer_arg_type(type_name, qt_class)
7
+ def ast_nested_types_by_class(ast)
8
+ @ast_nested_types_cache ||= {}.compare_by_identity
9
+ @ast_nested_types_cache[ast] ||= begin
10
+ nested = Hash.new { |hash, key| hash[key] = Set.new }
11
+ walk = lambda do |node, record_stack|
12
+ next unless node.is_a?(Hash)
13
+
14
+ parent_record = record_stack.last
15
+ nested_type_kind = %w[CXXRecordDecl EnumDecl TypedefDecl TypeAliasDecl].include?(node['kind'])
16
+ if nested_type_kind && parent_record && node['name'] && node['name'] != parent_record['name'] &&
17
+ (node['parentDeclContextId'].nil? || node['parentDeclContextId'] == parent_record['id'])
18
+ nested[parent_record['name']] << node['name']
19
+ end
20
+
21
+ child_stack = if node['kind'] == 'CXXRecordDecl' && node['name']
22
+ record_stack + [node]
23
+ else
24
+ record_stack
25
+ end
26
+ Array(node['inner']).each { |child| walk.call(child, child_stack) }
27
+ end
28
+ walk.call(ast, [])
29
+ nested
30
+ end
31
+ end
32
+
33
+ def ast_nested_type_owner(ast, qt_class, nested_name, visited = {})
34
+ return nil if qt_class.nil? || visited[qt_class]
35
+
36
+ visited[qt_class] = true
37
+ return qt_class if ast_nested_types_by_class(ast)[qt_class].include?(nested_name)
38
+
39
+ collect_class_bases(ast, qt_class).each do |base_class|
40
+ owner = ast_nested_type_owner(ast, base_class, nested_name, visited)
41
+ return owner if owner
42
+ end
43
+ nil
44
+ end
45
+
46
+ def map_cpp_pointer_arg_type(type_name, qt_class, ast:)
8
47
  return nil unless type_name.end_with?('*')
9
48
 
10
49
  base = type_name.sub(/\s*\*\z/, '').strip
11
- if qt_class && !base.include?('::') && base.match?(/\A[A-Z]\w*\z/) && !base.start_with?('Q')
12
- base = "#{qt_class}::#{base}"
50
+ if qt_class && !base.include?('::') && base.match?(/\A[A-Z]\w*\z/)
51
+ owner = ast_nested_type_owner(ast, qt_class, base)
52
+ base = "#{owner}::#{base}" if owner
13
53
  end
14
54
  { ffi: :pointer, cast: "#{base}*" }
15
55
  end
@@ -21,32 +61,52 @@ def map_builtin_intlike_arg_type(type_name)
21
61
  nil
22
62
  end
23
63
 
24
- def map_qualified_intlike_arg_type(type_name, qt_class, int_cast_types)
25
- return nil unless qt_class && type_name.match?(/\A[A-Z]\w*\z/)
64
+ def inherited_intlike_type_name(type_name, qt_class, ast, int_cast_types, visited = {})
65
+ return nil if qt_class.nil? || visited[qt_class]
26
66
 
67
+ visited[qt_class] = true
27
68
  qualified = "#{qt_class}::#{type_name}"
28
- return { ffi: :int, cast: qualified } if int_cast_types&.include?(qualified)
69
+ return qualified if int_cast_types&.include?(qualified)
70
+
71
+ collect_class_bases(ast, qt_class).each do |base_class|
72
+ inherited = inherited_intlike_type_name(type_name, base_class, ast, int_cast_types, visited)
73
+ return inherited if inherited
74
+ end
75
+ nil
76
+ end
77
+
78
+ def map_qualified_intlike_arg_type(type_name, qt_class, int_cast_types, ast:)
79
+ return nil unless qt_class && type_name.match?(/\A[A-Z]\w*\z/)
80
+
81
+ qualified = inherited_intlike_type_name(type_name, qt_class, ast, int_cast_types)
82
+ return { ffi: :int, cast: qualified } if qualified
29
83
 
30
84
  nil
31
85
  end
32
86
 
33
- def map_cpp_intlike_arg_type(type_name, qt_class, int_cast_types)
87
+ def map_cpp_intlike_arg_type(type_name, qt_class, int_cast_types, ast:)
34
88
  builtin = map_builtin_intlike_arg_type(type_name)
35
89
  return builtin if builtin
36
90
  return { ffi: :int, cast: type_name } if type_name.include?('::') && int_cast_types&.include?(type_name)
37
91
 
38
- map_qualified_intlike_arg_type(type_name, qt_class, int_cast_types)
92
+ map_qualified_intlike_arg_type(type_name, qt_class, int_cast_types, ast: ast)
39
93
  end
40
94
 
41
- def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
95
+ def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil, ast: nil)
42
96
  raw = type_name.to_s.strip
43
- return nil if raw.end_with?('&') && !raw.start_with?('const ')
97
+ if raw.end_with?('&') && !raw.start_with?('const ')
98
+ mutable_reference_type = normalized_cpp_type_name(raw)
99
+ return nil if SCALAR_BRIDGED_QT_TYPES.include?(mutable_reference_type) || %w[QCursor QIcon].include?(mutable_reference_type)
100
+ return nil unless ast_qt_value_class?(ast, mutable_reference_type)
101
+
102
+ return { ffi: :pointer, cast: :qt_value_ref, value_class: mutable_reference_type }
103
+ end
44
104
 
45
105
  compact_raw = raw.gsub(/\s+/, ' ')
46
106
 
47
107
  type = raw
48
108
  type = type.sub(/\Aconst\s+/, '').sub(/\s*&\z/, '').strip
49
- return nil if unsupported_cpp_type?(type)
109
+ return { ffi: :string, cast: :qint_list_from_variant } if type == 'QList<int>'
50
110
  return { ffi: :string, cast: :qstring } if type == 'QString'
51
111
  return { ffi: :string, cast: :qdatetime_from_utf8 } if type == 'QDateTime'
52
112
  return { ffi: :string, cast: :qdate_from_utf8 } if type == 'QDate'
@@ -58,7 +118,13 @@ def map_cpp_arg_type(type_name, qt_class: nil, int_cast_types: nil)
58
118
  return { ffi: :string, cast: :qvariant_from_utf8 } if type == 'QVariant'
59
119
  return { ffi: :string } if compact_raw.match?(/\Aconst\s+char\s*\*\z/)
60
120
 
61
- map_cpp_pointer_arg_type(type, qt_class) || map_cpp_intlike_arg_type(type, qt_class, int_cast_types)
121
+ if ast_qt_value_class?(ast, type)
122
+ return { ffi: :pointer, cast: :qt_value_ref, value_class: type }
123
+ end
124
+
125
+ return nil if unsupported_cpp_type?(type)
126
+
127
+ map_cpp_pointer_arg_type(type, qt_class, ast: ast) || map_cpp_intlike_arg_type(type, qt_class, int_cast_types, ast: ast)
62
128
  end
63
129
 
64
130
  def normalized_cpp_type_name(type_name)
@@ -68,13 +134,34 @@ def normalized_cpp_type_name(type_name)
68
134
  type
69
135
  end
70
136
 
71
- def map_cpp_return_type(type_name, ast: nil)
137
+ def map_cpp_return_type(type_name, ast: nil, qt_class: nil)
72
138
  raw = type_name.to_s.strip
139
+ type = raw.sub(/\Aconst\s+/, '').sub(/\s*&\z/, '').strip
140
+ return { ffi_return: :string, return_cast: :qint_list_to_variant } if type == 'QList<int>'
73
141
  return nil if unsupported_cpp_type?(raw)
74
142
  return nil if raw.start_with?('const ') && raw.end_with?('*')
75
143
 
76
- type = raw.sub(/\Aconst\s+/, '').sub(/\s*&\z/, '').strip
77
- map_scalar_cpp_return_type(type) || map_pointer_cpp_return_type(type, ast: ast)
144
+ scalar = map_scalar_cpp_return_type(type)
145
+ return scalar if scalar
146
+
147
+ int_cast_types = ast ? ast_int_cast_type_set(ast) : nil
148
+ intlike = map_cpp_intlike_arg_type(type, qt_class, int_cast_types, ast: ast)
149
+ return { ffi_return: :int, return_cast: :int_cast } if intlike
150
+
151
+ map_pointer_cpp_return_type(type, ast: ast) || map_qt_value_return_type(type, ast: ast)
152
+ end
153
+
154
+ def ast_qt_value_class?(ast, type)
155
+ return false unless ast && type.match?(/\AQ[A-Z]\w*\z/)
156
+ return false unless ast_class_index(ast)[:methods_by_class].key?(type)
157
+
158
+ !class_inherits?(ast, type, 'QObject')
159
+ end
160
+
161
+ def map_qt_value_return_type(type, ast:)
162
+ return nil unless ast_qt_value_class?(ast, type)
163
+
164
+ { ffi_return: :pointer, return_cast: :qt_value_copy, value_class: type }
78
165
  end
79
166
 
80
167
  def map_scalar_cpp_return_type(type)
@@ -82,6 +169,7 @@ def map_scalar_cpp_return_type(type)
82
169
  return { ffi_return: :int } if type == 'int'
83
170
  return { ffi_return: :bool } if type == 'bool'
84
171
  return { ffi_return: :string, return_cast: :qstring_to_utf8 } if type == 'QString'
172
+ return { ffi_return: :string, return_cast: :qstring_list_to_json } if type == 'QStringList'
85
173
  return { ffi_return: :string, return_cast: :qobject_list_to_wrapped_array, object_list_class: 'QObject' } if type == 'QObjectList'
86
174
  return { ffi_return: :string, return_cast: :qdatetime_to_utf8 } if type == 'QDateTime'
87
175
  return { ffi_return: :string, return_cast: :qdate_to_utf8 } if type == 'QDate'
@@ -135,7 +223,7 @@ def parse_method_signature(method_decl)
135
223
  }
136
224
  end
137
225
 
138
- def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
226
+ def build_auto_method_args(parsed, entry, qt_class, int_cast_types, ast)
139
227
  arg_cast_overrides = Array(entry[:arg_casts])
140
228
  params = parsed[:params]
141
229
  required_arg_count = 0
@@ -143,7 +231,7 @@ def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
143
231
 
144
232
  params.each_with_index do |param, idx|
145
233
  cast_override = arg_cast_overrides[idx]
146
- arg_info = map_cpp_arg_type(param[:type], qt_class: qt_class, int_cast_types: int_cast_types)
234
+ arg_info = map_cpp_arg_type(param[:type], qt_class: qt_class, int_cast_types: int_cast_types, ast: ast)
147
235
  arg_info ||= { ffi: :int } if cast_override
148
236
  unless arg_info
149
237
  return nil unless skip_unsupported_optional_tail?(params, idx, param)
@@ -151,7 +239,13 @@ def build_auto_method_args(parsed, entry, qt_class, int_cast_types)
151
239
  break
152
240
  end
153
241
 
154
- args << { name: param[:name], ffi: arg_info[:ffi], cast: cast_override || arg_info[:cast] }.compact
242
+ args << {
243
+ name: param[:name],
244
+ ffi: arg_info[:ffi],
245
+ cast: cast_override || arg_info[:cast],
246
+ value_class: arg_info[:value_class],
247
+ cpp_type: param[:type]
248
+ }.compact
155
249
  required_arg_count += 1 unless param[:has_default]
156
250
  end
157
251
 
@@ -172,8 +266,10 @@ def build_auto_method_hash(entry, ret_info, args, required_arg_count)
172
266
  args: args,
173
267
  required_arg_count: required_arg_count
174
268
  }
269
+ method[:static] = entry[:static] if entry.key?(:static)
175
270
  method[:return_cast] = ret_info[:return_cast] if ret_info[:return_cast]
176
271
  method[:pointer_class] = ret_info[:pointer_class] if ret_info[:pointer_class]
272
+ method[:value_class] = ret_info[:value_class] if ret_info[:value_class]
177
273
  method
178
274
  end
179
275
 
@@ -181,10 +277,10 @@ def build_auto_method_from_decl(method_decl, entry, qt_class:, int_cast_types:,
181
277
  parsed = parse_method_signature(method_decl)
182
278
  return nil unless parsed
183
279
 
184
- ret_info = map_cpp_return_type(parsed[:return_type], ast: ast)
280
+ ret_info = map_cpp_return_type(parsed[:return_type], ast: ast, qt_class: qt_class)
185
281
  return nil unless ret_info
186
282
 
187
- args, required_arg_count = build_auto_method_args(parsed, entry, qt_class, int_cast_types)
283
+ args, required_arg_count = build_auto_method_args(parsed, entry, qt_class, int_cast_types, ast)
188
284
  return nil unless args
189
285
 
190
286
  build_auto_method_hash(entry, ret_info, args, required_arg_count)
@@ -237,8 +333,15 @@ def build_auto_method_candidate(ast, decl, entry, qt_class, int_cast_types)
237
333
  parsed = parse_method_signature(decl)
238
334
  return nil unless parsed
239
335
 
240
- method = build_auto_method_from_decl(decl, entry, qt_class: qt_class, int_cast_types: int_cast_types, ast: ast)
336
+ method_entry = entry.merge(static: decl['storageClass'] == 'static')
337
+ method = build_auto_method_from_decl(decl, method_entry, qt_class: qt_class, int_cast_types: int_cast_types, ast: ast)
241
338
  return nil unless method
339
+ method[:owner_class] = qt_class if method[:static]
340
+ method[:cpp_return_type] = parsed[:return_type]
341
+ method[:virtual] = virtual_method_declaration?(ast, decl)
342
+ method[:cpp_const] = decl.dig('type', 'qualType').to_s.match?(/\)\s*const\b/)
343
+ method[:cpp_params] = parsed[:params].map { |param| param[:type] }
344
+ method[:final] = final_method_declaration?(ast, decl)
242
345
 
243
346
  {
244
347
  method: method,
@@ -246,6 +349,77 @@ def build_auto_method_candidate(ast, decl, entry, qt_class, int_cast_types)
246
349
  }
247
350
  end
248
351
 
352
+ def method_declaration_signature_key(decl)
353
+ parsed = parse_method_signature(decl)
354
+ return nil unless parsed
355
+
356
+ params = parsed[:params].map do |param|
357
+ param[:type].to_s.gsub(/\s+/, ' ').gsub(/\s*([*&])\s*/, '\\1').strip
358
+ end
359
+ const_method = decl.dig('type', 'qualType').to_s.match?(/\)\s*const\b/)
360
+ [params, const_method]
361
+ end
362
+
363
+ def method_declaration_has_attribute?(decl, attribute)
364
+ if attribute == 'OverrideAttr'
365
+ return decl['virtual'] == true || Array(decl['inner']).any? { |node| node['kind'] == attribute }
366
+ end
367
+
368
+ Array(decl['inner']).any? { |node| node['kind'] == attribute }
369
+ end
370
+
371
+ def inherited_method_attribute?(ast, owner_class, method_name, signature_key, attribute, visited = {})
372
+ return false if owner_class.nil? || visited[owner_class]
373
+
374
+ visited[owner_class] = true
375
+ collect_class_bases(ast, owner_class).any? do |base_class|
376
+ matching_declaration = collect_method_decls(ast, base_class, method_name).find do |candidate|
377
+ method_declaration_signature_key(candidate) == signature_key
378
+ end
379
+ next true if matching_declaration && method_declaration_has_attribute?(matching_declaration, attribute)
380
+ next false unless matching_declaration
381
+
382
+ inherited_method_attribute?(
383
+ ast,
384
+ base_class,
385
+ method_name,
386
+ signature_key,
387
+ attribute,
388
+ visited
389
+ )
390
+ end
391
+ end
392
+
393
+ def method_inherits_attribute?(ast, decl, attribute)
394
+ return true if method_declaration_has_attribute?(decl, attribute)
395
+
396
+ owner_class = decl['__owner_class']
397
+ signature_key = method_declaration_signature_key(decl)
398
+ return false unless owner_class && signature_key
399
+
400
+ cache = (@inherited_method_attribute_cache ||= {}.compare_by_identity)
401
+ per_ast = (cache[ast] ||= {})
402
+ cache_key = [owner_class, decl['name'], signature_key, attribute]
403
+ return per_ast[cache_key] if per_ast.key?(cache_key)
404
+
405
+ per_ast[cache_key] = inherited_method_attribute?(
406
+ ast,
407
+ owner_class,
408
+ decl['name'],
409
+ signature_key,
410
+ attribute
411
+ )
412
+ end
413
+
414
+ def virtual_method_declaration?(ast, decl)
415
+ method_inherits_attribute?(ast, decl, 'OverrideAttr') ||
416
+ decl['virtual'] == true
417
+ end
418
+
419
+ def final_method_declaration?(ast, decl)
420
+ method_inherits_attribute?(ast, decl, 'FinalAttr')
421
+ end
422
+
249
423
  def auto_method_decl_candidate?(decl)
250
424
  return false unless decl['__effective_access'] == 'public'
251
425
  return false if deprecated_method_decl?(decl)
@@ -352,7 +526,13 @@ def resolve_auto_method(ast, qt_class, auto_entry)
352
526
  built = resolve_auto_method_built_candidates(ast, qt_class, entry)
353
527
  return per_ast_cache[cache_key] = nil unless built
354
528
 
355
- per_ast_cache[cache_key] = built.min_by { |candidate| candidate[:method][:args].length }[:method]
529
+ methods = built.uniq { |candidate| candidate[:param_types] }
530
+ .sort_by do |candidate|
531
+ method = candidate[:method]
532
+ value_arg_count = method[:args].count { |arg| arg[:cast] == :qt_value_ref }
533
+ [method[:required_arg_count], -value_arg_count, method[:args].length, candidate[:param_types]]
534
+ end.map { |candidate| candidate[:method] }
535
+ per_ast_cache[cache_key] = methods
356
536
  end
357
537
 
358
538
  def auto_entries_for_spec(spec, ast)
@@ -371,9 +551,10 @@ def resolve_auto_methods_for_spec(ast, spec, auto_entries, manual_methods, auto_
371
551
  resolver = build_auto_method_resolver(ast, spec, manual_methods, auto_mode)
372
552
  spec_resolved = 0
373
553
  spec_skipped = 0
374
- auto_methods = auto_entries.filter_map do |entry|
554
+ auto_methods = []
555
+ auto_entries.each do |entry|
375
556
  method, spec_skipped, spec_resolved = resolve_with_resolver(resolver, entry, spec_skipped, spec_resolved)
376
- method
557
+ auto_methods.concat(Array(method))
377
558
  end
378
559
 
379
560
  [auto_methods, spec_resolved, spec_skipped]
@@ -10,6 +10,9 @@ class CppMethodReturnEmitter
10
10
 
11
11
  def emit
12
12
  return emit_void if method[:ffi_return] == :void
13
+ return emit_qt_value_copy if method[:return_cast] == :qt_value_copy
14
+ return emit_qint_list if method[:return_cast] == :qint_list_to_variant
15
+ return emit_qstring_list if method[:return_cast] == :qstring_list_to_json
13
16
  return emit_qstring if qstring_return?
14
17
  return emit_qobject_list if qobject_list_return?
15
18
  return emit_qdatetime if qdatetime_return?
@@ -17,6 +20,7 @@ class CppMethodReturnEmitter
17
20
  return emit_qtime if qtime_return?
18
21
  return emit_qvariant if qvariant_return?
19
22
  return emit_pointer if method[:ffi_return] == :pointer
23
+ return emit_int_cast if method[:return_cast] == :int_cast
20
24
 
21
25
  emit_value
22
26
  end
@@ -54,52 +58,99 @@ class CppMethodReturnEmitter
54
58
  end
55
59
 
56
60
  def emit_qstring
57
- lines << " const QString value = #{invocation};"
58
- lines << ' thread_local QByteArray utf8;'
59
- lines << ' utf8 = value.toUtf8();'
60
- lines << ' return utf8.constData();'
61
+ result = unique_local_name('result')
62
+ utf8 = unique_local_name('utf8')
63
+ lines << " const QString #{result} = #{invocation};"
64
+ lines << " thread_local QByteArray #{utf8};"
65
+ lines << " #{utf8} = #{result}.toUtf8();"
66
+ lines << " return #{utf8}.constData();"
67
+ end
68
+
69
+ def emit_qstring_list
70
+ result = unique_local_name('result')
71
+ utf8 = unique_local_name('utf8')
72
+ lines << " const QStringList #{result} = #{invocation};"
73
+ lines << " thread_local QByteArray #{utf8};"
74
+ lines << " #{utf8} = qstring_list_to_bridge_string(#{result}).toUtf8();"
75
+ lines << " return #{utf8}.constData();"
61
76
  end
62
77
 
63
78
  def emit_qvariant
64
- lines << " const QVariant value = #{invocation};"
65
- lines << ' thread_local QByteArray utf8;'
66
- lines << ' utf8 = qvariant_to_bridge_string(value).toUtf8();'
67
- lines << ' return utf8.constData();'
79
+ result = unique_local_name('result')
80
+ utf8 = unique_local_name('utf8')
81
+ lines << " const QVariant #{result} = #{invocation};"
82
+ lines << " thread_local QByteArray #{utf8};"
83
+ lines << " #{utf8} = qvariant_to_bridge_string(#{result}).toUtf8();"
84
+ lines << " return #{utf8}.constData();"
68
85
  end
69
86
 
70
87
  def emit_qobject_list
71
- lines << " const QObjectList value = #{invocation};"
72
- lines << ' thread_local QByteArray utf8;'
73
- lines << ' utf8 = qobject_list_to_bridge_string(value).toUtf8();'
74
- lines << ' return utf8.constData();'
88
+ result = unique_local_name('result')
89
+ utf8 = unique_local_name('utf8')
90
+ lines << " const QObjectList #{result} = #{invocation};"
91
+ lines << " thread_local QByteArray #{utf8};"
92
+ lines << " #{utf8} = qobject_list_to_bridge_string(#{result}).toUtf8();"
93
+ lines << " return #{utf8}.constData();"
75
94
  end
76
95
 
77
96
  def emit_qdatetime
78
- lines << " const QDateTime value = #{invocation};"
79
- lines << ' thread_local QByteArray utf8;'
80
- lines << ' utf8 = qdatetime_to_bridge_string(value).toUtf8();'
81
- lines << ' return utf8.constData();'
97
+ result = unique_local_name('result')
98
+ utf8 = unique_local_name('utf8')
99
+ lines << " const QDateTime #{result} = #{invocation};"
100
+ lines << " thread_local QByteArray #{utf8};"
101
+ lines << " #{utf8} = qdatetime_to_bridge_string(#{result}).toUtf8();"
102
+ lines << " return #{utf8}.constData();"
82
103
  end
83
104
 
84
105
  def emit_qdate
85
- lines << " const QDate value = #{invocation};"
86
- lines << ' thread_local QByteArray utf8;'
87
- lines << ' utf8 = qdate_to_bridge_string(value).toUtf8();'
88
- lines << ' return utf8.constData();'
106
+ result = unique_local_name('result')
107
+ utf8 = unique_local_name('utf8')
108
+ lines << " const QDate #{result} = #{invocation};"
109
+ lines << " thread_local QByteArray #{utf8};"
110
+ lines << " #{utf8} = qdate_to_bridge_string(#{result}).toUtf8();"
111
+ lines << " return #{utf8}.constData();"
89
112
  end
90
113
 
91
114
  def emit_qtime
92
- lines << " const QTime value = #{invocation};"
93
- lines << ' thread_local QByteArray utf8;'
94
- lines << ' utf8 = qtime_to_bridge_string(value).toUtf8();'
95
- lines << ' return utf8.constData();'
115
+ result = unique_local_name('result')
116
+ utf8 = unique_local_name('utf8')
117
+ lines << " const QTime #{result} = #{invocation};"
118
+ lines << " thread_local QByteArray #{utf8};"
119
+ lines << " #{utf8} = qtime_to_bridge_string(#{result}).toUtf8();"
120
+ lines << " return #{utf8}.constData();"
96
121
  end
97
122
 
98
123
  def emit_pointer
99
124
  lines << " return const_cast<void*>(static_cast<const void*>(#{invocation}));"
100
125
  end
101
126
 
127
+ def emit_qt_value_copy
128
+ lines << " return new #{method[:value_class]}(#{invocation});"
129
+ end
130
+
131
+ def emit_qint_list
132
+ result = unique_local_name('result')
133
+ variant = unique_local_name('variant')
134
+ utf8 = unique_local_name('utf8')
135
+ lines << " const QList<int> #{result} = #{invocation};"
136
+ lines << " const QVariant #{variant} = QtRubyRuntime::qvariant_from_qint_list(#{result});"
137
+ lines << " thread_local QByteArray #{utf8};"
138
+ lines << " #{utf8} = qvariant_to_bridge_string(#{variant}).toUtf8();"
139
+ lines << " return #{utf8}.constData();"
140
+ end
141
+
102
142
  def emit_value
103
143
  lines << " return #{invocation};"
104
144
  end
145
+
146
+ def emit_int_cast
147
+ lines << " return static_cast<int>(#{invocation});"
148
+ end
149
+
150
+ def unique_local_name(suffix)
151
+ used = Array(method[:args]).map { |arg| arg[:name].to_s }.to_set
152
+ candidate = "qt_ruby_#{suffix}"
153
+ candidate += '_' while used.include?(candidate)
154
+ candidate
155
+ end
105
156
  end
@@ -15,9 +15,25 @@ def all_ffi_functions(specs, free_function_specs:)
15
15
  append_method_ffi_functions(fns, spec)
16
16
  end
17
17
 
18
+ value_wrapper_classes(specs, free_function_specs: free_function_specs).each do |qt_class|
19
+ fns << { name: qt_value_delete_function_name(qt_class), ffi_return: :void, args: [:pointer] }
20
+ end
21
+
18
22
  fns
19
23
  end
20
24
 
25
+ def value_wrapper_classes(specs, free_function_specs: [])
26
+ spec_classes = specs.flat_map do |spec|
27
+ spec[:methods].filter_map { |method| method[:value_class] if method[:return_cast] == :qt_value_copy }
28
+ end
29
+ free_classes = free_function_specs.filter_map { |spec| spec[:value_class] }
30
+ (spec_classes + free_classes).uniq.sort
31
+ end
32
+
33
+ def qt_value_delete_function_name(qt_class)
34
+ "qt_ruby_#{to_snake(qt_class)}_value_delete"
35
+ end
36
+
21
37
  def append_constructor_ffi_function(fns, spec)
22
38
  return if spec[:constructor][:mode] == :wrap_only
23
39
 
@@ -42,7 +58,7 @@ end
42
58
 
43
59
  def append_method_ffi_functions(fns, spec)
44
60
  spec[:methods].each do |method|
45
- args = [:pointer] + method[:args].map { |arg| arg[:ffi] }
61
+ args = [:pointer] + (method[:dispatch] ? [:int] : []) + method[:args].map { |arg| arg[:ffi] }
46
62
  fns << { name: method_function_name(spec, method), ffi_return: method[:ffi_return], args: args }
47
63
  end
48
64
  end