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