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() { |line| ... } click to toggle source

Yields each line of the declaration of the function, including any documentation.

# File lib/wrapture/function_spec.rb, line 203
def declaration
  doc.format_as_doxygen(max_line_length: 76) { |line| yield line }

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

  yield "#{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?

  yield '  return *this;' if @return_type.self?

  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 246
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 264
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 277
def variadic?
  @params.last&.variadic?
end
virtual?() click to toggle source

True if the function is virtual.

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