wrapture 0.3.0 → 0.4.0

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.
@@ -1,6 +1,22 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
1
3
  # frozen_string_literal: true
2
4
 
3
- require 'wrapture/normalize'
5
+ #--
6
+ # Copyright 2019-2020 Joel E. Anderson
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #++
4
20
 
5
21
  module Wrapture
6
22
  # A description of a constant.
@@ -10,6 +26,8 @@ module Wrapture
10
26
  # will set missing keys to their default value (for example, an empty list
11
27
  # if no includes are given).
12
28
  def self.normalize_spec_hash(spec)
29
+ Comment.validate_doc(spec['doc']) if spec.key?('doc')
30
+
13
31
  normalized = spec.dup
14
32
 
15
33
  normalized['version'] = Wrapture.spec_version(spec)
@@ -26,8 +44,13 @@ module Wrapture
26
44
  # value:: the value to assign to the constant
27
45
  # includes:: a list of includes that need to be added in order for this
28
46
  # constant to be valid (for example, includes for the type and value).
47
+ #
48
+ # The following keys are optional:
49
+ # doc:: a string containing the documentation for this constant
29
50
  def initialize(spec)
30
- @spec = ConstantSpec.normalize_spec_hash spec
51
+ @spec = ConstantSpec.normalize_spec_hash(spec)
52
+ @doc = @spec.key?('doc') ? Comment.new(@spec['doc']) : nil
53
+ @type = TypeSpec.new(@spec['type'])
31
54
  end
32
55
 
33
56
  # A list of includes needed for the declaration of this constant.
@@ -40,9 +63,11 @@ module Wrapture
40
63
  @spec['includes'].dup
41
64
  end
42
65
 
43
- # The declaration of this constant.
66
+ # Yields each line of the declaration of this constant, including any
67
+ # documentation.
44
68
  def declaration
45
- "static const #{ClassSpec.typed_variable(@spec['type'], @spec['name'])}"
69
+ @doc&.format_as_doxygen(max_line_length: 76) { |line| yield line }
70
+ yield "static const #{@type.variable(@spec['name'])};"
46
71
  end
47
72
 
48
73
  # The definition of this constant.
@@ -2,6 +2,7 @@
2
2
 
3
3
  # frozen_string_literal: true
4
4
 
5
+ #--
5
6
  # Copyright 2019-2020 Joel E. Anderson
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +16,7 @@
15
16
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
17
  # See the License for the specific language governing permissions and
17
18
  # limitations under the License.
19
+ #++
18
20
 
19
21
  module Wrapture
20
22
  # A string denoting an equivalent struct type or value.
@@ -26,7 +28,13 @@ module Wrapture
26
28
  # A string denoting the return value of a wrapped function call.
27
29
  RETURN_VALUE_KEYWORD = 'return-value'
28
30
 
31
+ # A string denoting a reference to the object a method is called on.
32
+ SELF_REFERENCE_KEYWORD = 'self-reference'
33
+
34
+ # A string denoting a reference to a template.
35
+ TEMPLATE_USE_KEYWORD = 'use-template'
36
+
29
37
  # A list of all keywords.
30
38
  KEYWORDS = [EQUIVALENT_STRUCT_KEYWORD, EQUIVALENT_POINTER_KEYWORD,
31
- RETURN_VALUE_KEYWORD].freeze
39
+ RETURN_VALUE_KEYWORD, TEMPLATE_USE_KEYWORD].freeze
32
40
  end
