示例#1
0
def check_types(ir):
    """Checks that expressions within the IR have the correct top-level types.

  check_types ensures that expressions at the top level have correct types; in
  particular, it ensures that array sizes are integers ("UInt[true]" is not a
  valid array type) and that the starts and ends of ranges are integers.

  Arguments:
      ir: an IR to type check.

  Returns:
      A (possibly empty) list of errors.
  """
    errors = []
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.FieldLocation],
                                          _type_check_field_location,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.ArrayType, ir_pb2.Expression],
        _type_check_array_size,
        skip_descendants_of={ir_pb2.AtomicType},
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Field],
        _type_check_field_existence_condition,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.RuntimeParameter],
                                          _type_check_parameter,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.AtomicType],
                                          _type_check_passed_parameters,
                                          parameters={"errors": errors})
    return errors
示例#2
0
def _find_dependencies(ir):
    """Constructs a dependency graph for the entire IR."""
    dependencies = {}
    errors = []
    traverse_ir.fast_traverse_ir_top_down(
        ir,
        [ir_pb2.Reference],
        _add_reference_to_dependencies,
        # TODO(bolms): Add handling for references inside of attributes, once
        # there are attributes with non-constant values.
        skip_descendants_of={
            ir_pb2.AtomicType, ir_pb2.Attribute, ir_pb2.FieldReference
        },
        incidental_actions={
            ir_pb2.Field: _add_name_to_dependencies,
            ir_pb2.EnumValue: _add_name_to_dependencies,
            ir_pb2.RuntimeParameter: _add_name_to_dependencies,
        },
        parameters={
            "dependencies": dependencies,
            "errors": errors,
        })
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.FieldReference],
        _add_field_reference_to_dependencies,
        skip_descendants_of={ir_pb2.Attribute},
        incidental_actions={
            ir_pb2.Field: _add_name_to_dependencies,
            ir_pb2.EnumValue: _add_name_to_dependencies,
            ir_pb2.RuntimeParameter: _add_name_to_dependencies,
        },
        parameters={"dependencies": dependencies})
    return dependencies, errors
示例#3
0
 def test_filter_on_type_in_type(self):
     constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR,
         [ir_pb2.Function, ir_pb2.Expression, ir_pb2.NumericConstant],
         _record_constant,
         parameters={"constant_list": constants})
     self.assertEqual([1, 1], constants)
示例#4
0
 def test_filter_on_type(self):
     constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.NumericConstant],
         _record_constant,
         parameters={"constant_list": constants})
     self.assertEqual(
         _count_entries([0, 8, 8, 8, 16, 24, 32, 16, 32, 320, 1, 1, 1, 64]),
         _count_entries(constants))
示例#5
0
 def test_filter_on_not_type(self):
     notstruct_constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.NumericConstant],
         _record_constant,
         skip_descendants_of=(ir_pb2.Structure, ),
         parameters={"constant_list": notstruct_constants})
     self.assertEqual(_count_entries([1, 1, 1, 64]),
                      _count_entries(notstruct_constants))
示例#6
0
def set_write_methods(ir):
  """Sets the write_method member of all ir_pb2.Fields in ir.

  Arguments:
      ir: The IR to which to add write_methods.

  Returns:
      A list of errors, or an empty list.
  """
  traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Field], _add_write_method)
  return []
示例#7
0
 def test_field_is_populated(self):
     constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.Field, ir_pb2.NumericConstant],
         _record_field_name_and_constant,
         parameters={"constant_list": constants})
     self.assertEqual(
         _count_entries([("field1", 0), ("field1", 8), ("field2", 8),
                         ("field2", 8), ("field2", 16), ("bar_field1", 24),
                         ("bar_field1", 32), ("bar_field2", 16),
                         ("bar_field2", 32), ("bar_field2", 320)]),
         _count_entries(constants))
示例#8
0
def synthesize_fields(ir):
    """Adds synthetic fields to all structures.

  Adds aliases for all fields in anonymous `bits` to the enclosing structure.

  Arguments:
      ir: The IR to which to add fields.

  Returns:
      A list of errors, or an empty list.
  """
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure],
                                          _add_virtuals_to_structure)
    return []
