Esempio n. 1
0
def _compute_constraints_of_field_reference(expression, ir):
    """Computes the constraints of a reference to a structure's field."""
    field_path = expression.field_reference.path[-1]
    field = ir_util.find_object(field_path, ir)
    if isinstance(field, ir_pb2.Field) and ir_util.field_is_virtual(field):
        # References to virtual fields should have the virtual field's constraints
        # copied over.
        compute_constraints_of_expression(field.read_transform, ir)
        expression.type.CopyFrom(field.read_transform.type)
        return
    # Non-virtual non-integer fields do not (yet) have constraints.
    if expression.type.WhichOneof("type") == "integer":
        # TODO(bolms): These lines will need to change when support is added for
        # fixed-point types.
        expression.type.integer.modulus = "1"
        expression.type.integer.modular_value = "0"
        type_definition = ir_util.find_parent_object(field_path, ir)
        if isinstance(field, ir_pb2.Field):
            referrent_type = field.type
        else:
            referrent_type = field.physical_type_alias
        if referrent_type.HasField("size_in_bits"):
            type_size = ir_util.constant_value(referrent_type.size_in_bits)
        else:
            field_size = ir_util.constant_value(field.location.size)
            if field_size is None:
                type_size = None
            else:
                type_size = field_size * type_definition.addressable_unit
        assert referrent_type.HasField("atomic_type"), field
        assert not referrent_type.atomic_type.reference.canonical_name.module_file
        _set_integer_constraints_from_physical_type(expression, referrent_type,
                                                    type_size)
Esempio n. 2
0
def _verify_requires_attribute_on_field(field, source_file_name, ir, errors):
  """Verifies that [requires] is valid on the given field."""
  requires_attr = ir_util.get_attribute(field.attribute, attributes.REQUIRES)
  if not requires_attr:
    return
  if ir_util.field_is_virtual(field):
    field_expression_type = field.read_transform.type
  else:
    if not field.type.HasField("atomic_type"):
      errors.append([
          error.error(source_file_name, requires_attr.source_location,
                      "Attribute 'requires' is only allowed on integer, "
                      "enumeration, or boolean fields, not arrays."),
          error.note(source_file_name, field.type.source_location,
                     "Field type."),
      ])
      return
    field_type = ir_util.find_object(field.type.atomic_type.reference, ir)
    assert field_type, "Field type should be non-None after name resolution."
    field_expression_type = (
        type_check.unbounded_expression_type_for_physical_type(field_type))
  if field_expression_type.WhichOneof("type") not in (
      "integer", "enumeration", "boolean"):
    errors.append([error.error(
        source_file_name, requires_attr.source_location,
        "Attribute 'requires' is only allowed on integer, enumeration, or "
        "boolean fields.")])
Esempio n. 3
0
def _add_size_virtuals(structure, type_definition):
    """Adds a $size_in_bits or $size_in_bytes virtual field to structure."""
    names = {
        ir_pb2.TypeDefinition.BIT: "$size_in_bits",
        ir_pb2.TypeDefinition.BYTE: "$size_in_bytes",
    }
    size_field_name = names[type_definition.addressable_unit]
    size_clauses = []
    for field in structure.field:
        # Virtual fields do not have a physical location, and thus do not contribute
        # to the size of the structure.
        if ir_util.field_is_virtual(field):
            continue
        size_clause = ir_pb2.Expression()
        size_clause.CopyFrom(_SIZE_CLAUSE_SKELETON)
        # Copy the appropriate clauses into `existence_condition ? start + size : 0`
        size_clause.function.args[0].CopyFrom(field.existence_condition)
        size_clause.function.args[1].function.args[0].CopyFrom(
            field.location.start)
        size_clause.function.args[1].function.args[1].CopyFrom(
            field.location.size)
        size_clauses.append(size_clause)
    size_expression = ir_pb2.Expression()
    size_expression.CopyFrom(_SIZE_SKELETON)
    size_expression.function.args.extend(size_clauses)
    _mark_as_synthetic(size_expression)
    size_field = ir_pb2.Field(
        read_transform=size_expression,
        name=ir_pb2.NameDefinition(name=ir_pb2.Word(text=size_field_name)),
        existence_condition=ir_pb2.Expression(
            boolean_constant=ir_pb2.BooleanConstant(value=True)),
        attribute=[_skip_text_output_attribute()])
    structure.field.extend([size_field])
Esempio n. 4
0
def _type_check_constant_reference(expression, source_file_name, ir, errors):
    """Annotates the type of a constant reference."""
    referred_name = expression.constant_reference.canonical_name
    referred_object = ir_util.find_object(referred_name, ir)
    if isinstance(referred_object, ir_pb2.EnumValue):
        expression.type.enumeration.name.CopyFrom(
            expression.constant_reference)
        del expression.type.enumeration.name.canonical_name.object_path[-1]
    elif isinstance(referred_object, ir_pb2.Field):
        if not ir_util.field_is_virtual(referred_object):
            errors.append([
                error.error(
                    source_file_name, expression.source_location,
                    "Static references to physical fields are not allowed."),
                error.note(
                    referred_name.module_file, referred_object.source_location,
                    "{} is a physical field.".format(
                        referred_name.object_path[-1])),
            ])
            return
        _type_check_expression(referred_object.read_transform,
                               referred_name.module_file, ir, errors)
        expression.type.CopyFrom(referred_object.read_transform.type)
    else:
        assert False, "Unexpected constant reference type."