@@ -0,0 +1,154 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # frozen_string_literal: true
4
+
5
+ #--
6
+ # Copyright 2020 Joel E. Anderson
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #++
20
+
21
+ module Wrapture
22
+ # A description of an enumeration.
23
+ class EnumSpec
24
+ # Returns a normalized copy of a hash specification of an enumeration in
25
+ # place. See normalize_spec_hash! for details.
26
+ def self.normalize_spec_hash(spec)
27
+ normalize_spec_hash!(Marshal.load(Marshal.dump(spec)))
28
+ end
29
+
30
+ # Normalizes a hash specification of an enumeration in place. Normalization
31
+ # will remove duplicate entries in include lists and check for a name key.
32
+ def self.normalize_spec_hash!(spec)
33
+ unless spec.key?('name')
34
+ raise MissingSpecKey, 'a name is required for enumerations'
35
+ end
36
+
37
+ if spec.key?('elements')
38
+ unless spec['elements'].is_a?(Array)
39
+ raise InvalidSpecKey, 'the elements key must be an array'
40
+ end
41
+ else
42
+ raise MissingSpecKey, 'elements are required for enumerations'
43
+ end
44
+
45
+ spec['includes'] = Wrapture.normalize_includes(spec['includes'])
46
+ spec['elements'].each do |element|
47
+ element['includes'] = Wrapture.normalize_includes(element['includes'])
48
+ end
49
+
50
+ spec
51
+ end
52
+
53
+ # Creates an enumeration specification based on the provided hash spec.
54
+ def initialize(spec)
55
+ @spec = EnumSpec.normalize_spec_hash(spec)
56
+
57
+ @doc = Comment.new(@spec.fetch('doc', nil))
58
+ end
59
+
60
+ # Generates the wrapper definition file.
61
+ def generate_wrapper
62
+ filename = "#{@spec['name']}.hpp"
63
+
64
+ File.open(filename, 'w') do |file|
65
+ definition_contents do |line|
66
+ file.puts(line)
67
+ end
68
+ end
69
+
70
+ [filename]
71
+ end
72
+
73
+ # The name of the enumeration.
74
+ def name
75
+ @spec['name']
76
+ end
77
+
78
+ private
79
+
80
+ # Yields each line of the definition of the wrapper for this enum.
81
+ def definition_contents
82
+ indent = 0
83
+
84
+ yield "#ifndef #{header_guard}"
85
+ yield "#define #{header_guard}"
86
+ yield
87
+
88
+ definition_includes.each { |filename| yield "#include <#{filename}>" }
89
+ yield
90
+
91
+ if @spec.key?('namespace')
92
+ yield "namespace #{@spec['namespace']} {"
93
+ yield
94
+ indent += 2
95
+ end
96
+
97
+ @doc.format_as_doxygen(max_line_length: 76) do |line|
98
+ yield "#{' ' * indent}#{line}"
99
+ end
100
+
101
+ yield "#{' ' * indent}enum class #{name} {"
102
+ indent += 2
103
+
104
+ elements = @spec['elements']
105
+ elements[0...-1].each do |element|
106
+ element_doc(element) { |line| yield "#{' ' * indent}#{line}" }
107
+ element_definition(element) { |line| yield "#{' ' * indent}#{line}," }
108
+ end
109
+
110
+ element_doc(elements.last) { |line| yield "#{' ' * indent}#{line}" }
111
+ element_definition(elements.last) do |line|
112
+ yield "#{' ' * indent}#{line}"
113
+ end
114
+
115
+ indent -= 2
116
+ yield "#{' ' * indent}};"
117
+ yield
118
+ yield '}' if @spec.key?('namespace')
119
+ yield
120
+ yield "#endif /* #{header_guard} */"
121
+ end
122
+
123
+ # A list of the includes needed for the definition of the enumeration.
124
+ def definition_includes
125
+ includes = @spec['includes'].dup
126
+
127
+ @spec['elements'].each do |element|
128
+ includes.concat(element['includes'])
129
+ end
130
+
131
+ includes.uniq
132
+ end
133
+
134
+ # Yields each line of the definition of an element.
135
+ def element_definition(element)
136
+ if element.key?('value')
137
+ yield "#{element['name']} = #{element['value']}"
138
+ else
139
+ yield element['name']
140
+ end
141
+ end
142
+
143
+ # Yields each line of the documentation for an element.
144
+ def element_doc(element)
145
+ doc = Comment.new(element.fetch('doc', nil))
146
+ doc.format_as_doxygen(max_line_length: 74) { |line| yield line }
147
+ end
148
+
149
+ # The header guard for the enumeration.
150
+ def header_guard
151
+ "__#{@spec['name'].upcase}_HPP"
152
+ end
153
+ end
154
+ end
@@ -2,7 +2,8 @@
2
2
 
3
3
  # frozen_string_literal: true
4
4
 
5
- # Copyright 2019 Joel E. Anderson
5
+ #--
6
+ # Copyright 2019-2020 Joel E. Anderson
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License");
8
9
  # you may not use this file except in compliance with the License.
@@ -15,12 +16,29 @@
15
16
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
17
  # See the License for the specific language governing permissions and
17
18
  # limitations under the License.
19
+ #++
18
20
 
