1*99e0aae7SDavid Rees# Copyright 2019 Google LLC 2*99e0aae7SDavid Rees# 3*99e0aae7SDavid Rees# Licensed under the Apache License, Version 2.0 (the "License"); 4*99e0aae7SDavid Rees# you may not use this file except in compliance with the License. 5*99e0aae7SDavid Rees# You may obtain a copy of the License at 6*99e0aae7SDavid Rees# 7*99e0aae7SDavid Rees# https://www.apache.org/licenses/LICENSE-2.0 8*99e0aae7SDavid Rees# 9*99e0aae7SDavid Rees# Unless required by applicable law or agreed to in writing, software 10*99e0aae7SDavid Rees# distributed under the License is distributed on an "AS IS" BASIS, 11*99e0aae7SDavid Rees# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*99e0aae7SDavid Rees# See the License for the specific language governing permissions and 13*99e0aae7SDavid Rees# limitations under the License. 14*99e0aae7SDavid Rees 15*99e0aae7SDavid Rees"""Tests for front_end.synthetics.""" 16*99e0aae7SDavid Rees 17*99e0aae7SDavid Reesimport unittest 18*99e0aae7SDavid Reesfrom compiler.front_end import glue 19*99e0aae7SDavid Reesfrom compiler.front_end import synthetics 20*99e0aae7SDavid Reesfrom compiler.util import error 21*99e0aae7SDavid Reesfrom compiler.util import ir_data 22*99e0aae7SDavid Reesfrom compiler.util import test_util 23*99e0aae7SDavid Rees 24*99e0aae7SDavid Rees 25*99e0aae7SDavid Reesclass SyntheticsTest(unittest.TestCase): 26*99e0aae7SDavid Rees 27*99e0aae7SDavid Rees def _find_attribute(self, field, name): 28*99e0aae7SDavid Rees result = None 29*99e0aae7SDavid Rees for attribute in field.attribute: 30*99e0aae7SDavid Rees if attribute.name.text == name: 31*99e0aae7SDavid Rees self.assertIsNone(result) 32*99e0aae7SDavid Rees result = attribute 33*99e0aae7SDavid Rees self.assertIsNotNone(result) 34*99e0aae7SDavid Rees return result 35*99e0aae7SDavid Rees 36*99e0aae7SDavid Rees def _make_ir(self, emb_text): 37*99e0aae7SDavid Rees ir, unused_debug_info, errors = glue.parse_emboss_file( 38*99e0aae7SDavid Rees "m.emb", 39*99e0aae7SDavid Rees test_util.dict_file_reader({"m.emb": emb_text}), 40*99e0aae7SDavid Rees stop_before_step="desugar") 41*99e0aae7SDavid Rees assert not errors, errors 42*99e0aae7SDavid Rees return ir 43*99e0aae7SDavid Rees 44*99e0aae7SDavid Rees def test_nothing_to_do(self): 45*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 46*99e0aae7SDavid Rees " 0 [+1] UInt x\n" 47*99e0aae7SDavid Rees " 1 [+1] UInt:8[] y\n") 48*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 49*99e0aae7SDavid Rees 50*99e0aae7SDavid Rees def test_adds_anonymous_bits_fields(self): 51*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 52*99e0aae7SDavid Rees " 0 [+1] bits:\n" 53*99e0aae7SDavid Rees " 0 [+4] Bar bar\n" 54*99e0aae7SDavid Rees " 4 [+4] UInt uint\n" 55*99e0aae7SDavid Rees " 1 [+1] bits:\n" 56*99e0aae7SDavid Rees " 0 [+4] Bits nested_bits\n" 57*99e0aae7SDavid Rees "enum Bar:\n" 58*99e0aae7SDavid Rees " BAR = 0\n" 59*99e0aae7SDavid Rees "bits Bits:\n" 60*99e0aae7SDavid Rees " 0 [+4] UInt uint\n") 61*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 62*99e0aae7SDavid Rees structure = ir.module[0].type[0].structure 63*99e0aae7SDavid Rees # The first field should be the anonymous bits structure. 64*99e0aae7SDavid Rees self.assertTrue(structure.field[0].HasField("location")) 65*99e0aae7SDavid Rees # Then the aliases generated for those structures. 66*99e0aae7SDavid Rees self.assertEqual("bar", structure.field[1].name.name.text) 67*99e0aae7SDavid Rees self.assertEqual("uint", structure.field[2].name.name.text) 68*99e0aae7SDavid Rees # Then the second anonymous bits. 69*99e0aae7SDavid Rees self.assertTrue(structure.field[3].HasField("location")) 70*99e0aae7SDavid Rees # Then the alias from the second anonymous bits. 71*99e0aae7SDavid Rees self.assertEqual("nested_bits", structure.field[4].name.name.text) 72*99e0aae7SDavid Rees 73*99e0aae7SDavid Rees def test_adds_correct_existence_condition(self): 74*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 75*99e0aae7SDavid Rees " 0 [+1] bits:\n" 76*99e0aae7SDavid Rees " 0 [+4] UInt bar\n") 77*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 78*99e0aae7SDavid Rees bits_field = ir.module[0].type[0].structure.field[0] 79*99e0aae7SDavid Rees alias_field = ir.module[0].type[0].structure.field[1] 80*99e0aae7SDavid Rees self.assertEqual("bar", alias_field.name.name.text) 81*99e0aae7SDavid Rees self.assertEqual(bits_field.name.name.text, 82*99e0aae7SDavid Rees alias_field.existence_condition.function.args[0].function. 83*99e0aae7SDavid Rees args[0].field_reference.path[0].source_name[-1].text) 84*99e0aae7SDavid Rees self.assertEqual(bits_field.name.name.text, 85*99e0aae7SDavid Rees alias_field.existence_condition.function.args[1].function. 86*99e0aae7SDavid Rees args[0].field_reference.path[0].source_name[-1].text) 87*99e0aae7SDavid Rees self.assertEqual("bar", 88*99e0aae7SDavid Rees alias_field.existence_condition.function.args[1].function. 89*99e0aae7SDavid Rees args[0].field_reference.path[1].source_name[-1].text) 90*99e0aae7SDavid Rees self.assertEqual( 91*99e0aae7SDavid Rees ir_data.FunctionMapping.PRESENCE, 92*99e0aae7SDavid Rees alias_field.existence_condition.function.args[0].function.function) 93*99e0aae7SDavid Rees self.assertEqual( 94*99e0aae7SDavid Rees ir_data.FunctionMapping.PRESENCE, 95*99e0aae7SDavid Rees alias_field.existence_condition.function.args[1].function.function) 96*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.AND, 97*99e0aae7SDavid Rees alias_field.existence_condition.function.function) 98*99e0aae7SDavid Rees 99*99e0aae7SDavid Rees def test_adds_correct_read_transform(self): 100*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 101*99e0aae7SDavid Rees " 0 [+1] bits:\n" 102*99e0aae7SDavid Rees " 0 [+4] UInt bar\n") 103*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 104*99e0aae7SDavid Rees bits_field = ir.module[0].type[0].structure.field[0] 105*99e0aae7SDavid Rees alias_field = ir.module[0].type[0].structure.field[1] 106*99e0aae7SDavid Rees self.assertEqual("bar", alias_field.name.name.text) 107*99e0aae7SDavid Rees self.assertEqual( 108*99e0aae7SDavid Rees bits_field.name.name.text, 109*99e0aae7SDavid Rees alias_field.read_transform.field_reference.path[0].source_name[-1].text) 110*99e0aae7SDavid Rees self.assertEqual( 111*99e0aae7SDavid Rees "bar", 112*99e0aae7SDavid Rees alias_field.read_transform.field_reference.path[1].source_name[-1].text) 113*99e0aae7SDavid Rees 114*99e0aae7SDavid Rees def test_adds_correct_abbreviation(self): 115*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 116*99e0aae7SDavid Rees " 0 [+1] bits:\n" 117*99e0aae7SDavid Rees " 0 [+4] UInt bar\n" 118*99e0aae7SDavid Rees " 4 [+4] UInt baz (qux)\n") 119*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 120*99e0aae7SDavid Rees bar_alias = ir.module[0].type[0].structure.field[1] 121*99e0aae7SDavid Rees baz_alias = ir.module[0].type[0].structure.field[2] 122*99e0aae7SDavid Rees self.assertFalse(bar_alias.HasField("abbreviation")) 123*99e0aae7SDavid Rees self.assertEqual("qux", baz_alias.abbreviation.text) 124*99e0aae7SDavid Rees 125*99e0aae7SDavid Rees def test_anonymous_bits_sets_correct_is_synthetic(self): 126*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 127*99e0aae7SDavid Rees " 0 [+1] bits:\n" 128*99e0aae7SDavid Rees " 0 [+4] UInt bar (b)\n") 129*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 130*99e0aae7SDavid Rees bits_field = ir.module[0].type[0].subtype[0].structure.field[0] 131*99e0aae7SDavid Rees alias_field = ir.module[0].type[0].structure.field[1] 132*99e0aae7SDavid Rees self.assertFalse(alias_field.name.source_location.is_synthetic) 133*99e0aae7SDavid Rees self.assertTrue(alias_field.HasField("abbreviation")) 134*99e0aae7SDavid Rees self.assertFalse(alias_field.abbreviation.source_location.is_synthetic) 135*99e0aae7SDavid Rees self.assertTrue(alias_field.HasField("read_transform")) 136*99e0aae7SDavid Rees read_alias = alias_field.read_transform 137*99e0aae7SDavid Rees self.assertTrue(read_alias.source_location.is_synthetic) 138*99e0aae7SDavid Rees self.assertTrue( 139*99e0aae7SDavid Rees read_alias.field_reference.path[0].source_location.is_synthetic) 140*99e0aae7SDavid Rees alias_condition = alias_field.existence_condition 141*99e0aae7SDavid Rees self.assertTrue(alias_condition.source_location.is_synthetic) 142*99e0aae7SDavid Rees self.assertTrue( 143*99e0aae7SDavid Rees alias_condition.function.args[0].source_location.is_synthetic) 144*99e0aae7SDavid Rees self.assertTrue(bits_field.name.source_location.is_synthetic) 145*99e0aae7SDavid Rees self.assertTrue(bits_field.name.name.source_location.is_synthetic) 146*99e0aae7SDavid Rees self.assertTrue(bits_field.abbreviation.source_location.is_synthetic) 147*99e0aae7SDavid Rees 148*99e0aae7SDavid Rees def test_adds_text_output_skip_attribute_to_anonymous_bits(self): 149*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 150*99e0aae7SDavid Rees " 0 [+1] bits:\n" 151*99e0aae7SDavid Rees " 0 [+4] UInt bar (b)\n") 152*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 153*99e0aae7SDavid Rees bits_field = ir.module[0].type[0].structure.field[0] 154*99e0aae7SDavid Rees text_output_attribute = self._find_attribute(bits_field, "text_output") 155*99e0aae7SDavid Rees self.assertEqual("Skip", text_output_attribute.value.string_constant.text) 156*99e0aae7SDavid Rees 157*99e0aae7SDavid Rees def test_skip_attribute_is_marked_as_synthetic(self): 158*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 159*99e0aae7SDavid Rees " 0 [+1] bits:\n" 160*99e0aae7SDavid Rees " 0 [+4] UInt bar\n") 161*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 162*99e0aae7SDavid Rees bits_field = ir.module[0].type[0].structure.field[0] 163*99e0aae7SDavid Rees attribute = self._find_attribute(bits_field, "text_output") 164*99e0aae7SDavid Rees self.assertTrue(attribute.source_location.is_synthetic) 165*99e0aae7SDavid Rees self.assertTrue(attribute.name.source_location.is_synthetic) 166*99e0aae7SDavid Rees self.assertTrue(attribute.value.source_location.is_synthetic) 167*99e0aae7SDavid Rees self.assertTrue( 168*99e0aae7SDavid Rees attribute.value.string_constant.source_location.is_synthetic) 169*99e0aae7SDavid Rees 170*99e0aae7SDavid Rees def test_adds_size_in_bytes(self): 171*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 172*99e0aae7SDavid Rees " 1 [+l] UInt:8[] bytes\n" 173*99e0aae7SDavid Rees " 0 [+1] UInt length (l)\n") 174*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 175*99e0aae7SDavid Rees structure = ir.module[0].type[0].structure 176*99e0aae7SDavid Rees size_in_bytes_field = structure.field[2] 177*99e0aae7SDavid Rees max_size_in_bytes_field = structure.field[3] 178*99e0aae7SDavid Rees min_size_in_bytes_field = structure.field[4] 179*99e0aae7SDavid Rees self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text) 180*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.MAXIMUM, 181*99e0aae7SDavid Rees size_in_bytes_field.read_transform.function.function) 182*99e0aae7SDavid Rees self.assertEqual("$max_size_in_bytes", 183*99e0aae7SDavid Rees max_size_in_bytes_field.name.name.text) 184*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.UPPER_BOUND, 185*99e0aae7SDavid Rees max_size_in_bytes_field.read_transform.function.function) 186*99e0aae7SDavid Rees self.assertEqual("$min_size_in_bytes", 187*99e0aae7SDavid Rees min_size_in_bytes_field.name.name.text) 188*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.LOWER_BOUND, 189*99e0aae7SDavid Rees min_size_in_bytes_field.read_transform.function.function) 190*99e0aae7SDavid Rees # The correctness of $size_in_bytes et al are tested much further down 191*99e0aae7SDavid Rees # stream, in tests of the generated C++ code. 192*99e0aae7SDavid Rees 193*99e0aae7SDavid Rees def test_adds_size_in_bits(self): 194*99e0aae7SDavid Rees ir = self._make_ir("bits Foo:\n" 195*99e0aae7SDavid Rees " 1 [+9] UInt hi\n" 196*99e0aae7SDavid Rees " 0 [+1] Flag lo\n") 197*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 198*99e0aae7SDavid Rees structure = ir.module[0].type[0].structure 199*99e0aae7SDavid Rees size_in_bits_field = structure.field[2] 200*99e0aae7SDavid Rees max_size_in_bits_field = structure.field[3] 201*99e0aae7SDavid Rees min_size_in_bits_field = structure.field[4] 202*99e0aae7SDavid Rees self.assertEqual("$size_in_bits", size_in_bits_field.name.name.text) 203*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.MAXIMUM, 204*99e0aae7SDavid Rees size_in_bits_field.read_transform.function.function) 205*99e0aae7SDavid Rees self.assertEqual("$max_size_in_bits", 206*99e0aae7SDavid Rees max_size_in_bits_field.name.name.text) 207*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.UPPER_BOUND, 208*99e0aae7SDavid Rees max_size_in_bits_field.read_transform.function.function) 209*99e0aae7SDavid Rees self.assertEqual("$min_size_in_bits", 210*99e0aae7SDavid Rees min_size_in_bits_field.name.name.text) 211*99e0aae7SDavid Rees self.assertEqual(ir_data.FunctionMapping.LOWER_BOUND, 212*99e0aae7SDavid Rees min_size_in_bits_field.read_transform.function.function) 213*99e0aae7SDavid Rees # The correctness of $size_in_bits et al are tested much further down 214*99e0aae7SDavid Rees # stream, in tests of the generated C++ code. 215*99e0aae7SDavid Rees 216*99e0aae7SDavid Rees def test_adds_text_output_skip_attribute_to_size_in_bytes(self): 217*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 218*99e0aae7SDavid Rees " 1 [+l] UInt:8[] bytes\n" 219*99e0aae7SDavid Rees " 0 [+1] UInt length (l)\n") 220*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 221*99e0aae7SDavid Rees size_in_bytes_field = ir.module[0].type[0].structure.field[2] 222*99e0aae7SDavid Rees self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text) 223*99e0aae7SDavid Rees text_output_attribute = self._find_attribute(size_in_bytes_field, 224*99e0aae7SDavid Rees "text_output") 225*99e0aae7SDavid Rees self.assertEqual("Skip", text_output_attribute.value.string_constant.text) 226*99e0aae7SDavid Rees 227*99e0aae7SDavid Rees def test_replaces_next(self): 228*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 229*99e0aae7SDavid Rees " 1 [+2] UInt:8[] a\n" 230*99e0aae7SDavid Rees " $next [+4] UInt b\n" 231*99e0aae7SDavid Rees " $next [+1] UInt c\n") 232*99e0aae7SDavid Rees self.assertEqual([], synthetics.desugar(ir)) 233*99e0aae7SDavid Rees offset_of_b = ir.module[0].type[0].structure.field[1].location.start 234*99e0aae7SDavid Rees self.assertTrue(offset_of_b.HasField("function")) 235*99e0aae7SDavid Rees self.assertEqual(offset_of_b.function.function, ir_data.FunctionMapping.ADDITION) 236*99e0aae7SDavid Rees self.assertEqual(offset_of_b.function.args[0].constant.value, "1") 237*99e0aae7SDavid Rees self.assertEqual(offset_of_b.function.args[1].constant.value, "2") 238*99e0aae7SDavid Rees offset_of_c = ir.module[0].type[0].structure.field[2].location.start 239*99e0aae7SDavid Rees self.assertEqual( 240*99e0aae7SDavid Rees offset_of_c.function.args[0].function.args[0].constant.value, "1") 241*99e0aae7SDavid Rees self.assertEqual( 242*99e0aae7SDavid Rees offset_of_c.function.args[0].function.args[1].constant.value, "2") 243*99e0aae7SDavid Rees self.assertEqual(offset_of_c.function.args[1].constant.value, "4") 244*99e0aae7SDavid Rees 245*99e0aae7SDavid Rees def test_next_in_first_field(self): 246*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 247*99e0aae7SDavid Rees " $next [+2] UInt:8[] a\n" 248*99e0aae7SDavid Rees " $next [+4] UInt b\n") 249*99e0aae7SDavid Rees struct = ir.module[0].type[0].structure 250*99e0aae7SDavid Rees self.assertEqual([[ 251*99e0aae7SDavid Rees error.error("m.emb", struct.field[0].location.start.source_location, 252*99e0aae7SDavid Rees "`$next` may not be used in the first physical field of " + 253*99e0aae7SDavid Rees "a structure; perhaps you meant `0`?"), 254*99e0aae7SDavid Rees ]], synthetics.desugar(ir)) 255*99e0aae7SDavid Rees 256*99e0aae7SDavid Rees def test_next_in_size(self): 257*99e0aae7SDavid Rees ir = self._make_ir("struct Foo:\n" 258*99e0aae7SDavid Rees " 0 [+2] UInt:8[] a\n" 259*99e0aae7SDavid Rees " 1 [+$next] UInt b\n") 260*99e0aae7SDavid Rees struct = ir.module[0].type[0].structure 261*99e0aae7SDavid Rees self.assertEqual([[ 262*99e0aae7SDavid Rees error.error("m.emb", struct.field[1].location.size.source_location, 263*99e0aae7SDavid Rees "`$next` may only be used in the start expression of a " + 264*99e0aae7SDavid Rees "physical field."), 265*99e0aae7SDavid Rees ]], synthetics.desugar(ir)) 266*99e0aae7SDavid Rees 267*99e0aae7SDavid Rees 268*99e0aae7SDavid Reesif __name__ == "__main__": 269*99e0aae7SDavid Rees unittest.main() 270