blob: 904b6cfc4bf9d50e381e99da4deaaf3eccfc41a0 [file] [log] [blame]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for front_end.synthetics."""
import unittest
from compiler.front_end import glue
from compiler.front_end import synthetics
from compiler.front_end import test_util
from compiler.util import error
from compiler.util import ir_pb2
class SyntheticsTest(unittest.TestCase):
def _find_attribute(self, field, name):
result = None
for attribute in field.attribute:
if attribute.name.text == name:
self.assertIsNone(result)
result = attribute
self.assertIsNotNone(result)
return result
def _make_ir(self, emb_text):
ir, unused_debug_info, errors = glue.parse_emboss_file(
"m.emb",
test_util.dict_file_reader({"m.emb": emb_text}),
stop_before_step="desugar")
assert not errors, errors
return ir
def test_nothing_to_do(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] UInt x\n"
" 1 [+1] UInt:8[] y\n")
self.assertEqual([], synthetics.desugar(ir))
def test_adds_anonymous_bits_fields(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] Bar bar\n"
" 4 [+4] UInt uint\n"
" 1 [+1] bits:\n"
" 0 [+4] Bits nested_bits\n"
"enum Bar:\n"
" BAR = 0\n"
"bits Bits:\n"
" 0 [+4] UInt uint\n")
self.assertEqual([], synthetics.desugar(ir))
structure = ir.module[0].type[0].structure
# The first field should be the anonymous bits structure.
self.assertTrue(structure.field[0].HasField("location"))
# Then the aliases generated for those structures.
self.assertEqual("bar", structure.field[1].name.name.text)
self.assertEqual("uint", structure.field[2].name.name.text)
# Then the second anonymous bits.
self.assertTrue(structure.field[3].HasField("location"))
# Then the alias from the second anonymous bits.
self.assertEqual("nested_bits", structure.field[4].name.name.text)
def test_adds_correct_existence_condition(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar\n")
self.assertEqual([], synthetics.desugar(ir))
bits_field = ir.module[0].type[0].structure.field[0]
alias_field = ir.module[0].type[0].structure.field[1]
self.assertEqual("bar", alias_field.name.name.text)
self.assertEqual(bits_field.name.name.text,
alias_field.existence_condition.function.args[0].function.
args[0].field_reference.path[0].source_name[-1].text)
self.assertEqual(bits_field.name.name.text,
alias_field.existence_condition.function.args[1].function.
args[0].field_reference.path[0].source_name[-1].text)
self.assertEqual("bar",
alias_field.existence_condition.function.args[1].function.
args[0].field_reference.path[1].source_name[-1].text)
self.assertEqual(
ir_pb2.Function.PRESENCE,
alias_field.existence_condition.function.args[0].function.function)
self.assertEqual(
ir_pb2.Function.PRESENCE,
alias_field.existence_condition.function.args[1].function.function)
self.assertEqual(ir_pb2.Function.AND,
alias_field.existence_condition.function.function)
def test_adds_correct_read_transform(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar\n")
self.assertEqual([], synthetics.desugar(ir))
bits_field = ir.module[0].type[0].structure.field[0]
alias_field = ir.module[0].type[0].structure.field[1]
self.assertEqual("bar", alias_field.name.name.text)
self.assertEqual(
bits_field.name.name.text,
alias_field.read_transform.field_reference.path[0].source_name[-1].text)
self.assertEqual(
"bar",
alias_field.read_transform.field_reference.path[1].source_name[-1].text)
def test_adds_correct_abbreviation(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar\n"
" 4 [+4] UInt baz (qux)\n")
self.assertEqual([], synthetics.desugar(ir))
bar_alias = ir.module[0].type[0].structure.field[1]
baz_alias = ir.module[0].type[0].structure.field[2]
self.assertFalse(bar_alias.HasField("abbreviation"))
self.assertEqual("qux", baz_alias.abbreviation.text)
def test_anonymous_bits_sets_correct_is_synthetic(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar (b)\n")
self.assertEqual([], synthetics.desugar(ir))
bits_field = ir.module[0].type[0].subtype[0].structure.field[0]
alias_field = ir.module[0].type[0].structure.field[1]
self.assertFalse(alias_field.name.source_location.is_synthetic)
self.assertTrue(alias_field.HasField("abbreviation"))
self.assertFalse(alias_field.abbreviation.source_location.is_synthetic)
self.assertTrue(alias_field.HasField("read_transform"))
read_alias = alias_field.read_transform
self.assertTrue(read_alias.source_location.is_synthetic)
self.assertTrue(
read_alias.field_reference.path[0].source_location.is_synthetic)
alias_condition = alias_field.existence_condition
self.assertTrue(alias_condition.source_location.is_synthetic)
self.assertTrue(
alias_condition.function.args[0].source_location.is_synthetic)
self.assertTrue(bits_field.name.source_location.is_synthetic)
self.assertTrue(bits_field.name.name.source_location.is_synthetic)
self.assertTrue(bits_field.abbreviation.source_location.is_synthetic)
def test_adds_text_output_skip_attribute_to_anonymous_bits(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar (b)\n")
self.assertEqual([], synthetics.desugar(ir))
bits_field = ir.module[0].type[0].structure.field[0]
text_output_attribute = self._find_attribute(bits_field, "text_output")
self.assertEqual("Skip", text_output_attribute.value.string_constant.text)
def test_skip_attribute_is_marked_as_synthetic(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+1] bits:\n"
" 0 [+4] UInt bar\n")
self.assertEqual([], synthetics.desugar(ir))
bits_field = ir.module[0].type[0].structure.field[0]
attribute = self._find_attribute(bits_field, "text_output")
self.assertTrue(attribute.source_location.is_synthetic)
self.assertTrue(attribute.name.source_location.is_synthetic)
self.assertTrue(attribute.value.source_location.is_synthetic)
self.assertTrue(
attribute.value.string_constant.source_location.is_synthetic)
def test_adds_size_in_bytes(self):
ir = self._make_ir("struct Foo:\n"
" 1 [+l] UInt:8[] bytes\n"
" 0 [+1] UInt length (l)\n")
self.assertEqual([], synthetics.desugar(ir))
structure = ir.module[0].type[0].structure
size_in_bytes_field = structure.field[2]
max_size_in_bytes_field = structure.field[3]
min_size_in_bytes_field = structure.field[4]
self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text)
self.assertEqual(ir_pb2.Function.MAXIMUM,
size_in_bytes_field.read_transform.function.function)
self.assertEqual("$max_size_in_bytes",
max_size_in_bytes_field.name.name.text)
self.assertEqual(ir_pb2.Function.UPPER_BOUND,
max_size_in_bytes_field.read_transform.function.function)
self.assertEqual("$min_size_in_bytes",
min_size_in_bytes_field.name.name.text)
self.assertEqual(ir_pb2.Function.LOWER_BOUND,
min_size_in_bytes_field.read_transform.function.function)
# The correctness of $size_in_bytes et al are tested much further down
# stream, in tests of the generated C++ code.
def test_adds_size_in_bits(self):
ir = self._make_ir("bits Foo:\n"
" 1 [+9] UInt hi\n"
" 0 [+1] Flag lo\n")
self.assertEqual([], synthetics.desugar(ir))
structure = ir.module[0].type[0].structure
size_in_bits_field = structure.field[2]
max_size_in_bits_field = structure.field[3]
min_size_in_bits_field = structure.field[4]
self.assertEqual("$size_in_bits", size_in_bits_field.name.name.text)
self.assertEqual(ir_pb2.Function.MAXIMUM,
size_in_bits_field.read_transform.function.function)
self.assertEqual("$max_size_in_bits",
max_size_in_bits_field.name.name.text)
self.assertEqual(ir_pb2.Function.UPPER_BOUND,
max_size_in_bits_field.read_transform.function.function)
self.assertEqual("$min_size_in_bits",
min_size_in_bits_field.name.name.text)
self.assertEqual(ir_pb2.Function.LOWER_BOUND,
min_size_in_bits_field.read_transform.function.function)
# The correctness of $size_in_bits et al are tested much further down
# stream, in tests of the generated C++ code.
def test_adds_text_output_skip_attribute_to_size_in_bytes(self):
ir = self._make_ir("struct Foo:\n"
" 1 [+l] UInt:8[] bytes\n"
" 0 [+1] UInt length (l)\n")
self.assertEqual([], synthetics.desugar(ir))
size_in_bytes_field = ir.module[0].type[0].structure.field[2]
self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text)
text_output_attribute = self._find_attribute(size_in_bytes_field,
"text_output")
self.assertEqual("Skip", text_output_attribute.value.string_constant.text)
def test_replaces_next(self):
ir = self._make_ir("struct Foo:\n"
" 1 [+2] UInt:8[] a\n"
" $next [+4] UInt b\n"
" $next [+1] UInt c\n")
self.assertEqual([], synthetics.desugar(ir))
offset_of_b = ir.module[0].type[0].structure.field[1].location.start
self.assertTrue(offset_of_b.HasField("function"))
self.assertEqual(offset_of_b.function.function, ir_pb2.Function.ADDITION)
self.assertEqual(offset_of_b.function.args[0].constant.value, "1")
self.assertEqual(offset_of_b.function.args[1].constant.value, "2")
offset_of_c = ir.module[0].type[0].structure.field[2].location.start
self.assertEqual(
offset_of_c.function.args[0].function.args[0].constant.value, "1")
self.assertEqual(
offset_of_c.function.args[0].function.args[1].constant.value, "2")
self.assertEqual(offset_of_c.function.args[1].constant.value, "4")
def test_next_in_first_field(self):
ir = self._make_ir("struct Foo:\n"
" $next [+2] UInt:8[] a\n"
" $next [+4] UInt b\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].location.start.source_location,
"`$next` may not be used in the first physical field of " +
"a structure; perhaps you meant `0`?"),
]], synthetics.desugar(ir))
def test_next_in_size(self):
ir = self._make_ir("struct Foo:\n"
" 0 [+2] UInt:8[] a\n"
" 1 [+$next] UInt b\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[1].location.size.source_location,
"`$next` may only be used in the start expression of a " +
"physical field."),
]], synthetics.desugar(ir))
if __name__ == "__main__":
unittest.main()