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