2 Copyright (C) 2009-2011 Jeroen Frijters
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
25 using System.Collections.Generic;
28 using IKVM.Reflection.Reader;
29 using IKVM.Reflection.Emit;
30 using IKVM.Reflection.Metadata;
32 namespace IKVM.Reflection
34 public sealed class CustomAttributeData
36 internal static readonly IList<CustomAttributeData> EmptyList = new List<CustomAttributeData>(0).AsReadOnly();
39 * There are several states a CustomAttributeData object can be in:
41 * 1) Unresolved Custom Attribute
42 * - customAttributeIndex >= 0
43 * - declSecurityIndex == -1
44 * - declSecurityBlob == null
45 * - lazyConstructor = null
46 * - lazyConstructorArguments = null
47 * - lazyNamedArguments = null
49 * 2) Resolved Custom Attribute
50 * - customAttributeIndex >= 0
51 * - declSecurityIndex == -1
52 * - declSecurityBlob == null
53 * - lazyConstructor != null
54 * - lazyConstructorArguments != null
55 * - lazyNamedArguments != null
57 * 3) Pre-resolved Custom Attribute
58 * - customAttributeIndex = -1
59 * - declSecurityIndex == -1
60 * - declSecurityBlob == null
61 * - lazyConstructor != null
62 * - lazyConstructorArguments != null
63 * - lazyNamedArguments != null
65 * 4) Pseudo Custom Attribute, .NET 1.x declarative security or result of CustomAttributeBuilder.ToData()
66 * - customAttributeIndex = -1
67 * - declSecurityIndex == -1
68 * - declSecurityBlob == null
69 * - lazyConstructor != null
70 * - lazyConstructorArguments != null
71 * - lazyNamedArguments != null
73 * 5) Unresolved declarative security
74 * - customAttributeIndex = -1
75 * - declSecurityIndex >= 0
76 * - declSecurityBlob != null
77 * - lazyConstructor != null
78 * - lazyConstructorArguments != null
79 * - lazyNamedArguments == null
81 * 6) Resolved declarative security
82 * - customAttributeIndex = -1
83 * - declSecurityIndex >= 0
84 * - declSecurityBlob == null
85 * - lazyConstructor != null
86 * - lazyConstructorArguments != null
87 * - lazyNamedArguments != null
90 private readonly Module module;
91 private readonly int customAttributeIndex;
92 private readonly int declSecurityIndex;
93 private readonly byte[] declSecurityBlob;
94 private ConstructorInfo lazyConstructor;
95 private IList<CustomAttributeTypedArgument> lazyConstructorArguments;
96 private IList<CustomAttributeNamedArgument> lazyNamedArguments;
98 // 1) Unresolved Custom Attribute
99 internal CustomAttributeData(Module module, int index)
101 this.module = module;
102 this.customAttributeIndex = index;
103 this.declSecurityIndex = -1;
106 // 4) Pseudo Custom Attribute, .NET 1.x declarative security
107 internal CustomAttributeData(Module module, ConstructorInfo constructor, object[] args, List<CustomAttributeNamedArgument> namedArguments)
108 : this(module, constructor, WrapConstructorArgs(args, constructor.MethodSignature), namedArguments)
112 private static List<CustomAttributeTypedArgument> WrapConstructorArgs(object[] args, MethodSignature sig)
114 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>();
115 for (int i = 0; i < args.Length; i++)
117 list.Add(new CustomAttributeTypedArgument(sig.GetParameterType(i), args[i]));
122 // 4) Pseudo Custom Attribute, .NET 1.x declarative security or result of CustomAttributeBuilder.ToData()
123 internal CustomAttributeData(Module module, ConstructorInfo constructor, List<CustomAttributeTypedArgument> constructorArgs, List<CustomAttributeNamedArgument> namedArguments)
125 this.module = module;
126 this.customAttributeIndex = -1;
127 this.declSecurityIndex = -1;
128 this.lazyConstructor = constructor;
129 lazyConstructorArguments = constructorArgs.AsReadOnly();
130 if (namedArguments == null)
132 this.lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
136 this.lazyNamedArguments = namedArguments.AsReadOnly();
140 // 3) Pre-resolved Custom Attribute
141 internal CustomAttributeData(Assembly asm, ConstructorInfo constructor, ByteReader br)
143 this.module = asm.ManifestModule;
144 this.customAttributeIndex = -1;
145 this.declSecurityIndex = -1;
146 this.lazyConstructor = constructor;
149 // it's legal to have an empty blob
150 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
151 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
155 if (br.ReadUInt16() != 1)
157 throw new BadImageFormatException();
159 lazyConstructorArguments = ReadConstructorArguments(asm, br, constructor);
160 lazyNamedArguments = ReadNamedArguments(asm, br, br.ReadUInt16(), constructor.DeclaringType);
164 public override string ToString()
166 StringBuilder sb = new StringBuilder();
168 sb.Append(Constructor.DeclaringType.FullName);
171 ParameterInfo[] parameters = Constructor.GetParameters();
172 IList<CustomAttributeTypedArgument> args = ConstructorArguments;
173 for (int i = 0; i < parameters.Length; i++)
177 AppendValue(sb, parameters[i].ParameterType, args[i]);
179 foreach (CustomAttributeNamedArgument named in NamedArguments)
183 sb.Append(named.MemberInfo.Name);
185 FieldInfo fi = named.MemberInfo as FieldInfo;
186 Type type = fi != null ? fi.FieldType : ((PropertyInfo)named.MemberInfo).PropertyType;
187 AppendValue(sb, type, named.TypedValue);
191 return sb.ToString();
194 private static void AppendValue(StringBuilder sb, Type type, CustomAttributeTypedArgument arg)
196 if (arg.ArgumentType == arg.ArgumentType.Module.universe.System_String)
198 sb.Append('"').Append(arg.Value).Append('"');
200 else if (arg.ArgumentType.IsArray)
202 Type elementType = arg.ArgumentType.GetElementType();
203 string elementTypeName;
204 if (elementType.IsPrimitive
205 || elementType == type.Module.universe.System_Object
206 || elementType == type.Module.universe.System_String
207 || elementType == type.Module.universe.System_Type)
209 elementTypeName = elementType.Name;
213 elementTypeName = elementType.FullName;
215 sb.Append("new ").Append(elementTypeName).Append("[").Append(((Array)arg.Value).Length).Append("] { ");
217 foreach (CustomAttributeTypedArgument elem in (CustomAttributeTypedArgument[])arg.Value)
221 AppendValue(sb, elementType, elem);
227 if (arg.ArgumentType != type)
230 sb.Append(arg.ArgumentType.FullName);
233 sb.Append(arg.Value);
237 internal static void ReadDeclarativeSecurity(Module module, int index, List<CustomAttributeData> list)
239 Universe u = module.universe;
240 Assembly asm = module.Assembly;
241 int action = module.DeclSecurity.records[index].Action;
242 ByteReader br = module.GetBlob(module.DeclSecurity.records[index].PermissionSet);
243 if (br.PeekByte() == '.')
246 int count = br.ReadCompressedInt();
247 for (int j = 0; j < count; j++)
249 Type type = ReadType(asm, br);
250 ConstructorInfo constructor = type.GetPseudoCustomAttributeConstructor(u.System_Security_Permissions_SecurityAction);
251 // LAMESPEC there is an additional length here (probably of the named argument list)
252 byte[] blob = br.ReadBytes(br.ReadCompressedInt());
253 list.Add(new CustomAttributeData(asm, constructor, action, blob, index));
258 // .NET 1.x format (xml)
259 char[] buf = new char[br.Length / 2];
260 for (int i = 0; i < buf.Length; i++)
262 buf[i] = br.ReadChar();
264 string xml = new String(buf);
265 ConstructorInfo constructor = u.System_Security_Permissions_PermissionSetAttribute.GetPseudoCustomAttributeConstructor(u.System_Security_Permissions_SecurityAction);
266 List<CustomAttributeNamedArgument> args = new List<CustomAttributeNamedArgument>();
267 args.Add(new CustomAttributeNamedArgument(GetProperty(u.System_Security_Permissions_PermissionSetAttribute, "XML", u.System_String),
268 new CustomAttributeTypedArgument(u.System_String, xml)));
269 list.Add(new CustomAttributeData(asm.ManifestModule, constructor, new object[] { action }, args));
273 // 5) Unresolved declarative security
274 internal CustomAttributeData(Assembly asm, ConstructorInfo constructor, int securityAction, byte[] blob, int index)
276 this.module = asm.ManifestModule;
277 this.customAttributeIndex = -1;
278 this.declSecurityIndex = index;
279 Universe u = constructor.Module.universe;
280 this.lazyConstructor = constructor;
281 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>();
282 list.Add(new CustomAttributeTypedArgument(u.System_Security_Permissions_SecurityAction, securityAction));
283 this.lazyConstructorArguments = list.AsReadOnly();
284 this.declSecurityBlob = blob;
287 private static Type ReadFieldOrPropType(Assembly asm, ByteReader br)
289 Universe u = asm.universe;
290 switch (br.ReadByte())
292 case Signature.ELEMENT_TYPE_BOOLEAN:
293 return u.System_Boolean;
294 case Signature.ELEMENT_TYPE_CHAR:
295 return u.System_Char;
296 case Signature.ELEMENT_TYPE_I1:
297 return u.System_SByte;
298 case Signature.ELEMENT_TYPE_U1:
299 return u.System_Byte;
300 case Signature.ELEMENT_TYPE_I2:
301 return u.System_Int16;
302 case Signature.ELEMENT_TYPE_U2:
303 return u.System_UInt16;
304 case Signature.ELEMENT_TYPE_I4:
305 return u.System_Int32;
306 case Signature.ELEMENT_TYPE_U4:
307 return u.System_UInt32;
308 case Signature.ELEMENT_TYPE_I8:
309 return u.System_Int64;
310 case Signature.ELEMENT_TYPE_U8:
311 return u.System_UInt64;
312 case Signature.ELEMENT_TYPE_R4:
313 return u.System_Single;
314 case Signature.ELEMENT_TYPE_R8:
315 return u.System_Double;
316 case Signature.ELEMENT_TYPE_STRING:
317 return u.System_String;
318 case Signature.ELEMENT_TYPE_SZARRAY:
319 return ReadFieldOrPropType(asm, br).MakeArrayType();
321 return ReadType(asm, br);
323 return u.System_Type;
325 return u.System_Object;
327 throw new BadImageFormatException();
331 private static CustomAttributeTypedArgument ReadFixedArg(Assembly asm, ByteReader br, Type type)
333 Universe u = asm.universe;
334 if (type == u.System_String)
336 return new CustomAttributeTypedArgument(type, br.ReadString());
338 else if (type == u.System_Boolean)
340 return new CustomAttributeTypedArgument(type, br.ReadByte() != 0);
342 else if (type == u.System_Char)
344 return new CustomAttributeTypedArgument(type, br.ReadChar());
346 else if (type == u.System_Single)
348 return new CustomAttributeTypedArgument(type, br.ReadSingle());
350 else if (type == u.System_Double)
352 return new CustomAttributeTypedArgument(type, br.ReadDouble());
354 else if (type == u.System_SByte)
356 return new CustomAttributeTypedArgument(type, br.ReadSByte());
358 else if (type == u.System_Int16)
360 return new CustomAttributeTypedArgument(type, br.ReadInt16());
362 else if (type == u.System_Int32)
364 return new CustomAttributeTypedArgument(type, br.ReadInt32());
366 else if (type == u.System_Int64)
368 return new CustomAttributeTypedArgument(type, br.ReadInt64());
370 else if (type == u.System_Byte)
372 return new CustomAttributeTypedArgument(type, br.ReadByte());
374 else if (type == u.System_UInt16)
376 return new CustomAttributeTypedArgument(type, br.ReadUInt16());
378 else if (type == u.System_UInt32)
380 return new CustomAttributeTypedArgument(type, br.ReadUInt32());
382 else if (type == u.System_UInt64)
384 return new CustomAttributeTypedArgument(type, br.ReadUInt64());
386 else if (type == u.System_Type)
388 return new CustomAttributeTypedArgument(type, ReadType(asm, br));
390 else if (type == u.System_Object)
392 return ReadFixedArg(asm, br, ReadFieldOrPropType(asm, br));
394 else if (type.IsArray)
396 int length = br.ReadInt32();
399 return new CustomAttributeTypedArgument(type, null);
401 Type elementType = type.GetElementType();
402 CustomAttributeTypedArgument[] array = new CustomAttributeTypedArgument[length];
403 for (int i = 0; i < length; i++)
405 array[i] = ReadFixedArg(asm, br, elementType);
407 return new CustomAttributeTypedArgument(type, array);
409 else if (type.IsEnum)
411 return new CustomAttributeTypedArgument(type, ReadFixedArg(asm, br, type.GetEnumUnderlyingTypeImpl()).Value);
415 throw new InvalidOperationException();
419 private static Type ReadType(Assembly asm, ByteReader br)
421 string typeName = br.ReadString();
422 if (typeName == null)
426 if (typeName.Length > 0 && typeName[typeName.Length - 1] == 0)
428 // there are broken compilers that emit an extra NUL character after the type name
429 typeName = typeName.Substring(0, typeName.Length - 1);
431 return TypeNameParser.Parse(typeName, true).GetType(asm.universe, asm, true, typeName, true, false);
434 private static IList<CustomAttributeTypedArgument> ReadConstructorArguments(Assembly asm, ByteReader br, ConstructorInfo constructor)
436 MethodSignature sig = constructor.MethodSignature;
437 int count = sig.GetParameterCount();
438 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>(count);
439 for (int i = 0; i < count; i++)
441 list.Add(ReadFixedArg(asm, br, sig.GetParameterType(i)));
443 return list.AsReadOnly();
446 private static IList<CustomAttributeNamedArgument> ReadNamedArguments(Assembly asm, ByteReader br, int named, Type type)
448 List<CustomAttributeNamedArgument> list = new List<CustomAttributeNamedArgument>(named);
449 for (int i = 0; i < named; i++)
451 byte fieldOrProperty = br.ReadByte();
452 Type fieldOrPropertyType = ReadFieldOrPropType(asm, br);
453 string name = br.ReadString();
454 CustomAttributeTypedArgument value = ReadFixedArg(asm, br, fieldOrPropertyType);
456 switch (fieldOrProperty)
459 member = GetField(type, name, fieldOrPropertyType);
462 member = GetProperty(type, name, fieldOrPropertyType);
465 throw new BadImageFormatException();
467 list.Add(new CustomAttributeNamedArgument(member, value));
469 return list.AsReadOnly();
472 private static FieldInfo GetField(Type type, string name, Type fieldType)
475 for (; type != null && !type.__IsMissing; type = type.BaseType)
477 foreach (FieldInfo field in type.__GetDeclaredFields())
479 if (field.IsPublic && !field.IsStatic && field.Name == name)
485 // if the field is missing, we stick the missing field on the first missing base type
490 FieldSignature sig = FieldSignature.Create(fieldType, new CustomModifiers());
491 return type.FindField(name, sig)
492 ?? type.Module.universe.GetMissingFieldOrThrow(type, name, sig);
495 private static PropertyInfo GetProperty(Type type, string name, Type propertyType)
498 for (; type != null && !type.__IsMissing; type = type.BaseType)
500 foreach (PropertyInfo property in type.__GetDeclaredProperties())
502 if (property.IsPublic && !property.IsStatic && property.Name == name)
508 // if the property is missing, we stick the missing property on the first missing base type
513 return type.Module.universe.GetMissingPropertyOrThrow(type, name, PropertySignature.Create(CallingConventions.Standard | CallingConventions.HasThis, propertyType, null, new PackedCustomModifiers()));
516 [Obsolete("Use Constructor.DeclaringType instead.")]
517 internal bool __TryReadTypeName(out string ns, out string name)
519 if (Constructor.DeclaringType.IsNested)
525 ns = Constructor.DeclaringType.__Namespace;
526 name = Constructor.DeclaringType.__Name;
530 public byte[] __GetBlob()
532 if (declSecurityBlob != null)
534 return (byte[])declSecurityBlob.Clone();
536 else if (customAttributeIndex == -1)
538 return __ToBuilder().GetBlob(module.Assembly);
542 return ((ModuleReader)module).GetBlobCopy(module.CustomAttribute.records[customAttributeIndex].Value);
550 return customAttributeIndex >= 0
551 ? module.CustomAttribute.records[customAttributeIndex].Parent
552 : declSecurityIndex >= 0
553 ? module.DeclSecurity.records[declSecurityIndex].Parent
558 public ConstructorInfo Constructor
562 if (lazyConstructor == null)
564 lazyConstructor = (ConstructorInfo)module.ResolveMethod(module.CustomAttribute.records[customAttributeIndex].Type);
566 return lazyConstructor;
570 public IList<CustomAttributeTypedArgument> ConstructorArguments
574 if (lazyConstructorArguments == null)
576 LazyParseArguments();
578 return lazyConstructorArguments;
582 public IList<CustomAttributeNamedArgument> NamedArguments
586 if (lazyNamedArguments == null)
588 if (customAttributeIndex >= 0)
590 // 1) Unresolved Custom Attribute
591 LazyParseArguments();
595 // 5) Unresolved declarative security
596 ByteReader br = new ByteReader(declSecurityBlob, 0, declSecurityBlob.Length);
597 // LAMESPEC the count of named arguments is a compressed integer (instead of UInt16 as NumNamed in custom attributes)
598 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadCompressedInt(), Constructor.DeclaringType);
601 return lazyNamedArguments;
605 private void LazyParseArguments()
607 ByteReader br = module.GetBlob(module.CustomAttribute.records[customAttributeIndex].Value);
610 // it's legal to have an empty blob
611 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
612 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
616 if (br.ReadUInt16() != 1)
618 throw new BadImageFormatException();
620 lazyConstructorArguments = ReadConstructorArguments(module.Assembly, br, Constructor);
621 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadUInt16(), Constructor.DeclaringType);
625 public CustomAttributeBuilder __ToBuilder()
627 ParameterInfo[] parameters = Constructor.GetParameters();
628 object[] args = new object[ConstructorArguments.Count];
629 for (int i = 0; i < args.Length; i++)
631 args[i] = RewrapArray(parameters[i].ParameterType, ConstructorArguments[i]);
633 List<PropertyInfo> namedProperties = new List<PropertyInfo>();
634 List<object> propertyValues = new List<object>();
635 List<FieldInfo> namedFields = new List<FieldInfo>();
636 List<object> fieldValues = new List<object>();
637 foreach (CustomAttributeNamedArgument named in NamedArguments)
639 PropertyInfo pi = named.MemberInfo as PropertyInfo;
642 namedProperties.Add(pi);
643 propertyValues.Add(RewrapArray(pi.PropertyType, named.TypedValue));
647 FieldInfo fi = (FieldInfo)named.MemberInfo;
649 fieldValues.Add(RewrapArray(fi.FieldType, named.TypedValue));
652 return new CustomAttributeBuilder(Constructor, args, namedProperties.ToArray(), propertyValues.ToArray(), namedFields.ToArray(), fieldValues.ToArray());
655 private static object RewrapArray(Type type, CustomAttributeTypedArgument arg)
657 IList<CustomAttributeTypedArgument> list = arg.Value as IList<CustomAttributeTypedArgument>;
660 Type elementType = arg.ArgumentType.GetElementType();
661 object[] arr = new object[list.Count];
662 for (int i = 0; i < arr.Length; i++)
664 arr[i] = RewrapArray(elementType, list[i]);
666 if (type == type.Module.universe.System_Object)
668 return CustomAttributeBuilder.__MakeTypedArgument(arg.ArgumentType, arr);
678 public static IList<CustomAttributeData> GetCustomAttributes(MemberInfo member)
680 return member.GetCustomAttributesData(null);
683 public static IList<CustomAttributeData> GetCustomAttributes(Assembly assembly)
685 return assembly.GetCustomAttributesData(null);
688 public static IList<CustomAttributeData> GetCustomAttributes(Module module)
690 return module.GetCustomAttributesData(null);
693 public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo parameter)
695 return parameter.GetCustomAttributesData(null);
698 public static IList<CustomAttributeData> __GetCustomAttributes(Assembly assembly, Type attributeType, bool inherit)
700 return assembly.GetCustomAttributesData(attributeType);
703 public static IList<CustomAttributeData> __GetCustomAttributes(Module module, Type attributeType, bool inherit)
705 return module.GetCustomAttributesData(attributeType);
708 public static IList<CustomAttributeData> __GetCustomAttributes(ParameterInfo parameter, Type attributeType, bool inherit)
710 return parameter.GetCustomAttributesData(attributeType);
713 public static IList<CustomAttributeData> __GetCustomAttributes(MemberInfo member, Type attributeType, bool inherit)
715 if (!inherit || !IsInheritableAttribute(attributeType))
717 return member.GetCustomAttributesData(attributeType);
719 List<CustomAttributeData> list = new List<CustomAttributeData>();
722 list.AddRange(member.GetCustomAttributesData(attributeType));
723 Type type = member as Type;
726 type = type.BaseType;
734 MethodInfo method = member as MethodInfo;
737 MemberInfo prev = member;
738 method = method.GetBaseDefinition();
739 if (method == null || method == prev)
750 public static IList<CustomAttributeData> __GetCustomAttributes(Type type, Type interfaceType, Type attributeType, bool inherit)
752 return type.GetInterfaceImplCustomAttributes(interfaceType, attributeType);
755 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Assembly assembly)
757 if (assembly.__IsMissing)
759 throw new MissingAssemblyException((MissingAssembly)assembly);
761 return assembly.ManifestModule.GetDeclarativeSecurity(0x20000001);
764 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Type type)
766 if ((type.Attributes & TypeAttributes.HasSecurity) != 0)
768 return type.Module.GetDeclarativeSecurity(type.MetadataToken);
776 public static IList<CustomAttributeData> __GetDeclarativeSecurity(MethodBase method)
778 if ((method.Attributes & MethodAttributes.HasSecurity) != 0)
780 return method.Module.GetDeclarativeSecurity(method.MetadataToken);
788 private static bool IsInheritableAttribute(Type attribute)
790 Type attributeUsageAttribute = attribute.Module.universe.System_AttributeUsageAttribute;
791 IList<CustomAttributeData> attr = attribute.GetCustomAttributesData(attributeUsageAttribute);
794 foreach (CustomAttributeNamedArgument named in attr[0].NamedArguments)
796 if (named.MemberInfo.Name == "Inherited")
798 return (bool)named.TypedValue.Value;