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