Class: Wrapture::FunctionSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/wrapture/function_spec.rb

Overview

A description of a function to be generated, including details about the underlying implementation.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec, owner = Scope.new, constructor: false, destructor: false) ⇒ FunctionSpec

Creates a function spec based on the provided function spec.

The hash must have the following keys:

name

the name of the function

params

a list of parameter specifications

wrapped-function

a hash describing the function to be wrapped

Each parameter specification must have a ‘name’ key with the name of the parameter and a ‘type’ key with its type. The type key may be ommitted if the name of the parameter is ‘…’ in which case the generated function will be made variadic. It may optionally have an ‘includes’ key with includes that are required (for example to support the type) and/or a ‘doc’ key with documentation of the parameter.

Only one parameter named ‘…’ is allowed in a specification. If more than one is provided, then only the first encountered will be used. This parameter should also be last - if it is not, it will be moved to the end of the parameter list during normalization.

The wrapped-function must have a ‘name’ key with the name of the function, and a ‘params’ key with a list of parameters (each a hash with a ‘name’ and ‘type’ key). Optionally, it may also include an ‘includes’ key with a list of includes that are needed for this function to compile. The wrapped function may be left out entirely, but the function will not be definable if this is the case.

The following keys are optional:

doc

a string containing the documentation for this function

return

a specification of the return value for this function

static

set to true if this is a static function

The return specification may have either a ‘type’ key with the name of the type the function returns, and/or a ‘doc’ key with documentation on the return value itself. If neither of these is needed, then the return specification may simply be omitted.

The ‘type’ key of the return spec may also be set to ‘self-reference’ which will have the function return a reference to the instance it was called on. Of course, this cannot be used from a function that is not a class method.



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/wrapture/function_spec.rb', line 98

def initialize(spec, owner = Scope.new, constructor: false,
               destructor: false)
  @owner = owner
  @spec = FunctionSpec.normalize_spec_hash(spec)
  @wrapped = if @spec.key?('wrapped-function')
               WrappedFunctionSpec.new(@spec['wrapped-function'])
             end
  @params = ParamSpec.new_list(@spec['params'])
  @return_type = TypeSpec.new(@spec['return']['type'])
  @constructor = constructor
  @destructor = destructor
end

Instance Attribute Details

#return_typeObject (readonly)

A TypeSpec describing the return type of this function.



112
113
114
# File 'lib/wrapture/function_spec.rb', line 112

def return_type
  @return_type
end

Class Method Details

.normalize_return_hash(spec) ⇒ Object

Returns a copy of the return type specification spec.



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/wrapture/function_spec.rb', line 26

def self.normalize_return_hash(spec)
  if spec.nil?
    { 'type' => 'void', 'includes' => [] }
  else
    normalized = Marshal.load(Marshal.dump(spec))
    Comment.validate_doc(spec['doc']) if spec.key?('doc')
    normalized['type'] ||= 'void'
    normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
    normalized
  end
end

.normalize_spec_hash(spec) ⇒ Object

Normalizes a hash specification of a function. Normalization will check for things like invalid keys, duplicate entries in include lists, and will set missing keys to their default values (for example, an empty list if no includes are given).



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/wrapture/function_spec.rb', line 42

def self.normalize_spec_hash(spec)
  Comment.validate_doc(spec['doc']) if spec.key?('doc')

  normalized = spec.dup

  normalized['version'] = Wrapture.spec_version(spec)
  normalized['virtual'] = Wrapture.normalize_boolean(spec, 'virtual')
  normalized['params'] = ParamSpec.normalize_param_list(spec['params'])
  normalized['return'] = normalize_return_hash(spec['return'])

  overload = Wrapture.normalize_boolean(normalized['return'], 'overloaded')
  normalized['return']['overloaded'] = overload

  normalized
end

Instance Method Details

#constructor?Boolean

True if the function is a constructor, false otherwise.

Returns:

  • (Boolean)


115
116
117
# File 'lib/wrapture/function_spec.rb', line 115

def constructor?
  @constructor
end

#declaration(&block) ⇒ Object

Calls the given block once for each line of the declaration of the function, including any documentation.



203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/wrapture/function_spec.rb', line 203

def declaration(&block)
  doc.format_as_doxygen(max_line_length: 76) { |line| block.call(line) }

  modifier_prefix = if @spec['static']
                      'static '
                    elsif virtual?
                      'virtual '
                    else
                      ''
                    end

  block.call("#{modifier_prefix}#{return_expression};")
end

#declaration_includesObject

A list of includes needed for the declaration of the function.



120
121
122
123
124
125
# File 'lib/wrapture/function_spec.rb', line 120

def declaration_includes
  includes = @spec['return']['includes'].dup
  @params.each { |param| includes.concat(param.includes) }
  includes.concat(@return_type.includes)
  includes.uniq
end

#definable?Boolean

True if this function can be defined.

Returns:

  • (Boolean)


128
129
130
131
132
# File 'lib/wrapture/function_spec.rb', line 128

def definable?
  definable_check
rescue UndefinableSpec
  false
