dotnetdll/resolved/
attribute.rs

1use scroll::{Pread, Pwrite, Result};
2use scroll_buffer::DynamicBuffer;
3use std::borrow::Cow;
4
5use crate::binary::signature::{
6    attribute::{self, *},
7    compressed::Unsigned,
8};
9use crate::resolution::Resolution;
10
11pub use attribute::{CustomAttributeData, FixedArg, IntegralParam, NamedArg};
12
13use super::{
14    members,
15    signature::{Parameter, ParameterType},
16    types::*,
17};
18
19macro_rules! throw {
20    ($($arg:tt)*) => {
21        return Err(scroll::Error::Custom(format!($($arg)*)))
22    }
23}
24
25fn parse_from_type<'def, 'inst>(
26    f_type: FieldOrPropType<'inst>,
27    src: &'inst [u8],
28    offset: &mut usize,
29    resolve: &impl Fn(&str) -> Result<(&'def TypeDefinition<'def>, &'def Resolution<'def>)>,
30) -> Result<FixedArg<'inst>> {
31    use FieldOrPropType::*;
32    Ok(match f_type {
33        Boolean => FixedArg::Boolean(src.gread_with::<u8>(offset, scroll::LE)? == 1),
34        Char => {
35            let value = src.gread_with::<u16>(offset, scroll::LE)? as u32;
36            match char::from_u32(value) {
37                Some(c) => FixedArg::Char(c),
38                None => throw!("invalid UTF-32 character {:#06x}", value),
39            }
40        }
41        Int8 => FixedArg::Integral(IntegralParam::Int8(src.gread_with(offset, scroll::LE)?)),
42        UInt8 => FixedArg::Integral(IntegralParam::UInt8(src.gread_with(offset, scroll::LE)?)),
43        Int16 => FixedArg::Integral(IntegralParam::Int16(src.gread_with(offset, scroll::LE)?)),
44        UInt16 => FixedArg::Integral(IntegralParam::UInt16(src.gread_with(offset, scroll::LE)?)),
45        Int32 => FixedArg::Integral(IntegralParam::Int32(src.gread_with(offset, scroll::LE)?)),
46        UInt32 => FixedArg::Integral(IntegralParam::UInt32(src.gread_with(offset, scroll::LE)?)),
47        Int64 => FixedArg::Integral(IntegralParam::Int64(src.gread_with(offset, scroll::LE)?)),
48        UInt64 => FixedArg::Integral(IntegralParam::UInt64(src.gread_with(offset, scroll::LE)?)),
49        Float32 => FixedArg::Float32(src.gread_with(offset, scroll::LE)?),
50        Float64 => FixedArg::Float64(src.gread_with(offset, scroll::LE)?),
51        String => FixedArg::String(src.gread::<SerString>(offset)?.0),
52        Type => match src.gread::<SerString>(offset)?.0 {
53            Some(s) => FixedArg::Type(s),
54            None => throw!("invalid null type name"),
55        },
56        Object => FixedArg::Object(Box::new(parse_from_type(src.gread(offset)?, src, offset, resolve)?)),
57        Vector(t) => {
58            let num_elem: u32 = src.gread_with(offset, scroll::LE)?;
59            FixedArg::Array(
60                *t.clone(),
61                if num_elem == u32::MAX {
62                    None
63                } else {
64                    Some(
65                        (0..num_elem)
66                            .map(|_| parse_from_type(*t.clone(), src, offset, resolve))
67                            .collect::<Result<_>>()?,
68                    )
69                },
70            )
71        }
72        Enum(name) => {
73            let t = process_def(resolve(&name)?)?;
74            match parse_from_type(t, src, offset, resolve)? {
75                FixedArg::Enum(name, i) => FixedArg::Enum(name, i),
76                bad => throw!("bad value {:?} for enum {}", bad, name),
77            }
78        }
79    })
80}
81
82fn process_def<'def, 'inst>(
83    (def, res): (&'def TypeDefinition<'def>, &'def Resolution<'def>),
84) -> Result<FieldOrPropType<'inst>> {
85    let Some(supertype) = &def.extends else {
86        return Ok(FieldOrPropType::Object);
87    };
88    match supertype {
89        TypeSource::User(u) if u.type_name(res) == "System.Enum" => def
90            .fields
91            .iter()
92            .find(|f| f.name == "value__")
93            .ok_or(format!("cannot find underlying type for enum {}", def.type_name()))
94            .and_then(|f| match &f.return_type {
95                MemberType::Base(b) => match &**b {
96                    BaseType::Int8 => Ok(FieldOrPropType::Int8),
97                    BaseType::UInt8 => Ok(FieldOrPropType::UInt8),
98                    BaseType::Int16 => Ok(FieldOrPropType::Int16),
99                    BaseType::UInt16 => Ok(FieldOrPropType::UInt16),
100                    BaseType::Int32 => Ok(FieldOrPropType::Int32),
101                    BaseType::UInt32 => Ok(FieldOrPropType::UInt32),
102                    BaseType::Int64 => Ok(FieldOrPropType::Int64),
103                    BaseType::UInt64 => Ok(FieldOrPropType::UInt64),
104                    bad => Err(format!("invalid type {:?} in enum", bad)),
105                },
106                MemberType::TypeGeneric(_) => Err("invalid generic type in enum".to_string()),
107            }),
108        bad => Err(format!(
109            "type {} must extend System.Enum for custom attributes, not {:?}",
110            def.type_name(),
111            bad
112        )),
113    }
114    .map_err(scroll::Error::Custom)
115}
116
117fn method_to_type<'def, 'inst>(
118    m: &'def MethodType,
119    resolution: &Resolution<'def>,
120    resolve: &impl Fn(&str) -> Result<(&'def TypeDefinition<'def>, &'def Resolution<'def>)>,
121) -> Result<FieldOrPropType<'inst>> {
122    match m {
123        MethodType::Base(b) => {
124            use BaseType::*;
125            let t = match &**b {
126                Type { source: ts, .. } => match ts {
127                    TypeSource::User(t) => {
128                        let name = t.type_name(resolution);
129                        if name == "System.Type" {
130                            FieldOrPropType::Type
131                        } else {
132                            process_def(resolve(&name)?)?
133                        }
134                    }
135                    TypeSource::Generic { .. } => {
136                        throw!("bad type {:?} in custom attribute constructor", ts)
137                    }
138                },
139                Boolean => FieldOrPropType::Boolean,
140                Char => FieldOrPropType::Char,
141                Int8 => FieldOrPropType::Int8,
142                UInt8 => FieldOrPropType::UInt8,
143                Int16 => FieldOrPropType::Int16,
144                UInt16 => FieldOrPropType::UInt16,
145                Int32 => FieldOrPropType::Int32,
146                UInt32 => FieldOrPropType::UInt32,
147                Int64 => FieldOrPropType::Int64,
148                UInt64 => FieldOrPropType::UInt64,
149                Float32 => FieldOrPropType::Float32,
150                Float64 => FieldOrPropType::Float64,
151                String => FieldOrPropType::String,
152                Object => FieldOrPropType::Object,
153                Vector(_, v) => FieldOrPropType::Vector(Box::new(method_to_type(v, resolution, resolve)?)),
154                bad => throw!("bad type {:?} in custom attribute constructor", bad),
155            };
156
157            Ok(t)
158        }
159        MethodType::TypeGeneric(_) => {
160            throw!("type generic parameters are not allowed in custom attributes")
161        }
162        MethodType::MethodGeneric(_) => {
163            throw!("method generic parameters are not allowed in custom attributes")
164        }
165    }
166}
167
168fn parse_named<'def, 'inst>(
169    src: &'inst [u8],
170    offset: &mut usize,
171    resolve: &impl Fn(&str) -> Result<(&'def TypeDefinition<'def>, &'def Resolution<'def>)>,
172) -> Result<Vec<NamedArg<'inst>>> {
173    let num_named: u16 = src.gread_with(offset, scroll::LE)?;
174
175    (0..num_named)
176        .map(|_| {
177            let kind: u8 = src.gread_with(offset, scroll::LE)?;
178            let f_type: FieldOrPropType = src.gread(offset)?;
179            let name = src
180                .gread::<SerString>(offset)?
181                .0
182                .ok_or_else(|| scroll::Error::Custom("null string name found when parsing".to_string()))?;
183
184            let value = parse_from_type(f_type, src, offset, resolve)?;
185
186            Ok(match kind {
187                0x53 => NamedArg::Field(name, value),
188                0x54 => NamedArg::Property(name, value),
189                bad => throw!("bad named argument tag {:#04x}", bad),
190            })
191        })
192        .collect()
193}
194
195/// A custom attribute, which can be applied to many metadata elements.
196///
197/// See ECMA-335, II.22.10 (page 218) for more information.
198#[derive(Debug, Clone)]
199pub struct Attribute<'a> {
200    /// The constructor method used to create this attribute.
201    pub constructor: members::UserMethod,
202    pub(crate) value: Option<Cow<'a, [u8]>>,
203}
204
205impl<'def, 'inst> Attribute<'inst> {
206    /// Decodes the binary blob of the custom attribute into structured [`CustomAttributeData`].
207    ///
208    /// This requires a [`Resolver`] to find the types of enum values and named arguments.
209    pub fn instantiation_data(
210        &'inst self,
211        resolver: &impl Resolver<'def>,
212        resolution: &Resolution<'def>,
213    ) -> Result<CustomAttributeData<'inst>> {
214        let bytes = self
215            .value
216            .as_ref()
217            .ok_or_else(|| scroll::Error::Custom("null data for custom attribute".to_string()))?;
218
219        let offset = &mut 0;
220
221        let prolog: u16 = bytes.gread_with(offset, scroll::LE)?;
222        if prolog != 0x0001 {
223            throw!("bad custom attribute data prolog {:#06x}", prolog);
224        }
225
226        use members::UserMethod;
227
228        let sig = match &self.constructor {
229            UserMethod::Definition(m) => &resolution[*m].signature,
230            UserMethod::Reference(r) => &resolution[*r].signature,
231        };
232
233        let resolve = |s: &str| resolver.find_type(s).map_err(|e| scroll::Error::Custom(e.to_string()));
234
235        let fixed = sig
236            .parameters
237            .iter()
238            .map(|Parameter(_, param)| match param {
239                ParameterType::Value(p_type) => {
240                    parse_from_type(method_to_type(p_type, resolution, &resolve)?, bytes, offset, &resolve)
241                }
242                ParameterType::Ref(_) => {
243                    throw!("ref parameters are not allowed in custom attributes")
244                }
245                ParameterType::TypedReference => {
246                    throw!("TypedReference parameters are not allowed in custom attributes",)
247                }
248            })
249            .collect::<Result<_>>()?;
250
251        let named = parse_named(bytes, offset, &resolve)?;
252
253        Ok(CustomAttributeData {
254            constructor_args: fixed,
255            named_args: named,
256        })
257    }
258
259    pub fn new(constructor: members::UserMethod, data: CustomAttributeData<'inst>) -> Self {
260        let mut buffer = DynamicBuffer::with_increment(8);
261
262        // currently, there are no explicit throws in attribute data TryIntoCtx impls
263        // so since the buffer always expands, this should be infallible
264        buffer.pwrite(data, 0).unwrap();
265
266        Attribute {
267            constructor,
268            value: Some(buffer.into_vec().into()),
269        }
270    }
271}
272
273// we abstract away all the StandAloneSigs and TypeSpecs, so there's no good place to put attributes that belong to them
274// it's not really possible to use those unless you're writing raw metadata though so we'll ignore them (for now)
275
276/// A security declaration, which specifies the requested permissions for a method or type.
277///
278/// See ECMA-335, II.22.11 (page 220) for more information.
279#[derive(Debug, Clone)]
280pub struct SecurityDeclaration<'a> {
281    /// All attributes present on the security declaration.
282    pub attributes: Vec<Attribute<'a>>,
283    /// The security action requested.
284    pub action: u16,
285    pub(crate) value: Cow<'a, [u8]>,
286}
287
288/// A single permission requested in a [`SecurityDeclaration`].
289#[derive(Debug, Clone)]
290pub struct Permission<'a> {
291    /// The name of the permission type.
292    pub type_name: Cow<'a, str>,
293    /// The named arguments (fields or properties) set on the permission.
294    pub fields: Vec<NamedArg<'a>>,
295}
296
297impl<'a> SecurityDeclaration<'a> {
298    /// Decodes the binary blob of the security declaration into a list of [`Permission`]s.
299    pub fn requested_permissions(&'a self, resolver: &'a impl Resolver<'a>) -> Result<Vec<Permission<'a>>> {
300        let offset = &mut 0;
301
302        let value = self.value.as_ref();
303
304        let period: u8 = value.gread_with(offset, scroll::LE)?;
305        if period != b'.' {
306            throw!("bad security permission set sentinel {:#04x}", period);
307        }
308
309        let Unsigned(num_attributes) = value.gread(offset)?;
310
311        (0..num_attributes)
312            .map(|_| {
313                let type_name = value
314                    .gread::<SerString>(offset)?
315                    .0
316                    .ok_or_else(|| {
317                        scroll::Error::Custom("null attribute type name found when parsing security".to_string())
318                    })?
319                    .into();
320
321                let fields = parse_named(value, offset, &|s| {
322                    resolver.find_type(s).map_err(|e| scroll::Error::Custom(e.to_string()))
323                })?;
324
325                Ok(Permission { type_name, fields })
326            })
327            .collect()
328    }
329
330    pub fn new(attributes: Vec<Attribute<'a>>, action: u16, attrs: Vec<Permission<'a>>) -> Result<Self> {
331        let mut buffer = DynamicBuffer::with_increment(8);
332        let offset = &mut 0;
333
334        buffer.gwrite_with(b'.', offset, scroll::LE)?;
335        buffer.gwrite(Unsigned(attrs.len() as u32), offset)?;
336
337        for attr in attrs {
338            buffer.gwrite(SerString(Some(attr.type_name.clone())), offset)?;
339            for arg in attr.fields {
340                buffer.gwrite(arg, offset)?;
341            }
342        }
343
344        Ok(SecurityDeclaration {
345            attributes,
346            action,
347            value: buffer.into_vec().into(),
348        })
349    }
350}