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#[derive(Debug, Clone)]
199pub struct Attribute<'a> {
200 pub constructor: members::UserMethod,
202 pub(crate) value: Option<Cow<'a, [u8]>>,
203}
204
205impl<'def, 'inst> Attribute<'inst> {
206 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 buffer.pwrite(data, 0).unwrap();
265
266 Attribute {
267 constructor,
268 value: Some(buffer.into_vec().into()),
269 }
270 }
271}
272
273#[derive(Debug, Clone)]
280pub struct SecurityDeclaration<'a> {
281 pub attributes: Vec<Attribute<'a>>,
283 pub action: u16,
285 pub(crate) value: Cow<'a, [u8]>,
286}
287
288#[derive(Debug, Clone)]
290pub struct Permission<'a> {
291 pub type_name: Cow<'a, str>,
293 pub fields: Vec<NamedArg<'a>>,
295}
296
297impl<'a> SecurityDeclaration<'a> {
298 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}