1b3216368ae7bab80085e21b6975b396ce006300
[mono.git] / mcs / class / IKVM.Reflection / CustomAttributeData.cs
1 /*
2   Copyright (C) 2009-2011 Jeroen Frijters
3
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.
7
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:
11
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.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.Collections.Generic;
26 using System.Text;
27 using System.IO;
28 using IKVM.Reflection.Reader;
29 using IKVM.Reflection.Emit;
30 using IKVM.Reflection.Metadata;
31
32 namespace IKVM.Reflection
33 {
34         public sealed class CustomAttributeData
35         {
36                 internal static readonly IList<CustomAttributeData> EmptyList = new List<CustomAttributeData>(0).AsReadOnly();
37                 private Module module;
38                 private int index;
39                 private ConstructorInfo lazyConstructor;
40                 private IList<CustomAttributeTypedArgument> lazyConstructorArguments;
41                 private IList<CustomAttributeNamedArgument> lazyNamedArguments;
42
43                 internal CustomAttributeData(Module module, int index)
44                 {
45                         this.module = module;
46                         this.index = index;
47                 }
48
49                 internal CustomAttributeData(ConstructorInfo constructor, object[] args, List<CustomAttributeNamedArgument> namedArguments)
50                 {
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++)
55                         {
56                                 list.Add(new CustomAttributeTypedArgument(sig.GetParameterType(i), args[i]));
57                         }
58                         lazyConstructorArguments = list.AsReadOnly();
59                         if (namedArguments == null)
60                         {
61                                 this.lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
62                         }
63                         else
64                         {
65                                 this.lazyNamedArguments = namedArguments.AsReadOnly();
66                         }
67                 }
68
69                 internal CustomAttributeData(Assembly asm, ConstructorInfo constructor, ByteReader br)
70                 {
71                         this.lazyConstructor = constructor;
72                         if (br.Length == 0)
73                         {
74                                 // it's legal to have an empty blob
75                                 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
76                                 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
77                         }
78                         else
79                         {
80                                 if (br.ReadUInt16() != 1)
81                                 {
82                                         throw new BadImageFormatException();
83                                 }
84                                 lazyConstructorArguments = ReadConstructorArguments(asm, br, constructor);
85                                 lazyNamedArguments = ReadNamedArguments(asm, br, br.ReadUInt16(), constructor.DeclaringType);
86                         }
87                 }
88
89                 public override string ToString()
90                 {
91                         StringBuilder sb = new StringBuilder();
92                         sb.Append('[');
93                         sb.Append(Constructor.DeclaringType.FullName);
94                         sb.Append('(');
95                         string sep = "";
96                         foreach (CustomAttributeTypedArgument arg in ConstructorArguments)
97                         {
98                                 sb.Append(sep);
99                                 sep = ", ";
100                                 AppendValue(sb, arg);
101                         }
102                         foreach (CustomAttributeNamedArgument named in NamedArguments)
103                         {
104                                 sb.Append(sep);
105                                 sep = ", ";
106                                 sb.Append(named.MemberInfo.Name);
107                                 sb.Append(" = ");
108                                 AppendValue(sb, named.TypedValue);
109                         }
110                         sb.Append(')');
111                         sb.Append(']');
112                         return sb.ToString();
113                 }
114
115                 private static void AppendValue(StringBuilder sb, CustomAttributeTypedArgument arg)
116                 {
117                         if (arg.ArgumentType == arg.ArgumentType.Module.universe.System_String)
118                         {
119                                 sb.Append('"').Append(arg.Value).Append('"');
120                         }
121                         else
122                         {
123                                 if (arg.ArgumentType.IsEnum)
124                                 {
125                                         sb.Append('(');
126                                         sb.Append(arg.ArgumentType.FullName);
127                                         sb.Append(')');
128                                 }
129                                 sb.Append(arg.Value);
130                         }
131                 }
132
133                 internal static void ReadDeclarativeSecurity(Assembly asm, List<CustomAttributeData> list, int action, ByteReader br)
134                 {
135                         Universe u = asm.universe;
136                         if (br.PeekByte() == '.')
137                         {
138                                 br.ReadByte();
139                                 int count = br.ReadCompressedInt();
140                                 for (int j = 0; j < count; j++)
141                                 {
142                                         Type type = ReadType(asm, br);
143                                         ConstructorInfo constructor;
144                                         if (type == u.System_Security_Permissions_HostProtectionAttribute && action == (int)System.Security.Permissions.SecurityAction.LinkDemand)
145                                         {
146                                                 constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
147                                         }
148                                         else
149                                         {
150                                                 constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { u.System_Security_Permissions_SecurityAction }, null);
151                                         }
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)));
156                                 }
157                         }
158                         else
159                         {
160                                 // .NET 1.x format (xml)
161                                 char[] buf = new char[br.Length / 2];
162                                 for (int i = 0; i < buf.Length; i++)
163                                 {
164                                         buf[i] = br.ReadChar();
165                                 }
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));
172                         }
173                 }
174
175                 private CustomAttributeData(ConstructorInfo constructor, int securityAction, IList<CustomAttributeNamedArgument> namedArguments)
176                 {
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;
183                 }
184
185                 private static Type ReadFieldOrPropType(Assembly asm, ByteReader br)
186                 {
187                         Universe u = asm.universe;
188                         switch (br.ReadByte())
189                         {
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();
218                                 case 0x55:
219                                         return ReadType(asm, br);
220                                 case 0x50:
221                                         return u.System_Type;
222                                 case 0x51:
223                                         return u.System_Object;
224                                 default:
225                                         throw new InvalidOperationException();
226                         }
227                 }
228
229                 private static CustomAttributeTypedArgument ReadFixedArg(Assembly asm, ByteReader br, Type type)
230                 {
231                         Universe u = asm.universe;
232                         if (type == u.System_String)
233                         {
234                                 return new CustomAttributeTypedArgument(type, br.ReadString());
235                         }
236                         else if (type == u.System_Type)
237                         {
238                                 return new CustomAttributeTypedArgument(type, ReadType(asm, br));
239                         }
240                         else if (type == u.System_Object)
241                         {
242                                 return ReadFixedArg(asm, br, ReadFieldOrPropType(asm, br));
243                         }
244                         else if (type.IsArray)
245                         {
246                                 int length = br.ReadInt32();
247                                 if (length == -1)
248                                 {
249                                         return new CustomAttributeTypedArgument(type, null);
250                                 }
251                                 Type elementType = type.GetElementType();
252                                 CustomAttributeTypedArgument[] array = new CustomAttributeTypedArgument[length];
253                                 for (int i = 0; i < length; i++)
254                                 {
255                                         array[i] = ReadFixedArg(asm, br, elementType);
256                                 }
257                                 return new CustomAttributeTypedArgument(type, array);
258                         }
259                         else if (type.IsEnum)
260                         {
261                                 return new CustomAttributeTypedArgument(type, ReadFixedArg(asm, br, type.GetEnumUnderlyingTypeImpl()).Value);
262                         }
263                         else
264                         {
265                                 switch (Type.GetTypeCode(type))
266                                 {
267                                         case TypeCode.Boolean:
268                                                 return new CustomAttributeTypedArgument(type, br.ReadByte() != 0);
269                                         case TypeCode.Char:
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());
275                                         case TypeCode.SByte:
276                                                 return new CustomAttributeTypedArgument(type, br.ReadSByte());
277                                         case TypeCode.Int16:
278                                                 return new CustomAttributeTypedArgument(type, br.ReadInt16());
279                                         case TypeCode.Int32:
280                                                 return new CustomAttributeTypedArgument(type, br.ReadInt32());
281                                         case TypeCode.Int64:
282                                                 return new CustomAttributeTypedArgument(type, br.ReadInt64());
283                                         case TypeCode.Byte:
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());
291                                         default:
292                                                 throw new InvalidOperationException();
293                                 }
294                         }
295                 }
296
297                 private static Type ReadType(Assembly asm, ByteReader br)
298                 {
299                         string typeName = br.ReadString();
300                         if (typeName == null)
301                         {
302                                 return null;
303                         }
304                         if (typeName.Length > 0 && typeName[typeName.Length - 1] == 0)
305                         {
306                                 // there are broken compilers that emit an extra NUL character after the type name
307                                 typeName = typeName.Substring(0, typeName.Length - 1);
308                         }
309                         return asm.universe.GetType(asm, typeName, true);
310                 }
311
312                 private static IList<CustomAttributeTypedArgument> ReadConstructorArguments(Assembly asm, ByteReader br, ConstructorInfo constructor)
313                 {
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++)
318                         {
319                                 list.Add(ReadFixedArg(asm, br, sig.GetParameterType(i)));
320                         }
321                         return list.AsReadOnly();
322                 }
323
324                 private static IList<CustomAttributeNamedArgument> ReadNamedArguments(Assembly asm, ByteReader br, int named, Type type)
325                 {
326                         List<CustomAttributeNamedArgument> list = new List<CustomAttributeNamedArgument>(named);
327                         for (int i = 0; i < named; i++)
328                         {
329                                 byte fieldOrProperty = br.ReadByte();
330                                 Type fieldOrPropertyType = ReadFieldOrPropType(asm, br);
331                                 string name = br.ReadString();
332                                 CustomAttributeTypedArgument value = ReadFixedArg(asm, br, fieldOrPropertyType);
333                                 MemberInfo member;
334                                 switch (fieldOrProperty)
335                                 {
336                                         case 0x53:
337                                                 member = GetField(type, name);
338                                                 break;
339                                         case 0x54:
340                                                 member = GetProperty(type, name);
341                                                 break;
342                                         default:
343                                                 throw new BadImageFormatException();
344                                 }
345                                 if (member == null)
346                                 {
347                                         throw new BadImageFormatException();
348                                 }
349                                 list.Add(new CustomAttributeNamedArgument(member, value));
350                         }
351                         return list.AsReadOnly();
352                 }
353
354                 private static FieldInfo GetField(Type type, string name)
355                 {
356                         for (; type != null; type = type.BaseType)
357                         {
358                                 foreach (FieldInfo field in type.__GetDeclaredFields())
359                                 {
360                                         if (field.IsPublic && !field.IsStatic && field.Name == name)
361                                         {
362                                                 return field;
363                                         }
364                                 }
365                         }
366                         return null;
367                 }
368
369                 private static PropertyInfo GetProperty(Type type, string name)
370                 {
371                         for (; type != null; type = type.BaseType)
372                         {
373                                 foreach (PropertyInfo property in type.__GetDeclaredProperties())
374                                 {
375                                         if (property.IsPublic && !property.IsStatic && property.Name == name)
376                                         {
377                                                 return property;
378                                         }
379                                 }
380                         }
381                         return null;
382                 }
383
384                 public bool __TryReadTypeName(out string ns, out string name)
385                 {
386                         if (lazyConstructor == null)
387                         {
388                                 ModuleReader mod = module as ModuleReader;
389                                 if (mod != null)
390                                 {
391                                         // Note that we only need to manually handle the MemberRef case here,
392                                         // because a MethodDef will result in a lazy MethodInfo object being returned
393                                         // when the constructor is resolved, so we can safely do that without
394                                         // triggering an assembly load.
395                                         int methodToken = mod.CustomAttribute.records[index].Type;
396                                         if ((methodToken >> 24) == MemberRefTable.Index)
397                                         {
398                                                 int methodIndex = (methodToken & 0xFFFFFF) - 1;
399                                                 int typeToken = mod.MemberRef.records[methodIndex].Class;
400                                                 if ((typeToken >> 24) == TypeRefTable.Index)
401                                                 {
402                                                         int typeIndex = (typeToken & 0xFFFFFF) - 1;
403                                                         if ((mod.TypeRef.records[typeIndex].ResolutionScope >> 24) == TypeRefTable.Index)
404                                                         {
405                                                                 // nested types can't be represented using only a namespace and name,
406                                                                 // so we fail
407                                                                 ns = null;
408                                                                 name = null;
409                                                                 return false;
410                                                         }
411                                                         int typeNameSpace = mod.TypeRef.records[typeIndex].TypeNameSpace;
412                                                         ns = typeNameSpace == 0 ? null : mod.GetString(typeNameSpace);
413                                                         name = mod.GetString(mod.TypeRef.records[typeIndex].TypeName);
414                                                         return true;
415                                                 }
416                                         }
417                                 }
418                         }
419                         if (Constructor.DeclaringType.IsNested)
420                         {
421                                 ns = null;
422                                 name = null;
423                                 return false;
424                         }
425                         ns = Constructor.DeclaringType.__Namespace;
426                         name = Constructor.DeclaringType.__Name;
427                         return true;
428                 }
429
430                 public ConstructorInfo Constructor
431                 {
432                         get
433                         {
434                                 if (lazyConstructor == null)
435                                 {
436                                         lazyConstructor = (ConstructorInfo)module.ResolveMethod(module.CustomAttribute.records[index].Type);
437                                 }
438                                 return lazyConstructor;
439                         }
440                 }
441
442                 public IList<CustomAttributeTypedArgument> ConstructorArguments
443                 {
444                         get
445                         {
446                                 if (lazyConstructorArguments == null)
447                                 {
448                                         LazyParseArguments();
449                                 }
450                                 return lazyConstructorArguments;
451                         }
452                 }
453
454                 public IList<CustomAttributeNamedArgument> NamedArguments
455                 {
456                         get
457                         {
458                                 if (lazyNamedArguments == null)
459                                 {
460                                         LazyParseArguments();
461                                 }
462                                 return lazyNamedArguments;
463                         }
464                 }
465
466                 private void LazyParseArguments()
467                 {
468                         ByteReader br = module.GetBlob(module.CustomAttribute.records[index].Value);
469                         if (br.Length == 0)
470                         {
471                                 // it's legal to have an empty blob
472                                 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
473                                 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
474                         }
475                         else
476                         {
477                                 if (br.ReadUInt16() != 1)
478                                 {
479                                         throw new BadImageFormatException();
480                                 }
481                                 lazyConstructorArguments = ReadConstructorArguments(module.Assembly, br, Constructor);
482                                 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadUInt16(), Constructor.DeclaringType);
483                         }
484                 }
485
486                 public CustomAttributeBuilder __ToBuilder()
487                 {
488                         object[] args = new object[ConstructorArguments.Count];
489                         for (int i = 0; i < args.Length; i++)
490                         {
491                                 args[i] = ConstructorArguments[i].Value;
492                         }
493                         List<PropertyInfo> namedProperties = new List<PropertyInfo>();
494                         List<object> propertyValues = new List<object>();
495                         List<FieldInfo> namedFields = new List<FieldInfo>();
496                         List<object> fieldValues = new List<object>();
497                         foreach (CustomAttributeNamedArgument named in NamedArguments)
498                         {
499                                 if (named.MemberInfo is PropertyInfo)
500                                 {
501                                         namedProperties.Add((PropertyInfo)named.MemberInfo);
502                                         propertyValues.Add(named.TypedValue.Value);
503                                 }
504                                 else
505                                 {
506                                         namedFields.Add((FieldInfo)named.MemberInfo);
507                                         fieldValues.Add(named.TypedValue.Value);
508                                 }
509                         }
510                         return new CustomAttributeBuilder(Constructor, args, namedProperties.ToArray(), propertyValues.ToArray(), namedFields.ToArray(), fieldValues.ToArray());
511                 }
512
513                 public static IList<CustomAttributeData> GetCustomAttributes(MemberInfo member)
514                 {
515                         return member.GetCustomAttributesData(null);
516                 }
517
518                 public static IList<CustomAttributeData> GetCustomAttributes(Assembly assembly)
519                 {
520                         return assembly.GetCustomAttributesData(null);
521                 }
522
523                 public static IList<CustomAttributeData> GetCustomAttributes(Module module)
524                 {
525                         return module.GetCustomAttributesData(null);
526                 }
527
528                 public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo parameter)
529                 {
530                         return parameter.GetCustomAttributesData(null);
531                 }
532
533                 public static IList<CustomAttributeData> __GetCustomAttributes(Assembly assembly, Type attributeType, bool inherit)
534                 {
535                         return assembly.GetCustomAttributesData(attributeType);
536                 }
537
538                 public static IList<CustomAttributeData> __GetCustomAttributes(Module module, Type attributeType, bool inherit)
539                 {
540                         return module.GetCustomAttributesData(attributeType);
541                 }
542
543                 public static IList<CustomAttributeData> __GetCustomAttributes(ParameterInfo parameter, Type attributeType, bool inherit)
544                 {
545                         return parameter.GetCustomAttributesData(attributeType);
546                 }
547
548                 public static IList<CustomAttributeData> __GetCustomAttributes(MemberInfo member, Type attributeType, bool inherit)
549                 {
550                         if (!inherit || !IsInheritableAttribute(attributeType))
551                         {
552                                 return member.GetCustomAttributesData(attributeType);
553                         }
554                         List<CustomAttributeData> list = new List<CustomAttributeData>();
555                         for (; ; )
556                         {
557                                 list.AddRange(member.GetCustomAttributesData(attributeType));
558                                 Type type = member as Type;
559                                 if (type != null)
560                                 {
561                                         type = type.BaseType;
562                                         if (type == null)
563                                         {
564                                                 return list;
565                                         }
566                                         member = type;
567                                         continue;
568                                 }
569                                 MethodInfo method = member as MethodInfo;
570                                 if (method != null)
571                                 {
572                                         MemberInfo prev = member;
573                                         method = method.GetBaseDefinition();
574                                         if (method == null || method == prev)
575                                         {
576                                                 return list;
577                                         }
578                                         member = method;
579                                         continue;
580                                 }
581                                 return list;
582                         }
583                 }
584
585                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Assembly assembly)
586                 {
587                         if (assembly.__IsMissing)
588                         {
589                                 throw new MissingAssemblyException((MissingAssembly)assembly);
590                         }
591                         return assembly.ManifestModule.GetDeclarativeSecurity(0x20000001);
592                 }
593
594                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Type type)
595                 {
596                         if ((type.Attributes & TypeAttributes.HasSecurity) != 0)
597                         {
598                                 return type.Module.GetDeclarativeSecurity(type.MetadataToken);
599                         }
600                         else
601                         {
602                                 return EmptyList;
603                         }
604                 }
605
606                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(MethodBase method)
607                 {
608                         if ((method.Attributes & MethodAttributes.HasSecurity) != 0)
609                         {
610                                 return method.Module.GetDeclarativeSecurity(method.MetadataToken);
611                         }
612                         else
613                         {
614                                 return EmptyList;
615                         }
616                 }
617
618                 private static bool IsInheritableAttribute(Type attribute)
619                 {
620                         Type attributeUsageAttribute = attribute.Module.universe.System_AttributeUsageAttribute;
621                         IList<CustomAttributeData> attr = attribute.GetCustomAttributesData(attributeUsageAttribute);
622                         if (attr.Count != 0)
623                         {
624                                 foreach (CustomAttributeNamedArgument named in attr[0].NamedArguments)
625                                 {
626                                         if (named.MemberInfo.Name == "Inherited")
627                                         {
628                                                 return (bool)named.TypedValue.Value;
629                                         }
630                                 }
631                         }
632                         return true;
633                 }
634         }
635 }