示例#9
0
 def test_type_definition_is_populated(self):
     constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.NumericConstant],
         _record_kind_and_constant,
         parameters={"constant_list": constants})
     self.assertEqual(
         _count_entries([("structure", 0), ("structure", 8),
                         ("structure", 8), ("structure", 8),
                         ("structure", 16), ("structure", 24),
                         ("structure", 32), ("structure", 16),
                         ("structure", 32), ("structure", 320),
                         ("enumeration", 1), ("enumeration", 1),
                         ("enumeration", 1), ("external", 64)]),
         _count_entries(constants))
示例#10
0
 def test_filter_on_type_star_type(self):
     struct_constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.Structure, ir_pb2.NumericConstant],
         _record_constant,
         parameters={"constant_list": struct_constants})
     self.assertEqual(_count_entries([0, 8, 8, 8, 16, 24, 32, 16, 32, 320]),
                      _count_entries(struct_constants))
     enum_constants = []
     traverse_ir.fast_traverse_ir_top_down(
         _EXAMPLE_IR, [ir_pb2.Enum, ir_pb2.NumericConstant],
         _record_constant,
         parameters={"constant_list": enum_constants})
     self.assertEqual(_count_entries([1, 1, 1]),
                      _count_entries(enum_constants))
示例#11
0
def _find_dependency_ordering_for_fields(ir):
    """Populates the fields_in_dependency_order fields throughout ir."""
    dependencies = {}
    # TODO(bolms): This duplicates work in _find_dependencies that could be
    # shared.
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.FieldReference],
        _add_field_reference_to_dependencies,
        skip_descendants_of={ir_pb2.Attribute},
        incidental_actions={
            ir_pb2.Field: _add_name_to_dependencies,
            ir_pb2.EnumValue: _add_name_to_dependencies,
            ir_pb2.RuntimeParameter: _add_name_to_dependencies,
        },
        parameters={"dependencies": dependencies})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Structure],
        _find_dependency_ordering_for_fields_in_structure,
        parameters={"dependencies": dependencies})
示例#12
0
def resolve_field_references(ir):
    """Resolves structure member accesses ("field.subfield") in ir."""
    errors = []
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.FieldReference],
        _resolve_field_reference,
        incidental_actions={
            ir_pb2.TypeDefinition: _set_visible_scopes_for_type_definition,
            ir_pb2.Module: _set_visible_scopes_for_module,
            ir_pb2.Field: lambda f: {
                "field": f
            },
            ir_pb2.Attribute: _set_visible_scopes_for_attribute,
        },
        parameters={
            "errors": errors,
            "field": None
        })
    return errors
示例#13
0
def compute_constants(ir):
    """Computes constant values for all expressions in ir.

  compute_constants calculates all constant values and adds them to the type
  information for each expression and subexpression.

  Arguments:
      ir: an IR on which to compute constants

  Returns:
      A (possibly empty) list of errors.
  """
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Expression],
        compute_constraints_of_expression,
        skip_descendants_of={ir_pb2.Expression})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.RuntimeParameter],
        _compute_constraints_of_parameter,
        skip_descendants_of={ir_pb2.Expression})
    return []
示例#14
0
    def test_keyword_args_dict_in_action(self):
        call_counts = {"populated": 0, "not": 0}

        def check_field_is_populated(node, **kwargs):
            del node  # Unused.
            self.assertTrue(kwargs["field"])
            call_counts["populated"] += 1

        def check_field_is_not_populated(node, **kwargs):
            del node  # Unused.
            self.assertFalse("field" in kwargs)
            call_counts["not"] += 1

        traverse_ir.fast_traverse_ir_top_down(_EXAMPLE_IR,
                                              [ir_pb2.Field, ir_pb2.Type],
                                              check_field_is_populated)
        self.assertEqual(7, call_counts["populated"])

        traverse_ir.fast_traverse_ir_top_down(_EXAMPLE_IR,
                                              [ir_pb2.Enum, ir_pb2.EnumValue],
                                              check_field_is_not_populated)
        self.assertEqual(2, call_counts["not"])
