This should fix #76928. This fix incorporates ideas from a patch
[mono.git] / mcs / ilasm / codegen / MethodDef.cs
1 //
2 // Mono.ILASM.MethodDef
3 //
4 // Author(s):
5 //  Jackson Harper (Jackson@LatitudeGeo.com)
6 //
7 // (C) 2003 Jackson Harper, All rights reserved
8 //
9
10
11 using System;
12 using System.Text;
13 using System.Collections;
14 using System.Security;
15
16 namespace Mono.ILASM {
17
18         public class MethodDef : ICustomAttrTarget, IDeclSecurityTarget {
19
20                 protected class GenericInfo {
21                         public string Id;
22                         public int num;
23                         public ArrayList ConstraintList;
24                 }
25
26                 private PEAPI.MethAttr meth_attr;
27                 private PEAPI.CallConv call_conv;
28                 private PEAPI.ImplAttr impl_attr;
29                 private string name;
30                 private string signature;
31                 private Hashtable vararg_sig_table;
32                 private ITypeRef ret_type;
33                 private ArrayList param_list;
34                 private ArrayList inst_list;
35                 private ArrayList customattr_list;
36                 private DeclSecurity decl_sec;
37                 private Hashtable label_table;
38                 private Hashtable labelref_table;
39                 private ArrayList label_list;
40                 private PEAPI.MethodDef methoddef;
41                 private bool entry_point;
42                 private bool zero_init;
43                 private bool is_resolved;
44                 private bool is_defined;
45                 private ArrayList local_list;
46                 private Hashtable named_local_table;
47                 private bool init_locals;
48                 private int max_stack;
49                 private bool pinvoke_info;
50                 private ExternModule pinvoke_mod;
51                 private string pinvoke_name;
52                 private PEAPI.PInvokeAttr pinvoke_attr;
53                 private SourceMethod source;
54                 private PEAPI.NativeType ret_native_type;
55                 private TypeDef type_def;
56                 private GenericParameters gen_params;
57
58                 public MethodDef (CodeGen codegen, PEAPI.MethAttr meth_attr,
59                                   PEAPI.CallConv call_conv, PEAPI.ImplAttr impl_attr,
60                                   string name, ITypeRef ret_type, ArrayList param_list,
61                                   Location start, GenericParameters gen_params, TypeDef type_def)
62                 {
63                         this.meth_attr = meth_attr;
64                         this.call_conv = call_conv;
65                         this.impl_attr = impl_attr;
66                         this.name = name;
67                         this.ret_type = ret_type;
68                         this.param_list = param_list;
69                         this.type_def = type_def;
70                         this.gen_params = gen_params;
71
72                         inst_list = new ArrayList ();
73                         label_table = new Hashtable ();
74                         labelref_table = new Hashtable ();
75                         label_list = new ArrayList ();
76                         local_list = new ArrayList ();
77                         named_local_table = new Hashtable ();
78
79                         entry_point = false;
80                         zero_init = false;
81                         init_locals = false;
82                         max_stack = -1;
83                         pinvoke_info = false;
84
85                         is_defined = false;
86                         is_resolved = false;
87                         ResolveGenParams ();
88                         CreateSignature ();
89
90                         codegen.BeginMethodDef (this);
91
92                         if (codegen.SymbolWriter != null)
93                                 source = codegen.SymbolWriter.BeginMethod (this, start);
94                 }
95
96                 public string Name {
97                         get { return name; }
98                 }
99
100                 public string Signature {
101                         get { return signature; }
102                 }
103
104                 public ITypeRef RetType {
105                         get { return ret_type; }
106                 }
107
108                 public PEAPI.CallConv CallConv {
109                         get { return call_conv; }
110                 }
111
112                 public PEAPI.MethodDef PeapiMethodDef {
113                         get { return methoddef; }
114                 }
115
116                 public PEAPI.MethAttr Attributes {
117                         get { return meth_attr; }
118                         set { meth_attr = value; }
119                 }
120
121                 public bool IsVararg {
122                         get { return (call_conv & PEAPI.CallConv.Vararg) != 0; }
123                 }
124
125                 public bool IsStatic {
126                         get { return (meth_attr & PEAPI.MethAttr.Static) != 0; }
127                 }
128
129                 public bool IsVirtual {
130                         get { return (meth_attr & PEAPI.MethAttr.Virtual) != 0; }
131                 }
132
133                 public bool IsAbstract {
134                         get { return (meth_attr & PEAPI.MethAttr.Abstract) != 0; }
135                 }
136
137                 public ITypeRef[] ParamTypeList () {
138
139                         if (param_list == null)
140                                 return new ITypeRef[0];
141                         int count = 0;
142                         ITypeRef[] type_list = new ITypeRef[param_list.Count];
143                         foreach (ParamDef param in param_list) {
144                                 type_list[count++] = param.Type;
145                         }
146                         return type_list;
147                 }
148
149                 public void AddPInvokeInfo (PEAPI.PInvokeAttr pinvoke_attr, ExternModule pinvoke_mod,
150                                 string pinvoke_name)
151                 {
152                         this.pinvoke_attr = pinvoke_attr;
153                         this.pinvoke_mod = pinvoke_mod;
154                         this.pinvoke_name = pinvoke_name;
155                         pinvoke_info = true;
156                 }
157
158                 public int GenParamCount {
159                         get { return (gen_params != null ? gen_params.Count : 0); }
160                 }
161
162                 public int GetGenericParamNum (string id)
163                 {
164                         if (gen_params == null)
165                                 throw new Exception ("Not a generic method");
166                         
167                         return gen_params.GetGenericParamNum (id);
168                 }
169
170                 public void AddParamDefaultValue (int index, PEAPI.Constant defval)
171                 {
172                         if (param_list [index] != null) {
173                                 ((ParamDef)param_list [index]).AddDefaultValue (defval);
174                         }
175                 }
176
177                 public void AddCustomAttribute (CustomAttr customattr)
178                 {
179                         if (customattr_list == null)
180                                 customattr_list = new ArrayList ();
181
182                         customattr_list.Add (customattr);
183                 }
184
185                 public void AddPermissionSet (PEAPI.SecurityAction sec_action, PermissionSet ps)
186                 {
187                         if (decl_sec == null)
188                                 decl_sec = new DeclSecurity ();
189                         
190                         decl_sec.AddPermissionSet (sec_action, ps);
191                 }
192                 
193                 public void AddPermission (PEAPI.SecurityAction sec_action, IPermission iper)
194                 {
195                         if (decl_sec == null)
196                                 decl_sec = new DeclSecurity ();
197                         
198                         decl_sec.AddPermission (sec_action, iper);
199                 }
200
201                 public void AddRetTypeMarshalInfo (PEAPI.NativeType native_type)
202                 {
203                         this.ret_native_type = native_type;
204                 }
205
206                 public void AddLocals (ArrayList local_list)
207                 {
208                         int slot_pos = this.local_list.Count;
209
210                         foreach (Local local in local_list) {
211                                 if (local.Slot == -1) {
212                                         local.Slot = slot_pos;
213                                 }
214                                 slot_pos++;
215                                 if (local.Name == null)
216                                         continue;
217                                 if(!named_local_table.Contains(local.Name))
218                                   named_local_table.Add (local.Name, local);
219                         }
220
221                         this.local_list.AddRange (local_list);
222                 }
223
224                 public Local GetNamedLocal (string name)
225                 {
226                         return (Local) named_local_table[name];
227                 }
228
229                 public int GetNamedLocalSlot (string name)
230                 {
231                         Local local = (Local) named_local_table[name];
232
233                         return local.Slot;
234                 }
235
236                 public int GetNamedParamPos (string name)
237                 {
238                         int pos = -1;
239                         if (!IsStatic)
240                                 pos ++;
241                         foreach (ParamDef param in param_list) {
242                                 pos ++;
243                                 if (param.Name.CompareTo (name) == 0)
244                                         return pos;
245                         }
246
247                         return pos;
248                 }
249
250                 public ParamDef GetParam (int index)
251                 {
252                         if (param_list [index] != null)
253                                 return (ParamDef)param_list [index];
254                         else
255                                 return null;
256                 }
257
258                 public void InitLocals ()
259                 {
260                         init_locals = true;
261                 }
262
263                 public void EntryPoint ()
264                 {
265                         if (!IsStatic)
266                                 throw new Exception ("Non-static method as entrypoint.");
267                         entry_point = true;
268                 }
269
270                 public void ZeroInit ()
271                 {
272                         zero_init = true;
273                 }
274                 
275                 public void SetMaxStack (int max_stack)
276                 {
277                         this.max_stack = max_stack;
278                 }
279
280                 public void ResolveGenParam (PEAPI.GenParam gpar)
281                 {
282                         if (gpar.Index != -1)
283                                 return;
284         
285                         if (gpar.Type == PEAPI.GenParamType.MVar)
286                                 gpar.Index = GetGenericParamNum (gpar.Name); 
287                         else
288                                 gpar.Index = type_def.GetGenericParamNum (gpar.Name);
289
290                         if (gpar.Index < 0)
291                                 /* TODO: Report error */
292                                 throw new Exception (String.Format ("Invalid {0}type parameter '{1}'", 
293                                                         (gpar.Type == PEAPI.GenParamType.MVar ? "method " : ""),
294                                                          gpar.Name));
295                 }
296
297                 public void ResolveGenParams ()
298                 {
299                         GenericParameters type_params = (type_def != null) ? type_def.TypeParameters : null;
300
301                         if (gen_params == null && type_params == null)
302                                 return;
303
304                         if (gen_params != null)
305                                 gen_params.ResolveConstraints (type_params, gen_params);
306                         
307                         IGenericTypeRef gtr = ret_type as IGenericTypeRef;
308                         if (gtr != null)
309                                 gtr.Resolve (type_params, gen_params);
310
311                         if (param_list == null)
312                                 return;
313
314                         foreach (ParamDef param in param_list) {
315                                 gtr = param.Type as IGenericTypeRef;
316                                 if (gtr != null)
317                                         gtr.Resolve (type_params, gen_params);
318                         }        
319                 }
320
321                 public PEAPI.MethodDef Resolve (CodeGen code_gen)
322                 {
323                         return Resolve (code_gen, null);
324                 }
325
326                 public PEAPI.MethodDef Resolve (CodeGen code_gen, PEAPI.ClassDef classdef)
327                 {
328                         if (is_resolved)
329                                 return methoddef;
330
331                         PEAPI.Param [] param_array = GenerateParams (code_gen);
332                         FixAttributes ();
333                         ret_type.Resolve (code_gen);
334
335                         if (classdef == null)
336                                 methoddef = code_gen.PEFile.AddMethod (meth_attr, impl_attr,
337                                                 name, ret_type.PeapiType, param_array);
338                         else                    
339                                 methoddef = classdef.AddMethod (meth_attr, impl_attr,
340                                                 name, ret_type.PeapiType, param_array);
341
342                         methoddef.AddCallConv (call_conv);
343
344                         if (ret_native_type != null)
345                                 methoddef.AddRetTypeMarshallInfo (ret_native_type);
346
347                         is_resolved = true;
348
349                         return methoddef;
350                 }
351
352                 private PEAPI.Param [] GenerateParams (CodeGen code_gen)
353                 {
354                         PEAPI.Param[] param_array;
355
356                         if (param_list != null && param_list.Count > 0) {
357                                  int param_count = param_list.Count;
358
359                                  // Remove the last param if its the sentinel, not sure what
360                                 // should happen with more then one sentinel
361                                 ParamDef last = (ParamDef) param_list [param_count-1];
362                                 if (last.IsSentinel ())
363                                         param_count--;
364
365                                 param_array = new PEAPI.Param [param_count];
366                                 for (int i = 0; i < param_count; i++) {
367                                         ParamDef paramdef = (ParamDef) param_list [i];
368                                         paramdef.Define (code_gen);
369                                         param_array [i] = paramdef.PeapiParam;
370                                 }
371
372                         } else {
373                                 param_array = new PEAPI.Param [0];
374                         }
375
376                         return param_array;
377                 }
378
379                 public PEAPI.MethodRef GetVarargSig (PEAPI.Type[] opt)
380                 {
381                         if (!is_resolved)
382                                 throw new Exception ("Methods must be resolved before a vararg sig can be created.");
383
384                         PEAPI.MethodRef methref = null;
385                         StringBuilder sigbuilder = new StringBuilder ();
386                         string sig;
387                         foreach (PEAPI.Type t in opt)
388                                 sigbuilder.Append (opt + ", ");
389                         sig = sigbuilder.ToString ();
390
391                         if (vararg_sig_table == null) {
392                                 vararg_sig_table = new Hashtable ();                                
393                         } else {
394                                 methref = vararg_sig_table [sig] as PEAPI.MethodRef;
395                         }
396
397                         if (methref == null) {
398                                 methref = methoddef.MakeVarArgSignature (opt);
399                                 vararg_sig_table [sig] = methref;
400                         }
401
402                         return methref;
403                 }
404
405                 /// <summary>
406                 ///  Define a member method
407                 /// </summary>
408                 public void Define (CodeGen code_gen)
409                 {
410                         if (is_defined)
411                                 return;
412
413                         if (type_def == null)
414                                 /* Global method */
415                                 Resolve (code_gen, null);
416                         else
417                                 Resolve (code_gen, (PEAPI.ClassDef) type_def.ClassDef);
418                                 
419                         WriteCode (code_gen, methoddef);
420
421                         //code_gen.Report.Message (String.Format ("Assembled method {0}::{1}", typedef.FullName, name));                        is_defined = true;
422                 }
423
424                 public void AddInstr (IInstr instr)
425                 {
426                         inst_list.Add (instr);
427                 }
428
429                 protected void WriteCode (CodeGen code_gen, PEAPI.MethodDef methoddef)
430                 {
431                         /// Add the custrom attributes to this method
432                         if (customattr_list != null)
433                                 foreach (CustomAttr customattr in customattr_list) {
434                                         customattr.AddTo (code_gen, methoddef);
435                                         if (customattr.IsSuppressUnmanaged (code_gen))
436                                                 methoddef.AddMethAttribute (PEAPI.MethAttr.HasSecurity);
437                                 }
438
439                         /// Add declarative security to this method
440                         if (decl_sec != null) {
441                                 decl_sec.AddTo (code_gen, methoddef);
442                                 methoddef.AddMethAttribute (PEAPI.MethAttr.HasSecurity);
443                         }        
444
445                         if (IsAbstract)
446                                 return;
447
448                         // Generic type parameters
449                         if (gen_params != null)
450                                 gen_params.Resolve (code_gen, methoddef);
451
452                         if (entry_point)
453                                 methoddef.DeclareEntryPoint ();
454
455                         if (local_list.Count > 0) {
456                                 int ec = code_gen.Report.ErrorCount;
457                                 PEAPI.Local[] local_array = new PEAPI.Local[local_list.Count];
458                                 int i = 0;
459
460                                 foreach (Local local in local_list)
461                                         local_array[local.Slot]  = local.GetPeapiLocal (code_gen);
462
463                                 if (code_gen.Report.ErrorCount > ec)
464                                         return;
465
466                                 if (zero_init)
467                                         init_locals = true;
468                                 
469                                 methoddef.AddLocals (local_array, init_locals);
470                         }
471
472                         /// Nothing seems to work if maxstack is not set,
473                         /// i need to find out if this NEEDs to be set
474                         /// and what its default value should be
475                         if (max_stack < 0)
476                                 max_stack = 8;
477                         methoddef.SetMaxStack (max_stack);
478
479                         if (pinvoke_info) {
480                                 methoddef.AddPInvokeInfo (pinvoke_mod.ModuleRef,
481                                                 (pinvoke_name != null ? pinvoke_name : name), pinvoke_attr);
482                         }
483
484                         if (inst_list.Count < 1)
485                                 return;
486
487                         PEAPI.CILInstructions cil = methoddef.CreateCodeBuffer ();
488                         /// Create all the labels
489                         /// TODO: Most labels don't actually need to be created so we could
490                         /// probably only create the ones that need to be
491                         LabelInfo[] label_info = new LabelInfo[label_table.Count + label_list.Count];
492                         label_table.Values.CopyTo (label_info, 0);
493                         label_list.CopyTo (label_info, label_table.Count);
494                         int previous_pos = -1;
495                         LabelInfo previous_label = null;
496                         Array.Sort (label_info);
497
498                         foreach (LabelInfo label in label_info) {
499                                 if (label.UseOffset) {
500                                         label.Define (new PEAPI.CILLabel (label.Offset));
501                                         continue;
502                                 }
503                                 if (label.Pos == previous_pos)
504                                         label.Label = previous_label.Label;
505                                 else
506                                         label.Define (cil.NewLabel ());
507
508                                 previous_label = label;
509                                 previous_pos = label.Pos;
510                         }
511
512                         // Set all the label refs
513                         foreach (LabelInfo label in labelref_table.Values) {
514                                 LabelInfo def = (LabelInfo) label_table[label.Name];
515                                 if (def == null) {
516                                         code_gen.Report.Error ("Undefined Label:  " + label);
517                                         return;
518                                 }
519                                 label.Label = def.Label;
520                         }
521
522                         int label_pos = 0;
523                         int next_label_pos = (label_info.Length > 0 ? label_info[0].Pos : -1);
524
525                         for (int i=0; i<inst_list.Count; i++) {
526                                 IInstr instr = (IInstr) inst_list[i];
527                                 if (next_label_pos == i) {
528                                         cil.CodeLabel (label_info[label_pos].Label);
529                                         if (label_pos < label_info.Length) {
530                                                 while (next_label_pos == i && ++label_pos < label_info.Length) {
531                                                         if (label_info[label_pos].UseOffset)
532                                                                 cil.CodeLabel (label_info[label_pos].Label);
533                                                        next_label_pos = label_info[label_pos].Pos;
534                                                 }
535                                         }
536                                         if (label_pos >= label_info.Length)
537                                                 next_label_pos = -1;
538                                 }
539                                 if (source != null)
540                                         source.MarkLocation (instr.Location.line, cil.Offset);
541                                 instr.Emit (code_gen, this, cil);
542                         }
543
544                         if (source != null)
545                                 source.MarkLocation (source.EndLine, cil.Offset);
546                 }
547
548                 public LabelInfo AddLabel (string name)
549                 {
550                         LabelInfo label_info = (LabelInfo) label_table[name];
551                         if (label_info != null)
552                                 return label_info;
553                         label_info = new LabelInfo (name, inst_list.Count);
554                         label_table.Add (name, label_info);
555                         return label_info;
556                 }
557
558                 public LabelInfo AddLabelRef (string name)
559                 {
560                         LabelInfo label_info = (LabelInfo) label_table[name];
561                         if (label_info != null)
562                                 return label_info;
563                         label_info = (LabelInfo) labelref_table[name];
564                         if (label_info != null)
565                                 return label_info;
566                         label_info = new LabelInfo (name, -1);
567                         labelref_table.Add (name, label_info);
568                         return label_info;
569                 }
570
571                 public LabelInfo AddLabel (int offset)
572                 {
573                         // We go pos + 1 so this line is not counted
574                         LabelInfo label_info = new LabelInfo (null, inst_list.Count+1, (uint) offset);
575                         label_list.Add (label_info);
576                         return label_info;
577                 }
578
579                 public LabelInfo AddLabel ()
580                 {
581                         int pos = inst_list.Count;
582                         LabelInfo label_info = new LabelInfo (null, inst_list.Count);
583                         label_list.Add (label_info);
584                         return label_info;
585                 }
586
587                 public PEAPI.CILLabel GetLabelDef (string name)
588                 {
589                         LabelInfo label_info = (LabelInfo) label_table[name];
590
591                         return label_info.Label;
592                 }
593
594                 public PEAPI.CILLabel GetLabelDef (int pos)
595                 {
596                         foreach (LabelInfo li in label_list) {
597                                 if (li.Pos == pos)
598                                         return li.Label;
599                         }
600                         return null;
601                 }
602
603                 private void CreateSignature ()
604                 {
605                         if (IsVararg)
606                                 signature = CreateVarargSignature (RetType, name, param_list);
607                         else
608                                 signature = CreateSignature (RetType, name, param_list, GenParamCount);
609                 }
610
611                 public static string CreateSignature (ITypeRef RetType, string name, IList param_list, int gen_param_count)
612                 {
613                         StringBuilder builder = new StringBuilder ();
614
615                         builder.Append (RetType.FullName);
616                         builder.Append (" ");
617                         builder.Append (name);
618                         builder.AppendFormat ("`{0}", gen_param_count);
619                         builder.Append ('(');
620
621                         if (param_list != null) {
622                                 bool first = true;
623                                 foreach (ParamDef paramdef in param_list) {
624                                         if (!first)
625                                                 builder.Append (',');
626                                         builder.Append (paramdef.TypeName);
627                                         first = false;
628                                 }
629                         }
630                         builder.Append (')');
631
632                         return builder.ToString ();
633                 }
634
635                 public static string CreateVarargSignature (ITypeRef RetType, string name, IList param_list)
636                 {
637                         StringBuilder builder = new StringBuilder ();
638                         ParamDef last = null;
639
640                         builder.Append (RetType.FullName);
641                         builder.Append (" ");
642                         builder.Append (name);
643                         builder.Append ('(');
644
645                         bool first = true;
646                         if (param_list != null) {
647                                 foreach (ParamDef paramdef in param_list) {
648                                         if (!first)
649                                                 builder.Append (',');
650                                         builder.Append (paramdef.TypeName);
651                                         first = false;
652                                 }
653                                 last = (ParamDef) param_list[param_list.Count - 1];
654                         }
655
656                         
657                         if (last == null || !last.IsSentinel ()) {
658                                 if (!first)
659                                         builder.Append (',');
660                                 builder.Append ("...");
661                         }
662
663                         builder.Append (')');
664
665                         return builder.ToString ();
666                 }
667
668                 public static string CreateVarargSignature (ITypeRef RetType, string name, ITypeRef [] param_list)
669                 {
670                         StringBuilder builder = new StringBuilder ();
671                         ITypeRef last = null;
672
673                         builder.Append (RetType.FullName);
674                         builder.Append (" ");
675                         builder.Append (name);
676                         builder.Append ('(');
677
678                         bool first = true;
679                         if (param_list != null && param_list.Length > 0) {
680                                 foreach (ITypeRef param in param_list) {
681                                         if (!first)
682                                                 builder.Append (',');
683                                         builder.Append (param.FullName);
684                                         first = false;
685                                         last = param;
686                                         if (param is SentinelTypeRef)
687                                                 break;
688                                 }
689                                 
690                         }
691                         
692                         if (last == null || !(last is SentinelTypeRef)) {
693                                 if (!first)
694                                         builder.Append (',');
695                                 builder.Append ("...");
696                         }
697
698                         builder.Append (')');
699
700                         return builder.ToString ();
701                 }
702
703                 public static string CreateSignature (ITypeRef RetType, string name, ITypeRef[] param_list, int gen_param_count)
704                 {
705                         StringBuilder builder = new StringBuilder ();
706
707                         builder.Append (RetType.FullName);
708                         builder.Append (" ");
709                         builder.Append (name);
710                         builder.AppendFormat ("`{0}", gen_param_count);
711                         builder.Append ('(');
712
713                         if (param_list != null) {
714                                 bool first = true;
715                                 foreach (ITypeRef param in param_list) {
716                                         if (!first)
717                                                 builder.Append (',');
718                                         builder.Append (param.FullName);
719                                         first = false;
720                                         if (param is SentinelTypeRef)
721                                                 break;
722                                 }
723                         }
724                         builder.Append (')');
725
726                         return builder.ToString ();
727                 }
728
729                 private void FixAttributes ()
730                 {
731                         if (name == ".ctor" || name == ".cctor")
732                                 meth_attr |= PEAPI.MethAttr.SpecialName | PEAPI.MethAttr.RTSpecialName;
733                         // If methods aren't flagged as static they are instance
734                         if ((PEAPI.MethAttr.Static & meth_attr) == 0)
735                                 call_conv |= PEAPI.CallConv.Instance;
736                 }
737
738         }
739
740 }
741