1#!/usr/bin/env python3 2# ex: set filetype=python: 3 4"""Generate code to handle XDR unions""" 5 6from jinja2 import Environment 7 8from generators import SourceGenerator 9from generators import create_jinja2_environment, get_jinja2_template 10 11from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, get_header_name 12from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis, big_endian 13 14 15def emit_union_declaration(environment: Environment, node: _XdrUnion) -> None: 16 """Emit one declaration pair for an XDR union type""" 17 if node.name in public_apis: 18 template = get_jinja2_template(environment, "declaration", "close") 19 print(template.render(name=node.name)) 20 21 22def emit_union_switch_spec_definition( 23 environment: Environment, node: _XdrDeclaration 24) -> None: 25 """Emit a definition for an XDR union's discriminant""" 26 assert isinstance(node, _XdrBasic) 27 template = get_jinja2_template(environment, "definition", "switch_spec") 28 print( 29 template.render( 30 name=node.name, 31 type=node.spec.type_name, 32 classifier=node.spec.c_classifier, 33 ) 34 ) 35 36 37def emit_union_case_spec_definition( 38 environment: Environment, node: _XdrDeclaration 39) -> None: 40 """Emit a definition for an XDR union's case arm""" 41 if isinstance(node.arm, _XdrVoid): 42 return 43 assert isinstance(node.arm, _XdrBasic) 44 template = get_jinja2_template(environment, "definition", "case_spec") 45 print( 46 template.render( 47 name=node.arm.name, 48 type=node.arm.spec.type_name, 49 classifier=node.arm.spec.c_classifier, 50 ) 51 ) 52 53 54def emit_union_definition(environment: Environment, node: _XdrUnion) -> None: 55 """Emit one XDR union definition""" 56 template = get_jinja2_template(environment, "definition", "open") 57 print(template.render(name=node.name)) 58 59 emit_union_switch_spec_definition(environment, node.discriminant) 60 61 for case in node.cases: 62 emit_union_case_spec_definition(environment, case) 63 64 if node.default is not None: 65 emit_union_case_spec_definition(environment, node.default) 66 67 template = get_jinja2_template(environment, "definition", "close") 68 print(template.render(name=node.name)) 69 70 71def emit_union_switch_spec_decoder( 72 environment: Environment, node: _XdrDeclaration 73) -> None: 74 """Emit a decoder for an XDR union's discriminant""" 75 assert isinstance(node, _XdrBasic) 76 template = get_jinja2_template(environment, "decoder", "switch_spec") 77 print(template.render(name=node.name, type=node.spec.type_name)) 78 79 80def emit_union_case_spec_decoder( 81 environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool 82) -> None: 83 """Emit decoder functions for an XDR union's case arm""" 84 85 if isinstance(node.arm, _XdrVoid): 86 return 87 88 if big_endian_discriminant: 89 template = get_jinja2_template(environment, "decoder", "case_spec_be") 90 else: 91 template = get_jinja2_template(environment, "decoder", "case_spec") 92 for case in node.values: 93 print(template.render(case=case)) 94 95 assert isinstance(node.arm, _XdrBasic) 96 template = get_jinja2_template(environment, "decoder", node.arm.template) 97 print( 98 template.render( 99 name=node.arm.name, 100 type=node.arm.spec.type_name, 101 classifier=node.arm.spec.c_classifier, 102 ) 103 ) 104 105 template = get_jinja2_template(environment, "decoder", "break") 106 print(template.render()) 107 108 109def emit_union_default_spec_decoder(environment: Environment, node: _XdrUnion) -> None: 110 """Emit a decoder function for an XDR union's default arm""" 111 default_case = node.default 112 113 # Avoid a gcc warning about a default case with boolean discriminant 114 if default_case is None and node.discriminant.spec.type_name == "bool": 115 return 116 117 template = get_jinja2_template(environment, "decoder", "default_spec") 118 print(template.render()) 119 120 if default_case is None or isinstance(default_case.arm, _XdrVoid): 121 template = get_jinja2_template(environment, "decoder", "break") 122 print(template.render()) 123 return 124 125 assert isinstance(default_case.arm, _XdrBasic) 126 template = get_jinja2_template(environment, "decoder", default_case.arm.template) 127 print( 128 template.render( 129 name=default_case.arm.name, 130 type=default_case.arm.spec.type_name, 131 classifier=default_case.arm.spec.c_classifier, 132 ) 133 ) 134 135 136def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None: 137 """Emit one XDR union decoder""" 138 template = get_jinja2_template(environment, "decoder", "open") 139 print(template.render(name=node.name)) 140 141 emit_union_switch_spec_decoder(environment, node.discriminant) 142 143 for case in node.cases: 144 emit_union_case_spec_decoder( 145 environment, 146 case, 147 node.discriminant.spec.type_name in big_endian, 148 ) 149 150 emit_union_default_spec_decoder(environment, node) 151 152 template = get_jinja2_template(environment, "decoder", "close") 153 print(template.render()) 154 155 156def emit_union_switch_spec_encoder( 157 environment: Environment, node: _XdrDeclaration 158) -> None: 159 """Emit an encoder for an XDR union's discriminant""" 160 assert isinstance(node, _XdrBasic) 161 template = get_jinja2_template(environment, "encoder", "switch_spec") 162 print(template.render(name=node.name, type=node.spec.type_name)) 163 164 165def emit_union_case_spec_encoder( 166 environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool 167) -> None: 168 """Emit encoder functions for an XDR union's case arm""" 169 170 if isinstance(node.arm, _XdrVoid): 171 return 172 173 if big_endian_discriminant: 174 template = get_jinja2_template(environment, "encoder", "case_spec_be") 175 else: 176 template = get_jinja2_template(environment, "encoder", "case_spec") 177 for case in node.values: 178 print(template.render(case=case)) 179 180 template = get_jinja2_template(environment, "encoder", node.arm.template) 181 print( 182 template.render( 183 name=node.arm.name, 184 type=node.arm.spec.type_name, 185 ) 186 ) 187 188 template = get_jinja2_template(environment, "encoder", "break") 189 print(template.render()) 190 191 192def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -> None: 193 """Emit an encoder function for an XDR union's default arm""" 194 default_case = node.default 195 196 # Avoid a gcc warning about a default case with boolean discriminant 197 if default_case is None and node.discriminant.spec.type_name == "bool": 198 return 199 200 template = get_jinja2_template(environment, "encoder", "default_spec") 201 print(template.render()) 202 203 if default_case is None or isinstance(default_case.arm, _XdrVoid): 204 template = get_jinja2_template(environment, "encoder", "break") 205 print(template.render()) 206 return 207 208 template = get_jinja2_template(environment, "encoder", default_case.arm.template) 209 print( 210 template.render( 211 name=default_case.arm.name, 212 type=default_case.arm.spec.type_name, 213 ) 214 ) 215 216 217def emit_union_encoder(environment, node: _XdrUnion) -> None: 218 """Emit one XDR union encoder""" 219 template = get_jinja2_template(environment, "encoder", "open") 220 print(template.render(name=node.name)) 221 222 emit_union_switch_spec_encoder(environment, node.discriminant) 223 224 for case in node.cases: 225 emit_union_case_spec_encoder( 226 environment, 227 case, 228 node.discriminant.spec.type_name in big_endian, 229 ) 230 231 emit_union_default_spec_encoder(environment, node) 232 233 template = get_jinja2_template(environment, "encoder", "close") 234 print(template.render()) 235 236 237def emit_union_maxsize(environment: Environment, node: _XdrUnion) -> None: 238 """Emit one maxsize macro for an XDR union type""" 239 macro_name = get_header_name().upper() + "_" + node.name + "_sz" 240 template = get_jinja2_template(environment, "maxsize", "union") 241 print( 242 template.render( 243 macro=macro_name, 244 width=" + ".join(node.symbolic_width()), 245 ) 246 ) 247 248 249class XdrUnionGenerator(SourceGenerator): 250 """Generate source code for XDR unions""" 251 252 def __init__(self, language: str, peer: str): 253 """Initialize an instance of this class""" 254 self.environment = create_jinja2_environment(language, "union") 255 self.peer = peer 256 257 def emit_declaration(self, node: _XdrUnion) -> None: 258 """Emit one declaration pair for an XDR union""" 259 emit_union_declaration(self.environment, node) 260 261 def emit_definition(self, node: _XdrUnion) -> None: 262 """Emit one definition for an XDR union""" 263 emit_union_definition(self.environment, node) 264 265 def emit_decoder(self, node: _XdrUnion) -> None: 266 """Emit one decoder function for an XDR union""" 267 emit_union_decoder(self.environment, node) 268 269 def emit_encoder(self, node: _XdrUnion) -> None: 270 """Emit one encoder function for an XDR union""" 271 emit_union_encoder(self.environment, node) 272 273 def emit_maxsize(self, node: _XdrUnion) -> None: 274 """Emit one maxsize macro for an XDR union""" 275 emit_union_maxsize(self.environment, node) 276