19
21
  module Wrapture
20
22
  # An error from the Wrapture library
21
23
  class WraptureError < StandardError
22
24
  end
23
25
 
26
+ # A documentation string is invalid.
27
+ class InvalidDoc < WraptureError
28
+ # Creates an InvalidDoc with the given message.
29
+ def initialize(message)
30
+ super(message)
31
+ end
32
+ end
33
+
34
+ # A template has been invoked in an unsupported way.
35
+ class InvalidTemplateUsage < WraptureError
36
+ # Creates an InvalidTemplateUsage with the given message.
37
+ def initialize(message)
38
+ super(message)
39
+ end
40
+ end
41
+
24
42
  # The spec has a key that is not valid.
25
43
  class InvalidSpecKey < WraptureError
26
44
  # Creates an InvalidSpecKey with the given message. A list of valid values
@@ -51,6 +69,14 @@ module Wrapture
51
69
  class NoNamespace < WraptureError
52
70
  end
53
71
 
72
+ # The spec cannot be defined due to missing information.
73
+ class UndefinableSpec < WraptureError
74
+ # Creates an UndefinableSpec with error +message+.
75
+ def initialize(message)
76
+ super(message)
77
+ end
78
+ end
79
+
54
80
  # The spec version is not supported by this version of Wrapture.
55
81
  class UnsupportedSpecVersion < WraptureError
56
82
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  # frozen_string_literal: true
4
4
 
5
+ #--
5
6
  # Copyright 2019-2020 Joel E. Anderson
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,43 +16,38 @@
15
16
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
17
  # See the License for the specific language governing permissions and
17
18
  # limitations under the License.
18
-
19
- require 'wrapture/constants'
20
- require 'wrapture/errors'
21
- require 'wrapture/scope'
22
- require 'wrapture/wrapped_function_spec'
19
+ #++
23
20
 
24
21
  module Wrapture
25
22
  # A description of a function to be generated, including details about the
26
23
  # underlying implementation.
27
24
  class FunctionSpec
25
+ # Returns a copy of the return type specification +spec+.
26
+ def self.normalize_return_hash(spec)
27
+ if spec.nil?
28
+ { 'type' => 'void', 'includes' => [] }
29
+ else
30
+ normalized = Marshal.load(Marshal.dump(spec))
31
+ Comment.validate_doc(spec['doc']) if spec.key?('doc')
32
+ normalized['type'] ||= 'void'
33
+ normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
34
+ normalized
35
+ end
36
+ end
37
+
28
38
  # Normalizes a hash specification of a function. Normalization will check
29
39
  # for things like invalid keys, duplicate entries in include lists, and will
30
40
  # set missing keys to their default values (for example, an empty list if no
31
41
  # includes are given).
32
42
  def self.normalize_spec_hash(spec)
43
+ Comment.validate_doc(spec['doc']) if spec.key?('doc')
44
+
33
45
  normalized = spec.dup
34
- param_types = {}
35
46
 
36
47
  normalized['version'] = Wrapture.spec_version(spec)
37
48
  normalized['virtual'] = Wrapture.normalize_boolean(spec, 'virtual')
38
-
39
- normalized['params'] ||= []
40
- normalized['params'].each do |param_spec|
41
- param_types[param_spec['name']] = param_spec['type']
42
- includes = Wrapture.normalize_includes(param_spec['includes'])
43
- param_spec['includes'] = includes
44
- end
45
-
46
- if normalized['return'].nil?
47
- normalized['return'] = {}
48
- normalized['return']['type'] = 'void'
49
- normalized['return']['includes'] = []
50
- else
51
- normalized['return']['type'] ||= 'void'
52
- includes = Wrapture.normalize_includes(spec['return']['includes'])
53
- normalized['return']['includes'] = includes
54
- end
49
+ normalized['params'] = ParamSpec.normalize_param_list(spec['params'])
50
+ normalized['return'] = normalize_return_hash(spec['return'])
55
51
 
56
52
  overload = Wrapture.normalize_boolean(normalized['return'], 'overloaded')
57
53
  normalized['return']['overloaded'] = overload
@@ -66,22 +62,55 @@ module Wrapture
66
62
  # params:: a list of parameter specifications
67
63
  # wrapped-function:: a hash describing the function to be wrapped
68
64
  #
