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