Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / IKVM.Reflection / Emit / CustomAttributeBuilder.cs
1 /*
2   Copyright (C) 2008-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.IO;
26 using System.Collections.Generic;
27 using System.Diagnostics;
28 using System.Text;
29 using IKVM.Reflection.Writer;
30
31 namespace IKVM.Reflection.Emit
32 {
33         public sealed class CustomAttributeBuilder
34         {
35                 private readonly ConstructorInfo con;
36                 private readonly byte[] blob;
37                 private readonly object[] constructorArgs;
38                 private readonly PropertyInfo[] namedProperties;
39                 private readonly object[] propertyValues;
40                 private readonly FieldInfo[] namedFields;
41                 private readonly object[] fieldValues;
42
43                 internal CustomAttributeBuilder(ConstructorInfo con, byte[] blob)
44                 {
45                         this.con = con;
46                         this.blob = blob;
47                 }
48
49                 private CustomAttributeBuilder(ConstructorInfo con, int securityAction, byte[] blob)
50                 {
51                         this.con = con;
52                         this.blob = blob;
53                         this.constructorArgs = new object[] { securityAction };
54                 }
55
56                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs)
57                         : this(con, constructorArgs, null, null, null,null)
58                 {
59                 }
60
61                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues)
62                         : this(con, constructorArgs, null, null, namedFields, fieldValues)
63                 {
64                 }
65
66                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues)
67                         : this(con, constructorArgs, namedProperties, propertyValues, null, null)
68                 {
69                 }
70
71                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues)
72                 {
73                         this.con = con;
74                         this.constructorArgs = constructorArgs;
75                         this.namedProperties = namedProperties;
76                         this.propertyValues = propertyValues;
77                         this.namedFields = namedFields;
78                         this.fieldValues = fieldValues;
79                 }
80
81                 public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, byte[] blob)
82                 {
83                         return new CustomAttributeBuilder(con, blob);
84                 }
85
86                 public static CustomAttributeBuilder __FromBlob(ConstructorInfo con, int securityAction, byte[] blob)
87                 {
88                         return new CustomAttributeBuilder(con, securityAction, blob);
89                 }
90
91                 public static CustomAttributeTypedArgument __MakeTypedArgument(Type type, object value)
92                 {
93                         return new CustomAttributeTypedArgument(type, value);
94                 }
95
96                 private sealed class BlobWriter
97                 {
98                         private readonly Assembly assembly;
99                         private readonly CustomAttributeBuilder cab;
100                         private readonly ByteBuffer bb;
101
102                         internal BlobWriter(Assembly assembly, CustomAttributeBuilder cab, ByteBuffer bb)
103                         {
104                                 this.assembly = assembly;
105                                 this.cab = cab;
106                                 this.bb = bb;
107                         }
108
109                         internal void WriteCustomAttributeBlob()
110                         {
111                                 // prolog
112                                 WriteUInt16(1);
113                                 ParameterInfo[] pi = cab.con.GetParameters();
114                                 for (int i = 0; i < pi.Length; i++)
115                                 {
116                                         WriteFixedArg(pi[i].ParameterType, cab.constructorArgs[i]);
117                                 }
118                                 WriteNamedArguments(false);
119                         }
120
121                         internal void WriteNamedArguments(bool forDeclSecurity)
122                         {
123                                 // NumNamed
124                                 int named = 0;
125                                 if (cab.namedFields != null)
126                                 {
127                                         named += cab.namedFields.Length;
128                                 }
129                                 if (cab.namedProperties != null)
130                                 {
131                                         named += cab.namedProperties.Length;
132                                 }
133                                 if (forDeclSecurity)
134                                 {
135                                         WritePackedLen(named);
136                                 }
137                                 else
138                                 {
139                                         WriteUInt16((ushort)named);
140                                 }
141                                 if (cab.namedFields != null)
142                                 {
143                                         for (int i = 0; i < cab.namedFields.Length; i++)
144                                         {
145                                                 WriteNamedArg(0x53, cab.namedFields[i].FieldType, cab.namedFields[i].Name, cab.fieldValues[i]);
146                                         }
147                                 }
148                                 if (cab.namedProperties != null)
149                                 {
150                                         for (int i = 0; i < cab.namedProperties.Length; i++)
151                                         {
152                                                 WriteNamedArg(0x54, cab.namedProperties[i].PropertyType, cab.namedProperties[i].Name, cab.propertyValues[i]);
153                                         }
154                                 }
155                         }
156
157                         private void WriteNamedArg(byte fieldOrProperty, Type type, string name, object value)
158                         {
159                                 WriteByte(fieldOrProperty);
160                                 WriteFieldOrPropType(type);
161                                 WriteString(name);
162                                 WriteFixedArg(type, value);
163                         }
164
165                         private void WriteByte(byte value)
166                         {
167                                 bb.Write(value);
168                         }
169
170                         private void WriteUInt16(ushort value)
171                         {
172                                 bb.Write(value);
173                         }
174
175                         private void WriteInt32(int value)
176                         {
177                                 bb.Write(value);
178                         }
179
180                         private void WriteFixedArg(Type type, object value)
181                         {
182                                 Universe u = assembly.universe;
183                                 if (type == u.System_String)
184                                 {
185                                         WriteString((string)value);
186                                 }
187                                 else if (type == u.System_Boolean)
188                                 {
189                                         WriteByte((bool)value ? (byte)1 : (byte)0);
190                                 }
191                                 else if (type == u.System_Char)
192                                 {
193                                         WriteUInt16((char)value);
194                                 }
195                                 else if (type == u.System_SByte)
196                                 {
197                                         WriteByte((byte)(sbyte)value);
198                                 }
199                                 else if (type == u.System_Byte)
200                                 {
201                                         WriteByte((byte)value);
202                                 }
203                                 else if (type == u.System_Int16)
204                                 {
205                                         WriteUInt16((ushort)(short)value);
206                                 }
207                                 else if (type == u.System_UInt16)
208                                 {
209                                         WriteUInt16((ushort)value);
210                                 }
211                                 else if (type == u.System_Int32)
212                                 {
213                                         WriteInt32((int)value);
214                                 }
215                                 else if (type == u.System_UInt32)
216                                 {
217                                         WriteInt32((int)(uint)value);
218                                 }
219                                 else if (type == u.System_Int64)
220                                 {
221                                         WriteInt64((long)value);
222                                 }
223                                 else if (type == u.System_UInt64)
224                                 {
225                                         WriteInt64((long)(ulong)value);
226                                 }
227                                 else if (type == u.System_Single)
228                                 {
229                                         WriteSingle((float)value);
230                                 }
231                                 else if (type == u.System_Double)
232                                 {
233                                         WriteDouble((double)value);
234                                 }
235                                 else if (type == u.System_Type)
236                                 {
237                                         WriteTypeName((Type)value);
238                                 }
239                                 else if (type == u.System_Object)
240                                 {
241                                         if (value == null)
242                                         {
243                                                 type = u.System_String;
244                                         }
245                                         else if (value is Type)
246                                         {
247                                                 // value.GetType() would return a subclass of Type, but we don't want to deal with that
248                                                 type = u.System_Type;
249                                         }
250                                         else if (value is CustomAttributeTypedArgument)
251                                         {
252                                                 CustomAttributeTypedArgument cta = (CustomAttributeTypedArgument)value;
253                                                 value = cta.Value;
254                                                 type = cta.ArgumentType;
255                                         }
256                                         else
257                                         {
258                                                 type = u.Import(value.GetType());
259                                         }
260                                         WriteFieldOrPropType(type);
261                                         WriteFixedArg(type, value);
262                                 }
263                                 else if (type.IsArray)
264                                 {
265                                         if (value == null)
266                                         {
267                                                 WriteInt32(-1);
268                                         }
269                                         else
270                                         {
271                                                 Array array = (Array)value;
272                                                 Type elemType = type.GetElementType();
273                                                 WriteInt32(array.Length);
274                                                 foreach (object val in array)
275                                                 {
276                                                         WriteFixedArg(elemType, val);
277                                                 }
278                                         }
279                                 }
280                                 else if (type.IsEnum)
281                                 {
282                                         WriteFixedArg(type.GetEnumUnderlyingTypeImpl(), value);
283                                 }
284                                 else
285                                 {
286                                         throw new ArgumentException();
287                                 }
288                         }
289
290                         private void WriteInt64(long value)
291                         {
292                                 bb.Write(value);
293                         }
294
295                         private void WriteSingle(float value)
296                         {
297                                 bb.Write(value);
298                         }
299
300                         private void WriteDouble(double value)
301                         {
302                                 bb.Write(value);
303                         }
304
305                         private void WriteTypeName(Type type)
306                         {
307                                 string name = null;
308                                 if (type != null)
309                                 {
310                                         StringBuilder sb = new StringBuilder();
311                                         GetTypeName(sb, type, false);
312                                         name = sb.ToString();
313                                 }
314                                 WriteString(name);
315                         }
316
317                         private void GetTypeName(StringBuilder sb, Type type, bool isTypeParam)
318                         {
319                                 bool v1 = !assembly.ManifestModule.__IsMissing && assembly.ManifestModule.MDStreamVersion < 0x20000;
320                                 bool includeAssemblyName = type.Assembly != assembly && (!v1 || type.Assembly != type.Module.universe.Mscorlib);
321                                 if (isTypeParam && includeAssemblyName)
322                                 {
323                                         sb.Append('[');
324                                 }
325                                 GetTypeNameImpl(sb, type);
326                                 if (includeAssemblyName)
327                                 {
328                                         if (v1)
329                                         {
330                                                 sb.Append(',');
331                                         }
332                                         else
333                                         {
334                                                 sb.Append(", ");
335                                         }
336                                         if (isTypeParam)
337                                         {
338                                                 sb.Append(type.Assembly.FullName.Replace("]", "\\]")).Append(']');
339                                         }
340                                         else
341                                         {
342                                                 sb.Append(type.Assembly.FullName);
343                                         }
344                                 }
345                         }
346
347                         private void GetTypeNameImpl(StringBuilder sb, Type type)
348                         {
349                                 if (type.HasElementType)
350                                 {
351                                         GetTypeNameImpl(sb, type.GetElementType());
352                                         sb.Append(((ElementHolderType)type).GetSuffix());
353                                 }
354                                 else if (type.IsConstructedGenericType)
355                                 {
356                                         sb.Append(type.GetGenericTypeDefinition().FullName);
357                                         sb.Append('[');
358                                         string sep = "";
359                                         foreach (Type typeParam in type.GetGenericArguments())
360                                         {
361                                                 sb.Append(sep);
362                                                 GetTypeName(sb, typeParam, true);
363                                                 sep = ",";
364                                         }
365                                         sb.Append(']');
366                                 }
367                                 else
368                                 {
369                                         sb.Append(type.FullName);
370                                 }
371                         }
372         
373                         private void WriteString(string val)
374                         {
375                                 bb.Write(val);
376                         }
377
378                         private void WritePackedLen(int len)
379                         {
380                                 bb.WriteCompressedInt(len);
381                         }
382
383                         private void WriteFieldOrPropType(Type type)
384                         {
385                                 Universe u = type.Module.universe;
386                                 if (type == u.System_Type)
387                                 {
388                                         WriteByte(0x50);
389                                 }
390                                 else if (type == u.System_Object)
391                                 {
392                                         WriteByte(0x51);
393                                 }
394                                 else if (type == u.System_Boolean)
395                                 {
396                                         WriteByte(0x02);
397                                 }
398                                 else if (type == u.System_Char)
399                                 {
400                                         WriteByte(0x03);
401                                 }
402                                 else if (type == u.System_SByte)
403                                 {
404                                         WriteByte(0x04);
405                                 }
406                                 else if (type == u.System_Byte)
407                                 {
408                                         WriteByte(0x05);
409                                 }
410                                 else if (type == u.System_Int16)
411                                 {
412                                         WriteByte(0x06);
413                                 }
414                                 else if (type == u.System_UInt16)
415                                 {
416                                         WriteByte(0x07);
417                                 }
418                                 else if (type == u.System_Int32)
419                                 {
420                                         WriteByte(0x08);
421                                 }
422                                 else if (type == u.System_UInt32)
423                                 {
424                                         WriteByte(0x09);
425                                 }
426                                 else if (type == u.System_Int64)
427                                 {
428                                         WriteByte(0x0A);
429                                 }
430                                 else if (type == u.System_UInt64)
431                                 {
432                                         WriteByte(0x0B);
433                                 }
434                                 else if (type == u.System_Single)
435                                 {
436                                         WriteByte(0x0C);
437                                 }
438                                 else if (type == u.System_Double)
439                                 {
440                                         WriteByte(0x0D);
441                                 }
442                                 else if (type == u.System_String)
443                                 {
444                                         WriteByte(0x0E);
445                                 }
446                                 else if (type.IsArray)
447                                 {
448                                         WriteByte(0x1D);
449                                         WriteFieldOrPropType(type.GetElementType());
450                                 }
451                                 else if (type.IsEnum)
452                                 {
453                                         WriteByte(0x55);
454                                         WriteTypeName(type);
455                                 }
456                                 else
457                                 {
458                                         throw new ArgumentException();
459                                 }
460                         }
461                 }
462
463                 internal bool IsPseudoCustomAttribute
464                 {
465                         get { return con.DeclaringType.IsPseudoCustomAttribute; }
466                 }
467
468                 internal ConstructorInfo Constructor
469                 {
470                         get { return con; }
471                 }
472
473                 internal int WriteBlob(ModuleBuilder moduleBuilder)
474                 {
475                         ByteBuffer bb;
476                         if (blob != null)
477                         {
478                                 bb = ByteBuffer.Wrap(blob);
479                         }
480                         else
481                         {
482                                 bb = new ByteBuffer(100);
483                                 BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb);
484                                 bw.WriteCustomAttributeBlob();
485                         }
486                         return moduleBuilder.Blobs.Add(bb);
487                 }
488
489                 internal object GetConstructorArgument(int pos)
490                 {
491                         return constructorArgs[pos];
492                 }
493
494                 internal int ConstructorArgumentCount
495                 {
496                         get { return constructorArgs == null ? 0 : constructorArgs.Length; }
497                 }
498
499                 internal T? GetFieldValue<T>(string name) where T : struct
500                 {
501                         object val = GetFieldValue(name);
502                         if (val is T)
503                         {
504                                 return (T)val;
505                         }
506                         else if (val != null)
507                         {
508                                 if (typeof(T).IsEnum)
509                                 {
510                                         Debug.Assert(Enum.GetUnderlyingType(typeof(T)) == val.GetType());
511                                         return (T)Enum.ToObject(typeof(T), val);
512                                 }
513                                 else
514                                 {
515                                         Debug.Assert(Enum.GetUnderlyingType(val.GetType()) == typeof(T));
516                                         return (T)Convert.ChangeType(val, typeof(T));
517                                 }
518                         }
519                         else
520                         {
521                                 return null;
522                         }
523                 }
524
525                 internal object GetFieldValue(string name)
526                 {
527                         if (namedFields != null)
528                         {
529                                 for (int i = 0; i < namedFields.Length; i++)
530                                 {
531                                         if (namedFields[i].Name == name)
532                                         {
533                                                 return fieldValues[i];
534                                         }
535                                 }
536                         }
537                         return null;
538                 }
539
540                 internal string GetLegacyDeclSecurity()
541                 {
542                         if (con.DeclaringType == con.Module.universe.System_Security_Permissions_PermissionSetAttribute
543                                 && blob == null
544                                 && (namedFields == null || namedFields.Length == 0)
545                                 && namedProperties != null
546                                 && namedProperties.Length == 1
547                                 && namedProperties[0].Name == "XML")
548                         {
549                                 return propertyValues[0] as string;
550                         }
551                         return null;
552                 }
553
554                 internal void WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb)
555                 {
556                         if (blob != null)
557                         {
558                                 bb.Write(blob);
559                         }
560                         else
561                         {
562                                 BlobWriter bw = new BlobWriter(moduleBuilder.Assembly, this, bb);
563                                 bw.WriteNamedArguments(true);
564                         }
565                 }
566
567                 internal CustomAttributeData ToData(Assembly asm)
568                 {
569                         if (blob != null)
570                         {
571                                 if (constructorArgs != null)
572                                 {
573                                         return new CustomAttributeData(asm, con, (int)constructorArgs[0], blob, -1);
574                                 }
575                                 return new CustomAttributeData(asm, con, new IKVM.Reflection.Reader.ByteReader(blob, 0, blob.Length));
576                         }
577                         else
578                         {
579                                 List<CustomAttributeNamedArgument> namedArgs = new List<CustomAttributeNamedArgument>();
580                                 if (namedProperties != null)
581                                 {
582                                         for (int i = 0; i < namedProperties.Length; i++)
583                                         {
584                                                 namedArgs.Add(new CustomAttributeNamedArgument(namedProperties[i], RewrapValue(namedProperties[i].PropertyType, propertyValues[i])));
585                                         }
586                                 }
587                                 if (namedFields != null)
588                                 {
589                                         for (int i = 0; i < namedFields.Length; i++)
590                                         {
591                                                 namedArgs.Add(new CustomAttributeNamedArgument(namedFields[i], RewrapValue(namedFields[i].FieldType, fieldValues[i])));
592                                         }
593                                 }
594                                 List<CustomAttributeTypedArgument> args = new List<CustomAttributeTypedArgument>(constructorArgs.Length);
595                                 ParameterInfo[] parameters = this.Constructor.GetParameters();
596                                 for (int i = 0; i < constructorArgs.Length; i++)
597                                 {
598                                         args.Add(RewrapValue(parameters[i].ParameterType, constructorArgs[i]));
599                                 }
600                                 return new CustomAttributeData(asm.ManifestModule, con, args, namedArgs);
601                         }
602                 }
603
604                 private static CustomAttributeTypedArgument RewrapValue(Type type, object value)
605                 {
606                         if (value is Array)
607                         {
608                                 Array array = (Array)value;
609                                 Type arrayType = type.Module.universe.Import(array.GetType());
610                                 return RewrapArray(arrayType, array);
611                         }
612                         else if (value is CustomAttributeTypedArgument)
613                         {
614                                 CustomAttributeTypedArgument arg = (CustomAttributeTypedArgument)value;
615                                 if (arg.Value is Array)
616                                 {
617                                         return RewrapArray(arg.ArgumentType, (Array)arg.Value);
618                                 }
619                                 return arg;
620                         }
621                         else
622                         {
623                                 return new CustomAttributeTypedArgument(type, value);
624                         }
625                 }
626
627                 private static CustomAttributeTypedArgument RewrapArray(Type arrayType, Array array)
628                 {
629                         Type elementType = arrayType.GetElementType();
630                         CustomAttributeTypedArgument[] newArray = new CustomAttributeTypedArgument[array.Length];
631                         for (int i = 0; i < newArray.Length; i++)
632                         {
633                                 newArray[i] = RewrapValue(elementType, array.GetValue(i));
634                         }
635                         return new CustomAttributeTypedArgument(arrayType, newArray);
636                 }
637
638                 internal bool HasBlob
639                 {
640                         get { return blob != null; }
641                 }
642
643                 internal CustomAttributeBuilder DecodeBlob(Assembly asm)
644                 {
645                         if (blob == null)
646                         {
647                                 return this;
648                         }
649                         else
650                         {
651                                 return ToData(asm).__ToBuilder();
652                         }
653                 }
654
655                 internal byte[] GetBlob(Assembly asm)
656                 {
657                         ByteBuffer bb = new ByteBuffer(100);
658                         BlobWriter bw = new BlobWriter(asm, this, bb);
659                         bw.WriteCustomAttributeBlob();
660                         return bb.ToArray();
661                 }
662         }
663 }