Merge branch 'sgen-disable-gc'
[mono.git] / mcs / class / IKVM.Reflection / Emit / CustomAttributeBuilder.cs
1 /*
2   Copyright (C) 2008 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 IKVM.Reflection.Writer;
29
30 namespace IKVM.Reflection.Emit
31 {
32         public sealed class CustomAttributeBuilder
33         {
34                 private readonly ConstructorInfo con;
35                 private readonly byte[] blob;
36                 private readonly object[] constructorArgs;
37                 private readonly PropertyInfo[] namedProperties;
38                 private readonly object[] propertyValues;
39                 private readonly FieldInfo[] namedFields;
40                 private readonly object[] fieldValues;
41
42                 internal CustomAttributeBuilder(ConstructorInfo con, byte[] blob)
43                 {
44                         this.con = con;
45                         this.blob = blob;
46                 }
47
48                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs)
49                         : this(con, constructorArgs, null, null, null,null)
50                 {
51                 }
52
53                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues)
54                         : this(con, constructorArgs, null, null, namedFields, fieldValues)
55                 {
56                 }
57
58                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues)
59                         : this(con, constructorArgs, namedProperties, propertyValues, null, null)
60                 {
61                 }
62
63                 public CustomAttributeBuilder(ConstructorInfo con, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues)
64                 {
65                         this.con = con;
66                         this.constructorArgs = constructorArgs;
67                         this.namedProperties = namedProperties;
68                         this.propertyValues = propertyValues;
69                         this.namedFields = namedFields;
70                         this.fieldValues = fieldValues;
71                 }
72
73                 private sealed class BlobWriter
74                 {
75                         private readonly ModuleBuilder moduleBuilder;
76                         private readonly CustomAttributeBuilder cab;
77                         private readonly ByteBuffer bb;
78
79                         internal BlobWriter(ModuleBuilder moduleBuilder, CustomAttributeBuilder cab, ByteBuffer bb)
80                         {
81                                 this.moduleBuilder = moduleBuilder;
82                                 this.cab = cab;
83                                 this.bb = bb;
84                         }
85
86                         internal void WriteCustomAttributeBlob()
87                         {
88                                 // prolog
89                                 WriteUInt16(1);
90                                 ParameterInfo[] pi = cab.con.GetParameters();
91                                 for (int i = 0; i < pi.Length; i++)
92                                 {
93                                         WriteFixedArg(pi[i].ParameterType, cab.constructorArgs[i]);
94                                 }
95                                 WriteNamedArguments(false);
96                         }
97
98                         internal void WriteNamedArguments(bool forDeclSecurity)
99                         {
100                                 // NumNamed
101                                 int named = 0;
102                                 if (cab.namedFields != null)
103                                 {
104                                         named += cab.namedFields.Length;
105                                 }
106                                 if (cab.namedProperties != null)
107                                 {
108                                         named += cab.namedProperties.Length;
109                                 }
110                                 if (forDeclSecurity)
111                                 {
112                                         WritePackedLen(named);
113                                 }
114                                 else
115                                 {
116                                         WriteUInt16((ushort)named);
117                                 }
118                                 if (cab.namedFields != null)
119                                 {
120                                         for (int i = 0; i < cab.namedFields.Length; i++)
121                                         {
122                                                 WriteNamedArg(0x53, cab.namedFields[i].FieldType, cab.namedFields[i].Name, cab.fieldValues[i]);
123                                         }
124                                 }
125                                 if (cab.namedProperties != null)
126                                 {
127                                         for (int i = 0; i < cab.namedProperties.Length; i++)
128                                         {
129                                                 WriteNamedArg(0x54, cab.namedProperties[i].PropertyType, cab.namedProperties[i].Name, cab.propertyValues[i]);
130                                         }
131                                 }
132                         }
133
134                         private void WriteNamedArg(byte fieldOrProperty, Type type, string name, object value)
135                         {
136                                 WriteByte(fieldOrProperty);
137                                 WriteFieldOrPropType(type);
138                                 WriteString(name);
139                                 WriteFixedArg(type, value);
140                         }
141
142                         private void WriteByte(byte value)
143                         {
144                                 bb.Write(value);
145                         }
146
147                         private void WriteUInt16(ushort value)
148                         {
149                                 bb.Write(value);
150                         }
151
152                         private void WriteInt32(int value)
153                         {
154                                 bb.Write(value);
155                         }
156
157                         private void WriteFixedArg(Type type, object value)
158                         {
159                                 Universe u = moduleBuilder.universe;
160                                 if (type == u.System_String)
161                                 {
162                                         WriteString((string)value);
163                                 }
164                                 else if (type == u.System_Type)
165                                 {
166                                         WriteTypeName((Type)value);
167                                 }
168                                 else if (type == u.System_Object)
169                                 {
170                                         if (value == null)
171                                         {
172                                                 type = u.System_String;
173                                         }
174                                         else if (value is Type)
175                                         {
176                                                 // value.GetType() would return a subclass of Type, but we don't want to deal with that
177                                                 type = u.System_Type;
178                                         }
179                                         else
180                                         {
181                                                 type = u.Import(value.GetType());
182                                         }
183                                         WriteFieldOrPropType(type);
184                                         WriteFixedArg(type, value);
185                                 }
186                                 else if (type.IsArray)
187                                 {
188                                         if (value == null)
189                                         {
190                                                 WriteInt32(-1);
191                                         }
192                                         else
193                                         {
194                                                 Array array = (Array)value;
195                                                 Type elemType = type.GetElementType();
196                                                 WriteInt32(array.Length);
197                                                 foreach (object val in array)
198                                                 {
199                                                         WriteFixedArg(elemType, val);
200                                                 }
201                                         }
202                                 }
203                                 else if (type.IsEnum)
204                                 {
205                                         WriteFixedArg(type.GetEnumUnderlyingTypeImpl(), value);
206                                 }
207                                 else
208                                 {
209                                         switch (Type.GetTypeCode(type))
210                                         {
211                                                 case TypeCode.Boolean:
212                                                         WriteByte((bool)value ? (byte)1 : (byte)0);
213                                                         break;
214                                                 case TypeCode.Char:
215                                                         WriteUInt16((char)value);
216                                                         break;
217                                                 case TypeCode.SByte:
218                                                         WriteByte((byte)(sbyte)value);
219                                                         break;
220                                                 case TypeCode.Byte:
221                                                         WriteByte((byte)value);
222                                                         break;
223                                                 case TypeCode.Int16:
224                                                         WriteUInt16((ushort)(short)value);
225                                                         break;
226                                                 case TypeCode.UInt16:
227                                                         WriteUInt16((ushort)value);
228                                                         break;
229                                                 case TypeCode.Int32:
230                                                         WriteInt32((int)value);
231                                                         break;
232                                                 case TypeCode.UInt32:
233                                                         WriteInt32((int)(uint)value);
234                                                         break;
235                                                 case TypeCode.Int64:
236                                                         WriteInt64((long)value);
237                                                         break;
238                                                 case TypeCode.UInt64:
239                                                         WriteInt64((long)(ulong)value);
240                                                         break;
241                                                 case TypeCode.Single:
242                                                         WriteSingle((float)value);
243                                                         break;
244                                                 case TypeCode.Double:
245                                                         WriteDouble((double)value);
246                                                         break;
247                                                 default:
248                                                         throw new ArgumentException();
249                                         }
250                                 }
251                         }
252
253                         private void WriteInt64(long value)
254                         {
255                                 bb.Write(value);
256                         }
257
258                         private void WriteSingle(float value)
259                         {
260                                 bb.Write(value);
261                         }
262
263                         private void WriteDouble(double value)
264                         {
265                                 bb.Write(value);
266                         }
267
268                         private void WriteTypeName(Type type)
269                         {
270                                 string name = null;
271                                 if (type != null)
272                                 {
273                                         if (type.Assembly == moduleBuilder.Assembly)
274                                         {
275                                                 name = type.FullName;
276                                         }
277                                         else
278                                         {
279                                                 name = type.AssemblyQualifiedName;
280                                         }
281                                 }
282                                 WriteString(name);
283                         }
284
285                         private void WriteString(string val)
286                         {
287                                 bb.Write(val);
288                         }
289
290                         private void WritePackedLen(int len)
291                         {
292                                 bb.WriteCompressedInt(len);
293                         }
294
295                         private void WriteFieldOrPropType(Type type)
296                         {
297                                 Universe u = type.Module.universe;
298                                 if (type == u.System_Type)
299                                 {
300                                         WriteByte(0x50);
301                                 }
302                                 else if (type == u.System_Object)
303                                 {
304                                         WriteByte(0x51);
305                                 }
306                                 else if (type.IsArray)
307                                 {
308                                         WriteByte(0x1D);
309                                         WriteFieldOrPropType(type.GetElementType());
310                                 }
311                                 else if (type.IsEnum)
312                                 {
313                                         WriteByte(0x55);
314                                         WriteTypeName(type);
315                                 }
316                                 else
317                                 {
318                                         switch (Type.GetTypeCode(type))
319                                         {
320                                                 case TypeCode.Boolean:
321                                                         WriteByte(0x02);
322                                                         break;
323                                                 case TypeCode.Char:
324                                                         WriteByte(0x03);
325                                                         break;
326                                                 case TypeCode.SByte:
327                                                         WriteByte(0x04);
328                                                         break;
329                                                 case TypeCode.Byte:
330                                                         WriteByte(0x05);
331                                                         break;
332                                                 case TypeCode.Int16:
333                                                         WriteByte(0x06);
334                                                         break;
335                                                 case TypeCode.UInt16:
336                                                         WriteByte(0x07);
337                                                         break;
338                                                 case TypeCode.Int32:
339                                                         WriteByte(0x08);
340                                                         break;
341                                                 case TypeCode.UInt32:
342                                                         WriteByte(0x09);
343                                                         break;
344                                                 case TypeCode.Int64:
345                                                         WriteByte(0x0A);
346                                                         break;
347                                                 case TypeCode.UInt64:
348                                                         WriteByte(0x0B);
349                                                         break;
350                                                 case TypeCode.Single:
351                                                         WriteByte(0x0C);
352                                                         break;
353                                                 case TypeCode.Double:
354                                                         WriteByte(0x0D);
355                                                         break;
356                                                 case TypeCode.String:
357                                                         WriteByte(0x0E);
358                                                         break;
359                                                 default:
360                                                         throw new ArgumentException();
361                                         }
362                                 }
363                         }
364                 }
365
366                 internal bool IsPseudoCustomAttribute
367                 {
368                         get { return con.DeclaringType.IsPseudoCustomAttribute; }
369                 }
370
371                 internal ConstructorInfo Constructor
372                 {
373                         get { return con; }
374                 }
375
376                 internal int WriteBlob(ModuleBuilder moduleBuilder)
377                 {
378                         ByteBuffer bb;
379                         if (blob != null)
380                         {
381                                 bb = ByteBuffer.Wrap(blob);
382                         }
383                         else
384                         {
385                                 bb = new ByteBuffer(100);
386                                 BlobWriter bw = new BlobWriter(moduleBuilder, this, bb);
387                                 bw.WriteCustomAttributeBlob();
388                         }
389                         return moduleBuilder.Blobs.Add(bb);
390                 }
391
392                 internal object GetConstructorArgument(int pos)
393                 {
394                         return constructorArgs[pos];
395                 }
396
397                 internal int ConstructorArgumentCount
398                 {
399                         get { return constructorArgs == null ? 0 : constructorArgs.Length; }
400                 }
401
402                 internal T? GetFieldValue<T>(string name) where T : struct
403                 {
404                         object val = GetFieldValue(name);
405                         if (val is T)
406                         {
407                                 return (T)val;
408                         }
409                         else if (val != null)
410                         {
411                                 if (typeof(T).IsEnum)
412                                 {
413                                         Debug.Assert(Enum.GetUnderlyingType(typeof(T)) == val.GetType());
414                                         return (T)Enum.ToObject(typeof(T), val);
415                                 }
416                                 else
417                                 {
418                                         Debug.Assert(Enum.GetUnderlyingType(val.GetType()) == typeof(T));
419                                         return (T)Convert.ChangeType(val, typeof(T));
420                                 }
421                         }
422                         else
423                         {
424                                 return null;
425                         }
426                 }
427
428                 internal object GetFieldValue(string name)
429                 {
430                         if (namedFields != null)
431                         {
432                                 for (int i = 0; i < namedFields.Length; i++)
433                                 {
434                                         if (namedFields[i].Name == name)
435                                         {
436                                                 return fieldValues[i];
437                                         }
438                                 }
439                         }
440                         return null;
441                 }
442
443                 internal void WriteNamedArgumentsForDeclSecurity(ModuleBuilder moduleBuilder, ByteBuffer bb)
444                 {
445                         BlobWriter bw = new BlobWriter(moduleBuilder, this, bb);
446                         bw.WriteNamedArguments(true);
447                 }
448
449                 internal CustomAttributeData ToData(Assembly asm)
450                 {
451                         if (blob != null)
452                         {
453                                 return new CustomAttributeData(asm, con, new IKVM.Reflection.Reader.ByteReader(blob, 0, blob.Length));
454                         }
455                         else
456                         {
457                                 List<CustomAttributeNamedArgument> namedArgs = new List<CustomAttributeNamedArgument>();
458                                 if (namedProperties != null)
459                                 {
460                                         for (int i = 0; i < namedProperties.Length; i++)
461                                         {
462                                                 namedArgs.Add(new CustomAttributeNamedArgument(namedProperties[i], new CustomAttributeTypedArgument(namedProperties[i].PropertyType, propertyValues[i])));
463                                         }
464                                 }
465                                 if (namedFields != null)
466                                 {
467                                         for (int i = 0; i < namedFields.Length; i++)
468                                         {
469                                                 namedArgs.Add(new CustomAttributeNamedArgument(namedFields[i], new CustomAttributeTypedArgument(namedFields[i].FieldType, fieldValues[i])));
470                                         }
471                                 }
472                                 return new CustomAttributeData(con, constructorArgs, namedArgs);
473                         }
474                 }
475
476                 internal bool HasBlob
477                 {
478                         get { return blob != null; }
479                 }
480
481                 internal CustomAttributeBuilder DecodeBlob(Assembly asm)
482                 {
483                         if (blob == null)
484                         {
485                                 return this;
486                         }
487                         else
488                         {
489                                 return ToData(asm).__ToBuilder();
490                         }
491                 }
492         }
493 }