ae495c92ce298cafddcaeca7ba27bf1fc4389884
[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                 [Obsolete("Use Constructor.DeclaringType instead.")]
385                 internal bool __TryReadTypeName(out string ns, out string name)
386                 {
387                         if (Constructor.DeclaringType.IsNested)
388                         {
389                                 ns = null;
390                                 name = null;
391                                 return false;
392                         }
393                         ns = Constructor.DeclaringType.__Namespace;
394                         name = Constructor.DeclaringType.__Name;
395                         return true;
396                 }
397
398                 // for use by mcs
399                 internal byte[] __GetBlob()
400                 {
401                         return ((ModuleReader)module).GetBlobCopy(module.CustomAttribute.records[index].Value);
402                 }
403
404                 public ConstructorInfo Constructor
405                 {
406                         get
407                         {
408                                 if (lazyConstructor == null)
409                                 {
410                                         lazyConstructor = (ConstructorInfo)module.ResolveMethod(module.CustomAttribute.records[index].Type);
411                                 }
412                                 return lazyConstructor;
413                         }
414                 }
415
416                 public IList<CustomAttributeTypedArgument> ConstructorArguments
417                 {
418                         get
419                         {
420                                 if (lazyConstructorArguments == null)
421                                 {
422                                         LazyParseArguments();
423                                 }
424                                 return lazyConstructorArguments;
425                         }
426                 }
427
428                 public IList<CustomAttributeNamedArgument> NamedArguments
429                 {
430                         get
431                         {
432                                 if (lazyNamedArguments == null)
433                                 {
434                                         LazyParseArguments();
435                                 }
436                                 return lazyNamedArguments;
437                         }
438                 }
439
440                 private void LazyParseArguments()
441                 {
442                         ByteReader br = module.GetBlob(module.CustomAttribute.records[index].Value);
443                         if (br.Length == 0)
444                         {
445                                 // it's legal to have an empty blob
446                                 lazyConstructorArguments = Empty<CustomAttributeTypedArgument>.Array;
447                                 lazyNamedArguments = Empty<CustomAttributeNamedArgument>.Array;
448                         }
449                         else
450                         {
451                                 if (br.ReadUInt16() != 1)
452                                 {
453                                         throw new BadImageFormatException();
454                                 }
455                                 lazyConstructorArguments = ReadConstructorArguments(module.Assembly, br, Constructor);
456                                 lazyNamedArguments = ReadNamedArguments(module.Assembly, br, br.ReadUInt16(), Constructor.DeclaringType);
457                         }
458                 }
459
460                 public CustomAttributeBuilder __ToBuilder()
461                 {
462                         object[] args = new object[ConstructorArguments.Count];
463                         for (int i = 0; i < args.Length; i++)
464                         {
465                                 args[i] = ConstructorArguments[i].Value;
466                         }
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)
472                         {
473                                 if (named.MemberInfo is PropertyInfo)
474                                 {
475                                         namedProperties.Add((PropertyInfo)named.MemberInfo);
476                                         propertyValues.Add(named.TypedValue.Value);
477                                 }
478                                 else
479                                 {
480                                         namedFields.Add((FieldInfo)named.MemberInfo);
481                                         fieldValues.Add(named.TypedValue.Value);
482                                 }
483                         }
484                         return new CustomAttributeBuilder(Constructor, args, namedProperties.ToArray(), propertyValues.ToArray(), namedFields.ToArray(), fieldValues.ToArray());
485                 }
486
487                 public static IList<CustomAttributeData> GetCustomAttributes(MemberInfo member)
488                 {
489                         return member.GetCustomAttributesData(null);
490                 }
491
492                 public static IList<CustomAttributeData> GetCustomAttributes(Assembly assembly)
493                 {
494                         return assembly.GetCustomAttributesData(null);
495                 }
496
497                 public static IList<CustomAttributeData> GetCustomAttributes(Module module)
498                 {
499                         return module.GetCustomAttributesData(null);
500                 }
501
502                 public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo parameter)
503                 {
504                         return parameter.GetCustomAttributesData(null);
505                 }
506
507                 public static IList<CustomAttributeData> __GetCustomAttributes(Assembly assembly, Type attributeType, bool inherit)
508                 {
509                         return assembly.GetCustomAttributesData(attributeType);
510                 }
511
512                 public static IList<CustomAttributeData> __GetCustomAttributes(Module module, Type attributeType, bool inherit)
513                 {
514                         return module.GetCustomAttributesData(attributeType);
515                 }
516
517                 public static IList<CustomAttributeData> __GetCustomAttributes(ParameterInfo parameter, Type attributeType, bool inherit)
518                 {
519                         return parameter.GetCustomAttributesData(attributeType);
520                 }
521
522                 public static IList<CustomAttributeData> __GetCustomAttributes(MemberInfo member, Type attributeType, bool inherit)
523                 {
524                         if (!inherit || !IsInheritableAttribute(attributeType))
525                         {
526                                 return member.GetCustomAttributesData(attributeType);
527                         }
528                         List<CustomAttributeData> list = new List<CustomAttributeData>();
529                         for (; ; )
530                         {
531                                 list.AddRange(member.GetCustomAttributesData(attributeType));
532                                 Type type = member as Type;
533                                 if (type != null)
534                                 {
535                                         type = type.BaseType;
536                                         if (type == null)
537                                         {
538                                                 return list;
539                                         }
540                                         member = type;
541                                         continue;
542                                 }
543                                 MethodInfo method = member as MethodInfo;
544                                 if (method != null)
545                                 {
546                                         MemberInfo prev = member;
547                                         method = method.GetBaseDefinition();
548                                         if (method == null || method == prev)
549                                         {
550                                                 return list;
551                                         }
552                                         member = method;
553                                         continue;
554                                 }
555                                 return list;
556                         }
557                 }
558
559                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Assembly assembly)
560                 {
561                         if (assembly.__IsMissing)
562                         {
563                                 throw new MissingAssemblyException((MissingAssembly)assembly);
564                         }
565                         return assembly.ManifestModule.GetDeclarativeSecurity(0x20000001);
566                 }
567
568                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(Type type)
569                 {
570                         if ((type.Attributes & TypeAttributes.HasSecurity) != 0)
571                         {
572                                 return type.Module.GetDeclarativeSecurity(type.MetadataToken);
573                         }
574                         else
575                         {
576                                 return EmptyList;
577                         }
578                 }
579
580                 public static IList<CustomAttributeData> __GetDeclarativeSecurity(MethodBase method)
581                 {
582                         if ((method.Attributes & MethodAttributes.HasSecurity) != 0)
583                         {
584                                 return method.Module.GetDeclarativeSecurity(method.MetadataToken);
585                         }
586                         else
587                         {
588                                 return EmptyList;
589                         }
590                 }
591
592                 private static bool IsInheritableAttribute(Type attribute)
593                 {
594                         Type attributeUsageAttribute = attribute.Module.universe.System_AttributeUsageAttribute;
595                         IList<CustomAttributeData> attr = attribute.GetCustomAttributesData(attributeUsageAttribute);
596                         if (attr.Count != 0)
597                         {
598                                 foreach (CustomAttributeNamedArgument named in attr[0].NamedArguments)
599                                 {
600                                         if (named.MemberInfo.Name == "Inherited")
601                                         {
602                                                 return (bool)named.TypedValue.Value;
603                                         }
604                                 }
605                         }
606                         return true;
607                 }
608         }
609 }