65
+ # Each parameter specification must have a 'name' key with the name of the
66
+ # parameter and a 'type' key with its type. The type key may be ommitted
67
+ # if the name of the parameter is '...' in which case the generated function
68
+ # will be made variadic. It may optionally have an 'includes' key with
69
+ # includes that are required (for example to support the type) and/or a
70
+ # 'doc' key with documentation of the parameter.
71
+ #
72
+ # Only one parameter named '...' is allowed in a specification. If more than
73
+ # one is provided, then only the first encountered will be used. This
74
+ # parameter should also be last - if it is not, it will be moved to the end
75
+ # of the parameter list during normalization.
76
+ #
69
77
  # The wrapped-function must have a 'name' key with the name of the function,
70
78
  # and a 'params' key with a list of parameters (each a hash with a 'name'
71
79
  # and 'type' key). Optionally, it may also include an 'includes' key with a
72
- # list of includes that are needed for this function to compile.
80
+ # list of includes that are needed for this function to compile. The wrapped
81
+ # function may be left out entirely, but the function will not be definable
82
+ # if this is the case.
73
83
  #
74
84
  # The following keys are optional:
75
- # static:: set to true if this is a static function.
85
+ # doc:: a string containing the documentation for this function
86
+ # return:: a specification of the return value for this function
87
+ # static:: set to true if this is a static function
88
+ #
89
+ # The return specification may have either a 'type' key with the name of the
90
+ # type the function returns, and/or a 'doc' key with documentation on the
91
+ # return value itself. If neither of these is needed, then the return
92
+ # specification may simply be omitted.
93
+ #
94
+ # The 'type' key of the return spec may also be set to 'self-reference'
95
+ # which will have the function return a reference to the instance it was
96
+ # called on. Of course, this cannot be used from a function that is not a
97
+ # class method.
76
98
  def initialize(spec, owner = Scope.new, constructor: false,
77
99
  destructor: false)
78
100
  @owner = owner
79
101
  @spec = FunctionSpec.normalize_spec_hash(spec)
80
- @wrapped = WrappedFunctionSpec.new(spec['wrapped-function'])
102
+ @wrapped = if @spec.key?('wrapped-function')
103
+ WrappedFunctionSpec.new(@spec['wrapped-function'])
104
+ end
105
+ @params = ParamSpec.new_list(@spec['params'])
106
+ @return_type = TypeSpec.new(@spec['return']['type'])
81
107
  @constructor = constructor
82
108
  @destructor = destructor
83
109
  end
84
110
 
111
+ # A TypeSpec describing the return type of this function.
112
+ attr_reader :return_type
113
+
85
114
  # True if the function is a constructor, false otherwise.
86
115
  def constructor?
87
116
  @constructor
@@ -90,61 +119,89 @@ module Wrapture
90
119
  # A list of includes needed for the declaration of the function.
91
120
  def declaration_includes
92
121
  includes = @spec['return']['includes'].dup
93
- includes.concat(param_includes)
122
+ @params.each { |param| includes.concat(param.includes) }
123
+ includes.concat(@return_type.includes)
94
124
  includes.uniq
95
125
  end
96
126
 
127
+ # True if this function can be defined.
128
+ def definable?
129
+ definable_check
130
+ rescue UndefinableSpec
131
+ false
132
+ end
133
+
97
134
  # A list of includes needed for the definition of the function.
98
135
  def definition_includes
99
136
  includes = @wrapped.includes
100
137
  includes.concat(@spec['return']['includes'])
101
- includes.concat(param_includes)
138
+ @params.each { |param| includes.concat(param.includes) }
139
+ includes.concat(@return_type.includes)
140
+ includes << 'stdarg.h' if variadic?
102
141
  includes.uniq
103
142
  end
104
143
 
105
- # A comma-separated list of parameters and resolved types fit for use in a
106
- # function signature or declaration.
107
- def param_list
108
- return 'void' if @spec['params'].empty?
144
+ # The name of the function.
145
+ def name
146
+ @spec['name']
147
+ end
109
148
 
110
- params = []
149
+ # A string with the parameter list for this function.
150
+ def param_list
151
+ ParamSpec.signature(@params, self)
152
+ end
111
153
 
112
- @spec['params'].each do |param|
113
- type = resolve_type(param['type'])
114
- params << ClassSpec.typed_variable(type, param['name'])
154
+ # The name of the function with the class name, if it exists.
155
+ def qualified_name
156
+ if @owner.is_a?(ClassSpec)
157
+ "#{@owner.name}::#{name}"
158
+ else
159
+ name
115
160
  end
