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