示例#15
0
def _verify_attributes_on_ir(ir):
  """Verifies attributes in a complete IR."""
  errors = []
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Structure], _verify_size_attributes_on_structure,
      parameters={"errors": errors})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Enum], _verify_width_attribute_on_enum,
      parameters={"errors": errors})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.External], _verify_addressable_unit_attribute_on_external,
      parameters={"errors": errors})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Field], _verify_field_attributes,
      parameters={"errors": errors})
  return errors
示例#16
0
def desugar(ir):
    """Translates pure syntactic sugar to its desugared form.

  Replaces `$next` symbols with the start+length of the previous physical
  field.

  Adds aliases for all fields in anonymous `bits` to the enclosing structure.

  Arguments:
      ir: The IR to desugar.

  Returns:
      A list of errors, or an empty list.
  """
    errors = []
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure],
                                          _replace_next_keyword,
                                          parameters={"errors": errors})
    if errors:
        return errors
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure],
                                          _add_virtuals_to_structure)
    return []
示例#17
0
    def test_pass_only_to_sub_nodes(self):
        constants = []

        def pass_location_down(field):
            return {
                "location": (int(field.location.start.constant.value),
                             int(field.location.size.constant.value))
            }

        traverse_ir.fast_traverse_ir_top_down(
            _EXAMPLE_IR, [ir_pb2.NumericConstant],
            _record_location_parameter_and_constant,
            incidental_actions={ir_pb2.Field: pass_location_down},
            parameters={
                "constant_list": constants,
                "location": None
            })
        self.assertEqual(
            _count_entries([((0, 8), 0), ((0, 8), 8), ((8, 16), 8),
                            ((8, 16), 8), ((8, 16), 16), ((24, 32), 24),
                            ((24, 32), 32), ((32, 320), 16), ((32, 320), 32),
                            ((32, 320), 320), (None, 1), (None, 1), (None, 1),
                            (None, 64)]), _count_entries(constants))
示例#18
0
def _resolve_symbols_from_table(ir, table):
    """Resolves all references in the given IR, given the constructed table."""
    errors = []
    # Symbol resolution is broken into five passes.  First, this code resolves any
    # imports, and adds import aliases to modules.
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Import],
        _add_import_to_scope,
        incidental_actions={
            ir_pb2.Module: lambda m, table: {
                "module": table[m.source_file_name]
            },
        },
        parameters={
            "errors": errors,
            "table": table
        })
    if errors:
        return errors
    # Next, this resolves all absolute references (e.g., it resolves "UInt" in
    # "0:1  UInt  field" to [prelude]::UInt).
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Reference],
        _resolve_reference,
        skip_descendants_of=(ir_pb2.FieldReference, ),
        incidental_actions={
            ir_pb2.TypeDefinition: _set_visible_scopes_for_type_definition,
            ir_pb2.Module: _set_visible_scopes_for_module,
            ir_pb2.Field: lambda f: {
                "field": f
            },
            ir_pb2.Attribute: _set_visible_scopes_for_attribute,
        },
        parameters={
            "table": table,
            "errors": errors,
            "field": None
        })
    # Lastly, head References to fields (e.g., the `a` of `a.b.c`) are resolved.
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.FieldReference],
        _resolve_head_of_field_reference,
        incidental_actions={
            ir_pb2.TypeDefinition: _set_visible_scopes_for_type_definition,
            ir_pb2.Module: _set_visible_scopes_for_module,
            ir_pb2.Field: lambda f: {
                "field": f
            },
            ir_pb2.Attribute: _set_visible_scopes_for_attribute,
        },
        parameters={
            "table": table,
            "errors": errors,
            "field": None
        })
    return errors