116
-
117
- params.join(', ')
118
161
  end
119
162
 
120
163
  # Gives an expression for calling a given parameter within this function.
121
164
  # Equivalent structs and pointers are resolved, as well as casts between
122
165
  # types if they are known within the scope of this function.
123
166
  def resolve_wrapped_param(param_spec)
124
- used_param = @spec['params'].find { |p| p['name'] == param_spec['value'] }
167
+ used_param = @params.find { |p| p.name == param_spec['value'] }
125
168
 
126
169
  if param_spec['value'] == EQUIVALENT_STRUCT_KEYWORD
127
170
  @owner.this_struct
128
171
  elsif param_spec['value'] == EQUIVALENT_POINTER_KEYWORD
129
172
  @owner.this_struct_pointer
130
- elsif used_param &&
131
- @owner.type?(used_param['type']) &&
132
- !param_spec['type'].nil?
133
- param_class = @owner.type(used_param['type'])
134
- param_class.cast_to(used_param['name'], param_spec['type'])
173
+ elsif param_spec['value'] == '...'
174
+ 'variadic_args'
175
+ elsif castable?(param_spec)
176
+ param_class = @owner.type(used_param.type)
177
+ param_class.cast(used_param.name,
178
+ param_spec['type'],
179
+ used_param.type)
135
180
  else
136
181
  param_spec['value']
137
182
  end
138
183
  end
139
184
 
185
+ # Calls return_expression on the return type of this function. +func_name+
186
+ # is passed to return_expression if provided.
187
+ def return_expression(func_name: name)
188
+ resolved_return.return_expression(self, func_name: func_name)
189
+ end
190
+
140
191
  # The signature of the function.
141
192
  def signature
142
- "#{@spec['name']}( #{param_list} )"
193
+ "#{name}( #{param_list} )"
143
194
  end
144
195
 
145
- # The declaration of the function.
196
+ # Yields each line of the declaration of the function, including any
197
+ # documentation.
146
198
  def declaration
147
- return signature if @constructor || @destructor
199
+ doc.format_as_doxygen(max_line_length: 76) { |line| yield line }
200
+
201
+ if @constructor || @destructor
202
+ yield "#{signature};"
203
+ return
204
+ end
148
205
 
149
206
  modifier_prefix = if @spec['static']
150
207
  'static '
@@ -153,39 +210,72 @@ module Wrapture
153
210
  else
154
211
  ''
155
212
  end
156
- "#{modifier_prefix}#{@spec['return']['type']} #{signature}"
213
+
214
+ yield "#{modifier_prefix}#{return_expression};"
157
215
  end
158
216
 
159
- # Gives the definition of the function to a block, line by line.
160
- def definition(class_name)
161
- yield "#{return_prefix}#{class_name}::#{signature} {"
217
+ # Gives the definition of the function in a block, line by line.
218
+ def definition
219
+ definable_check
162
220
 
163
- if @wrapped.error_check?
164
- yield " #{@wrapped.return_val_type} return_val;"
165
- yield
166
- end
221
+ yield "#{return_expression(func_name: qualified_name)} {"
167
222
 
168
- call = @wrapped.call_from(self)
169
- call_line = if @constructor
170
- "this->equivalent = #{call}"
171
- elsif @wrapped.error_check?
172
- "return_val = #{call}"
173
- elsif returns_value?
174
- "return #{return_cast(call)}"
175
- else
176
- call
177
- end
178
-
179
- yield " #{call_line};"
223
+ locals { |declaration| yield " #{declaration}" }
224
+
225
+ yield " va_start( variadic_args, #{@params[-2].name} );" if variadic?
180
226
 
181
227
  if @wrapped.error_check?
228
+ yield
229
+ yield " #{wrapped_call_expression};"
182
230
  yield
183
231
  @wrapped.error_check { |line| yield " #{line}" }
232
+ else
233
+ yield " #{wrapped_call_expression};"
184
234
  end
185
235
 
236
+ yield ' va_end( variadic_args );' if variadic?
237
+
238
+ yield ' return *this;' if @return_type.self?
239
+
186
240
  yield '}'
187
241
  end
188
242
 
