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