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