Esempio n. 5
0
def _resolve_field_reference(field_reference, source_file_name, errors, ir):
    """Resolves the References inside of a FieldReference."""
    if field_reference.path[-1].HasField("canonical_name"):
        # Already done.
        return
    previous_field = ir_util.find_object_or_none(field_reference.path[0], ir)
    previous_reference = field_reference.path[0]
    for ref in field_reference.path[1:]:
        while ir_util.field_is_virtual(previous_field):
            if (previous_field.read_transform.WhichOneof("expression") ==
                    "field_reference"):
                # Pass a separate error list into the recursive _resolve_field_reference
                # call so that only one copy of the error for a particular reference
                # will actually surface: in particular, the one that results from a
                # direct call from traverse_ir_top_down into _resolve_field_reference.
                new_errors = []
                _resolve_field_reference(
                    previous_field.read_transform.field_reference,
                    previous_field.name.canonical_name.module_file, new_errors,
                    ir)
                # If the recursive _resolve_field_reference was unable to resolve the
                # field, then bail.  Otherwise we get a cascade of errors, where an
                # error in `x` leads to errors in anything trying to reach a member of
                # `x`.
                if not previous_field.read_transform.field_reference.path[
                        -1].HasField("canonical_name"):
                    return
                previous_field = ir_util.find_object(
                    previous_field.read_transform.field_reference.path[-1], ir)
            else:
                errors.append(
                    noncomposite_subfield_error(
                        source_file_name, previous_reference.source_location,
                        previous_reference.source_name[0].text))
                return
        if previous_field.type.WhichOneof("type") == "array_type":
            errors.append(
                array_subfield_error(source_file_name,
                                     previous_reference.source_location,
                                     previous_reference.source_name[0].text))
            return
        assert previous_field.type.WhichOneof("type") == "atomic_type"
        member_name = ir_pb2.CanonicalName()
        member_name.CopyFrom(
            previous_field.type.atomic_type.reference.canonical_name)
        member_name.object_path.extend([ref.source_name[0].text])
        previous_field = ir_util.find_object_or_none(member_name, ir)
        if previous_field is None:
            errors.append(
                missing_name_error(source_file_name,
                                   ref.source_name[0].source_location,
                                   ref.source_name[0].text))
            return
        ref.canonical_name.CopyFrom(member_name)
        previous_reference = ref
Esempio n. 6
0
def _field_needs_byte_order(field, type_definition, ir):
  """Returns true if the given field needs a byte_order attribute."""
  if ir_util.field_is_virtual(field):
    # Virtual fields have no physical type, and thus do not need a byte order.
    return False
  field_type = ir_util.find_object(
      ir_util.get_base_type(field.type).atomic_type.reference.canonical_name,
      ir)
  assert field_type is not None
  assert field_type.addressable_unit != ir_pb2.TypeDefinition.NONE
  return field_type.addressable_unit != type_definition.addressable_unit
Esempio n. 7
0
 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))
Esempio n. 8
0
def _compute_constant_value_of_constant_reference(expression, ir):
    referred_object = ir_util.find_object(
        expression.constant_reference.canonical_name, ir)
    if isinstance(referred_object, ir_pb2.EnumValue):
        compute_constraints_of_expression(referred_object.value, ir)
        assert ir_util.is_constant(referred_object.value)
        new_value = str(ir_util.constant_value(referred_object.value))
        expression.type.enumeration.value = new_value
    elif isinstance(referred_object, ir_pb2.Field):
        assert ir_util.field_is_virtual(referred_object), (
            "Non-virtual non-enum-value constant reference should have been caught "
            "in type_check.py")
        compute_constraints_of_expression(referred_object.read_transform, ir)
        expression.type.CopyFrom(referred_object.read_transform.type)
    else:
        assert False, "Unexpected constant reference type."
Esempio n. 9
0
def _replace_next_keyword(structure, source_file_name, errors):
    last_physical_field_location = None
    new_errors = []
    for field in structure.field:
        if ir_util.field_is_virtual(field):
            # TODO(bolms): It could be useful to allow `$next` in a virtual field, in
            # order to reuse the value (say, to allow overlapping fields in a
            # mostly-packed structure), but it seems better to add `$end_of(field)`,
            # `$offset_of(field)`, and `$size_of(field)` constructs of some sort,
            # instead.
            continue
        traverse_ir.fast_traverse_node_top_down(
            field.location.size, [ir_pb2.Expression],
            _check_for_bad_next_keyword_in_size,
            parameters={
                "errors": new_errors,
                "source_file_name": source_file_name,
            })
        # If `$next` is misused in a field size, it can end up causing a
        # `RecursionError` in fast_traverse_node_top_down.  (When the `$next` node
        # in the next field is replaced, its replacement gets traversed, but the
        # replacement also contains a `$next` node, leading to infinite recursion.)
        #
        # Technically, we could scan all of the sizes instead of bailing early, but
        # it seems relatively unlikely that someone will have `$next` in multiple
        # sizes and not figure out what is going on relatively quickly.
        if new_errors:
            errors.extend(new_errors)
            return
        traverse_ir.fast_traverse_node_top_down(
            field.location.start, [ir_pb2.Expression],
            _maybe_replace_next_keyword_in_expression,
            parameters={
                "last_location": last_physical_field_location,
                "errors": new_errors,
                "source_file_name": source_file_name,
            })
        # The only possible error from _maybe_replace_next_keyword_in_expression is
        # `$next` occurring in the start expression of the first physical field,
        # which leads to similar recursion issue if `$next` is used in the start
        # expression of the next physical field.
        if new_errors:
            errors.extend(new_errors)
            return
        last_physical_field_location = field.location
Esempio n. 10
0
def _type_check_local_reference(expression, ir, errors):
    """Annotates the type of a local reference."""
    referrent = ir_util.find_object(expression.field_reference.path[-1], ir)
    assert referrent, "Local reference should be non-None after name resolution."
    if isinstance(referrent, ir_pb2.RuntimeParameter):
        parameter = referrent
        _set_expression_type_from_physical_type_reference(
            expression, parameter.physical_type_alias.atomic_type.reference,
            ir)
        return
    field = referrent
    if ir_util.field_is_virtual(field):
        _type_check_expression(field.read_transform,
                               expression.field_reference.path[0], ir, errors)
        expression.type.CopyFrom(field.read_transform.type)
        return
    if not field.type.HasField("atomic_type"):
        expression.type.opaque.CopyFrom(ir_pb2.OpaqueType())
    else:
        _set_expression_type_from_physical_type_reference(
            expression, field.type.atomic_type.reference, ir)
Esempio n. 11
0
 def test_field_is_not_virtual(self):
     self.assertFalse(
         ir_util.field_is_virtual(
             ir_pb2.Field(location=ir_pb2.FieldLocation())))
Esempio n. 12
0
 def test_field_is_virtual(self):
     self.assertTrue(ir_util.field_is_virtual(ir_pb2.Field()))
Esempio n. 13
0
def _add_write_method(field, ir):
  """Adds an appropriate write_method to field, if applicable.

  Currently, the "alias" write_method will be added for virtual fields of the
  form `let v = some_field_reference` when `some_field_reference` is a physical
  field or a writeable alias.  The "physical" write_method will be added for
  physical fields.  The "transform" write_method will be added when the virtual
  field's value is an easily-invertible function of a single writeable field.
  All other fields will have the "read_only" write_method; i.e., they will not
  be writeable.

  Arguments:
    field: an ir_pb2.Field to which to add a write_method.
    ir: The IR in which to look up field_references.

  Returns:
    None
  """
  if field.HasField("write_method"):
    # Do not recompute anything.
    return

  if not ir_util.field_is_virtual(field):
    # If the field is not virtual, writes are physical.
    field.write_method.physical = True
    return

  # A virtual field cannot be a direct alias if it has an additional
  # requirement.
  requires_attr = ir_util.get_attribute(field.attribute, attributes.REQUIRES)
  if (field.read_transform.WhichOneof("expression") != "field_reference" or
      requires_attr is not None):
    inverse = _invert_expression(field.read_transform, ir)
    if inverse:
      field_reference, function_body = inverse
      referenced_field = ir_util.find_object(
          field_reference.field_reference.path[-1], ir)
      if not isinstance(referenced_field, ir_pb2.Field):
        reference_is_read_only = True
      else:
        _add_write_method(referenced_field, ir)
        reference_is_read_only = referenced_field.write_method.read_only
      if not reference_is_read_only:
        field.write_method.transform.destination.CopyFrom(
            field_reference.field_reference)
        field.write_method.transform.function_body.CopyFrom(function_body)
      else:
        # If the virtual field's expression is invertible, but its target field
        # is read-only, it is also read-only.
        field.write_method.read_only = True
    else:
      # If the virtual field's expression is not invertible, it is
      # read-only.
      field.write_method.read_only = True
    return

  referenced_field = ir_util.find_object(
      field.read_transform.field_reference.path[-1], ir)
  if not isinstance(referenced_field, ir_pb2.Field):
    # If the virtual field aliases a non-field (i.e., a parameter), it is
    # read-only.
    field.write_method.read_only = True
    return

  _add_write_method(referenced_field, ir)
  if referenced_field.write_method.read_only:
    # If the virtual field directly aliases a read-only field, it is read-only.
    field.write_method.read_only = True
    return

  # Otherwise, it can be written as a direct alias.
  field.write_method.alias.CopyFrom(
      field.read_transform.field_reference)