Example #1
0
 def test_is_constant_enum(self):
     self.assertTrue(
         ir_util.is_constant(
             ir_pb2.Expression(
                 constant_reference=ir_pb2.Reference(),
                 type=ir_pb2.ExpressionType(enumeration=ir_pb2.EnumType(
                     value="12")))))
Example #2
0
 def test_constant_value_of_boolean_reference(self):
     self.assertTrue(
         ir_util.constant_value(
             ir_pb2.Expression(
                 constant_reference=ir_pb2.Reference(),
                 type=ir_pb2.ExpressionType(boolean=ir_pb2.BooleanType(
                     value=True)))))
Example #3
0
 def test_constant_value_of_builtin_reference(self):
     self.assertEqual(
         12,
         ir_util.constant_value(
             ir_pb2.Expression(builtin_reference=ir_pb2.Reference(
                 canonical_name=ir_pb2.CanonicalName(
                     object_path=["$foo"]))), {"$foo": 12}))
Example #4
0
def unbounded_expression_type_for_physical_type(type_definition):
    """Gets the ExpressionType for a field of the given TypeDefinition.

  Arguments:
    type_definition: an ir_pb2.TypeDefinition.

  Returns:
    An ir_pb2.ExpressionType with the corresponding expression type filled in:
    for example, [prelude].UInt will result in an ExpressionType with the
    `integer` field filled in.

    The returned ExpressionType will not have any bounds set.
  """
    # TODO(bolms): Add a `[value_type]` attribute for `external`s.
    if ir_util.get_boolean_attribute(type_definition.attribute,
                                     attributes.IS_INTEGER):
        return ir_pb2.ExpressionType(integer=ir_pb2.IntegerType())
    elif tuple(type_definition.name.canonical_name.object_path) == ("Flag", ):
        # This is a hack: the Flag type should say that it is a boolean.
        return ir_pb2.ExpressionType(boolean=ir_pb2.BooleanType())
    elif type_definition.HasField("enumeration"):
        return ir_pb2.ExpressionType(enumeration=ir_pb2.EnumType(
            name=ir_pb2.Reference(
                canonical_name=type_definition.name.canonical_name)))
    else:
        return ir_pb2.ExpressionType(opaque=ir_pb2.OpaqueType())
Example #5
0
 def test_constant_value_of_integer_reference(self):
     self.assertEqual(
         12,
         ir_util.constant_value(
             ir_pb2.Expression(
                 constant_reference=ir_pb2.Reference(),
                 type=ir_pb2.ExpressionType(integer=ir_pb2.IntegerType(
                     modulus="infinity", modular_value="12")))))
Example #6
0
 def test_constant_value_of_enum(self):
     self.assertEqual(
         12,
         ir_util.constant_value(
             ir_pb2.Expression(
                 constant_reference=ir_pb2.Reference(),
                 type=ir_pb2.ExpressionType(enumeration=ir_pb2.EnumType(
                     value="12")))))
Example #7
0
 def test_hashable_form_of_reference(self):
     self.assertEqual(
         ("t.emb", "Foo", "Bar"),
         ir_util.hashable_form_of_reference(
             ir_pb2.Reference(canonical_name=ir_pb2.CanonicalName(
                 module_file="t.emb", object_path=["Foo", "Bar"]))))
     self.assertEqual(
         ("t.emb", "Foo", "Bar"),
         ir_util.hashable_form_of_reference(
             ir_pb2.NameDefinition(canonical_name=ir_pb2.CanonicalName(
                 module_file="t.emb", object_path=["Foo", "Bar"]))))
