예제 #1
0
 def test_adds_null_byte_order_attributes(self):
     ir = _make_ir_from_emb("struct Foo:\n"
                            "  0 [+1]  UInt      bar\n"
                            "  1 [+1]  UInt      baz\n"
                            '    [byte_order: "LittleEndian"]\n'
                            "  2 [+2]  UInt:8[]  baseball\n"
                            "  4 [+2]  UInt:8[]  bat\n"
                            '    [byte_order: "LittleEndian"]\n')
     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
     structure = ir.module[0].type[0].structure
     byte_order_attr = ir_util.get_attribute(structure.field[0].attribute,
                                             _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("Null", byte_order_attr.string_constant.text)
     self.assertEqual(structure.field[0].source_location,
                      byte_order_attr.source_location)
     byte_order_attr = ir_util.get_attribute(structure.field[1].attribute,
                                             _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
     byte_order_attr = ir_util.get_attribute(structure.field[2].attribute,
                                             _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("Null", byte_order_attr.string_constant.text)
     self.assertEqual(structure.field[2].source_location,
                      byte_order_attr.source_location)
     byte_order_attr = ir_util.get_attribute(structure.field[3].attribute,
                                             _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
예제 #2
0
 def test_adds_fixed_size_attribute_to_anonymous_bits(self):
     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
                                   "struct Foo:\n"
                                   "  0 [+4]  bits:\n"
                                   "    0 [+8]  UInt  field\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
     size_attr = ir_util.get_attribute(
         struct_ir.module[0].type[0].attribute, _FIXED_SIZE)
     self.assertEqual(32, ir_util.constant_value(size_attr.expression))
     bits_size_attr = ir_util.get_attribute(
         struct_ir.module[0].type[0].subtype[0].attribute, _FIXED_SIZE)
     self.assertEqual(8, ir_util.constant_value(bits_size_attr.expression))
     self.assertEqual(struct_ir.module[0].type[0].source_location,
                      size_attr.source_location)
예제 #3
0
 def test_adds_byte_order_attributes_from_default(self):
     ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
                            "struct Foo:\n"
                            "  0 [+2]  UInt  bar\n"
                            "  2 [+2]  UInt  baz\n"
                            '    [byte_order: "LittleEndian"]\n')
     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
     byte_order_attr = ir_util.get_attribute(
         ir.module[0].type[0].structure.field[0].attribute, _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("BigEndian", byte_order_attr.string_constant.text)
     byte_order_attr = ir_util.get_attribute(
         ir.module[0].type[0].structure.field[1].attribute, _BYTE_ORDER)
     self.assertTrue(byte_order_attr.HasField("string_constant"))
     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
예제 #4
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.")])
예제 #5
0
 def test_adds_max_bits_attribute(self):
     ir = _make_ir_from_emb("enum Foo:\n" "  ZERO = 0\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
     enum = ir.module[0].type[0]
     max_bits_attr = ir_util.get_attribute(enum.attribute, _MAX_BITS)
     self.assertTrue(max_bits_attr.expression.HasField("constant"))
     self.assertEqual("64", max_bits_attr.expression.constant.value)
예제 #6
0
 def test_adds_true_is_signed_attribute(self):
     ir = _make_ir_from_emb("enum Foo:\n" "  NEGATIVE_ONE = -1\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
     enum = ir.module[0].type[0]
     is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
     self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
     self.assertTrue(is_signed_attr.expression.boolean_constant.value)
예제 #7
0
 def test_does_not_add_fixed_size_attribute_to_variable_size_struct(self):
     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
                                   "struct Foo:\n"
                                   "  0 [+4]  UInt      n\n"
                                   "  4 [+n]  UInt:8[]  payload\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
     self.assertIsNone(
         ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
                               _FIXED_SIZE))
예제 #8
0
 def test_accepts_correct_size_attribute_on_struct(self):
     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
                                   "struct Foo:\n"
                                   "  [fixed_size_in_bits: 64]\n"
                                   "  0 [+2]  UInt  field1\n"
                                   "  4 [+4]  UInt  field3\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
     size_attr = ir_util.get_attribute(
         struct_ir.module[0].type[0].attribute, _FIXED_SIZE)
     self.assertTrue(size_attr.expression)
     self.assertEqual(64, ir_util.constant_value(size_attr.expression))
예제 #9
0
 def test_adds_fixed_size_attribute_to_struct_with_virtual_field(self):
     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
                                   "struct Foo:\n"
                                   "  0 [+2]  UInt  field1\n"
                                   "  let field2 = field1\n"
                                   "  2 [+2]  UInt  field3\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
     size_attr = ir_util.get_attribute(
         struct_ir.module[0].type[0].attribute, _FIXED_SIZE)
     self.assertEqual(32, ir_util.constant_value(size_attr.expression))
     self.assertEqual(struct_ir.module[0].type[0].source_location,
                      size_attr.source_location)
예제 #10
0
 def test_explicit_size_too_small(self):
   ir = _make_ir_from_emb("bits Foo:\n"
                          "  0 [+0]  UInt:0  zero_bit\n")
   error_field = ir.module[0].type[0].structure.field[0]
   uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir)
   uint_requirements = ir_util.get_attribute(uint_type.attribute,
                                             attributes.STATIC_REQUIREMENTS)
   self.assertEqual([[
       error.error("m.emb", error_field.source_location,
                   "Requirements of UInt not met."),
       error.note("", uint_requirements.source_location,
                  "Requirements specified here."),
   ]], error.filter_errors(constraints.check_constraints(ir)))
예제 #11
0
def _add_missing_byte_order_attribute_on_field(field, type_definition, ir,
                                               defaults):
  """Adds missing byte_order attributes to fields that need them."""
  if _field_needs_byte_order(field, type_definition, ir):
    byte_order_attr = ir_util.get_attribute(field.attribute,
                                            attributes.BYTE_ORDER)
    if byte_order_attr is None:
      if attributes.BYTE_ORDER in defaults:
        field.attribute.extend([defaults[attributes.BYTE_ORDER]])
      elif _field_may_have_null_byte_order(field, type_definition, ir):
        field.attribute.extend(
            [_construct_string_attribute(attributes.BYTE_ORDER, "Null",
                                         field.source_location)])
예제 #12
0
def _verify_width_attribute_on_enum(enum, type_definition, source_file_name,
                                    errors):
  """Verifies the maximum_bits attribute for an enum TypeDefinition."""
  max_bits_value = ir_util.get_integer_attribute(type_definition.attribute,
                                                attributes.ENUM_MAXIMUM_BITS)
  # The attribute should already have been defaulted, if not originally present.
  assert max_bits_value is not None, "maximum_bits not set"
  if max_bits_value > 64 or max_bits_value < 1:
    max_bits_attr = ir_util.get_attribute(type_definition.attribute,
                                          attributes.ENUM_MAXIMUM_BITS)
    errors.append([
        error.error(source_file_name, max_bits_attr.source_location,
                    "'maximum_bits' on an 'enum' must be between 1 and 64.")
    ])
예제 #13
0
 def test_adds_fixed_size_attribute_to_struct(self):
     # field2 is intentionally after field3, in order to trigger certain code
     # paths in attribute_checker.py.
     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
                                   "struct Foo:\n"
                                   "  0 [+2]  UInt  field1\n"
                                   "  4 [+4]  UInt  field2\n"
                                   "  2 [+2]  UInt  field3\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
     size_attr = ir_util.get_attribute(
         struct_ir.module[0].type[0].attribute, _FIXED_SIZE)
     self.assertEqual(64, ir_util.constant_value(size_attr.expression))
     self.assertEqual(struct_ir.module[0].type[0].source_location,
                      size_attr.source_location)
예제 #14
0
 def test_explicit_size_too_big(self):
   ir = _make_ir_from_emb("struct Foo:\n"
                          "  0 [+16]  UInt:128  one_twenty_eight_bit\n"
                          '    [byte_order: "LittleEndian"]\n')
   error_field = ir.module[0].type[0].structure.field[0]
   uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir)
   uint_requirements = ir_util.get_attribute(uint_type.attribute,
                                             attributes.STATIC_REQUIREMENTS)
   self.assertEqual([[
       error.error("m.emb", error_field.source_location,
                   "Requirements of UInt not met."),
       error.note("", uint_requirements.source_location,
                  "Requirements specified here."),
   ]], error.filter_errors(constraints.check_constraints(ir)))
예제 #15
0
def _add_missing_size_attributes_on_structure(struct, type_definition):
  """Adds missing size attributes on a struct."""
  fixed_size = _fixed_size_of_struct_or_bits(struct,
                                             type_definition.addressable_unit)
  if fixed_size is None:
    return
  fixed_size_attr = ir_util.get_attribute(type_definition.attribute,
                                          attributes.FIXED_SIZE)
  if not fixed_size_attr:
    # TODO(bolms): Use the offset and length of the last field as the
    # source_location of the fixed_size attribute?
    type_definition.attribute.extend([
        _construct_integer_attribute(attributes.FIXED_SIZE, fixed_size,
                                     type_definition.source_location)])
예제 #16
0
 def test_get_attribute(self):
     type_def = ir_pb2.TypeDefinition(attribute=[
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=ir_pb2.Expression()),
                          name=ir_pb2.Word(text="phil")),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("false")),
                          name=ir_pb2.Word(text="bob"),
                          is_default=True),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("true")),
                          name=ir_pb2.Word(text="bob")),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("false")),
                          name=ir_pb2.Word(text="bob2")),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("true")),
                          name=ir_pb2.Word(text="bob2"),
                          is_default=True),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("false")),
                          name=ir_pb2.Word(text="bob3"),
                          is_default=True),
         ir_pb2.Attribute(value=ir_pb2.AttributeValue(
             expression=_parse_expression("false")),
                          name=ir_pb2.Word()),
     ])
     self.assertEqual(
         ir_pb2.AttributeValue(expression=_parse_expression("true")),
         ir_util.get_attribute(type_def.attribute, "bob"))
     self.assertEqual(
         ir_pb2.AttributeValue(expression=_parse_expression("false")),
         ir_util.get_attribute(type_def.attribute, "bob2"))
     self.assertEqual(None, ir_util.get_attribute(type_def.attribute,
                                                  "Bob"))
     self.assertEqual(None, ir_util.get_attribute(type_def.attribute,
                                                  "bob3"))