示例#19
0
def annotate_types(ir):
    """Adds type annotations to all expressions in ir.

  annotate_types adds type information to all expressions (and subexpressions)
  in the IR.  Additionally, it checks expressions for internal type consistency:
  it will generate an error for constructs like "1 + true", where the types of
  the operands are not accepted by the operator.

  Arguments:
      ir: an IR to which to add type annotations

  Returns:
      A (possibly empty) list of errors.
  """
    errors = []
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Expression],
        _type_check_expression,
        skip_descendants_of={ir_pb2.Expression},
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.RuntimeParameter],
                                          _annotate_parameter_type,
                                          parameters={"errors": errors})
    return errors
示例#20
0
def _add_missing_attributes_on_ir(ir):
  """Adds missing attributes in a complete IR."""
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.External], _add_addressable_unit_to_external)
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Enum], _add_missing_width_and_sign_attributes_on_enum)
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Structure], _add_missing_size_attributes_on_structure,
      incidental_actions={
          ir_pb2.Module: _gather_default_attributes,
          ir_pb2.TypeDefinition: _gather_default_attributes,
          ir_pb2.Field: _gather_default_attributes,
      },
      parameters={"defaults": {}})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Field], _add_missing_byte_order_attribute_on_field,
      incidental_actions={
          ir_pb2.Module: _gather_default_attributes,
          ir_pb2.TypeDefinition: _gather_default_attributes,
          ir_pb2.Field: _gather_default_attributes,
      },
      parameters={"defaults": {}})
  return []
示例#21
0
def check_constraints(ir):
    """Checks miscellaneous validity constraints in ir.

  Checks that auto array sizes are only used for the outermost size of
  multidimensional arrays.  That is, Type[3][] is OK, but Type[][3] is not.

  Checks that fixed-size fields are a correct size to hold statically-sized
  types.

  Checks that inner array dimensions are constant.

  Checks that only constant-size types are used in arrays.

  Arguments:
    ir: An ir_pb2.EmbossIr object to check.

  Returns:
    A list of ConstraintViolations, or an empty list if there are none.
  """
    errors = []
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure, ir_pb2.Type],
                                          _check_allowed_in_bits,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        # TODO(bolms): look for [ir_pb2.ArrayType], [ir_pb2.AtomicType], and
        # simplify _check_that_array_base_types_are_fixed_size.
        ir,
        [ir_pb2.ArrayType],
        _check_that_array_base_types_are_fixed_size,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Structure, ir_pb2.ArrayType],
        _check_that_array_base_types_in_structs_are_multiples_of_bytes,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.ArrayType, ir_pb2.ArrayType],
        _check_that_inner_array_dimensions_are_constant,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure],
                                          _check_size_of_bits,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Structure, ir_pb2.Type],
                                          _check_type_requirements_for_field,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Field],
                                          _check_field_name_for_reserved_words,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.EnumValue],
                                          _check_enum_name_for_reserved_words,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.TypeDefinition],
                                          _check_type_name_for_reserved_words,
                                          parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Expression],
        _check_constancy_of_constant_references,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Enum],
        _check_that_enum_values_are_representable,
        parameters={"errors": errors})
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.Expression],
        _check_bounds_on_runtime_integer_expressions,
        incidental_actions={ir_pb2.Attribute: lambda a: {
            "in_attribute": a
        }},
        skip_descendants_of={ir_pb2.EnumValue, ir_pb2.Expression},
        parameters={
            "errors": errors,
            "in_attribute": None
        })
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.RuntimeParameter],
        _check_type_requirements_for_parameter_type,
        parameters={"errors": errors})
    return errors