Example #8
0
    def test_find_object(self):
        ir = ir_pb2.EmbossIr.from_json("""{
          "module": [
            {
              "type": [
                {
                  "structure": {
                    "field": [
                      {
                        "name": {
                          "name": { "text": "field" },
                          "canonical_name": {
                            "module_file": "test.emb",
                            "object_path": [ "Foo", "field" ]
                          }
                        }
                      }
                    ]
                  },
                  "name": {
                    "name": { "text": "Foo" },
                    "canonical_name": {
                      "module_file": "test.emb",
                      "object_path": [ "Foo" ]
                    }
                  },
                  "runtime_parameter": [
                    {
                      "name": {
                        "name": { "text": "parameter" },
                        "canonical_name": {
                          "module_file": "test.emb",
                          "object_path": [ "Foo", "parameter" ]
                        }
                      }
                    }
                  ]
                },
                {
                  "enumeration": {
                    "value": [
                      {
                        "name": {
                          "name": { "text": "QUX" },
                          "canonical_name": {
                            "module_file": "test.emb",
                            "object_path": [ "Bar", "QUX" ]
                          }
                        }
                      }
                    ]
                  },
                  "name": {
                    "name": { "text": "Bar" },
                    "canonical_name": {
                      "module_file": "test.emb",
                      "object_path": [ "Bar" ]
                    }
                  }
                }
              ],
              "source_file_name": "test.emb"
            },
            {
              "type": [
                {
                  "external": {},
                  "name": {
                    "name": { "text": "UInt" },
                    "canonical_name": {
                      "module_file": "",
                      "object_path": [ "UInt" ]
                    }
                  }
                }
              ],
              "source_file_name": ""
            }
          ]
        }""")

        # Test that find_object works with any of its four "name" types.
        canonical_name_of_foo = ir_pb2.CanonicalName(module_file="test.emb",
                                                     object_path=["Foo"])
        self.assertEqual(
            ir.module[0].type[0],
            ir_util.find_object(
                ir_pb2.Reference(canonical_name=canonical_name_of_foo), ir))
        self.assertEqual(
            ir.module[0].type[0],
            ir_util.find_object(
                ir_pb2.NameDefinition(canonical_name=canonical_name_of_foo),
                ir))
        self.assertEqual(ir.module[0].type[0],
                         ir_util.find_object(canonical_name_of_foo, ir))
        self.assertEqual(ir.module[0].type[0],
                         ir_util.find_object(("test.emb", "Foo"), ir))

        # Test that find_object works with objects other than structs.
        self.assertEqual(ir.module[0].type[1],
                         ir_util.find_object(("test.emb", "Bar"), ir))
        self.assertEqual(ir.module[1].type[0],
                         ir_util.find_object(("", "UInt"), ir))
        self.assertEqual(ir.module[0].type[0].structure.field[0],
                         ir_util.find_object(("test.emb", "Foo", "field"), ir))
        self.assertEqual(
            ir.module[0].type[0].runtime_parameter[0],
            ir_util.find_object(("test.emb", "Foo", "parameter"), ir))
        self.assertEqual(ir.module[0].type[1].enumeration.value[0],
                         ir_util.find_object(("test.emb", "Bar", "QUX"), ir))
        self.assertEqual(ir.module[0], ir_util.find_object(("test.emb", ), ir))
        self.assertEqual(ir.module[1], ir_util.find_object(("", ), ir))

        # Test searching for non-present objects.
        self.assertIsNone(ir_util.find_object_or_none(("not_test.emb", ), ir))
        self.assertIsNone(
            ir_util.find_object_or_none(("test.emb", "NotFoo"), ir))
        self.assertIsNone(
            ir_util.find_object_or_none(("test.emb", "Foo", "not_field"), ir))
        self.assertIsNone(
            ir_util.find_object_or_none(
                ("test.emb", "Foo", "field", "no_subfield"), ir))
        self.assertIsNone(
            ir_util.find_object_or_none(("test.emb", "Bar", "NOT_QUX"), ir))
        self.assertIsNone(
            ir_util.find_object_or_none(
                ("test.emb", "Bar", "QUX", "no_subenum"), ir))

        # Test that find_parent_object works with any of its four "name" types.
        self.assertEqual(
            ir.module[0],
            ir_util.find_parent_object(
                ir_pb2.Reference(canonical_name=canonical_name_of_foo), ir))
        self.assertEqual(
            ir.module[0],
            ir_util.find_parent_object(
                ir_pb2.NameDefinition(canonical_name=canonical_name_of_foo),
                ir))
        self.assertEqual(ir.module[0],
                         ir_util.find_parent_object(canonical_name_of_foo, ir))
        self.assertEqual(ir.module[0],
                         ir_util.find_parent_object(("test.emb", "Foo"), ir))

        # Test that find_parent_object works with objects other than structs.
        self.assertEqual(ir.module[0],
                         ir_util.find_parent_object(("test.emb", "Bar"), ir))
        self.assertEqual(ir.module[1],
                         ir_util.find_parent_object(("", "UInt"), ir))
        self.assertEqual(
            ir.module[0].type[0],
            ir_util.find_parent_object(("test.emb", "Foo", "field"), ir))
        self.assertEqual(
            ir.module[0].type[1],
            ir_util.find_parent_object(("test.emb", "Bar", "QUX"), ir))
