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")))))
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)))))
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}))
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())
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")))))
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")))))
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"]))))
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))
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
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)