Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96d7b102 authored by Henri Chataing's avatar Henri Chataing
Browse files

pdl: Fix generation of aliased child packet parsing

The generator did not handle well packets with child aliases,
as it was generating a wildcard with no constraint for
the child specialization.

Extend the generator to handle such cases, within a limit
- fields must have constraints in immediate children
  (i.e. cannot generate matcher for fields if they are constrained
   by granchildren only)
- double aliased packets are not allowed (i.e. packets with no
  constraints for two generations)

Bug: 279465240
Test: atest pdl_generated_files_compile
Change-Id: If4c3a82e8a06edeab4ef54a8c109b994f6a2dc25
parent e0734c31
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -97,6 +97,8 @@ filegroup {
        "tests/generated/packet_decl_mask_scalar_value_little_endian.rs",
        "tests/generated/packet_decl_mixed_scalars_enums_big_endian.rs",
        "tests/generated/packet_decl_mixed_scalars_enums_little_endian.rs",
        "tests/generated/packet_decl_parent_with_alias_child_big_endian.rs",
        "tests/generated/packet_decl_parent_with_alias_child_little_endian.rs",
        "tests/generated/packet_decl_parent_with_no_payload_big_endian.rs",
        "tests/generated/packet_decl_parent_with_no_payload_little_endian.rs",
        "tests/generated/packet_decl_payload_field_unknown_size_big_endian.rs",
+30 −0
Original line number Diff line number Diff line
@@ -1498,6 +1498,36 @@ mod tests {
        "
    );

    test_pdl!(
        packet_decl_parent_with_alias_child,
        "
          enum Enum8 : 8 {
            A = 0,
            B = 1,
            C = 2,
          }

          packet Parent {
            v : Enum8,
            _payload_,
          }

          packet AliasChild : Parent {
            _payload_
          }

          packet NormalChild : Parent (v = A) {
          }

          packet NormalGrandChild1 : AliasChild (v = B) {
          }

          packet NormalGrandChild2 : AliasChild (v = C) {
              _payload_
          }
        "
    );

    // TODO(mgeisler): enable this test when we have an approach to
    // struct fields with parents.
    //
+50 −37
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ use crate::backends::rust::{
};
use crate::{ast, lint};
use quote::{format_ident, quote};
use std::collections::{BTreeSet, HashMap};
use std::collections::BTreeSet;

fn size_field_ident(id: &str) -> proc_macro2::Ident {
    format_ident!("{}_size", id.trim_matches('_'))
@@ -627,51 +627,64 @@ impl<'a> FieldParser<'a> {
            return;
        }

        let child_ids = children
        // Gather fields that are constrained in immediate child declarations.
        // Keep the fields sorted by name.
        // TODO: fields that are only matched in grand children will not be included.
        let constrained_fields = children
            .iter()
            .map(|child| format_ident!("{}", child.id().unwrap()))
            .collect::<Vec<_>>();
        let child_ids_data = child_ids.iter().map(|ident| format_ident!("{ident}Data"));

        // Set of field names (sorted by name).
        let mut constrained_fields = BTreeSet::new();
        // Maps (child name, field name) -> value.
        let mut constraint_values = HashMap::new();
            .flat_map(|child| child.constraints().map(|c| &c.id))
            .collect::<BTreeSet<_>>();

        let mut match_values = Vec::new();
        let mut child_parse_args = Vec::new();
        let mut child_ids_data = Vec::new();
        let mut child_ids = Vec::new();
        let get_constraint_value = |mut constraints: std::slice::Iter<'_, ast::Constraint>,
                                    id: &str|
         -> Option<proc_macro2::TokenStream> {
            constraints.find(|c| c.id == id).map(|c| constraint_to_value(packet_scope, c))
        };

        for child in children.iter() {
            match &child.desc {
                ast::DeclDesc::Packet { id, constraints, .. }
                | ast::DeclDesc::Struct { id, constraints, .. } => {
                    for constraint in constraints.iter() {
                        constrained_fields.insert(&constraint.id);
                        constraint_values.insert(
                            (id.as_str(), &constraint.id),
                            constraint_to_value(packet_scope, constraint),
                        );
                    }
                }
                _ => unreachable!("Invalid child: {child:?}"),
            }
            let tuple_values = constrained_fields
                .iter()
                .map(|id| {
                    get_constraint_value(child.constraints(), id).map(|v| vec![v]).unwrap_or_else(
                        || {
                            self.scope
                                .file
                                .iter_children(child)
                                .filter_map(|d| get_constraint_value(d.constraints(), id))
                                .collect()
                        },
                    )
                })
                .collect::<Vec<_>>();

            // If no constraint values are found for the tuple just skip the child
            // packet as it would capture unwanted input packets.
            if tuple_values.iter().all(|v| v.is_empty()) {
                continue;
            }

        let wildcard = quote!(_);
        let match_values = children.iter().map(|child| {
            let child_id = child.id().unwrap();
            let values = constrained_fields.iter().map(|field_name| {
                constraint_values.get(&(child_id, field_name)).unwrap_or(&wildcard)
            });
            quote! {
                (#(#values),*)
            let tuple_values = tuple_values
                .iter()
                .map(|v| v.is_empty().then_some(quote!(_)).unwrap_or_else(|| quote!( #(#v)|* )))
                .collect::<Vec<_>>();

            let fields = find_constrained_parent_fields(self.scope, child.id().unwrap())
                .map(|field| format_ident!("{}", field.id().unwrap()));

            match_values.push(quote!( (#(#tuple_values),*) ));
            child_parse_args.push(quote!( #(, #fields)*));
            child_ids_data.push(format_ident!("{}Data", child.id().unwrap()));
            child_ids.push(format_ident!("{}", child.id().unwrap()));
        }
        });

        let constrained_field_idents =
            constrained_fields.iter().map(|field| format_ident!("{field}"));
        let child_parse_args = children.iter().map(|child| {
            let fields = find_constrained_parent_fields(self.scope, child.id().unwrap())
                .map(|field| format_ident!("{}", field.id().unwrap()));
            quote!(#(, #fields)*)
        });
        let packet_data_child = format_ident!("{}DataChild", self.packet_name);

        // Parsing of packet children requires having a payload field;
        // it is allowed to inherit from a packet with empty payload, in this
        // case generate an empty payload value.
+1 −1
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ use crate::ast::*;
#[derive(Debug)]
pub struct Scope<'d> {
    // Original file.
    file: &'d analyzer_ast::File,
    pub file: &'d analyzer_ast::File,

    // Collection of Group, Packet, Enum, Struct, Checksum, and CustomField declarations.
    pub typedef: HashMap<String, &'d analyzer_ast::Decl>,
+930 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading