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