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