2 Copyright (C) 2009-2010 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 public void __ReadTypeName(out string ns, out string name)
386 if (lazyConstructor == null)
388 ModuleReader mod = module as ModuleReader;
391 int methodToken = mod.CustomAttribute.records[index].Type;
392 if ((methodToken >> 24) == MemberRefTable.Index)
394 int methodIndex = (methodToken & 0xFFFFFF) - 1;
395 int typeToken = mod.MemberRef.records[methodIndex].Class;
396 if ((typeToken >> 24) == TypeRefTable.Index)
398 int typeIndex = (typeToken & 0xFFFFFF) - 1;
399 int typeNameSpace = mod.TypeRef.records[typeIndex].TypeNameSpace;
400 ns = typeNameSpace == 0 ? null : mod.GetString(typeNameSpace);
401 name = mod.GetString(mod.TypeRef.records[typeIndex].TypeName);
407 ns = Constructor.DeclaringType.Namespace;
408 name = Constructor.DeclaringType.Name;
411 public ConstructorInfo Constructor
415 if (lazyConstructor == null)
417 lazyConstructor = (ConstructorInfo)module.ResolveMethod(module.CustomAttribute.records[index].Type);
419 return lazyConstructor;
423 public IList<CustomAttributeTypedArgument> ConstructorArguments
427 if (lazyConstructorArguments == null)
429 LazyParseArguments();
431 return lazyConstructorArguments;
435 public IList<CustomAttributeNamedArgument> NamedArguments
439 if (lazyNamedArguments == null)
441 LazyParseArguments();
443 return lazyNamedArguments;
447 private void LazyParseArguments()
449 ByteReader br = module.GetBlob(module.CustomAttribute.records[index].Value);
452 // it's legal to have an empty blob
453 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
454 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
458 if (br.ReadUInt16() != 1)
460 throw new BadImageFormatException();
462 lazyConstructorArguments = ReadConstructorArguments(module.Assembly, br, Constructor);
463 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadUInt16(), Constructor.DeclaringType);
467 public CustomAttributeBuilder __ToBuilder()
469 object[] args = new object[ConstructorArguments.Count];
470 for (int i = 0; i < args.Length; i++)
472 args[i] = ConstructorArguments[i].Value;
474 List<PropertyInfo> namedProperties = new List<PropertyInfo>();
475 List<object> propertyValues = new List<object>();
476 List<FieldInfo> namedFields = new List<FieldInfo>();
477 List<object> fieldValues = new List<object>();
478 foreach (CustomAttributeNamedArgument named in NamedArguments)
480 if (named.MemberInfo is PropertyInfo)
482 namedProperties.Add((PropertyInfo)named.MemberInfo);
483 propertyValues.Add(named.TypedValue.Value);
487 namedFields.Add((FieldInfo)named.MemberInfo);
488 fieldValues.Add(named.TypedValue.Value);
491 return new CustomAttributeBuilder(Constructor, args, namedProperties.ToArray(), propertyValues.ToArray(), namedFields.ToArray(), fieldValues.ToArray());
494 public static IList<CustomAttributeData> GetCustomAttributes(MemberInfo member)
496 return member.GetCustomAttributesData(null);
499 public static IList<CustomAttributeData> GetCustomAttributes(Assembly assembly)
501 return assembly.GetCustomAttributesData(null);
504 public static IList<CustomAttributeData> GetCustomAttributes(Module module)
506 return module.GetCustomAttributesData(null);
509 public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo parameter)
511 return parameter.GetCustomAttributesData(null);
514 public static IList<CustomAttributeData> __GetCustomAttributes(Assembly assembly, Type attributeType, bool inherit)
516 return assembly.GetCustomAttributesData(attributeType);
519 public static IList<CustomAttributeData> __GetCustomAttributes(Module module, Type attributeType, bool inherit)
521 return module.GetCustomAttributesData(attributeType);
524 public static IList<CustomAttributeData> __GetCustomAttributes(ParameterInfo parameter, Type attributeType, bool inherit)
526 return parameter.GetCustomAttributesData(attributeType);
529 public static IList<CustomAttributeData> __GetCustomAttributes(MemberInfo member, Type attributeType, bool inherit)
531 if (!inherit || !IsInheritableAttribute(attributeType))
533 return member.GetCustomAttributesData(attributeType);
535 List<CustomAttributeData> list = new List<CustomAttributeData>();
538 list.AddRange(member.GetCustomAttributesData(attributeType));
539 Type type = member as Type;
542 type = type.BaseType;
550 MethodInfo method = member as MethodInfo;
553 MemberInfo prev = member;
554 method = method.GetBaseDefinition();
555 if (method == null || method == prev)
566 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Assembly assembly)
568 return assembly.ManifestModule.GetDeclarativeSecurity(0x20000001);
571 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Type type)
573 if ((type.Attributes & TypeAttributes.HasSecurity) != 0)
575 return type.Module.GetDeclarativeSecurity(type.MetadataToken);
583 public static IList<CustomAttributeData> __GetDeclarativeSecurity(MethodBase method)
585 if ((method.Attributes & MethodAttributes.HasSecurity) != 0)
587 return method.Module.GetDeclarativeSecurity(method.MetadataToken);
595 private static bool IsInheritableAttribute(Type attribute)
597 Type attributeUsageAttribute = attribute.Module.universe.System_AttributeUsageAttribute;
598 IList<CustomAttributeData> attr = attribute.GetCustomAttributesData(attributeUsageAttribute);
601 foreach (CustomAttributeNamedArgument named in attr[0].NamedArguments)
603 if (named.MemberInfo.Name == "Inherited")
605 return (bool)named.TypedValue.Value;