Class: Idl::FunctionDefAst

Inherits:
AstNode
  • Object
show all
Includes:
Declaration
Defined in:
lib/idl/ast.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, interval, name, targs, return_types, arguments, desc, body) ⇒ FunctionDefAst

Returns a new instance of FunctionDefAst.

Parameters:

  • input (String)

    The source code

  • interval (Range)

    The range in the source code for this function definition

  • name (String)

    The name of the function

  • targs (Array<AstNode>)

    Template arguments

  • arguments (Array<AstNode>)

    Arguments

  • desc (String)

    Description

  • body (AstNode, nil)

    Body, unless the function is builtin



4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
# File 'lib/idl/ast.rb', line 4882

def initialize(input, interval, name, targs, return_types, arguments, desc, body)
  if body.nil?
    super(input, interval, targs + return_types + arguments)
  else
    super(input, interval, targs + return_types + arguments + [body])
  end

  @name = name
  @targs = targs
  @return_type_nodes = return_types
  @argument_nodes = arguments
  @desc = desc
  @body = body

  @cached_return_type = {}
  @reachable_functions_cache ||= {}
end

Instance Attribute Details

#reachable_functions_cacheObject (readonly)

Returns the value of attribute reachable_functions_cache.



4900
4901
4902
# File 'lib/idl/ast.rb', line 4900

def reachable_functions_cache
  @reachable_functions_cache
end

Instance Method Details

#add_symbol(symtab) ⇒ Object

Add symbol(s) at the outermost scope of the symbol table

Parameters:

  • symtab (SymbolTable)

    Symbol table at the scope that the symbol(s) will be inserted



5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
# File 'lib/idl/ast.rb', line 5102

def add_symbol(symtab)
  internal_error "Functions should be declared at global scope" unless symtab.levels == 1

  # now add the function in global scope
  def_type = FunctionType.new(
    name,
    self,
    symtab
  )

  symtab.add!(name, def_type)
end

#arguments(symtab) ⇒ Array<Array(Type,String)>

Returns containing the argument types and names, in order.

Returns:

  • (Array<Array(Type,String)>)

    containing the argument types and names, in order



4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
# File 'lib/idl/ast.rb', line 4930

def arguments(symtab)
  return @arglist unless @arglist.nil?

  if templated?
    template_names.each do |tname|
      internal_error "Template values missing in symtab" unless symtab.get(tname)
    end
  end

  return EMPTY_ARRAY if @argument_nodes.empty?

  arglist = []

  @argument_nodes.each do |a|
    atype = a.type(symtab)
    type_error "No type for #{a.text_value}" if atype.nil?

    atype = atype.ref_type if atype.kind == :enum

    arglist << [atype, a.name]
  end

  arglist.freeze
  unless templated?
    @arglist = arglist
  end
  arglist
end

#arguments_list_strObject

returns an array of arguments, as a string function (or template instance) does not need to be resolved



4961
4962
4963
# File 'lib/idl/ast.rb', line 4961

def arguments_list_str
  @argument_nodes.map(&:text_value)
end

#bodyObject



5153
5154
5155
5156
5157
# File 'lib/idl/ast.rb', line 5153

def body
  internal_error "Function has no body" if builtin?

  @body
end

#builtin?Boolean

Returns:

  • (Boolean)


5159
5160
5161
# File 'lib/idl/ast.rb', line 5159

def builtin?
  @body.nil?
end

#descriptionString

Returns Asciidoc formatted function description.

Returns:

  • (String)

    Asciidoc formatted function description



4915
4916
4917
# File 'lib/idl/ast.rb', line 4915

def description
  unindent(@desc)
end

#freeze_tree(global_symtab) ⇒ Object

freeze the entire tree from further modification This is also an opportunity to pre-calculate anything that only needs global symbols

Parameters:

  • global_symtab (SymbolTable)

    Symbol table with global scope populated



4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
# File 'lib/idl/ast.rb', line 4903

def freeze_tree(global_symtab)
  return if frozen?

  unless templated?
    arguments(global_symtab)
  end

  @children.each { |child| child.freeze_tree(global_symtab) }
  freeze
end

#nameObject



5035
5036
5037
# File 'lib/idl/ast.rb', line 5035

def name
  @name
end

#num_argsInteger

Returns The number of arguments to the function.

Returns:

  • (Integer)

    The number of arguments to the function



4925
4926
4927
# File 'lib/idl/ast.rb', line 4925

def num_args
  @argument_nodes.size
end

#return_type(symtab) ⇒ Object

return the return type, which may be a tuple of multiple types



4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
# File 'lib/idl/ast.rb', line 4966

def return_type(symtab)
  cached = @cached_return_type[symtab.cfg_arch]
  return cached unless cached.nil?

  unless symtab.levels == 2
    internal_error "Function bodies should be at global + 1 scope (at global + #{symtab.levels - 1})"
  end

  if @return_type_nodes.empty?
    @cached_return_type[symtab.cfg_arch] = VoidType
    return VoidType
  end

  unless templated?
    # with no templates, the return type does not change for a given cfg_arch
    rtype =
      if @return_type_nodes.size == 1
        rtype = @return_type_nodes[0].type(symtab)
        rtype = rtype.ref_type if rtype.kind == :enum
        rtype
      else
        tuple_types = @return_type_nodes.map do |r|
          rtype = r.type(symtab)
          rtype = rtype.ref_type if rtype.kind == :enum
          rtype
        end

        Type.new(:tuple, tuple_types:)
      end

    raise "??????" if rtype.nil?

    return @cached_return_type[symtab.cfg_arch] = rtype
  end

  if templated?
    template_names.each do |tname|
      internal_error "Template values missing" unless symtab.get(tname)
    end
  end



  if @return_type_nodes.size == 1
    rtype = @return_type_nodes[0].type(symtab)
    rtype = rtype.ref_type if rtype.kind == :enum
    rtype
  else

    tuple_types = @return_type_nodes.map do |r|
      rtype = r.type(symtab)
      rtype = rtype.ref_type if rtype.kind == :enum
      rtype
    end

    Type.new(:tuple, tuple_types:)
  end