Example #9
0
def _invert_expression(expression, ir):
  """For the given expression, searches for an algebraic inverse expression.

  That is, it takes the notional equation:

      $logical_value = expression

  and, if there is exactly one `field_reference` in `expression`, it will
  attempt to solve the equation for that field.  For example, if the expression
  is `x + 1`, it will iteratively transform:

      $logical_value = x + 1
      $logical_value - 1 = x + 1 - 1
      $logical_value - 1 = x

  and finally return `x` and `$logical_value - 1`.

  The purpose of this transformation is to find an assignment statement that can
  be used to write back through certain virtual fields.  E.g., given:

      struct Foo:
        0 [+1]  UInt  raw_value
        let actual_value = raw_value + 100

  it should be possible to write a value to the `actual_value` field, and have
  it set `raw_value` to the appropriate value.

  Arguments:
    expression: an ir_pb2.Expression to be inverted.
    ir: the full IR, for looking up symbols.

  Returns:
    (field_reference, inverse_expression) if expression can be inverted,
    otherwise None.
  """
  reference_path = _find_field_reference_path(expression)
  if reference_path is None:
    return None
  subexpression = expression
  result = ir_pb2.Expression(
      builtin_reference=ir_pb2.Reference(
          canonical_name=ir_pb2.CanonicalName(
              module_file="",
              object_path=["$logical_value"]
          ),
          source_name=[ir_pb2.Word(
              text="$logical_value",
              source_location=ir_pb2.Location(is_synthetic=True)
          )],
          source_location=ir_pb2.Location(is_synthetic=True)
      ),
      type=expression.type,
      source_location=ir_pb2.Location(is_synthetic=True)
  )

  # This loop essentially starts with:
  #
  #     f(g(x)) == $logical_value
  #
  # and ends with
  #
  #     x == g_inv(f_inv($logical_value))
  #
  # At each step, `subexpression` has one layer removed, and `result` has a
  # corresponding inverse function applied.  So, for example, it might start
  # with:
  #
  #     2 + ((3 - x) - 10)  ==  $logical_value
  #
  # On each iteration, `subexpression` and `result` will become:
  #
  #     (3 - x) - 10  ==  $logical_value - 2    [subtract 2 from both sides]
  #     (3 - x)  ==  ($logical_value - 2) + 10  [add 10 to both sides]
  #     x  ==  3 - (($logical_value - 2) + 10)  [subtract both sides from 3]
  #
  # This is an extremely limited algebraic solver, but it covers common-enough
  # cases.
  #
  # Note that any equation that can be solved here becomes part of Emboss's
  # contract, forever, so be conservative in expanding its solving capabilities!
  for index in reference_path:
    if subexpression.function.function == ir_pb2.Function.ADDITION:
      result = ir_pb2.Expression(
          function=ir_pb2.Function(
              function=ir_pb2.Function.SUBTRACTION,
              args=[
                  result,
                  subexpression.function.args[1 - index],
              ]
          ),
          type=ir_pb2.ExpressionType(integer=ir_pb2.IntegerType())
      )
    elif subexpression.function.function == ir_pb2.Function.SUBTRACTION:
      if index == 0:
        result = ir_pb2.Expression(
            function=ir_pb2.Function(
                function=ir_pb2.Function.ADDITION,
                args=[
                    result,
                    subexpression.function.args[1],
                ]
            ),
            type=ir_pb2.ExpressionType(integer=ir_pb2.IntegerType())
        )
      else:
        result = ir_pb2.Expression(
            function=ir_pb2.Function(
                function=ir_pb2.Function.SUBTRACTION,
                args=[
                    subexpression.function.args[0],
                    result,
                ]
            ),
            type=ir_pb2.ExpressionType(integer=ir_pb2.IntegerType())
        )
    else:
      return None
    subexpression = subexpression.function.args[index]
  expression_bounds.compute_constraints_of_expression(result, ir)
  return subexpression, result
