Update to the latest IKVM
[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 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                         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 override MethodImplAttributes GetMethodImplementationFlags()
408                 {
409                         return implFlags;
410                 }
411
412                 private sealed class ParameterInfoImpl : ParameterInfo
413                 {
414                         private readonly MethodBuilder method;
415                         private readonly int parameter;
416
417                         internal ParameterInfoImpl(MethodBuilder method, int parameter)
418                         {
419                                 this.method = method;
420                                 this.parameter = parameter;
421                         }
422
423                         private ParameterBuilder ParameterBuilder
424                         {
425                                 get
426                                 {
427                                         if (method.parameters != null)
428                                         {
429                                                 foreach (ParameterBuilder pb in method.parameters)
430                                                 {
431                                                         if (pb.Position == parameter)
432                                                         {
433                                                                 return pb;
434                                                         }
435                                                 }
436                                         }
437                                         return null;
438                                 }
439                         }
440
441                         public override string Name
442                         {
443                                 get
444                                 {
445                                         ParameterBuilder pb = this.ParameterBuilder;
446                                         return pb != null ? pb.Name : null;
447                                 }
448                         }
449
450                         public override Type ParameterType
451                         {
452                                 get { return parameter == -1 ? method.returnType : method.parameterTypes[parameter]; }
453                         }
454
455                         public override ParameterAttributes Attributes
456                         {
457                                 get
458                                 {
459                                         ParameterBuilder pb = this.ParameterBuilder;
460                                         return pb != null ? (ParameterAttributes)pb.Attributes : ParameterAttributes.None;
461                                 }
462                         }
463
464                         public override int Position
465                         {
466                                 get { return parameter; }
467                         }
468
469                         public override object RawDefaultValue
470                         {
471                                 get
472                                 {
473                                         ParameterBuilder pb = this.ParameterBuilder;
474                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.HasDefault) != 0)
475                                         {
476                                                 return method.ModuleBuilder.Constant.GetRawConstantValue(method.ModuleBuilder, pb.PseudoToken);
477                                         }
478                                         if (pb != null && (pb.Attributes & (int)ParameterAttributes.Optional) != 0)
479                                         {
480                                                 return Missing.Value;
481                                         }
482                                         return null;
483                                 }
484                         }
485
486                         private Type[] GetCustomModifiers(int optOrReq)
487                         {
488                                 if (method.modifiers == null || method.modifiers[parameter + 1] == null)
489                                 {
490                                         return Type.EmptyTypes;
491                                 }
492                                 return Util.Copy(method.modifiers[parameter + 1][optOrReq]);
493                         }
494
495                         public override Type[] GetOptionalCustomModifiers()
496                         {
497                                 return GetCustomModifiers(0);
498                         }
499
500                         public override Type[] GetRequiredCustomModifiers()
501                         {
502                                 return GetCustomModifiers(1);
503                         }
504
505                         public override MemberInfo Member
506                         {
507                                 get { return method; }
508                         }
509
510                         public override int MetadataToken
511                         {
512                                 get
513                                 {
514                                         ParameterBuilder pb = this.ParameterBuilder;
515                                         return pb != null ? pb.PseudoToken : 0x08000000;
516                                 }
517                         }
518
519                         internal override Module Module
520                         {
521                                 get { return method.Module; }
522                         }
523                 }
524
525                 public override ParameterInfo[] GetParameters()
526                 {
527                         ParameterInfo[] parameters = new ParameterInfo[parameterTypes.Length];
528                         for (int i = 0; i < parameters.Length; i++)
529                         {
530                                 parameters[i] = new ParameterInfoImpl(this, i);
531                         }
532                         return parameters;
533                 }
534
535                 internal override int ParameterCount
536                 {
537                         get { return parameterTypes.Length; }
538                 }
539
540                 public override Type DeclaringType
541                 {
542                         get { return typeBuilder.IsModulePseudoType ? null : typeBuilder; }
543                 }
544
545                 public override string Name
546                 {
547                         get { return name; }
548                 }
549
550                 public override CallingConventions CallingConvention
551                 {
552                         get { return callingConvention; }
553                 }
554
555                 public override int MetadataToken
556                 {
557                         get { return pseudoToken; }
558                 }
559
560                 public override bool IsGenericMethod
561                 {
562                         get { return gtpb != null; }
563                 }
564
565                 public override bool IsGenericMethodDefinition
566                 {
567                         get { return gtpb != null; }
568                 }
569
570                 public override Module Module
571                 {
572                         get { return typeBuilder.Module; }
573                 }
574
575                 public Module GetModule()
576                 {
577                         return typeBuilder.Module;
578                 }
579
580                 public MethodToken GetToken()
581                 {
582                         return new MethodToken(pseudoToken);
583                 }
584
585                 public override MethodBody GetMethodBody()
586                 {
587                         throw new NotSupportedException();
588                 }
589
590                 public bool InitLocals
591                 {
592                         get { return initLocals; }
593                         set { initLocals = value; }
594                 }
595
596                 internal void Bake()
597                 {
598                         this.nameIndex = this.ModuleBuilder.Strings.Add(name);
599                         this.signature = this.ModuleBuilder.GetSignatureBlobIndex(this.MethodSignature);
600
601                         __ReleaseILGenerator();
602
603                         if (declarativeSecurity != null)
604                         {
605                                 this.ModuleBuilder.AddDeclarativeSecurity(pseudoToken, declarativeSecurity);
606                         }
607                 }
608
609                 internal ModuleBuilder ModuleBuilder
610                 {
611                         get { return typeBuilder.ModuleBuilder; }
612                 }
613
614                 internal void WriteMethodDefRecord(int baseRVA, MetadataWriter mw, ref int paramList)
615                 {
616                         if (rva != -1)
617                         {
618                                 mw.Write(rva + baseRVA);
619                         }
620                         else
621                         {
622                                 mw.Write(0);
623                         }
624                         mw.Write((short)implFlags);
625                         mw.Write((short)attributes);
626                         mw.WriteStringIndex(nameIndex);
627                         mw.WriteBlobIndex(signature);
628                         mw.WriteParam(paramList);
629                         if (parameters != null)
630                         {
631                                 paramList += parameters.Count;
632                         }
633                 }
634
635                 internal void WriteParamRecords(MetadataWriter mw)
636                 {
637                         if (parameters != null)
638                         {
639                                 foreach (ParameterBuilder pb in parameters)
640                                 {
641                                         pb.WriteParamRecord(mw);
642                                 }
643                         }
644                 }
645
646                 internal void FixupToken(int token, ref int parameterToken)
647                 {
648                         typeBuilder.ModuleBuilder.RegisterTokenFixup(this.pseudoToken, token);
649                         if (parameters != null)
650                         {
651                                 foreach (ParameterBuilder pb in parameters)
652                                 {
653                                         pb.FixupToken(parameterToken++);
654                                 }
655                         }
656                 }
657
658                 internal override MethodSignature MethodSignature
659                 {
660                         get
661                         {
662                                 if (methodSignature == null)
663                                 {
664                                         methodSignature = MethodSignature.MakeFromBuilder(returnType, parameterTypes, modifiers, callingConvention, gtpb == null ? 0 : gtpb.Length);
665                                 }
666                                 return methodSignature;
667                         }
668                 }
669
670                 internal override int ImportTo(ModuleBuilder other)
671                 {
672                         if (typeBuilder.IsGenericTypeDefinition)
673                         {
674                                 return other.ImportMember(TypeBuilder.GetMethod(typeBuilder, this));
675                         }
676                         else if (other == typeBuilder.ModuleBuilder)
677                         {
678                                 return pseudoToken;
679                         }
680                         else
681                         {
682                                 return other.ImportMethodOrField(typeBuilder, name, this.MethodSignature);
683                         }
684                 }
685
686                 internal void CheckBaked()
687                 {
688                         typeBuilder.CheckBaked();
689                 }
690         }
691 }