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    }
16}
17
18#[derive(Default, Debug, Clone)]
19pub struct FunctionConfig {
20    rules: Vec<FunctionRule>,
21    default: FunctionFlags,
22}
23
24#[derive(Debug, Clone)]
25struct FunctionRule {
26    filter: String,
27    flags: FunctionFlags,
28    used: bool,
29}
30
31#[derive(Debug, Clone)]
32pub enum FunctionFilter {
33    Name(String),
34    Default,
35}
36
37impl FunctionConfig {
38    /// Creates a blank set of configuration.
39    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.
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.
67    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
98    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
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 fule 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
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}