예제 #17
0
def _check_physical_type_requirements(type_ir, usage_source_location, size, ir,
                                      source_file_name):
    """Checks that the given atomic `type_ir` is allowed to be `size` bits."""
    referenced_type_definition = ir_util.find_object(
        type_ir.atomic_type.reference, ir)
    # TODO(bolms): replace this with a check against an automatically-generated
    # `static_requirements` attribute on enum types.  (The main problem is that
    # the generated attribute would have no source text, so there would be a crash
    # when trying to display the error.)
    if referenced_type_definition.HasField("enumeration"):
        if size is None:
            return [[
                error.error(
                    source_file_name, type_ir.source_location,
                    "Enumeration {} cannot be placed in a dynamically-sized "
                    "field.".format(_render_type(type_ir, ir)))
            ]]
        elif size < 1 or size > 64:
            return [[
                error.error(
                    source_file_name, type_ir.source_location,
                    "Enumeration {} cannot be {} bits; enumerations must be between "
                    "1 and 64 bits, inclusive.".format(
                        _render_atomic_type_name(type_ir, ir), size))
            ]]

    if size is None:
        bindings = {"$is_statically_sized": False}
    else:
        bindings = {"$is_statically_sized": True, "$static_size_in_bits": size}
    requires_attr = ir_util.get_attribute(referenced_type_definition.attribute,
                                          attributes.STATIC_REQUIREMENTS)
    if requires_attr and not ir_util.constant_value(requires_attr.expression,
                                                    bindings):
        # TODO(bolms): Figure out a better way to build this error message.
        # The "Requirements specified here." message should print out the actual
        # source text of the requires attribute, so that should help, but it's still
        # a bit generic and unfriendly.
        return [[
            error.error(
                source_file_name, usage_source_location,
                "Requirements of {} not met.".format(
                    type_ir.atomic_type.reference.canonical_name.
                    object_path[-1])),
            error.note(
                type_ir.atomic_type.reference.canonical_name.module_file,
                requires_attr.source_location, "Requirements specified here.")
        ]]
    return []
