1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use super::*;

#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct MethodDef(pub Row);

impl From<Row> for MethodDef {
    fn from(row: Row) -> Self {
        Self(row)
    }
}

impl MethodDef {
    pub fn is_special(&self) -> bool {
        self.0.u32(2) & 0b1000_0000_0000 != 0
    }

    pub fn preserve_sig(&self) -> bool {
        self.0.u32(1) & 0b1000_0000 != 0
    }

    pub fn params(&self) -> impl Iterator<Item = Param> {
        self.0.list(5, TableIndex::Param).map(Param)
    }

    pub fn name(&self) -> &'static str {
        self.0.str(3)
    }

    pub fn rust_name(&self) -> String {
        let name = self.name();

        if self.is_special() {
            if name.starts_with("get") {
                name[4..].to_string()
            } else if name.starts_with("put") {
                format!("Set{}", &name[4..])
            } else if name.starts_with("add") {
                name[4..].to_string()
            } else if name.starts_with("remove") {
                format!("Remove{}", &name[7..])
            } else {
                // A delegate's 'Invoke' method is "special" but lacks a preamble.
                "Invoke".to_string()
            }
        } else {
            for attribute in self.attributes() {
                if attribute.name() == "OverloadAttribute" {
                    for (_, arg) in attribute.args() {
                        if let ConstantValue::String(name) = arg {
                            return name;
                        }
                    }
                }
            }

            name.to_string()
        }
    }

    pub fn attributes(&self) -> impl Iterator<Item = Attribute> {
        self.0.file.attributes(HasAttribute::MethodDef(self.clone()))
    }

    fn has_attribute(&self, name: &str) -> bool {
        self.attributes().any(|attribute| attribute.name() == name)
    }

    pub fn is_deprecated(&self) -> bool {
        self.has_attribute("DeprecatedAttribute")
    }

    pub fn does_not_return(&self) -> bool {
        self.has_attribute("DoesNotReturnAttribute")
    }

    pub fn static_lib(&self) -> Option<String> {
        self.attributes()
            .filter_map(|attribute| match attribute.name() {
                "StaticLibraryAttribute" => {
                    let args = attribute.args();
                    match &args[0].1 {
                        ConstantValue::String(value) => Some(value.clone()),
                        _ => None,
                    }
                }
                _ => None,
            })
            .next()
    }

    pub fn impl_map(&self) -> Option<ImplMap> {
        self.0.file.equal_range(TableIndex::ImplMap, 1, MemberForwarded::MethodDef(self.clone()).encode()).map(ImplMap).next()
    }

    pub fn signature(&self, generics: &[Type]) -> Signature {
        let reader = TypeReader::get();
        let params = self.params();

        let mut blob = self.0.blob(4);
        blob.read_unsigned();
        blob.read_unsigned(); // parameter count

        let return_type = reader.type_from_blob(&mut blob, None, generics);
        let mut return_param = None;
        let preserve_sig = self.preserve_sig();

        let mut params: Vec<MethodParam> = params
            .filter_map(|def| {
                if def.sequence() == 0 {
                    return_param = Some(def);
                    None
                } else {
                    let ty = reader.type_from_blob(&mut blob, None, generics).expect("MethodDef");
                    let ty = if !def.flags().output() { ty.to_const() } else { ty };
                    let array_info = def.array_info();
                    Some(MethodParam { def, ty, array_info })
                }
            })
            .collect();

        for position in 0..params.len() {
            // Point len params back to the corresponding ptr params.
            if let Some(ArrayInfo::RelativeLen(relative)) = params[position].array_info {
                // The len params must be input only.
                // TODO: workaround for https://github.com/microsoft/win32metadata/issues/813
                if !params[relative].def.flags().output() && position != relative {
                    params[relative].array_info = Some(ArrayInfo::RelativePtr(position));
                } else {
                    params[position].array_info = None;
                }
            }
        }

        let mut sets = BTreeMap::<usize, Vec<usize>>::new();

        // Finds sets of ptr params pointing at the same len param.
        for (position, param) in params.iter().enumerate() {
            if let Some(ArrayInfo::RelativeLen(relative)) = param.array_info {
                sets.entry(relative).or_default().push(position);
            }
        }

        // Remove all sets.
        for (len, ptrs) in sets {
            if ptrs.len() > 1 {
                params[len].array_info = None;
                for ptr in ptrs {
                    params[ptr].array_info = None;
                }
            }
        }

        Signature { params, return_type, return_param, preserve_sig }
    }

    pub fn cfg(&self) -> Cfg {
        let mut cfg = Cfg::new();
        self.combine_cfg(&mut cfg);
        cfg.add_attributes(self.attributes());
        cfg
    }

    pub(crate) fn combine_cfg(&self, cfg: &mut Cfg) {
        self.signature(&[]).combine_cfg(cfg);
    }
}