end

#definition {|"#{return_expression(func_name: qualified_name)} {"| ... } ⇒ Object

Gives the definition of the function in a block, line by line.

Yields:



218
219
220
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
# File 'lib/wrapture/function_spec.rb', line 218

def definition
  definable_check

  yield "#{return_expression(func_name: qualified_name)} {"

  locals { |declaration| yield "  #{declaration}" }

  yield "  va_start( variadic_args, #{@params[-2].name} );" if variadic?

  if @wrapped.error_check?
    yield
    yield "  #{wrapped_call_expression};"
    yield
    @wrapped.error_check(return_val: return_variable) do |line|
      yield "  #{line}"
    end
  else
    yield "  #{wrapped_call_expression};"
  end

  yield '  va_end( variadic_args );' if variadic?

  if @return_type.self?
    yield '  return *this;'
  elsif @spec['return']['type'] != 'void' && !returns_call_directly?
    yield '  return return_val;'
  end

  yield '}'
end

#definition_includesObject

A list of includes needed for the definition of the function.



135
136
137
138
139
140
141
142
# File 'lib/wrapture/function_spec.rb', line 135

def definition_includes
  includes = @wrapped.includes
  includes.concat(@spec['return']['includes'])
  @params.each { |param| includes.concat(param.includes) }
  includes.concat(@return_type.includes)
  includes << 'stdarg.h' if variadic?
  includes.uniq
end

#docObject

A Comment holding the function documentation.



250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/wrapture/function_spec.rb', line 250

def doc
  comment = String.new
  comment << @spec['doc'] if @spec.key?('doc')

  @params
    .reject { |param| param.doc.empty? }
    .each { |param| comment << "\n\n" << param.doc.text }

  if @spec['return'].key?('doc')
    comment << "\n\n@return " << @spec['return']['doc']
  end

  Comment.new(comment)
end

#nameObject

The name of the function.



145
146
147
# File 'lib/wrapture/function_spec.rb', line 145

def name
  @spec['name']
end

#param_listObject

A string with the parameter list for this function.



150
151
152
# File 'lib/wrapture/function_spec.rb', line 150

def param_list
  ParamSpec.signature(@params, self)
end

#qualified_nameObject

The name of the function with the class name, if it exists.



155
156
157
158
159
160
161
# File 'lib/wrapture/function_spec.rb', line 155

def qualified_name
  if @owner.is_a?(ClassSpec)
    "#{@owner.name}::#{name}"
  else
    name
  end
end

#resolve_type(type) ⇒ Object

A resolved type, given a TypeSpec type. Resolved types will not have any keywords like equivalent-struct, which will be resolved to their effective type.



268
269
270
271
272
273
274
275
276
277
278
# File 'lib/wrapture/function_spec.rb', line 268

def resolve_type(type)
  if type.equivalent_struct?
    TypeSpec.new("struct #{@owner.struct_name}")
  elsif type.equivalent_pointer?
    TypeSpec.new("struct #{@owner.struct_name} *")
  elsif type.self?
    TypeSpec.new("#{@owner.name}&")
  else
    type
  end
end

#resolve_wrapped_param(param_spec) ⇒ Object

Gives an expression for calling a given parameter within this function. Equivalent structs and pointers are resolved, as well as casts between types if they are known within the scope of this function.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/wrapture/function_spec.rb', line 166

def resolve_wrapped_param(param_spec)
  used_param = @params.find { |p| p.name == param_spec['value'] }

  if param_spec['value'] == EQUIVALENT_STRUCT_KEYWORD
    @owner.this_struct
  elsif param_spec['value'] == EQUIVALENT_POINTER_KEYWORD
    @owner.this_struct_pointer
  elsif param_spec['value'] == '...'
    'variadic_args'
  elsif castable?(param_spec)
    param_class = @owner.type(used_param.type)
    param_class.cast(used_param.name,
                     param_spec['type'],
                     used_param.type)
  else
    param_spec['value']
  end
end

#return_expression(func_name: name) ⇒ Object

Calls return_expression on the return type of this function. func_name is passed to return_expression if provided.



187
188
189
190
191
192
193
# File 'lib/wrapture/function_spec.rb', line 187

def return_expression(func_name: name)
  if @constructor || @destructor
    signature(func_name: func_name)
  else
    resolved_return.return_expression(self, func_name: func_name)
  end
end

#signature(func_name: name) ⇒ Object

The signature of the function. func_name can be used to override the function name if needed, for example if a class name qualifier is needed.



197
198
199
# File 'lib/wrapture/function_spec.rb', line 197

def signature(func_name: name)
  "#{func_name}( #{param_list} )"
end

#variadic?Boolean

True if the function is variadic.

Returns:

  • (Boolean)


281
282
283
# File 'lib/wrapture/function_spec.rb', line 281

def variadic?
  @params.last&.variadic?
end

#virtual?Boolean

True if the function is virtual.

Returns:

  • (Boolean)


286
287
288
# File 'lib/wrapture/function_spec.rb', line 286

def virtual?
  @spec['virtual']
end