示例#22
0
def _construct_symbol_tables(ir):
    """Constructs per-module symbol tables for each module in ir."""
    symbol_tables = {}
    errors = []
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Module],
                                          _add_module_to_scope,
                                          parameters={
                                              "errors": errors,
                                              "scope": symbol_tables
                                          })
    traverse_ir.fast_traverse_ir_top_down(
        ir, [ir_pb2.TypeDefinition],
        _add_type_name_to_scope,
        incidental_actions={ir_pb2.Module: _set_scope_for_module},
        parameters={
            "errors": errors,
            "scope": symbol_tables
        })
    if errors:
        # Ideally, we would find duplicate field names elsewhere in the module, even
        # if there are duplicate type names, but field/enum names in the colliding
        # types also end up colliding, leading to spurious errors.  E.g., if you
        # have two `struct Foo`s, then the field check will also discover a
        # collision for `$size_in_bytes`, since there are two `Foo.$size_in_bytes`.
        return symbol_tables, errors

    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.EnumValue],
                                          _add_enum_value_to_scope,
                                          incidental_actions={
                                              ir_pb2.Module:
                                              _set_scope_for_module,
                                              ir_pb2.TypeDefinition:
                                              _set_scope_for_type_definition,
                                          },
                                          parameters={
                                              "errors": errors,
                                              "scope": symbol_tables
                                          })
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.Field],
                                          _add_struct_field_to_scope,
                                          incidental_actions={
                                              ir_pb2.Module:
                                              _set_scope_for_module,
                                              ir_pb2.TypeDefinition:
                                              _set_scope_for_type_definition,
                                          },
                                          parameters={
                                              "errors": errors,
                                              "scope": symbol_tables
                                          })
    traverse_ir.fast_traverse_ir_top_down(ir, [ir_pb2.RuntimeParameter],
                                          _add_parameter_name_to_scope,
                                          incidental_actions={
                                              ir_pb2.Module:
                                              _set_scope_for_module,
                                              ir_pb2.TypeDefinition:
                                              _set_scope_for_type_definition,
                                          },
                                          parameters={
                                              "errors": errors,
                                              "scope": symbol_tables
                                          })
    return symbol_tables, errors
示例#23
0
def _check_attributes_in_ir(ir):
  """Performs basic checks on all attributes in the given ir.

  This function calls _check_attributes on each attribute list in ir.

  Arguments:
    ir: An ir_pb2.EmbossIr to check.

  Returns:
    A list of lists of error.error, or an empty list if there were no errors.
  """

  def check_module(module, errors):
    errors.extend(_check_attributes(
        module.attribute, _MODULE_ATTRIBUTES, "module '{}'".format(
            module.source_file_name), module.source_file_name))

  def check_type_definition(type_definition, source_file_name, errors):
    if type_definition.HasField("structure"):
      if type_definition.addressable_unit == ir_pb2.TypeDefinition.BYTE:
        errors.extend(_check_attributes(
            type_definition.attribute, _STRUCT_ATTRIBUTES, "struct '{}'".format(
                type_definition.name.name.text), source_file_name))
      elif type_definition.addressable_unit == ir_pb2.TypeDefinition.BIT:
        errors.extend(_check_attributes(
            type_definition.attribute, _BITS_ATTRIBUTES, "bits '{}'".format(
                type_definition.name.name.text), source_file_name))
      else:
        assert False, "Unexpected addressable_unit '{}'".format(
            type_definition.addressable_unit)
    elif type_definition.HasField("enumeration"):
      errors.extend(_check_attributes(
          type_definition.attribute, _ENUM_ATTRIBUTES, "enum '{}'".format(
              type_definition.name.name.text), source_file_name))
    elif type_definition.HasField("external"):
      errors.extend(_check_attributes(
          type_definition.attribute, _EXTERNAL_ATTRIBUTES,
          "external '{}'".format(
              type_definition.name.name.text), source_file_name))

  def check_struct_field(field, source_file_name, errors):
    if ir_util.field_is_virtual(field):
      field_attributes = _STRUCT_VIRTUAL_FIELD_ATTRIBUTES
      field_adjective = "virtual "
    else:
      field_attributes = _STRUCT_PHYSICAL_FIELD_ATTRIBUTES
      field_adjective = ""
    errors.extend(_check_attributes(
        field.attribute, field_attributes,
        "{}struct field '{}'".format(field_adjective, field.name.name.text),
        source_file_name))

  errors = []
  # TODO(bolms): Add a check that only known $default'ed attributes are
  # used.
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Module], check_module,
      parameters={"errors": errors})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.TypeDefinition], check_type_definition,
      parameters={"errors": errors})
  traverse_ir.fast_traverse_ir_top_down(
      ir, [ir_pb2.Field], check_struct_field,
      parameters={"errors": errors})
  return errors