xref: /linux/tools/net/sunrpc/xdrgen/generators/struct.py (revision fcc202d324e5d2a21557ddd20fc3f96e08c4f72d)
1#!/usr/bin/env python3
2# ex: set filetype=python:
3
4"""Generate code to handle XDR struct types"""
5
6from jinja2 import Environment
7
8from generators import SourceGenerator, kernel_c_type
9from generators import create_jinja2_environment, get_jinja2_template
10
11from xdr_ast import _XdrBasic, _XdrString
12from xdr_ast import _XdrFixedLengthOpaque, _XdrVariableLengthOpaque
13from xdr_ast import _XdrFixedLengthArray, _XdrVariableLengthArray
14from xdr_ast import _XdrOptionalData, _XdrStruct, _XdrDeclaration
15from xdr_ast import public_apis, get_header_name, aggregate_members
16from xdr_ast import pages_members, pages_member_maxsize, pages_member_is_decoded
17
18
19def aggregate_hook_base(struct_name: str) -> str:
20    """Return the application hook base name for a struct's aggregate members.
21
22    The pragma header name prefixes it so two programs that share a
23    type name (nfs_acl2 vs nfs_acl3) derive distinct external hook
24    symbols. All marked members of one struct share a hook set; the
25    cursor's member_id tells them apart.
26    """
27    return "_".join((get_header_name(), struct_name))
28
29
30def aggregate_member_symbol(struct_name: str, member_name: str) -> str:
31    """Return the symbolic member id for one marked aggregate member.
32
33    The generated framing sets the cursor's member_id to this constant
34    and the hooks compare against it. The enum in aggregate_hooks.j2
35    assigns the values implicitly in field order, so a hook that
36    compared against the bare integer would silently bind to the
37    wrong member once the specification's members were reordered.
38    """
39    return "_".join((aggregate_hook_base(struct_name), member_name)).upper()
40
41
42def emit_struct_declaration(environment: Environment, node: _XdrStruct) -> None:
43    """Emit one declaration pair for an XDR struct type"""
44    if node.name in public_apis:
45        template = get_jinja2_template(environment, "declaration", "close")
46        print(template.render(name=node.name))
47    marked = [
48        field
49        for field in node.fields
50        if (node.name, field.name) in aggregate_members
51    ]
52    if marked:
53        template = get_jinja2_template(
54            environment, "declaration", "aggregate_hooks"
55        )
56        print(
57            template.render(
58                hook=aggregate_hook_base(node.name),
59                c_type=kernel_c_type(marked[0].spec),
60                classifier=marked[0].spec.c_classifier,
61                members=[
62                    aggregate_member_symbol(node.name, field.name)
63                    for field in marked
64                ],
65            )
66        )
67
68
69def emit_struct_member_definition(
70    environment: Environment, field: _XdrDeclaration, struct_name: str, peer: str
71) -> None:
72    """Emit a definition for one field in an XDR struct"""
73    if peer == "server" and pages_member_is_decoded(struct_name, field.name):
74        template = get_jinja2_template(environment, "definition", "pages_opaque")
75        print(template.render(name=field.name))
76        return
77    if isinstance(field, _XdrBasic):
78        template = get_jinja2_template(environment, "definition", field.template)
79        print(
80            template.render(
81                name=field.name,
82                type=kernel_c_type(field.spec),
83                classifier=field.spec.c_classifier,
84            )
85        )
86    elif isinstance(field, _XdrFixedLengthOpaque):
87        template = get_jinja2_template(environment, "definition", field.template)
88        print(
89            template.render(
90                name=field.name,
91                size=field.size,
92            )
93        )
94    elif isinstance(field, _XdrVariableLengthOpaque):
95        template = get_jinja2_template(environment, "definition", field.template)
96        print(template.render(name=field.name))
97    elif isinstance(field, _XdrString):
98        template = get_jinja2_template(environment, "definition", field.template)
99        print(template.render(name=field.name))
100    elif isinstance(field, _XdrFixedLengthArray):
101        template = get_jinja2_template(environment, "definition", field.template)
102        print(
103            template.render(
104                name=field.name,
105                type=kernel_c_type(field.spec),
106                size=field.size,
107            )
108        )
109    elif isinstance(field, _XdrVariableLengthArray):
110        template = get_jinja2_template(environment, "definition", field.template)
111        print(
112            template.render(
113                name=field.name,
114                type=kernel_c_type(field.spec),
115                classifier=field.spec.c_classifier,
116            )
117        )
118    elif isinstance(field, _XdrOptionalData):
119        template = get_jinja2_template(environment, "definition", field.template)
120        print(
121            template.render(
122                name=field.name,
123                type=kernel_c_type(field.spec),
124                classifier=field.spec.c_classifier,
125            )
126        )
127
128
129def emit_struct_definition(
130    environment: Environment, node: _XdrStruct, peer: str
131) -> None:
132    """Emit one definition for an XDR struct type"""
133    template = get_jinja2_template(environment, "definition", "open")
134    print(template.render(name=node.name))
135
136    for field in node.fields:
137        emit_struct_member_definition(environment, field, node.name, peer)
138
139    template = get_jinja2_template(environment, "definition", "close")
140    print(template.render(name=node.name))
141
142
143def emit_struct_member_decoder(
144    environment: Environment,
145    field: _XdrDeclaration,
146    struct_name: str,
147    peer: str,
148) -> None:
149    """Emit a decoder for one field in an XDR struct"""
150    if peer == "server" and pages_member_is_decoded(struct_name, field.name):
151        template = get_jinja2_template(environment, "decoder", "pages_opaque")
152        print(
153            template.render(
154                name=field.name,
155                maxsize=pages_member_maxsize(field),
156            )
157        )
158        return
159    if isinstance(field, _XdrVariableLengthArray) and (
160        (struct_name, field.name) in aggregate_members
161    ):
162        if peer != "server":
163            raise NotImplementedError(
164                "pragma aggregate is server-side only; "
165                + peer
166                + " generation is not yet supported"
167            )
168        template = get_jinja2_template(environment, "decoder", "aggregate_array")
169        print(
170            template.render(
171                name=field.name,
172                type=field.spec.type_name,
173                c_type=kernel_c_type(field.spec),
174                classifier=field.spec.c_classifier,
175                maxsize=field.maxsize,
176                hook=aggregate_hook_base(struct_name),
177                member_sym=aggregate_member_symbol(struct_name, field.name),
178            )
179        )
180        return
181    if isinstance(field, _XdrOptionalData) and (
182        (struct_name, field.name) in aggregate_members
183    ):
184        if peer != "server":
185            raise NotImplementedError(
186                "pragma aggregate is server-side only; "
187                + peer
188                + " generation is not yet supported"
189            )
190        template = get_jinja2_template(environment, "decoder", "aggregate_optional")
191        print(
192            template.render(
193                name=field.name,
194                type=field.spec.type_name,
195                c_type=kernel_c_type(field.spec),
196                classifier=field.spec.c_classifier,
197                hook=aggregate_hook_base(struct_name),
198                member_sym=aggregate_member_symbol(struct_name, field.name),
199            )
200        )
201        return
202    if isinstance(field, _XdrBasic):
203        template = get_jinja2_template(environment, "decoder", field.template)
204        print(
205            template.render(
206                name=field.name,
207                type=field.spec.type_name,
208                classifier=field.spec.c_classifier,
209            )
210        )
211    elif isinstance(field, _XdrFixedLengthOpaque):
212        template = get_jinja2_template(environment, "decoder", field.template)
213        print(
214            template.render(
215                name=field.name,
216                size=field.size,
217            )
218        )
219    elif isinstance(field, _XdrVariableLengthOpaque):
220        template = get_jinja2_template(environment, "decoder", field.template)
221        print(
222            template.render(
223                name=field.name,
224                maxsize=field.maxsize,
225            )
226        )
227    elif isinstance(field, _XdrString):
228        template = get_jinja2_template(environment, "decoder", field.template)
229        print(
230            template.render(
231                name=field.name,
232                maxsize=field.maxsize,
233            )
234        )
235    elif isinstance(field, _XdrFixedLengthArray):
236        template = get_jinja2_template(environment, "decoder", field.template)
237        print(
238            template.render(
239                name=field.name,
240                type=field.spec.type_name,
241                size=field.size,
242                classifier=field.spec.c_classifier,
243            )
244        )
245    elif isinstance(field, _XdrVariableLengthArray):
246        template = get_jinja2_template(environment, "decoder", field.template)
247        print(
248            template.render(
249                name=field.name,
250                type=field.spec.type_name,
251                maxsize=field.maxsize,
252                classifier=field.spec.c_classifier,
253            )
254        )
255    elif isinstance(field, _XdrOptionalData):
256        template = get_jinja2_template(environment, "decoder", field.template)
257        print(
258            template.render(
259                name=field.name,
260                type=field.spec.type_name,
261                classifier=field.spec.c_classifier,
262            )
263        )
264
265
266def emit_struct_decoder(
267    environment: Environment, node: _XdrStruct, peer: str
268) -> None:
269    """Emit one decoder function for an XDR struct type"""
270    template = get_jinja2_template(environment, "decoder", "open")
271    print(template.render(name=node.name))
272
273    for field in node.fields:
274        emit_struct_member_decoder(environment, field, node.name, peer)
275        # The optional-data aggregate decoder fails and closes the
276        # function; the members after it are never decoded.
277        if isinstance(field, _XdrOptionalData) and (
278            (node.name, field.name) in aggregate_members
279        ):
280            return
281
282    template = get_jinja2_template(environment, "decoder", "close")
283    print(template.render())
284
285
286def emit_struct_member_encoder(
287    environment: Environment,
288    field: _XdrDeclaration,
289    struct_name: str,
290    peer: str,
291) -> None:
292    """Emit an encoder for one field in an XDR struct"""
293    if isinstance(field, _XdrVariableLengthArray) and (
294        (struct_name, field.name) in aggregate_members
295    ):
296        if peer != "server":
297            raise NotImplementedError(
298                "pragma aggregate is server-side only; "
299                + peer
300                + " generation is not yet supported"
301            )
302        template = get_jinja2_template(environment, "encoder", "aggregate_array")
303        print(
304            template.render(
305                name=field.name,
306                type=field.spec.type_name,
307                c_type=kernel_c_type(field.spec),
308                classifier=field.spec.c_classifier,
309                maxsize=field.maxsize,
310                hook=aggregate_hook_base(struct_name),
311                member_sym=aggregate_member_symbol(struct_name, field.name),
312            )
313        )
314        return
315    if isinstance(field, _XdrOptionalData) and (
316        (struct_name, field.name) in aggregate_members
317    ):
318        if peer != "server":
319            raise NotImplementedError(
320                "pragma aggregate is server-side only; "
321                + peer
322                + " generation is not yet supported"
323            )
324        template = get_jinja2_template(environment, "encoder", "aggregate_optional")
325        print(
326            template.render(
327                name=field.name,
328                type=field.spec.type_name,
329                c_type=kernel_c_type(field.spec),
330                classifier=field.spec.c_classifier,
331                hook=aggregate_hook_base(struct_name),
332                member_sym=aggregate_member_symbol(struct_name, field.name),
333            )
334        )
335        return
336    if (struct_name, field.name) in pages_members:
337        if peer != "server":
338            raise NotImplementedError(
339                "pragma pages is server-side only; "
340                + peer
341                + " generation is not yet supported"
342            )
343        # Both representations carry the length field, which is all the
344        # pages encoder reads, so pages_member_is_decoded() does not
345        # gate this site.
346        template = get_jinja2_template(environment, "encoder", "pages_opaque")
347        print(
348            template.render(
349                name=field.name,
350                maxsize=pages_member_maxsize(field),
351            )
352        )
353        return
354    if isinstance(field, _XdrBasic):
355        template = get_jinja2_template(environment, "encoder", field.template)
356        print(
357            template.render(
358                name=field.name,
359                type=field.spec.type_name,
360            )
361        )
362    elif isinstance(field, _XdrFixedLengthOpaque):
363        template = get_jinja2_template(environment, "encoder", field.template)
364        print(
365            template.render(
366                name=field.name,
367                size=field.size,
368            )
369        )
370    elif isinstance(field, _XdrVariableLengthOpaque):
371        template = get_jinja2_template(environment, "encoder", field.template)
372        print(
373            template.render(
374                name=field.name,
375                maxsize=field.maxsize,
376            )
377        )
378    elif isinstance(field, _XdrString):
379        template = get_jinja2_template(environment, "encoder", field.template)
380        print(
381            template.render(
382                name=field.name,
383                maxsize=field.maxsize,
384            )
385        )
386    elif isinstance(field, _XdrFixedLengthArray):
387        template = get_jinja2_template(environment, "encoder", field.template)
388        print(
389            template.render(
390                name=field.name,
391                type=field.spec.type_name,
392                size=field.size,
393            )
394        )
395    elif isinstance(field, _XdrVariableLengthArray):
396        template = get_jinja2_template(environment, "encoder", field.template)
397        print(
398            template.render(
399                name=field.name,
400                type=field.spec.type_name,
401                maxsize=field.maxsize,
402            )
403        )
404    elif isinstance(field, _XdrOptionalData):
405        template = get_jinja2_template(environment, "encoder", field.template)
406        print(
407            template.render(
408                name=field.name,
409                type=field.spec.type_name,
410                classifier=field.spec.c_classifier,
411            )
412        )
413
414
415def emit_struct_encoder(environment: Environment, node: _XdrStruct, peer: str) -> None:
416    """Emit one encoder function for an XDR struct type"""
417    template = get_jinja2_template(environment, "encoder", "open")
418    print(template.render(name=node.name))
419
420    for field in node.fields:
421        emit_struct_member_encoder(environment, field, node.name, peer)
422
423    template = get_jinja2_template(environment, "encoder", "close")
424    print(template.render())
425
426
427def emit_struct_maxsize(environment: Environment, node: _XdrStruct) -> None:
428    """Emit one maxsize macro for an XDR struct type"""
429    macro_name = get_header_name().upper() + "_" + node.name + "_sz"
430    template = get_jinja2_template(environment, "maxsize", "struct")
431    print(
432        template.render(
433            macro=macro_name,
434            width=" + ".join(node.symbolic_width()),
435        )
436    )
437
438
439class XdrStructGenerator(SourceGenerator):
440    """Generate source code for XDR structs"""
441
442    def __init__(self, language: str, peer: str):
443        """Initialize an instance of this class"""
444        self.environment = create_jinja2_environment(language, "struct")
445        self.peer = peer
446
447    def emit_declaration(self, node: _XdrStruct) -> None:
448        """Emit one declaration pair for an XDR struct type"""
449        emit_struct_declaration(self.environment, node)
450
451    def emit_definition(self, node: _XdrStruct) -> None:
452        """Emit one definition for an XDR struct type"""
453        emit_struct_definition(self.environment, node, self.peer)
454
455    def emit_decoder(self, node: _XdrStruct) -> None:
456        """Emit one decoder function for an XDR struct type"""
457        emit_struct_decoder(self.environment, node, self.peer)
458
459    def emit_encoder(self, node: _XdrStruct) -> None:
460        """Emit one encoder function for an XDR struct type"""
461        emit_struct_encoder(self.environment, node, self.peer)
462
463    def emit_maxsize(self, node: _XdrStruct) -> None:
464        """Emit one maxsize macro for an XDR struct type"""
465        emit_struct_maxsize(self.environment, node)
466