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();
37 private Module module;
39 private ConstructorInfo lazyConstructor;
40 private IList<CustomAttributeTypedArgument> lazyConstructorArguments;
41 private IList<CustomAttributeNamedArgument> lazyNamedArguments;
43 internal CustomAttributeData(Module module, int index)
49 internal CustomAttributeData(ConstructorInfo constructor, object[] args, List<CustomAttributeNamedArgument> namedArguments)
51 this.lazyConstructor = constructor;
52 MethodSignature sig = constructor.MethodSignature;
53 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>();
54 for (int i = 0; i < args.Length; i++)
56 list.Add(new CustomAttributeTypedArgument(sig.GetParameterType(i), args[i]));
58 lazyConstructorArguments = list.AsReadOnly();
59 if (namedArguments == null)
61 this.lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
65 this.lazyNamedArguments = namedArguments.AsReadOnly();
69 internal CustomAttributeData(Assembly asm, ConstructorInfo constructor, ByteReader br)
71 this.lazyConstructor = constructor;
74 // it's legal to have an empty blob
75 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
76 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
80 if (br.ReadUInt16() != 1)
82 throw new BadImageFormatException();
84 lazyConstructorArguments = ReadConstructorArguments(asm, br, constructor);
85 lazyNamedArguments = ReadNamedArguments(asm, br, br.ReadUInt16(), constructor.DeclaringType);
89 public override string ToString()
91 StringBuilder sb = new StringBuilder();
93 sb.Append(Constructor.DeclaringType.FullName);
96 foreach (CustomAttributeTypedArgument arg in ConstructorArguments)
100 AppendValue(sb, arg);
102 foreach (CustomAttributeNamedArgument named in NamedArguments)
106 sb.Append(named.MemberInfo.Name);
108 AppendValue(sb, named.TypedValue);
112 return sb.ToString();
115 private static void AppendValue(StringBuilder sb, CustomAttributeTypedArgument arg)
117 if (arg.ArgumentType == arg.ArgumentType.Module.universe.System_String)
119 sb.Append('"').Append(arg.Value).Append('"');
123 if (arg.ArgumentType.IsEnum)
126 sb.Append(arg.ArgumentType.FullName);
129 sb.Append(arg.Value);
133 internal static void ReadDeclarativeSecurity(Assembly asm, List<CustomAttributeData> list, int action, ByteReader br)
135 Universe u = asm.universe;
136 if (br.PeekByte() == '.')
139 int count = br.ReadCompressedInt();
140 for (int j = 0; j < count; j++)
142 Type type = ReadType(asm, br);
143 ConstructorInfo constructor;
144 if (type == u.System_Security_Permissions_HostProtectionAttribute && action == (int)System.Security.Permissions.SecurityAction.LinkDemand)
146 constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
150 constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { u.System_Security_Permissions_SecurityAction }, null);
152 // LAMESPEC there is an additional length here (probably of the named argument list)
153 ByteReader slice = br.Slice(br.ReadCompressedInt());
154 // LAMESPEC the count of named arguments is a compressed integer (instead of UInt16 as NumNamed in custom attributes)
155 list.Add(new CustomAttributeData(constructor, action, ReadNamedArguments(asm, slice, slice.ReadCompressedInt(), type)));
160 // .NET 1.x format (xml)
161 char[] buf = new char[br.Length / 2];
162 for (int i = 0; i < buf.Length; i++)
164 buf[i] = br.ReadChar();
166 string xml = new String(buf);
167 ConstructorInfo constructor = u.System_Security_Permissions_PermissionSetAttribute.GetConstructor(new Type[] { u.System_Security_Permissions_SecurityAction });
168 List<CustomAttributeNamedArgument> args = new List<CustomAttributeNamedArgument>();
169 args.Add(new CustomAttributeNamedArgument(u.System_Security_Permissions_PermissionSetAttribute.GetProperty("XML"),
170 new CustomAttributeTypedArgument(u.System_String, xml)));
171 list.Add(new CustomAttributeData(constructor, action, args));
175 private CustomAttributeData(ConstructorInfo constructor, int securityAction, IList<CustomAttributeNamedArgument> namedArguments)
177 Universe u = constructor.Module.universe;
178 this.lazyConstructor = constructor;
179 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>();
180 list.Add(new CustomAttributeTypedArgument(u.System_Security_Permissions_SecurityAction, securityAction));
181 this.lazyConstructorArguments = list.AsReadOnly();
182 this.lazyNamedArguments = namedArguments;
185 private static Type ReadFieldOrPropType(Assembly asm, ByteReader br)
187 Universe u = asm.universe;
188 switch (br.ReadByte())
190 case Signature.ELEMENT_TYPE_BOOLEAN:
191 return u.System_Boolean;
192 case Signature.ELEMENT_TYPE_CHAR:
193 return u.System_Char;
194 case Signature.ELEMENT_TYPE_I1:
195 return u.System_SByte;
196 case Signature.ELEMENT_TYPE_U1:
197 return u.System_Byte;
198 case Signature.ELEMENT_TYPE_I2:
199 return u.System_Int16;
200 case Signature.ELEMENT_TYPE_U2:
201 return u.System_UInt16;
202 case Signature.ELEMENT_TYPE_I4:
203 return u.System_Int32;
204 case Signature.ELEMENT_TYPE_U4:
205 return u.System_UInt32;
206 case Signature.ELEMENT_TYPE_I8:
207 return u.System_Int64;
208 case Signature.ELEMENT_TYPE_U8:
209 return u.System_UInt64;
210 case Signature.ELEMENT_TYPE_R4:
211 return u.System_Single;
212 case Signature.ELEMENT_TYPE_R8:
213 return u.System_Double;
214 case Signature.ELEMENT_TYPE_STRING:
215 return u.System_String;
216 case Signature.ELEMENT_TYPE_SZARRAY:
217 return ReadFieldOrPropType(asm, br).MakeArrayType();
219 return ReadType(asm, br);
221 return u.System_Type;
223 return u.System_Object;
225 throw new InvalidOperationException();
229 private static CustomAttributeTypedArgument ReadFixedArg(Assembly asm, ByteReader br, Type type)
231 Universe u = asm.universe;
232 if (type == u.System_String)
234 return new CustomAttributeTypedArgument(type, br.ReadString());
236 else if (type == u.System_Type)
238 return new CustomAttributeTypedArgument(type, ReadType(asm, br));
240 else if (type == u.System_Object)
242 return ReadFixedArg(asm, br, ReadFieldOrPropType(asm, br));
244 else if (type.IsArray)
246 int length = br.ReadInt32();
249 return new CustomAttributeTypedArgument(type, null);
251 Type elementType = type.GetElementType();
252 CustomAttributeTypedArgument[] array = new CustomAttributeTypedArgument[length];
253 for (int i = 0; i < length; i++)
255 array[i] = ReadFixedArg(asm, br, elementType);
257 return new CustomAttributeTypedArgument(type, array);
259 else if (type.IsEnum)
261 return new CustomAttributeTypedArgument(type, ReadFixedArg(asm, br, type.GetEnumUnderlyingTypeImpl()).Value);
265 switch (Type.GetTypeCode(type))
267 case TypeCode.Boolean:
268 return new CustomAttributeTypedArgument(type, br.ReadByte() != 0);
270 return new CustomAttributeTypedArgument(type, br.ReadChar());
271 case TypeCode.Single:
272 return new CustomAttributeTypedArgument(type, br.ReadSingle());
273 case TypeCode.Double:
274 return new CustomAttributeTypedArgument(type, br.ReadDouble());
276 return new CustomAttributeTypedArgument(type, br.ReadSByte());
278 return new CustomAttributeTypedArgument(type, br.ReadInt16());
280 return new CustomAttributeTypedArgument(type, br.ReadInt32());
282 return new CustomAttributeTypedArgument(type, br.ReadInt64());
284 return new CustomAttributeTypedArgument(type, br.ReadByte());
285 case TypeCode.UInt16:
286 return new CustomAttributeTypedArgument(type, br.ReadUInt16());
287 case TypeCode.UInt32:
288 return new CustomAttributeTypedArgument(type, br.ReadUInt32());
289 case TypeCode.UInt64:
290 return new CustomAttributeTypedArgument(type, br.ReadUInt64());
292 throw new InvalidOperationException();
297 private static Type ReadType(Assembly asm, ByteReader br)
299 string typeName = br.ReadString();
300 if (typeName == null)
304 if (typeName.Length > 0 && typeName[typeName.Length - 1] == 0)
306 // there are broken compilers that emit an extra NUL character after the type name
307 typeName = typeName.Substring(0, typeName.Length - 1);
309 return asm.universe.GetType(asm, typeName, true);
312 private static IList<CustomAttributeTypedArgument> ReadConstructorArguments(Assembly asm, ByteReader br, ConstructorInfo constructor)
314 MethodSignature sig = constructor.MethodSignature;
315 int count = sig.GetParameterCount();
316 List<CustomAttributeTypedArgument> list = new List<CustomAttributeTypedArgument>(count);
317 for (int i = 0; i < count; i++)
319 list.Add(ReadFixedArg(asm, br, sig.GetParameterType(i)));
321 return list.AsReadOnly();
324 private static IList<CustomAttributeNamedArgument> ReadNamedArguments(Assembly asm, ByteReader br, int named, Type type)
326 List<CustomAttributeNamedArgument> list = new List<CustomAttributeNamedArgument>(named);
327 for (int i = 0; i < named; i++)
329 byte fieldOrProperty = br.ReadByte();
330 Type fieldOrPropertyType = ReadFieldOrPropType(asm, br);
331 string name = br.ReadString();
332 CustomAttributeTypedArgument value = ReadFixedArg(asm, br, fieldOrPropertyType);
334 switch (fieldOrProperty)
337 member = GetField(type, name);
340 member = GetProperty(type, name);
343 throw new BadImageFormatException();
347 throw new BadImageFormatException();
349 list.Add(new CustomAttributeNamedArgument(member, value));
351 return list.AsReadOnly();
354 private static FieldInfo GetField(Type type, string name)
356 for (; type != null; type = type.BaseType)
358 foreach (FieldInfo field in type.__GetDeclaredFields())
360 if (field.IsPublic && !field.IsStatic && field.Name == name)
369 private static PropertyInfo GetProperty(Type type, string name)
371 for (; type != null; type = type.BaseType)
373 foreach (PropertyInfo property in type.__GetDeclaredProperties())
375 if (property.IsPublic && !property.IsStatic && property.Name == name)
384 [Obsolete("Use Constructor.DeclaringType instead.")]
385 internal bool __TryReadTypeName(out string ns, out string name)
387 if (Constructor.DeclaringType.IsNested)
393 ns = Constructor.DeclaringType.__Namespace;
394 name = Constructor.DeclaringType.__Name;
399 internal byte[] __GetBlob()
401 return ((ModuleReader)module).GetBlobCopy(module.CustomAttribute.records[index].Value);
404 public ConstructorInfo Constructor
408 if (lazyConstructor == null)
410 lazyConstructor = (ConstructorInfo)module.ResolveMethod(module.CustomAttribute.records[index].Type);
412 return lazyConstructor;
416 public IList<CustomAttributeTypedArgument> ConstructorArguments
420 if (lazyConstructorArguments == null)
422 LazyParseArguments();
424 return lazyConstructorArguments;
428 public IList<CustomAttributeNamedArgument> NamedArguments
432 if (lazyNamedArguments == null)
434 LazyParseArguments();
436 return lazyNamedArguments;
440 private void LazyParseArguments()
442 ByteReader br = module.GetBlob(module.CustomAttribute.records[index].Value);
445 // it's legal to have an empty blob
446 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
447 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
451 if (br.ReadUInt16() != 1)
453 throw new BadImageFormatException();
455 lazyConstructorArguments = ReadConstructorArguments(module.Assembly, br, Constructor);
456 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadUInt16(), Constructor.DeclaringType);
460 public CustomAttributeBuilder __ToBuilder()
462 object[] args = new object[ConstructorArguments.Count];
463 for (int i = 0; i < args.Length; i++)
465 args[i] = ConstructorArguments[i].Value;
467 List<PropertyInfo> namedProperties = new List<PropertyInfo>();
468 List<object> propertyValues = new List<object>();
469 List<FieldInfo> namedFields = new List<FieldInfo>();
470 List<object> fieldValues = new List<object>();
471 foreach (CustomAttributeNamedArgument named in NamedArguments)
473 if (named.MemberInfo is PropertyInfo)
475 namedProperties.Add((PropertyInfo)named.MemberInfo);
476 propertyValues.Add(named.TypedValue.Value);
480 namedFields.Add((FieldInfo)named.MemberInfo);
481 fieldValues.Add(named.TypedValue.Value);
484 return new CustomAttributeBuilder(Constructor, args, namedProperties.ToArray(), propertyValues.ToArray(), namedFields.ToArray(), fieldValues.ToArray());
487 public static IList<CustomAttributeData> GetCustomAttributes(MemberInfo member)
489 return member.GetCustomAttributesData(null);
492 public static IList<CustomAttributeData> GetCustomAttributes(Assembly assembly)
494 return assembly.GetCustomAttributesData(null);
497 public static IList<CustomAttributeData> GetCustomAttributes(Module module)
499 return module.GetCustomAttributesData(null);
502 public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo parameter)
504 return parameter.GetCustomAttributesData(null);
507 public static IList<CustomAttributeData> __GetCustomAttributes(Assembly assembly, Type attributeType, bool inherit)
509 return assembly.GetCustomAttributesData(attributeType);
512 public static IList<CustomAttributeData> __GetCustomAttributes(Module module, Type attributeType, bool inherit)
514 return module.GetCustomAttributesData(attributeType);
517 public static IList<CustomAttributeData> __GetCustomAttributes(ParameterInfo parameter, Type attributeType, bool inherit)
519 return parameter.GetCustomAttributesData(attributeType);
522 public static IList<CustomAttributeData> __GetCustomAttributes(MemberInfo member, Type attributeType, bool inherit)
524 if (!inherit || !IsInheritableAttribute(attributeType))
526 return member.GetCustomAttributesData(attributeType);
528 List<CustomAttributeData> list = new List<CustomAttributeData>();
531 list.AddRange(member.GetCustomAttributesData(attributeType));
532 Type type = member as Type;
535 type = type.BaseType;
543 MethodInfo method = member as MethodInfo;
546 MemberInfo prev = member;
547 method = method.GetBaseDefinition();
548 if (method == null || method == prev)
559 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Assembly assembly)
561 if (assembly.__IsMissing)
563 throw new MissingAssemblyException((MissingAssembly)assembly);
565 return assembly.ManifestModule.GetDeclarativeSecurity(0x20000001);
568 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Type type)
570 if ((type.Attributes & TypeAttributes.HasSecurity) != 0)
572 return type.Module.GetDeclarativeSecurity(type.MetadataToken);
580 public static IList<CustomAttributeData> __GetDeclarativeSecurity(MethodBase method)
582 if ((method.Attributes & MethodAttributes.HasSecurity) != 0)
584 return method.Module.GetDeclarativeSecurity(method.MetadataToken);
592 private static bool IsInheritableAttribute(Type attribute)
594 Type attributeUsageAttribute = attribute.Module.universe.System_AttributeUsageAttribute;
595 IList<CustomAttributeData> attr = attribute.GetCustomAttributesData(attributeUsageAttribute);
598 foreach (CustomAttributeNamedArgument named in attr[0].NamedArguments)
600 if (named.MemberInfo.Name == "Inherited")
602 return (bool)named.TypedValue.Value;