• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 use std::collections::HashMap;
16 
17 use crate::analyzer::ast as analyzer_ast;
18 use crate::ast::*;
19 
20 /// Gather information about the full AST.
21 #[derive(Debug)]
22 pub struct Scope<'d> {
23     // Original file.
24     file: &'d analyzer_ast::File,
25 
26     // Collection of Group, Packet, Enum, Struct, Checksum, and CustomField declarations.
27     pub typedef: HashMap<String, &'d analyzer_ast::Decl>,
28 
29     // Collection of Packet, Struct, and Group scope declarations.
30     pub scopes: HashMap<&'d analyzer_ast::Decl, PacketScope<'d>>,
31 }
32 
33 /// Gather information about a Packet, Struct, or Group declaration.
34 #[derive(Debug)]
35 pub struct PacketScope<'d> {
36     // Original decl.
37     decl: &'d analyzer_ast::Decl,
38 
39     // Local and inherited field declarations. Only named fields are preserved.
40     // Saved here for reference for parent constraint resolving.
41     pub all_fields: HashMap<String, &'d analyzer_ast::Field>,
42 
43     // Local and inherited constraint declarations.
44     // Saved here for constraint conflict checks.
45     pub all_constraints: HashMap<String, &'d Constraint>,
46 }
47 
48 impl<'d> std::hash::Hash for &'d analyzer_ast::Decl {
hash<H: std::hash::Hasher>(&self, state: &mut H)49     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
50         std::ptr::hash(*self, state);
51     }
52 }
53 
54 impl<'d> PacketScope<'d> {
55     /// Add parent fields and constraints to the scope.
56     /// Only named fields are imported.
inherit( &mut self, parent: &PacketScope<'d>, constraints: impl Iterator<Item = &'d Constraint>, )57     fn inherit(
58         &mut self,
59         parent: &PacketScope<'d>,
60         constraints: impl Iterator<Item = &'d Constraint>,
61     ) {
62         // Check constraints.
63         assert!(self.all_constraints.is_empty());
64         self.all_constraints = parent.all_constraints.clone();
65         for constraint in constraints {
66             let id = constraint.id.clone();
67             self.all_constraints.insert(id, constraint);
68         }
69 
70         // Save parent fields.
71         self.all_fields = parent.all_fields.clone();
72     }
73 
74     /// Iterate over the packet's fields.
iter_fields(&self) -> impl Iterator<Item = &'d analyzer_ast::Field>75     pub fn iter_fields(&self) -> impl Iterator<Item = &'d analyzer_ast::Field> {
76         self.decl.fields()
77     }
78 
79     /// Lookup a field by name. This will also find the special
80     /// `_payload_` and `_body_` fields.
get_packet_field(&self, id: &str) -> Option<&analyzer_ast::Field>81     pub fn get_packet_field(&self, id: &str) -> Option<&analyzer_ast::Field> {
82         self.decl.fields().find(|field| match &field.desc {
83             FieldDesc::Payload { .. } => id == "_payload_",
84             FieldDesc::Body { .. } => id == "_body_",
85             _ => field.id() == Some(id),
86         })
87     }
88 
89     /// Find the payload or body field, if any.
get_payload_field(&self) -> Option<&analyzer_ast::Field>90     pub fn get_payload_field(&self) -> Option<&analyzer_ast::Field> {
91         self.decl
92             .fields()
93             .find(|field| matches!(&field.desc, FieldDesc::Payload { .. } | FieldDesc::Body { .. }))
94     }
95 
96     /// Lookup the size field for an array field.
get_array_size_field(&self, id: &str) -> Option<&analyzer_ast::Field>97     pub fn get_array_size_field(&self, id: &str) -> Option<&analyzer_ast::Field> {
98         self.decl.fields().find(|field| match &field.desc {
99             FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id,
100             _ => false,
101         })
102     }
103 
104     /// Find the size field corresponding to the payload or body
105     /// field of this packet.
get_payload_size_field(&self) -> Option<&analyzer_ast::Field>106     pub fn get_payload_size_field(&self) -> Option<&analyzer_ast::Field> {
107         self.decl.fields().find(|field| match &field.desc {
108             FieldDesc::Size { field_id, .. } => field_id == "_payload_" || field_id == "_body_",
109             _ => false,
110         })
111     }
112 
113     /// Cleanup scope after processing all fields.
finalize(&mut self)114     fn finalize(&mut self) {
115         // Check field shadowing.
116         for f in self.decl.fields() {
117             if let Some(id) = f.id() {
118                 self.all_fields.insert(id.to_string(), f);
119             }
120         }
121     }
122 }
123 
124 impl<'d> Scope<'d> {
new(file: &analyzer_ast::File) -> Scope<'_>125     pub fn new(file: &analyzer_ast::File) -> Scope<'_> {
126         let mut scope = Scope { file, typedef: HashMap::new(), scopes: HashMap::new() };
127 
128         // Gather top-level declarations.
129         // Validate the top-level scopes (Group, Packet, Typedef).
130         //
131         // TODO: switch to try_insert when stable
132         for decl in &file.declarations {
133             if let Some(id) = decl.id() {
134                 scope.typedef.insert(id.to_string(), decl);
135             }
136         }
137 
138         scope.finalize();
139         scope
140     }
141 
142     // Sort Packet, Struct, and Group declarations by reverse topological
143     // order.
finalize(&mut self) -> Vec<&'d analyzer_ast::Decl>144     fn finalize(&mut self) -> Vec<&'d analyzer_ast::Decl> {
145         // Auxiliary function implementing BFS on Packet tree.
146         enum Mark {
147             Temporary,
148             Permanent,
149         }
150         struct Context<'d> {
151             list: Vec<&'d analyzer_ast::Decl>,
152             visited: HashMap<&'d analyzer_ast::Decl, Mark>,
153             scopes: HashMap<&'d analyzer_ast::Decl, PacketScope<'d>>,
154         }
155 
156         fn bfs<'s, 'd>(
157             decl: &'d analyzer_ast::Decl,
158             context: &'s mut Context<'d>,
159             scope: &Scope<'d>,
160         ) -> Option<&'s PacketScope<'d>> {
161             match context.visited.get(&decl) {
162                 Some(Mark::Permanent) => return context.scopes.get(&decl),
163                 Some(Mark::Temporary) => {
164                     return None;
165                 }
166                 _ => (),
167             }
168 
169             let (parent_id, fields) = match &decl.desc {
170                 DeclDesc::Packet { parent_id, fields, .. }
171                 | DeclDesc::Struct { parent_id, fields, .. } => (parent_id.as_ref(), fields),
172                 DeclDesc::Group { fields, .. } => (None, fields),
173                 _ => return None,
174             };
175 
176             context.visited.insert(decl, Mark::Temporary);
177             let mut lscope =
178                 PacketScope { decl, all_fields: HashMap::new(), all_constraints: HashMap::new() };
179 
180             // Iterate over Struct and Group fields.
181             for f in fields {
182                 match &f.desc {
183                     FieldDesc::Group { .. } => unreachable!(),
184                     FieldDesc::Typedef { type_id, .. } => match scope.typedef.get(type_id) {
185                         Some(struct_decl @ Decl { desc: DeclDesc::Struct { .. }, .. }) => {
186                             bfs(struct_decl, context, scope);
187                         }
188                         None | Some(_) => (),
189                     },
190                     _ => (),
191                 }
192             }
193 
194             // Iterate over parent declaration.
195             let parent = parent_id.and_then(|id| scope.typedef.get(id));
196             if let Some(parent_decl) = parent {
197                 if let Some(rscope) = bfs(parent_decl, context, scope) {
198                     // Import the parent fields and constraints into the current scope.
199                     lscope.inherit(rscope, decl.constraints())
200                 }
201             }
202 
203             lscope.finalize();
204             context.list.push(decl);
205             context.visited.insert(decl, Mark::Permanent);
206             context.scopes.insert(decl, lscope);
207             context.scopes.get(&decl)
208         }
209 
210         let mut context =
211             Context::<'d> { list: vec![], visited: HashMap::new(), scopes: HashMap::new() };
212 
213         for decl in self.typedef.values() {
214             bfs(decl, &mut context, self);
215         }
216 
217         self.scopes = context.scopes;
218         context.list
219     }
220 
iter_children<'a>( &'a self, id: &'a str, ) -> impl Iterator<Item = &'d analyzer_ast::Decl> + 'a221     pub fn iter_children<'a>(
222         &'a self,
223         id: &'a str,
224     ) -> impl Iterator<Item = &'d analyzer_ast::Decl> + 'a {
225         self.file.iter_children(self.typedef.get(id).unwrap())
226     }
227 
228     /// Return the declaration of the typedef type backing the
229     /// selected field.
get_field_declaration( &self, field: &analyzer_ast::Field, ) -> Option<&'d analyzer_ast::Decl>230     pub fn get_field_declaration(
231         &self,
232         field: &analyzer_ast::Field,
233     ) -> Option<&'d analyzer_ast::Decl> {
234         match &field.desc {
235             FieldDesc::FixedEnum { enum_id, .. } => self.typedef.get(enum_id).copied(),
236             FieldDesc::Array { type_id: Some(type_id), .. } => self.typedef.get(type_id).copied(),
237             FieldDesc::Typedef { type_id, .. } => self.typedef.get(type_id.as_str()).copied(),
238             _ => None,
239         }
240     }
241 
242     /// Test if the selected field is a bitfield.
is_bitfield(&self, field: &analyzer_ast::Field) -> bool243     pub fn is_bitfield(&self, field: &analyzer_ast::Field) -> bool {
244         match &field.desc {
245             FieldDesc::Size { .. }
246             | FieldDesc::Count { .. }
247             | FieldDesc::ElementSize { .. }
248             | FieldDesc::FixedScalar { .. }
249             | FieldDesc::FixedEnum { .. }
250             | FieldDesc::Reserved { .. }
251             | FieldDesc::Scalar { .. } => true,
252             FieldDesc::Typedef { type_id, .. } => {
253                 let field = self.typedef.get(type_id.as_str());
254                 matches!(field, Some(Decl { desc: DeclDesc::Enum { .. }, .. }))
255             }
256             _ => false,
257         }
258     }
259 
260     /// Determine the size of a field in bits, if possible.
261     ///
262     /// If the field is dynamically sized (e.g. unsized array or
263     /// payload field), `None` is returned. If `skip_payload` is set,
264     /// payload and body fields are counted as having size `0` rather
265     /// than a variable size.
get_field_width( &self, field: &analyzer_ast::Field, skip_payload: bool, ) -> Option<usize>266     pub fn get_field_width(
267         &self,
268         field: &analyzer_ast::Field,
269         skip_payload: bool,
270     ) -> Option<usize> {
271         match &field.desc {
272             FieldDesc::Scalar { width, .. }
273             | FieldDesc::Size { width, .. }
274             | FieldDesc::Count { width, .. }
275             | FieldDesc::ElementSize { width, .. }
276             | FieldDesc::Reserved { width, .. }
277             | FieldDesc::FixedScalar { width, .. } => Some(*width),
278             FieldDesc::Padding { .. } => todo!(),
279             FieldDesc::Array { size: Some(size), width, .. } => {
280                 let element_width = width
281                     .or_else(|| self.get_decl_width(self.get_field_declaration(field)?, false))?;
282                 Some(element_width * size)
283             }
284             FieldDesc::FixedEnum { .. } | FieldDesc::Typedef { .. } => {
285                 self.get_decl_width(self.get_field_declaration(field)?, false)
286             }
287             FieldDesc::Checksum { .. } => Some(0),
288             FieldDesc::Payload { .. } | FieldDesc::Body { .. } if skip_payload => Some(0),
289             _ => None,
290         }
291     }
292 
293     /// Determine the size of a declaration type in bits, if possible.
294     ///
295     /// If the type is dynamically sized (e.g. contains an array or
296     /// payload), `None` is returned. If `skip_payload` is set,
297     /// payload and body fields are counted as having size `0` rather
298     /// than a variable size.
get_decl_width(&self, decl: &analyzer_ast::Decl, skip_payload: bool) -> Option<usize>299     pub fn get_decl_width(&self, decl: &analyzer_ast::Decl, skip_payload: bool) -> Option<usize> {
300         match &decl.desc {
301             DeclDesc::Enum { width, .. } | DeclDesc::Checksum { width, .. } => Some(*width),
302             DeclDesc::CustomField { width, .. } => *width,
303             DeclDesc::Packet { fields, parent_id, .. }
304             | DeclDesc::Struct { fields, parent_id, .. } => {
305                 let mut packet_size = match parent_id {
306                     None => 0,
307                     Some(id) => self.get_decl_width(self.typedef.get(id.as_str())?, true)?,
308                 };
309                 for field in fields.iter() {
310                     packet_size += self.get_field_width(field, skip_payload)?;
311                 }
312                 Some(packet_size)
313             }
314             DeclDesc::Group { .. } | DeclDesc::Test { .. } => None,
315         }
316     }
317 }
318