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