Example #10
0
def _add_anonymous_aliases(structure, type_definition):
    """Adds synthetic alias fields for all fields in anonymous fields.

  This essentially completes the rewrite of this:

      struct Foo:
        0  [+4]  bits:
          0  [+1]  Flag  low
          31 [+1]  Flag  high

  Into this:

      struct Foo:
        bits EmbossReservedAnonymous0:
          [text_output: "Skip"]
          0  [+1]  Flag  low
          31 [+1]  Flag  high
        0 [+4]  EmbossReservedAnonymous0  emboss_reserved_anonymous_1
        let low = emboss_reserved_anonymous_1.low
        let high = emboss_reserved_anonymous_1.high

  Note that this pass runs very, very early -- even before symbols have been
  resolved -- so very little in ir_util will work at this point.

  Arguments:
      structure: The ir_pb2.Structure on which to synthesize fields.
      type_definition: The ir_pb2.TypeDefinition containing structure.

  Returns:
      None
  """
    new_fields = []
    for field in structure.field:
        new_fields.append(field)
        if not field.name.is_anonymous:
            continue
        field.attribute.extend([_skip_text_output_attribute()])
        for subtype in type_definition.subtype:
            if (subtype.name.name.text ==
                    field.type.atomic_type.reference.source_name[-1].text):
                field_type = subtype
                break
        else:
            assert False, (
                "Unable to find corresponding type {} for anonymous field "
                "in {}.".format(field.type.atomic_type.reference,
                                type_definition))
        anonymous_reference = ir_pb2.Reference(source_name=[field.name.name])
        anonymous_field_reference = ir_pb2.FieldReference(
            path=[anonymous_reference])
        for subfield in field_type.structure.field:
            alias_field_reference = ir_pb2.FieldReference(path=[
                anonymous_reference,
                ir_pb2.Reference(source_name=[subfield.name.name]),
            ])
            new_existence_condition = ir_pb2.Expression()
            new_existence_condition.CopyFrom(
                _ANONYMOUS_BITS_ALIAS_EXISTENCE_SKELETON)
            existence_clauses = new_existence_condition.function.args
            existence_clauses[0].function.args[0].field_reference.CopyFrom(
                anonymous_field_reference)
            existence_clauses[1].function.args[0].field_reference.CopyFrom(
                alias_field_reference)
            new_read_transform = ir_pb2.Expression(
                field_reference=alias_field_reference)
            # This treats *most* of the alias field as synthetic, but not its name(s):
            # leaving the name(s) as "real" means that symbol collisions with the
            # surrounding structure will be properly reported to the user.
            _mark_as_synthetic(new_existence_condition)
            _mark_as_synthetic(new_read_transform)
            new_alias = ir_pb2.Field(
                read_transform=new_read_transform,
                existence_condition=new_existence_condition,
                name=subfield.name)
            if subfield.HasField("abbreviation"):
                new_alias.abbreviation.CopyFrom(subfield.abbreviation)
            _mark_as_synthetic(new_alias.existence_condition)
            _mark_as_synthetic(new_alias.read_transform)
            new_fields.append(new_alias)
            # Since the alias field's name(s) are "real," it is important to mark the
            # original field's name(s) as synthetic, to avoid duplicate error
            # messages.
            _mark_as_synthetic(subfield.name)
            if subfield.HasField("abbreviation"):
                _mark_as_synthetic(subfield.abbreviation)
    del structure.field[:]
    structure.field.extend(new_fields)