end

#return_type_list_strArray<String>

function (or template instance) does not need to be resolved

Returns:

  • (Array<String>)

    return type strings



5027
5028
5029
5030
5031
5032
5033
# File 'lib/idl/ast.rb', line 5027

def return_type_list_str
  if @return_type_nodes.empty?
    ["void"]
  else
    @return_type_nodes.map(&:text_value)
  end
end

#template_namesArray<String>

Returns Template argument names, in order.

Returns:

  • (Array<String>)

    Template argument names, in order



5116
5117
5118
# File 'lib/idl/ast.rb', line 5116

def template_names
  @targs.map(&:name)
end

#template_types(symtab) ⇒ Array<Type>

Returns Template argument types, in order.

Parameters:

Returns:

  • (Array<Type>)

    Template argument types, in order



5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
# File 'lib/idl/ast.rb', line 5122

def template_types(symtab)
  return EMPTY_ARRAY unless templated?

  ttypes = []
  @targs.each do |a|
    ttype = a.type(symtab)
    ttype = ttype.ref_type if ttype.kind == :enum
    ttypes << ttype.clone.make_const
  end
  ttypes
end

#templated?Boolean

Returns whether or not the function is templated.

Returns:

  • (Boolean)

    whether or not the function is templated



4920
4921
4922
# File 'lib/idl/ast.rb', line 4920

def templated?
  !@targs.empty?
end

#type_check(symtab) ⇒ void

This method returns an undefined value.

type check this node and all children

Calls to #type and/or #value may depend on type_check being called first with the same symtab. If not, those functions may raise an AstNode::InternalError

Parameters:

Raises:



5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
# File 'lib/idl/ast.rb', line 5075

def type_check(symtab)
  internal_error "Functions must be declared at global scope (at #{symtab.levels})" unless symtab.levels == 1

  type_check_targs(symtab)

  symtab = symtab.deep_clone
  symtab.push(self)
  template_names.each_with_index do |tname, index|
    symtab.add(tname, Var.new(tname, template_types(symtab)[index]))
  end

  type_check_return(symtab)

  arguments(symtab).each do |arg_type, arg_name|
    symtab.add(arg_name, Var.new(arg_name, arg_type))
  end
  type_check_args(symtab)


  # template functions are checked as they are called
  unless templated?
    type_check_body(symtab)
  end
  symtab.pop
end

#type_check_args(symtab) ⇒ Object



5143
5144
5145
# File 'lib/idl/ast.rb', line 5143

def type_check_args(symtab)
  @argument_nodes.each { |a| a.type_check(symtab, false) }
end

#type_check_body(symtab) ⇒ Object



5147
5148
5149
5150
5151
# File 'lib/idl/ast.rb', line 5147

def type_check_body(symtab)
  return if @body.nil?

  @body.type_check(symtab)
end

#type_check_from_call(symtab) ⇒ Object

we do lazy type checking of the function body so that we never check uncalled functions, which avoids dealing with mentions of CSRs that may not exist in a given implementation



5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
# File 'lib/idl/ast.rb', line 5058

def type_check_from_call(symtab)
  internal_error "Function definitions should be at global + 1 scope" unless symtab.levels == 2

  type_check_return(symtab)
  type_check_args(symtab)
  # @argument_nodes.each do |a|
  #   value_result = value_try do
  #     symtab.add(a.name, Var.new(a.name, a.type(symtab), a.value(symtab)))
  #   end
  #   value_else(value_result) do
  #     symtab.add(a.name, Var.new(a.name, a.type(symtab)))
  #   end
  # end
  type_check_body(symtab)
end

#type_check_return(symtab) ⇒ Object



5139
5140
5141
# File 'lib/idl/ast.rb', line 5139

def type_check_return(symtab)
  @return_type_nodes.each { |r| r.type_check(symtab) }
end

#type_check_targs(symtab) ⇒ Object



5134
5135
5136
5137
# File 'lib/idl/ast.rb', line 5134

def type_check_targs(symtab)
  @targs.each { |t| type_error "Template arguments must be uppercase" unless t.text_value[0] == t.text_value[0].upcase }
  @targs.each { |t| type_error "Template types must be integral" unless t.type(symtab).integral? }
end

#type_check_template_instance(symtab) ⇒ Object

Parameters:

  • template (Array<Integer>)

    values to apply



5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
# File 'lib/idl/ast.rb', line 5040

def type_check_template_instance(symtab)
  internal_error "Function definitions should be at global + 1 scope" unless symtab.levels == 2

  internal_error "Not a template function" unless templated?

  template_names.each do |tname|
    internal_error "Template values missing" unless symtab.get(tname)
  end

  type_check_return(symtab)
  type_check_args(symtab)
  @argument_nodes.each { |a| symtab.add(a.name, Var.new(a.name, a.type(symtab))) }
  type_check_body(symtab)
end