class Wrapture::FunctionSpec

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

Attributes

return_type[R]

A TypeSpec describing the return type of this function.

Public Class Methods

new(spec, owner = Scope.new, constructor: false, destructor: false) click to toggle source

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.

# 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
normalize_return_hash(spec) click to toggle source

Returns a copy of the return type specification spec.

# 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) click to toggle source

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).

# 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

Public Instance Methods

constructor?() click to toggle source

True if the function is a constructor, false otherwise.

# File lib/wrapture/function_spec.rb, line 115
def constructor?
  @constructor
end
declaration(&block) click to toggle source

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

# 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_includes() click to toggle source

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

# 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?() click to toggle source

True if this function can be defined.

# File lib/wrapture/function_spec.rb, line 128
def definable?
  definable_check
rescue UndefinableSpec
  false
end
definition() { |"#{return_expression(func_name: qualified_name)} {"| ... } click to toggle source

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

# 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_includes() click to toggle source

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

# 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
doc() click to toggle source

A Comment holding the function documentation.

# 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
name() click to toggle source

The name of the function.

# File lib/wrapture/function_spec.rb, line 145
def name
  @spec['name']
end
param_list() click to toggle source

A string with the parameter list for this function.

# File lib/wrapture/function_spec.rb, line 150
def param_list
  ParamSpec.signature(@params, self)
end
qualified_name() click to toggle source

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

# 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) click to toggle source

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.

# 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) click to toggle source

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.

# 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) click to toggle source

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

# 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) click to toggle source

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.

# File lib/wrapture/function_spec.rb, line 197
def signature(func_name: name)
  "#{func_name}( #{param_list} )"
end
variadic?() click to toggle source

True if the function is variadic.

# File lib/wrapture/function_spec.rb, line 281
def variadic?
  @params.last&.variadic?
end
virtual?() click to toggle source

True if the function is virtual.

# File lib/wrapture/function_spec.rb, line 286
def virtual?
  @spec['virtual']
end

Private Instance Methods

capture_return?() click to toggle source

True if the return value of the wrapped call needs to be captured in a local variable.

# File lib/wrapture/function_spec.rb, line 294
def capture_return?
  !@constructor &&
    @wrapped.use_return? || returns_return_val?
end
castable?(wrapped_param) click to toggle source

True if the provided wrapped param spec can be cast to when used in this function.

# File lib/wrapture/function_spec.rb, line 301
def castable?(wrapped_param)
  param = @params.find { |p| p.name == wrapped_param['value'] }

  !param.nil? &&
    !wrapped_param['type'].nil? &&
    @owner.type?(param.type)
end
definable_check() click to toggle source

Raises an exception if this function cannot be defined as is. Returns true otherwise.

# File lib/wrapture/function_spec.rb, line 311
def definable_check
  if @wrapped.nil?
    raise UndefinableSpec, 'no wrapped function was specified'
  end

  true
end
locals() { |'va_list variadic_args;'| ... } click to toggle source

Yields a declaration of each local variable used by the function.

# File lib/wrapture/function_spec.rb, line 320
def locals
  yield 'va_list variadic_args;' if variadic?

  if capture_return?
    wrapped_type = resolve_type(@wrapped.return_val_type)
    yield "#{wrapped_type.variable('return_val')};"
  end
end
resolved_return() click to toggle source

The resolved type of the return type.

# File lib/wrapture/function_spec.rb, line 330
def resolved_return
  @return_type.resolve(self)
end
return_cast(value) click to toggle source

The function to use to create the return value of the function.

# File lib/wrapture/function_spec.rb, line 335
def return_cast(value)
  if @return_type == @wrapped.return_val_type
    value
  elsif @spec['return']['overloaded']
    "new#{@spec['return']['type'].chomp('*').strip} ( #{value} )"
  else
    resolved_return.cast_expression(value)
  end
end
return_variable() click to toggle source

The name of the variable holding the return value.

# File lib/wrapture/function_spec.rb, line 346
def return_variable
  if @constructor
    'this->equivalent'
  else
    'return_val'
  end
end
returns_call_directly?() click to toggle source

True if the function returns the result of the wrapped function call directly without any after actions.

# File lib/wrapture/function_spec.rb, line 356
def returns_call_directly?
  !@constructor &&
    !@destructor &&
    !%w[void self-reference].include?(@spec['return']['type']) &&
    !@wrapped.error_check?
end
returns_return_val?() click to toggle source

True if the function returns the return_val variable.

# File lib/wrapture/function_spec.rb, line 364
def returns_return_val?
  !@return_type.self? &&
    @spec['return']['type'] != 'void' &&
    !returns_call_directly?
end
wrapped_call_expression() click to toggle source

The expression containing the call to the underlying wrapped function.

# File lib/wrapture/function_spec.rb, line 371
def wrapped_call_expression
  call = @wrapped.call_from(self)

  if @constructor
    "this->equivalent = #{call}"
  elsif @wrapped.error_check?
    "return_val = #{call}"
  elsif returns_call_directly?
    "return #{return_cast(call)}"
  else
    call
  end
end