wasmtime_internal_wit_bindgen/
config.rs

1use crate::{LookupItem, lookup_keys};
2use anyhow::Result;
3use wit_parser::{Function, FunctionKind, Resolve, WorldKey};
4
5bitflags::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)]
20pub struct FunctionConfig {
21    rules: Vec<FunctionRule>,
22    pub(crate) default: FunctionFlags,
23}
24
25#[derive(Debug, Clone)]
26struct FunctionRule {
27    filter: String,
28    flags: FunctionFlags,
29    used: bool,
30}
31
32#[derive(Debug, Clone)]
33pub enum FunctionFilter {
34    Name(String),
35    Default,
36}
37
38impl 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}