1 use crate::{LookupItem, lookup_keys}; 2 use anyhow::Result; 3 use wit_parser::{Function, FunctionKind, Resolve, WorldKey}; 4 5 bitflags::bitflags! { 6 #[derive(Default, Copy, Clone, Debug)] 7 pub struct FunctionFlags: u8 { 8 const ASYNC = 1 << 0; 9 const TRAPPABLE = 1 << 1; 10 const STORE = 1 << 2; 11 const TRACING = 1 << 3; 12 const VERBOSE_TRACING = 1 << 4; 13 const IGNORE_WIT = 1 << 5; 14 const EXACT = 1 << 6; 15 } 16 } 17 18 #[derive(Default, Debug, Clone)] 19 pub struct FunctionConfig { 20 rules: Vec<FunctionRule>, 21 pub(crate) default: FunctionFlags, 22 } 23 24 #[derive(Debug, Clone)] 25 struct FunctionRule { 26 filter: String, 27 flags: FunctionFlags, 28 used: bool, 29 } 30 31 #[derive(Debug, Clone)] 32 pub enum FunctionFilter { 33 Name(String), 34 Default, 35 } 36 37 impl FunctionConfig { 38 /// Creates a blank set of configuration. new() -> FunctionConfig39 pub fn new() -> FunctionConfig { 40 FunctionConfig::default() 41 } 42 43 /// Adds a new rule to this configuration. 44 /// 45 /// Note that the order rules are added is significant as only the first 46 /// matching rule is used for a function. push(&mut self, filter: FunctionFilter, flags: FunctionFlags)47 pub fn push(&mut self, filter: FunctionFilter, flags: FunctionFlags) { 48 match filter { 49 FunctionFilter::Name(filter) => { 50 self.rules.push(FunctionRule { 51 filter, 52 flags, 53 used: false, 54 }); 55 } 56 FunctionFilter::Default => { 57 self.default = flags; 58 } 59 } 60 } 61 62 /// Returns the set of configuration flags associated with `func`. 63 /// 64 /// The `name` provided should include the full name of the function 65 /// including its interface. The `kind` is the classification of the 66 /// function in WIT which affects the default set of flags. flags( &mut self, resolve: &Resolve, ns: Option<&WorldKey>, func: &Function, ) -> FunctionFlags67 pub(crate) fn flags( 68 &mut self, 69 resolve: &Resolve, 70 ns: Option<&WorldKey>, 71 func: &Function, 72 ) -> FunctionFlags { 73 let mut wit_flags = FunctionFlags::empty(); 74 75 // If the kind is async, then set the async/store flags as that's a 76 // concurrent function which requires access to both. 77 match &func.kind { 78 FunctionKind::Freestanding 79 | FunctionKind::Method(_) 80 | FunctionKind::Static(_) 81 | FunctionKind::Constructor(_) => {} 82 83 FunctionKind::AsyncFreestanding 84 | FunctionKind::AsyncMethod(_) 85 | FunctionKind::AsyncStatic(_) => { 86 wit_flags |= FunctionFlags::ASYNC | FunctionFlags::STORE; 87 } 88 } 89 90 let mut ret = FunctionFlags::empty(); 91 self.add_function_flags(resolve, ns, &func.name, &mut ret); 92 if !ret.contains(FunctionFlags::IGNORE_WIT) { 93 ret |= wit_flags; 94 } 95 ret 96 } 97 resource_drop_flags( &mut self, resolve: &Resolve, ns: Option<&WorldKey>, resource_name: &str, ) -> FunctionFlags98 pub(crate) fn resource_drop_flags( 99 &mut self, 100 resolve: &Resolve, 101 ns: Option<&WorldKey>, 102 resource_name: &str, 103 ) -> FunctionFlags { 104 let mut ret = FunctionFlags::empty(); 105 self.add_function_flags(resolve, ns, &format!("[drop]{resource_name}"), &mut ret); 106 ret 107 } 108 add_function_flags( &mut self, resolve: &Resolve, key: Option<&WorldKey>, name: &str, base: &mut FunctionFlags, )109 fn add_function_flags( 110 &mut self, 111 resolve: &Resolve, 112 key: Option<&WorldKey>, 113 name: &str, 114 base: &mut FunctionFlags, 115 ) { 116 let mut apply_rules = |name: &str, is_exact: bool| { 117 for rule in self.rules.iter_mut() { 118 if name != rule.filter { 119 continue; 120 } 121 if !is_exact && rule.flags.contains(FunctionFlags::EXACT) { 122 continue; 123 } 124 rule.used = true; 125 *base |= rule.flags; 126 127 // only the first rule is used. 128 return true; 129 } 130 131 false 132 }; 133 match key { 134 Some(key) => { 135 for (lookup, projection) in lookup_keys(resolve, key, LookupItem::Name(name)) { 136 if apply_rules(&lookup, projection.is_empty()) { 137 return; 138 } 139 } 140 } 141 None => { 142 if apply_rules(name, true) { 143 return; 144 } 145 } 146 } 147 148 *base |= self.default; 149 } 150 assert_all_rules_used(&self, kind: &str) -> Result<()>151 pub(crate) fn assert_all_rules_used(&self, kind: &str) -> Result<()> { 152 let mut unused = Vec::new(); 153 for rule in self.rules.iter().filter(|r| !r.used) { 154 unused.push(format!("{:?}: {:?}", rule.filter, rule.flags)); 155 } 156 157 if unused.is_empty() { 158 return Ok(()); 159 } 160 161 anyhow::bail!("unused `{kind}` rules found: {unused:?}"); 162 } 163 } 164