243
+ # A Comment holding the function documentation.
244
+ def doc
245
+ comment = String.new
246
+ comment << @spec['doc'] if @spec.key?('doc')
247
+
248
+ @params
249
+ .reject { |param| param.doc.empty? }
250
+ .each { |param| comment << "\n\n" << param.doc.text }
251
+
252
+ if @spec['return'].key?('doc')
253
+ comment << "\n\n@return " << @spec['return']['doc']
254
+ end
255
+
256
+ Comment.new(comment)
257
+ end
258
+
259
+ # A resolved type, given a TypeSpec +type+. Resolved types will not have any
260
+ # keywords like +equivalent-struct+, which will be resolved to their
261
+ # effective type.
262
+ def resolve_type(type)
263
+ if type.equivalent_struct?
264
+ TypeSpec.new("struct #{@owner.struct_name}")
265
+ elsif type.equivalent_pointer?
266
+ TypeSpec.new("struct #{@owner.struct_name} *")
267
+ elsif type.self?
268
+ TypeSpec.new("#{@owner.name}&")
269
+ else
270
+ type
271
+ end
272
+ end
273
+
274
+ # True if the function is variadic.
275
+ def variadic?
276
+ @params.last&.variadic?
277
+ end
278
+
189
279
  # True if the function is virtual.
190
280
  def virtual?
191
281
  @spec['virtual']
@@ -193,26 +283,35 @@ module Wrapture
193
283
 
194
284
  private
195
285
 
196
- # A list of includes needed for the parameters of the function.
197
- def param_includes
198
- includes = []
286
+ # The resolved type of the return type.
287
+ def resolved_return
288
+ @return_type.resolve(self)
289
+ end
199
290
 
200
- @spec['params'].each do |param_spec|
201
- includes.concat(param_spec['includes'])
202
- end
291
+ # True if the provided wrapped param spec can be cast to when used in this
292
+ # function.
293
+ def castable?(wrapped_param)
294
+ param = @params.find { |p| p.name == wrapped_param['value'] }
203
295
 
204
- includes
296
+ !param.nil? &&
297
+ !wrapped_param['type'].nil? &&
298
+ @owner.type?(param.type)
205
299
  end
206
300
 
207
- # A resolved type name.
208
- def resolve_type(type)
209
- if type == EQUIVALENT_STRUCT_KEYWORD
210
- "struct #{@owner.struct_name}"
211
- elsif type == EQUIVALENT_POINTER_KEYWORD
212
- "struct #{@owner.struct_name} *"
213
- else
214
- type
301
+ # Raises an exception if this function cannot be defined as is. Returns
302
+ # true otherwise.
303
+ def definable_check
304
+ if @wrapped.nil?
305
+ raise UndefinableSpec, 'no wrapped function was specified'
215
306
  end
307
+
308
+ true
309
+ end
310
+
311
+ # Yields a declaration of each local variable used by the function.
312
+ def locals
313
+ yield 'va_list variadic_args;' if variadic?
314
+ yield "#{@wrapped.return_val_type} return_val;" if @wrapped.error_check?
216
315
  end
217
316
 
218
317
  # The function to use to create the return value of the function.
@@ -226,20 +325,25 @@ module Wrapture
226
325
  end
227
326
  end
228
327
 
229
- # The return type prefix to use for the function definition.
230
- def return_prefix
231
- if @constructor || @destructor
232
- ''
233
- elsif @spec['return']['type'].end_with?('*')
234
- @spec['return']['type']
235
- else
236
- "#{@spec['return']['type']} "
237
- end
328
+ # True if the function returns the result of the wrapped function call.
329
+ def returns_call_result?
330
+ !@constructor && !@destructor &&
331
+ !%w[void self-reference].include?(@spec['return']['type'])
238
332
  end
239
333
 
240
- # True if the function returns a value.
241
- def returns_value?
242
- !@constructor && !@destructor && @spec['return']['type'] != 'void'
334
+ # The expression containing the call to the underlying wrapped function.
335
+ def wrapped_call_expression
336
+ call = @wrapped.call_from(self)
337
+
338
+ if @constructor
339
+ "this->equivalent = #{call}"
340
+ elsif @wrapped.error_check?
341
+ "return_val = #{call}"
342
+ elsif returns_call_result?
343
+ "return #{return_cast(call)}"
344
+ else
345
+ call
346
+ end
243
347
  end
244
348
  end
245
349
  end