Add IKVM.Reflection
[mono.git] / mcs / class / IKVM.Reflection / Emit / MethodBuilder.cs
1 /*
2   Copyright (C) 2008-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.IO;
26 using System.Diagnostics;
27 using System.Collections.Generic;
28 using System.Runtime.InteropServices;
29 using System.Runtime.CompilerServices;
30 using System.Diagnostics.SymbolStore;
31 using IKVM.Reflection.Metadata;
32 using IKVM.Reflection.Writer;
33
34 namespace IKVM.Reflection.Emit
35 {
36         public sealed class MethodBuilder : MethodInfo
37         {
38                 private readonly TypeBuilder typeBuilder;
39                 private readonly string name;
40                 private readonly int nameIndex;
41                 private readonly int pseudoToken;
42                 private int signature;
43                 private Type returnType;
44                 private Type[] parameterTypes;
45                 private Type[][][] modifiers;   // see PackedCustomModifiers
46                 private MethodAttributes attributes;
47                 private MethodImplAttributes implFlags;
48                 private ILGenerator ilgen;
49                 private int rva;
50                 private readonly CallingConventions callingConvention;
51                 private List<ParameterBuilder> parameters;
52                 private GenericTypeParameterBuilder[] gtpb;
53                 private List<CustomAttributeBuilder> declarativeSecurity;
54                 private MethodSignature methodSignature;
55                 private bool initLocals = true;
56
57                 internal MethodBuilder(TypeBuilder typeBuilder, string name, MethodAttributes attributes, CallingConventions callingConvention)
58                 {
59                         this.typeBuilder = typeBuilder;
60                         this.name = name;
61                         this.pseudoToken = typeBuilder.ModuleBuilder.AllocPseudoToken();
62                         // because all the MethodBuilders constitute a virtual MethodDef table, we cannot allocate the string during WriteMethodDefRecord,
63                         // since by then the metadata has already been frozen
64                         this.nameIndex = typeBuilder.ModuleBuilder.Strings.Add(name);
65                         this.attributes = attributes;
66                         if ((attributes & MethodAttributes.Static) == 0)
67                         {
68                                 callingConvention |= CallingConventions.HasThis;
69                         }
70                         this.callingConvention = callingConvention;
71                 }
72
73                 public ILGenerator GetILGenerator()
74                 {
75                         return GetILGenerator(16);
76                 }
77
78                 public ILGenerator GetILGenerator(int streamSize)
79                 {
80                         if (ilgen == null)
81                         {
82                                 ilgen = new ILGenerator(typeBuilder.ModuleBuilder, streamSize);
83                         }
84                         return ilgen;
85                 }
86
87                 public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
88                 {
89                         SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
90                 }
91
92                 private void SetDllImportPseudoCustomAttribute(CustomAttributeBuilder customBuilder)
93                 {
94                         CallingConvention? callingConvention = customBuilder.GetFieldValue<CallingConvention>("CallingConvention");
95                         CharSet? charSet = customBuilder.GetFieldValue<CharSet>("CharSet");
96                         SetDllImportPseudoCustomAttribute((string)customBuilder.GetConstructorArgument(0),
97                                 (string)customBuilder.GetFieldValue("EntryPoint"),
98                                 callingConvention,
99                                 charSet,
100                                 (bool?)customBuilder.GetFieldValue("BestFitMapping"),
101                                 (bool?)customBuilder.GetFieldValue("ThrowOnUnmappableChar"),
102                                 (bool?)customBuilder.GetFieldValue("SetLastError"),
103                                 (bool?)customBuilder.GetFieldValue("PreserveSig"),
104                                 (bool?)customBuilder.GetFieldValue("ExactSpelling"));
105                 }
106
107                 internal void SetDllImportPseudoCustomAttribute(string dllName, string entryName, CallingConvention? nativeCallConv, CharSet? nativeCharSet,
108                         bool? bestFitMapping, bool? throwOnUnmappableChar, bool? setLastError, bool? preserveSig, bool? exactSpelling)
109                 {
110                         const short NoMangle = 0x0001;
111                         const short CharSetMask = 0x0006;
112                         const short CharSetNotSpec = 0x0000;
113                         const short CharSetAnsi = 0x0002;
114                         const short CharSetUnicode = 0x0004;
115                         const short CharSetAuto = 0x0006;
116                         const short SupportsLastError = 0x0040;
117                         const short CallConvMask = 0x0700;
118                         const short CallConvWinapi = 0x0100;
119                         const short CallConvCdecl = 0x0200;
120                         const short CallConvStdcall = 0x0300;
121                         const short CallConvThiscall = 0x0400;
122                         const short CallConvFastcall = 0x0500;
123                         // non-standard flags
124                         const short BestFitOn = 0x0010;
125                         const short BestFitOff = 0x0020;
126                         const short CharMapErrorOn = 0x1000;
127                         const short CharMapErrorOff = 0x2000;
128                         int name = this.nameIndex;
129                         short flags = CharSetNotSpec | CallConvWinapi;
130                         if (bestFitMapping.HasValue)
131                         {
132                                 flags |= bestFitMapping.Value ? BestFitOn : BestFitOff;
133                         }
134                         if (throwOnUnmappableChar.HasValue)
135                         {
136                                 flags |= throwOnUnmappableChar.Value ? CharMapErrorOn : CharMapErrorOff;
137                         }
138                         if (nativeCallConv.HasValue)
139                         {
140                                 flags &= ~CallConvMask;
141                                 switch (nativeCallConv.Value)
142                                 {
143                                         case System.Runtime.InteropServices.CallingConvention.Cdecl:
144                                                 flags |= CallConvCdecl;
145                                                 break;
146                                         case System.Runtime.InteropServices.CallingConvention.FastCall:
147                                                 flags |= CallConvFastcall;
148                                                 break;
149                                         case System.Runtime.InteropServices.CallingConvention.StdCall:
150                                                 flags |= CallConvStdcall;
151                                                 break;
152                                         case System.Runtime.InteropServices.CallingConvention.ThisCall:
153                                                 flags |= CallConvThiscall;
154                                                 break;
155                                         case System.Runtime.InteropServices.CallingConvention.Winapi:
156                                                 flags |= CallConvWinapi;
157                                                 break;
158                                 }
159                         }
160                         if (nativeCharSet.HasValue)
161                         {
162                                 flags &= ~CharSetMask;
163                                 switch (nativeCharSet.Value)
164                                 {
165                                         case CharSet.Ansi:
166                                         case CharSet.None:
167                                                 flags |= CharSetAnsi;
168                                                 break;
169                                         case CharSet.Auto:
170                                                 flags |= CharSetAuto;
171                                                 break;
172                                         case CharSet.Unicode:
173                                                 flags |= CharSetUnicode;
174                                                 break;
175                                 }
176                         }
177                         if (entryName != null)
178                         {
179                                 name = this.ModuleBuilder.Strings.Add(entryName);
180                         }
181                         if (exactSpelling.HasValue && exactSpelling.Value)
182                         {
183                                 flags |= NoMangle;
184                         }
185                         if (!preserveSig.HasValue || preserveSig.Value)
186                         {
187                                 implFlags |= MethodImplAttributes.PreserveSig;
188                         }
189                         if (setLastError.HasValue && setLastError.Value)
190                         {
191                                 flags |= SupportsLastError;
192                         }
193                         ImplMapTable.Record rec = new ImplMapTable.Record();
194                         rec.MappingFlags = flags;
195                         rec.MemberForwarded = pseudoToken;
196                         rec.ImportName = name;
197                         rec.ImportScope = this.ModuleBuilder.ModuleRef.FindOrAddRecord(dllName == null ? 0 : this.ModuleBuilder.Strings.Add(dllName));
198                         this.ModuleBuilder.ImplMap.AddRecord(rec);
199                 }
200
201                 private void SetMethodImplAttribute(CustomAttributeBuilder customBuilder)
202                 {
203                         MethodImplOptions opt;
204                         switch (customBuilder.Constructor.ParameterCount)
205                         {
206                                 case 0:
207                                         opt = 0;
208                                         break;
209                                 case 1:
210                                         {
211                                                 object val = customBuilder.GetConstructorArgument(0);
212                                                 if (val is short)
213                                                 {
214                                                         opt = (MethodImplOptions)(short)val;
215                                                 }
216                                                 else if (val is int)
217                                                 {
218                                                         opt = (MethodImplOptions)(int)val;
219                                                 }
220                                                 else
221                                                 {
222                                                         opt = (MethodImplOptions)val;
223                                                 }
224                                                 break;
225                                         }
226                                 default:
227                                         throw new NotSupportedException();
228                         }
229                         MethodCodeType? type = customBuilder.GetFieldValue<MethodCodeType>("MethodCodeType");
230                         implFlags = (MethodImplAttributes)opt;
231                         if (type.HasValue)
232                         {
233                                 implFlags |= (MethodImplAttributes)type;
234                         }
235                 }
236
237                 public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
238                 {
239                         Universe u = this.ModuleBuilder.universe;
240                         Type type = customBuilder.Constructor.DeclaringType;
241                         if (type == u.System_Runtime_InteropServices_DllImportAttribute)
242                         {
243                                 attributes |= MethodAttributes.PinvokeImpl;
244                                 SetDllImportPseudoCustomAttribute(customBuilder.DecodeBlob(this.Module.Assembly));
245                         }
246                         else if (type == u.System_Runtime_CompilerServices_MethodImplAttribute)
247                         {
248                                 SetMethodImplAttribute(customBuilder.DecodeBlob(this.Module.Assembly));
249                         }
250                         else if (type == u.System_Runtime_InteropServices_PreserveSigAttribute)
251                         {
252                                 implFlags |= MethodImplAttributes.PreserveSig;
253                         }
254                         else if (type == u.System_Runtime_CompilerServices_SpecialNameAttribute)
255                         {
256                                 attributes |= MethodAttributes.SpecialName;
257                         }
258                         else
259                         {
260                                 if (type == u.System_Security_SuppressUnmanagedCodeSecurityAttribute)
261                                 {
262                                         attributes |= MethodAttributes.HasSecurity;
263                                 }
264                                 this.ModuleBuilder.SetCustomAttribute(pseudoToken, customBuilder);
265                         }
266                 }
267
268                 public void __AddDeclarativeSecurity(CustomAttributeBuilder customBuilder)
269                 {
270                         attributes |= MethodAttributes.HasSecurity;
271                         if (declarativeSecurity == null)
272                         {
273                                 declarativeSecurity = new List<CustomAttributeBuilder>();
274                         }
275                         declarativeSecurity.Add(customBuilder);
276                 }
277
278                 public void AddDeclarativeSecurity(System.Security.Permissions.SecurityAction securityAction, System.Security.PermissionSet permissionSet)
279                 {
280                         this.ModuleBuilder.AddDeclarativeSecurity(pseudoToken, securityAction, permissionSet);
281                         this.attributes |= MethodAttributes.HasSecurity;
282                 }
283
284                 public void SetImplementationFlags(MethodImplAttributes attributes)
285                 {
286                         implFlags = attributes;
287                 }
288
289                 public ParameterBuilder DefineParameter(int position, ParameterAttributes attributes, string strParamName)
290                 {
291                         // the parameter is named "position", but it is actually a sequence number (i.e. 0 = return parameter, 1 = first parameter)
292                         int sequence = position--;
293                         if (parameters == null)
294                         {
295                                 parameters = new List<ParameterBuilder>();
296                         }
297                         this.ModuleBuilder.Param.AddVirtualRecord();
298                         ParameterBuilder pb = new ParameterBuilder(this.ModuleBuilder, sequence, attributes, strParamName);
299                         if (parameters.Count == 0 || position > parameters[parameters.Count - 1].Position)
300                         {
301                                 parameters.Add(pb);
302                         }
303                         else
304                         {
305                                 for (int i = 0; i < parameters.Count; i++)
306                                 {
307                                         if (parameters[i].Position > position)
308                                         {
309                                                 parameters.Insert(i, pb);
310                                                 break;
311                                         }
312                                 }
313                         }
314                         return pb;
315                 }
316
317                 public void SetParameters(params Type[] parameterTypes)
318                 {
319                         this.parameterTypes = Util.Copy(parameterTypes);
320                 }
321
322                 public void SetReturnType(Type returnType)
323                 {
324                         this.returnType = returnType ?? this.Module.universe.System_Void;
325                 }
326
327                 public void SetSignature(Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
328                 {
329                         this.returnType = returnType ?? this.Module.universe.System_Void;
330                         this.parameterTypes = Util.Copy(parameterTypes);
331                         this.modifiers = PackedCustomModifiers.CreateFromExternal(returnTypeOptionalCustomModifiers, returnTypeRequiredCustomModifiers,
332                                 parameterTypeOptionalCustomModifiers, parameterTypeRequiredCustomModifiers, this.parameterTypes.Length);
333                 }
334
335                 public GenericTypeParameterBuilder[] DefineGenericParameters(params string[] names)
336                 {
337                         gtpb = new GenericTypeParameterBuilder[names.Length];
338                         for (int i = 0; i < names.Length; i++)
339                         {
340                                 gtpb[i] = new GenericTypeParameterBuilder(names[i], null, this, i);
341                         }
342                         return (GenericTypeParameterBuilder[])gtpb.Clone();
343                 }
344
345                 public override MethodInfo MakeGenericMethod(params Type[] typeArguments)
346                 {
347                         return new GenericMethodInstance(typeBuilder, this, typeArguments);
348                 }
349
350                 public override MethodInfo GetGenericMethodDefinition()
351                 {
352                         if (gtpb == null)
353                         {
354                                 throw new InvalidOperationException();
355                         }
356                         return this;
357                 }
358
359                 public override Type[] GetGenericArguments()
360                 {
361                         return Util.Copy(gtpb);
362                 }
363
364                 internal override Type GetGenericMethodArgument(int index)
365                 {
366                         return gtpb[index];
367                 }
368
369                 internal override int GetGenericMethodArgumentCount()
370                 {
371                         return gtpb == null ? 0 : gtpb.Length;
372                 }
373
374                 public override Type ReturnType
375                 {
376                         get { return returnType; }
377                 }
378
379                 public override ParameterInfo ReturnParameter
380                 {
381                         get { return new ParameterInfoImpl(this, -1); }
382                 }
383
384                 public override MethodAttributes Attributes
385                 {
386                         get { return attributes; }
387                 }
388
389                 public void __SetAttributes(MethodAttributes attributes)
390                 {
391                         this.attributes = attributes;
392                 }
393
394                 public override MethodImplAttributes GetMethodImplementationFlags()
395                 {
396                         return implFlags;
397                 }
398
399                 private sealed class ParameterInfoImpl : ParameterInfo
400                 {
401                         private readonly MethodBuilder method;
402                         private readonly int parameter;
403
404                         internal ParameterInfoImpl(MethodBuilder method, int parameter)
405                         {
406                                 this.method = method;
407                                 this.parameter = parameter;
408                         }
409
410                         private ParameterBuilder ParameterBuilder
411                         {
412                                 get
413                                 {
414                                         if (method.parameters != null)
415                                         {
416                                                 foreach (ParameterBuilder pb in method.parameters)
417                                                 {
418                                                         if (pb.Position == parameter)
419                                                         {
420                                                                 return pb;
421                                                         }
422                                                 }
423                                         }
424                                         return null;
425                                 }
426                         }
427
428                         public override string Name
429                         {
430                                 get
431                                 {
432                                         ParameterBuilder pb = this.ParameterBuilder;
433                                         return pb != null ? pb.Name : null;
434                                 }
435                         }
436
437                         public override Type ParameterType
438                         {
439                                 get { return parameter == -1 ? method.returnType : method.parameterTypes[parameter]; }
440                         }
441
442                         public override ParameterAttributes Attributes
443                         {
444                                 get
445                                 {
446                                         ParameterBuilder pb = this.ParameterBuilder;
447                                         return pb != null ? (ParameterAttributes)pb.Attributes : ParameterAttributes.None;
448                                 }
449                         }
450
451                         public override int Position
452                         {
453                                 get { return parameter; }
454                         }
455
456                         public override object RawDefaultValue
457                         {
458                                 get
459                                 {
460                                         ParameterBuilder pb = this.ParameterBuilder;
461                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.HasDefault) != 0)
462                                         {
463                                                 return method.ModuleBuilder.Constant.GetRawConstantValue(method.ModuleBuilder, pb.PseudoToken);
464                                         }
465                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.Optional) != 0)
466                                         {
467                                                 return Missing.Value;
468                                         }
469                                         return null;
470                                 }
471                         }
472
473                         private Type[] GetCustomModifiers(int optOrReq)
474                         {
475                                 if (method.modifiers == null || method.modifiers[parameter + 1] == null)
476                                 {
477                                         return Type.EmptyTypes;
478                                 }
479                                 return Util.Copy(method.modifiers[parameter + 1][optOrReq]);
480                         }
481
482                         public override Type[] GetOptionalCustomModifiers()
483                         {
484                                 return GetCustomModifiers(0);
485                         }
486
487                         public override Type[] GetRequiredCustomModifiers()
488                         {
489                                 return GetCustomModifiers(1);
490                         }
491
492                         public override MemberInfo Member
493                         {
494                                 get { return method; }
495                         }
496
497                         public override int MetadataToken
498                         {
499                                 get
500                                 {
501                                         ParameterBuilder pb = this.ParameterBuilder;
502                                         return pb != null ? pb.PseudoToken : 0x08000000;
503                                 }
504                         }
505
506                         internal override Module Module
507                         {
508                                 get { return method.Module; }
509                         }
510                 }
511
512                 public override ParameterInfo[] GetParameters()
513                 {
514                         ParameterInfo[] parameters = new ParameterInfo[parameterTypes.Length];
515                         for (int i = 0; i < parameters.Length; i++)
516                         {
517                                 parameters[i] = new ParameterInfoImpl(this, i);
518                         }
519                         return parameters;
520                 }
521
522                 internal override int ParameterCount
523                 {
524                         get { return parameterTypes.Length; }
525                 }
526
527                 public override Type DeclaringType
528                 {
529                         get { return typeBuilder.IsModulePseudoType ? null : typeBuilder; }
530                 }
531
532                 public override string Name
533                 {
534                         get { return name; }
535                 }
536
537                 public override CallingConventions CallingConvention
538                 {
539                         get { return callingConvention; }
540                 }
541
542                 public override int MetadataToken
543                 {
544                         get { return pseudoToken; }
545                 }
546
547                 public override bool IsGenericMethod
548                 {
549                         get { return gtpb != null; }
550                 }
551
552                 public override bool IsGenericMethodDefinition
553                 {
554                         get { return gtpb != null; }
555                 }
556
557                 public override Module Module
558                 {
559                         get { return typeBuilder.Module; }
560                 }
561
562                 public Module GetModule()
563                 {
564                         return typeBuilder.Module;
565                 }
566
567                 public MethodToken GetToken()
568                 {
569                         return new MethodToken(pseudoToken);
570                 }
571
572                 public override MethodBody GetMethodBody()
573                 {
574                         throw new NotSupportedException();
575                 }
576
577                 public bool InitLocals
578                 {
579                         get { return initLocals; }
580                         set { initLocals = value; }
581                 }
582
583                 internal void Bake()
584                 {
585                         this.signature = this.ModuleBuilder.GetSignatureBlobIndex(this.MethodSignature);
586
587                         if (ilgen != null)
588                         {
589                                 if (this.ModuleBuilder.symbolWriter != null)
590                                 {
591                                         this.ModuleBuilder.symbolWriter.OpenMethod(new SymbolToken(-pseudoToken | 0x06000000));
592                                 }
593                                 rva = ilgen.WriteBody(initLocals);
594                                 if (this.ModuleBuilder.symbolWriter != null)
595                                 {
596                                         this.ModuleBuilder.symbolWriter.CloseMethod();
597                                 }
598                                 ilgen = null;
599                         }
600                         else
601                         {
602                                 rva = -1;
603                         }
604
605                         if (declarativeSecurity != null)
606                         {
607                                 this.ModuleBuilder.AddDeclarativeSecurity(pseudoToken, declarativeSecurity);
608                         }
609                 }
610
611                 internal ModuleBuilder ModuleBuilder
612                 {
613                         get { return typeBuilder.ModuleBuilder; }
614                 }
615
616                 internal void WriteMethodDefRecord(int baseRVA, MetadataWriter mw, ref int paramList)
617                 {
618                         if (rva != -1)
619                         {
620                                 mw.Write(rva + baseRVA);
621                         }
622                         else
623                         {
624                                 mw.Write(0);
625                         }
626                         mw.Write((short)implFlags);
627                         mw.Write((short)attributes);
628                         mw.WriteStringIndex(nameIndex);
629                         mw.WriteBlobIndex(signature);
630                         mw.WriteParam(paramList);
631                         if (parameters != null)
632                         {
633                                 paramList += parameters.Count;
634                         }
635                 }
636
637                 internal void WriteParamRecords(MetadataWriter mw)
638                 {
639                         if (parameters != null)
640                         {
641                                 foreach (ParameterBuilder pb in parameters)
642                                 {
643                                         pb.WriteParamRecord(mw);
644                                 }
645                         }
646                 }
647
648                 internal void FixupToken(int token, ref int parameterToken)
649                 {
650                         typeBuilder.ModuleBuilder.RegisterTokenFixup(this.pseudoToken, token);
651                         if (parameters != null)
652                         {
653                                 foreach (ParameterBuilder pb in parameters)
654                                 {
655                                         pb.FixupToken(parameterToken++);
656                                 }
657                         }
658                 }
659
660                 internal override MethodSignature MethodSignature
661                 {
662                         get
663                         {
664                                 if (methodSignature == null)
665                                 {
666                                         methodSignature = MethodSignature.MakeFromBuilder(returnType, parameterTypes, modifiers, callingConvention, gtpb == null ? 0 : gtpb.Length);
667                                 }
668                                 return methodSignature;
669                         }
670                 }
671
672                 internal override int ImportTo(ModuleBuilder other)
673                 {
674                         if (typeBuilder.IsGenericTypeDefinition)
675                         {
676                                 return other.ImportMember(TypeBuilder.GetMethod(typeBuilder, this));
677                         }
678                         else if (other == typeBuilder.ModuleBuilder)
679                         {
680                                 return pseudoToken;
681                         }
682                         else
683                         {
684                                 return other.ImportMethodOrField(typeBuilder, name, this.MethodSignature);
685                         }
686                 }
687
688                 internal void CheckBaked()
689                 {
690                         typeBuilder.CheckBaked();
691                 }
692         }
693 }