Loading tools/pdl/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,10 @@ 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", "tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs", "tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs", Loading tools/pdl/src/backends/rust.rs +50 −1 Original line number Diff line number Diff line Loading @@ -477,6 +477,9 @@ fn generate_packet_decl( #parent_data_child::#prev_parent_id(#prev_parent_id_lower) }); } } else if scope.iter_children(parent_id).next().is_some() { field.push(format_ident!("child")); value.push(quote! { #parent_data_child::None }); } quote! { Loading Loading @@ -907,7 +910,7 @@ fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStrea let id = format_ident!("{}", id); let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let max_value = mask_bits(width, "usize"); let max_value = mask_bits(width, &format!("u{}", backing_type.width)); let common = quote! { impl From<&#id> for #backing_type { fn from(value: &#id) -> #backing_type { Loading Loading @@ -1479,6 +1482,52 @@ mod tests { " ); test_pdl!( packet_decl_parent_with_no_payload, " enum Enum8 : 8 { A = 0, } packet Parent { v : Enum8, } packet Child : Parent (v = A) { } " ); 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. // Loading tools/pdl/src/backends/rust/parser.rs +61 −37 Original line number Diff line number Diff line Loading @@ -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('_')) Loading Loading @@ -627,51 +627,75 @@ 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. if !decl .fields() .any(|f| matches!(&f.desc, ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body)) { self.code.push(quote! { let payload: &[u8] = &[]; }) } self.code.push(quote! { let child = match (#(#constrained_field_idents),*) { #(#match_values if #child_ids_data::conforms(&payload) => { Loading tools/pdl/src/backends/rust/types.rs +1 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ pub fn rust_borrow( ast::FieldDesc::Typedef { type_id, .. } => match &scope.typedef[type_id].desc { ast::DeclDesc::Enum { .. } => quote!(), ast::DeclDesc::Struct { .. } => quote!(&), ast::DeclDesc::CustomField { .. } => quote!(&), ast::DeclDesc::CustomField { .. } => quote!(), desc => unreachable!("unexpected declaration: {desc:?}"), }, ast::FieldDesc::Array { .. } => quote!(&), Loading tools/pdl/src/lint.rs +1 −1 Original line number Diff line number Diff line Loading @@ -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>, Loading Loading
tools/pdl/Android.bp +4 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,10 @@ 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", "tests/generated/packet_decl_payload_field_unknown_size_little_endian.rs", "tests/generated/packet_decl_payload_field_unknown_size_terminal_big_endian.rs", Loading
tools/pdl/src/backends/rust.rs +50 −1 Original line number Diff line number Diff line Loading @@ -477,6 +477,9 @@ fn generate_packet_decl( #parent_data_child::#prev_parent_id(#prev_parent_id_lower) }); } } else if scope.iter_children(parent_id).next().is_some() { field.push(format_ident!("child")); value.push(quote! { #parent_data_child::None }); } quote! { Loading Loading @@ -907,7 +910,7 @@ fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStrea let id = format_ident!("{}", id); let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let max_value = mask_bits(width, "usize"); let max_value = mask_bits(width, &format!("u{}", backing_type.width)); let common = quote! { impl From<&#id> for #backing_type { fn from(value: &#id) -> #backing_type { Loading Loading @@ -1479,6 +1482,52 @@ mod tests { " ); test_pdl!( packet_decl_parent_with_no_payload, " enum Enum8 : 8 { A = 0, } packet Parent { v : Enum8, } packet Child : Parent (v = A) { } " ); 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. // Loading
tools/pdl/src/backends/rust/parser.rs +61 −37 Original line number Diff line number Diff line Loading @@ -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('_')) Loading Loading @@ -627,51 +627,75 @@ 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. if !decl .fields() .any(|f| matches!(&f.desc, ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body)) { self.code.push(quote! { let payload: &[u8] = &[]; }) } self.code.push(quote! { let child = match (#(#constrained_field_idents),*) { #(#match_values if #child_ids_data::conforms(&payload) => { Loading
tools/pdl/src/backends/rust/types.rs +1 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ pub fn rust_borrow( ast::FieldDesc::Typedef { type_id, .. } => match &scope.typedef[type_id].desc { ast::DeclDesc::Enum { .. } => quote!(), ast::DeclDesc::Struct { .. } => quote!(&), ast::DeclDesc::CustomField { .. } => quote!(&), ast::DeclDesc::CustomField { .. } => quote!(), desc => unreachable!("unexpected declaration: {desc:?}"), }, ast::FieldDesc::Array { .. } => quote!(&), Loading
tools/pdl/src/lint.rs +1 −1 Original line number Diff line number Diff line Loading @@ -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>, Loading