예제 #18
0
def _verify_size_attributes_on_structure(struct, type_definition,
                                         source_file_name, errors):
  """Verifies size attributes on a struct or bits."""
  fixed_size = _fixed_size_of_struct_or_bits(struct,
                                             type_definition.addressable_unit)
  fixed_size_attr = ir_util.get_attribute(type_definition.attribute,
                                          attributes.FIXED_SIZE)
  if not fixed_size_attr:
    return
  if fixed_size is None:
    errors.append([error.error(
        source_file_name, fixed_size_attr.source_location,
        "Struct is marked as fixed size, but contains variable-location "
        "fields.")])
  elif ir_util.constant_value(fixed_size_attr.expression) != fixed_size:
    errors.append([error.error(
        source_file_name, fixed_size_attr.source_location,
        "Struct is {} bits, but is marked as {} bits.".format(
            fixed_size, ir_util.constant_value(fixed_size_attr.expression)))])
예제 #19
0
def _verify_byte_order_attribute_on_field(field, type_definition,
                                          source_file_name, ir, errors):
  """Verifies the byte_order attribute on the given field."""
  byte_order_attr = ir_util.get_attribute(field.attribute,
                                          attributes.BYTE_ORDER)
  field_needs_byte_order = _field_needs_byte_order(field, type_definition, ir)
  if byte_order_attr and not field_needs_byte_order:
    errors.append([error.error(
        source_file_name, byte_order_attr.source_location,
        "Attribute 'byte_order' not allowed on field which is not byte order "
        "dependent.")])
  if not byte_order_attr and field_needs_byte_order:
    errors.append([error.error(
        source_file_name, field.source_location,
        "Attribute 'byte_order' required on field which is byte order "
        "dependent.")])
  if (byte_order_attr and byte_order_attr.string_constant.text == "Null" and
      not _field_may_have_null_byte_order(field, type_definition, ir)):
    errors.append([error.error(
        source_file_name, byte_order_attr.source_location,
        "Attribute 'byte_order' may only be 'Null' for one-byte fields.")])
