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