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