예제 #20
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)
예제 #21
0
def _check_type_requirements_for_field(type_ir, type_definition, field, ir,
                                       source_file_name, errors):
    """Checks that the `requires` attribute of each field's type is fulfilled."""
    if not type_ir.HasField("atomic_type"):
        return

    if field.type.HasField("atomic_type"):
        field_min_size = (int(field.location.size.type.integer.minimum_value) *
                          type_definition.addressable_unit)
        field_max_size = (int(field.location.size.type.integer.maximum_value) *
                          type_definition.addressable_unit)
        field_is_atomic = True
    else:
        field_is_atomic = False

    if type_ir.HasField("size_in_bits"):
        element_size = ir_util.constant_value(type_ir.size_in_bits)
    else:
        element_size = None

    referenced_type_definition = ir_util.find_object(
        type_ir.atomic_type.reference, ir)
    type_is_anonymous = referenced_type_definition.name.is_anonymous
    type_size_attr = ir_util.get_attribute(
        referenced_type_definition.attribute, attributes.FIXED_SIZE)
    if type_size_attr:
        type_size = ir_util.constant_value(type_size_attr.expression)
    else:
        type_size = None

    if (element_size is not None and type_size is not None
            and element_size != type_size):
        errors.append([
            error.error(
                source_file_name, type_ir.size_in_bits.source_location,
                "Explicit size of {} bits does not match fixed size ({} bits) of "
                "{}.".format(element_size, type_size,
                             _render_atomic_type_name(type_ir, ir))),
            error.note(
                type_ir.atomic_type.reference.canonical_name.module_file,
                type_size_attr.source_location, "Size specified here.")
        ])
        return

    # If the type had no size specifier (the ':32' in 'UInt:32'), but the type is
    # fixed size, then continue as if the type's size were explicitly stated.
    if element_size is None:
        element_size = type_size

    # TODO(bolms): When the full dynamic size expression for types is generated,
    # add a check that dynamically-sized types can, at least potentially, fit in
    # their fields.

    if field_is_atomic and element_size is not None:
        # If the field has a fixed size, and the (atomic) type contained therein is
        # also fixed size, then the sizes should match.
        #
        # TODO(bolms): Maybe change the case where the field is bigger than
        # necessary into a warning?
        if (field_max_size == field_min_size and
            (element_size > field_max_size or
             (element_size < field_min_size and not type_is_anonymous))):
            errors.append([
                error.error(
                    source_file_name, type_ir.source_location,
                    "Fixed-size {} cannot be placed in field of size {} bits; "
                    "requires {} bits.".format(_render_type(type_ir, ir),
                                               field_max_size, element_size))
            ])
            return
        elif element_size > field_max_size:
            errors.append([
                error.error(
                    source_file_name, type_ir.source_location,
                    "Field of maximum size {} bits cannot hold fixed-size {}, which "
                    "requires {} bits.".format(field_max_size,
                                               _render_type(type_ir, ir),
                                               element_size))
            ])
            return

    # If we're here, then field/type sizes are consistent.
    if (element_size is None and field_is_atomic
            and field_min_size == field_max_size):
        # From here down, we just use element_size.
        element_size = field_min_size

    errors.extend(
        _check_physical_type_requirements(type_ir, field.source_location,
                                          element_size, ir, source_file_name))