* LabelInfo.cs: Move this type outside of MethodDef, because other
[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                 private PEAPI.MethAttr meth_attr;
21                 private PEAPI.CallConv call_conv;
22                 private PEAPI.ImplAttr impl_attr;
23                 private string name;
24                 private string signature;
25                 private ITypeRef ret_type;
26                 private ArrayList param_list;
27                 private Hashtable named_param_table;
28                 private ArrayList inst_list;
29                 private ArrayList customattr_list;
30                 private Hashtable label_table;
31                 private Hashtable labelref_table;
32                 private ArrayList label_list;
33                 private PEAPI.MethodDef methoddef;
34                 private bool entry_point;
35                 private bool is_resolved;
36                 private bool is_defined;
37                 private ArrayList local_list;
38                 private Hashtable named_local_table;
39                 private bool init_locals;
40                 private int max_stack;
41                 private Random label_random;
42
43                 public MethodDef (PEAPI.MethAttr meth_attr, PEAPI.CallConv call_conv,
44                                 PEAPI.ImplAttr impl_attr, string name,
45                                 ITypeRef ret_type, ArrayList param_list)
46                 {
47                         this.meth_attr = meth_attr;
48                         this.call_conv = call_conv;
49                         this.impl_attr = impl_attr;
50                         this.name = name;
51                         this.ret_type = ret_type;
52                         this.param_list = param_list;
53
54                         inst_list = new ArrayList ();
55                         customattr_list = new ArrayList ();
56                         label_table = new Hashtable ();
57                         labelref_table = new Hashtable ();
58                         label_list = new ArrayList ();
59                         local_list = new ArrayList ();
60                         named_local_table = new Hashtable ();
61                         named_param_table = new Hashtable ();
62                         label_random = new Random ();
63                         entry_point = false;
64                         init_locals = false;
65                         max_stack = -1;
66
67                         is_defined = false;
68                         is_resolved = false;
69                         CreateSignature ();
70                         CreateNamedParamTable ();
71                 }
72
73                 public string Name {
74                         get { return name; }
75                 }
76
77                 public string Signature {
78                         get { return signature; }
79                 }
80
81                 public PEAPI.MethodDef PeapiMethodDef {
82                         get { return methoddef; }
83                 }
84
85                 public bool IsVararg {
86                         get { return (call_conv & PEAPI.CallConv.Vararg) != 0; }
87                 }
88
89                 public void AddLocals (ArrayList local_list)
90                 {
91                         int slot_pos = this.local_list.Count;
92
93                         foreach (Local local in local_list) {
94                                 if (local.Slot == -1) {
95                                         local.Slot = slot_pos;
96                                 }
97                                 slot_pos++;
98                                 if (local.Name == null)
99                                         continue;
100                                 named_local_table.Add (local.Name, local);
101                         }
102
103                         this.local_list.AddRange (local_list);
104                 }
105
106                 public Local GetNamedLocal (string name)
107                 {
108                         return (Local) named_local_table[name];
109                 }
110
111                 public int GetNamedLocalSlot (string name)
112                 {
113                         Local local = (Local) named_local_table[name];
114
115                         return local.Slot;
116                 }
117
118                 public int GetNamedParamPos (string name)
119                 {
120                         int pos = (int) named_param_table[name];
121
122                         return pos;
123                 }
124
125                 public void InitLocals ()
126                 {
127                         init_locals = true;
128                 }
129
130                 public void EntryPoint ()
131                 {
132                         entry_point = true;
133                 }
134
135                 public void SetMaxStack (int max_stack)
136                 {
137                         this.max_stack = max_stack;
138                 }
139
140                 public void AddCustomAttr (CustomAttr customattr)
141                 {
142                         customattr_list.Add (customattr);
143                 }
144
145                 public PEAPI.MethodDef Resolve (CodeGen code_gen)
146                 {
147                         if (is_resolved)
148                                 return methoddef;
149
150                         PEAPI.Param[] param_array;
151
152                         if (param_list != null) {
153                                 int param_count = param_list.Count;
154                                 if (IsVararg && param_list[param_count-1] == ParamDef.Ellipsis)
155                                         param_count--;
156                                 param_array = new PEAPI.Param[param_count];
157                                 int count = 0;
158
159                                 foreach (ParamDef paramdef in param_list) {
160                                         if (paramdef == ParamDef.Ellipsis)
161                                                 break;
162                                         paramdef.Define (code_gen);
163                                         param_array[count++] = paramdef.PeapiParam;
164                                 }
165                         } else {
166                                 param_array = new PEAPI.Param[0];
167                         }
168
169                         FixAttributes ();
170                         ret_type.Resolve (code_gen);
171
172                         methoddef = code_gen.PEFile.AddMethod (meth_attr, impl_attr,
173                                         name, ret_type.PeapiType, param_array);
174
175                         methoddef.AddCallConv (call_conv);
176                         is_resolved = true;
177
178                         return methoddef;
179                 }
180
181                 public PEAPI.MethodDef Resolve (CodeGen code_gen, PEAPI.ClassDef classdef)
182                 {
183                         if (is_resolved)
184                                 return methoddef;
185
186                         PEAPI.Param[] param_array;
187
188                         if (param_list != null) {
189                                 int param_count = param_list.Count;
190                                 if (IsVararg && param_list[param_count-1] == ParamDef.Ellipsis)         
191                                         param_count--;
192                                 param_array = new PEAPI.Param[param_count];
193                                 int count = 0;
194                                 
195                                 foreach (ParamDef paramdef in param_list) {
196                                         if (paramdef == ParamDef.Ellipsis)
197                                                 break;
198                                         paramdef.Define (code_gen);
199                                         param_array[count++] = paramdef.PeapiParam;
200                                 }
201                         } else {
202                                 param_array = new PEAPI.Param[0];
203                         }
204
205                         FixAttributes ();
206                         ret_type.Resolve (code_gen);
207
208                         methoddef = classdef.AddMethod (meth_attr, impl_attr,
209                                         name, ret_type.PeapiType, param_array);
210
211                         methoddef.AddCallConv (call_conv);
212                         is_resolved = true;
213
214                         return methoddef;
215                 }
216
217                 public PEAPI.MethodRef GetVarargSig (PEAPI.Type[] opt)
218                 {
219                         if (!is_resolved)
220                                 throw new Exception ("Methods must be resolved before a vararg sig can be created.");
221
222                         return methoddef.MakeVarArgSignature (opt);
223                 }
224
225                 /// <summary>
226                 ///  Define a global method
227                 /// </summary>
228                 public void Define (CodeGen code_gen)
229                 {
230                         if (is_defined)
231                                 return;
232
233                         Resolve (code_gen);
234
235                         WriteCode (code_gen, methoddef);
236
237                         is_defined = true;
238                 }
239
240                 /// <summary>
241                 ///  Define a member method
242                 /// </summary>
243                 public void Define (CodeGen code_gen, PEAPI.ClassDef classdef)
244                 {
245                         if (is_defined)
246                                 return;
247
248                         Resolve (code_gen, classdef);
249                         WriteCode (code_gen, methoddef);
250
251                         is_defined = true;
252                 }
253
254                 public void AddInstr (IInstr instr)
255                 {
256                         inst_list.Add (instr);
257                 }
258
259                 protected void WriteCode (CodeGen code_gen, PEAPI.MethodDef methoddef)
260                 {
261                         if (entry_point)
262                                 methoddef.DeclareEntryPoint ();
263
264                         if (local_list != null) {
265                                 PEAPI.Local[] local_array = new PEAPI.Local[local_list.Count];
266                                 int i = 0;
267
268                                 foreach (Local local in local_list)
269                                         local_array[local.Slot]  = local.GetPeapiLocal (code_gen);
270
271                                 methoddef.AddLocals (local_array, init_locals);
272                         }
273
274                         /// Nothing seems to work if maxstack is not set,
275                         /// i need to find out if this NEEDs to be set
276                         /// and what its default value should be
277                         if (max_stack < 0)
278                                 max_stack = 8;
279                         methoddef.SetMaxStack (max_stack);
280
281                         /// Add the custrom attributes to this method
282                         foreach (CustomAttr customattr in customattr_list)
283                                 customattr.AddTo (code_gen, methoddef);
284
285                         if (inst_list.Count < 1)
286                                 return;
287
288                         PEAPI.CILInstructions cil = methoddef.CreateCodeBuffer ();
289                         /// Create all the labels
290                         /// TODO: Most labels don't actually need to be created so we could
291                         /// probably only create the ones that need to be
292                         LabelInfo[] label_info = new LabelInfo[label_table.Count + label_list.Count];
293                         label_table.Values.CopyTo (label_info, 0);
294                         label_list.CopyTo (label_info, label_table.Count);
295                         int previous_pos = -1;
296                         LabelInfo previous_label = null;
297                         Array.Sort (label_info);
298
299                         foreach (LabelInfo label in label_info) {
300                                 if (label.UseOffset) {
301                                         label.Define (new PEAPI.CILLabel (label.Offset));
302                                         continue;
303                                 }
304                                 if (label.Pos == previous_pos)
305                                         label.Label = previous_label.Label;
306                                 else
307                                         label.Define (cil.NewLabel ());
308                                 
309                                 previous_label = label;
310                                 previous_pos = label.Pos;
311                         }
312
313                         // Set all the label refs
314                         foreach (LabelInfo label in labelref_table.Values) {
315                                 LabelInfo def = (LabelInfo) label_table[label.Name];
316                                 if (def == null) {
317                                         Console.WriteLine ("Undefined Label:  " + label);
318                                         return;
319                                 }
320                                 label.Label = def.Label;
321                         }
322
323                         int label_pos = 0;
324                         int next_label_pos = (label_info.Length > 0 ? label_info[0].Pos : -1);
325
326                         for (int i=0; i<inst_list.Count; i++) {
327                                 IInstr instr = (IInstr) inst_list[i];
328                                 if (next_label_pos == i) {
329                                         cil.CodeLabel (label_info[label_pos].Label);
330                                         if (label_pos < label_info.Length) {
331                                                 while (next_label_pos == i && ++label_pos < label_info.Length) {
332                                                         if (label_info[label_pos].UseOffset)
333                                                                 cil.CodeLabel (label_info[label_pos].Label);
334                                                        next_label_pos = label_info[label_pos].Pos;
335                                                 }
336                                         }
337                                         if (label_pos >= label_info.Length)
338                                                 next_label_pos = -1;
339                                 }
340                                 instr.Emit (code_gen, this, cil);
341                         }
342
343                 }
344
345                 public LabelInfo AddLabel (string name)
346                 {
347                         LabelInfo label_info = (LabelInfo) label_table[name];
348                         if (label_info != null)
349                                 return label_info;
350                         label_info = new LabelInfo (name, inst_list.Count);
351                         label_table.Add (name, label_info);
352                         return label_info;
353                 }
354
355                 public LabelInfo AddLabelRef (string name)
356                 {
357                         LabelInfo label_info = (LabelInfo) label_table[name];
358                         if (label_info != null)
359                                 return label_info;
360                         label_info = (LabelInfo) labelref_table[name];
361                         if (label_info != null)
362                                 return label_info;
363                         label_info = new LabelInfo (name, -1);
364                         labelref_table.Add (name, label_info);
365                         return label_info;
366                 }
367
368                 public LabelInfo AddLabel (int offset)
369                 {
370                         // We go pos + 1 so this line is not counted
371                         LabelInfo label_info = new LabelInfo (null, inst_list.Count+1, (uint) offset);
372                         label_list.Add (label_info);
373                         return label_info;
374                 }
375
376                 public LabelInfo AddLabel ()
377                 {
378                         int pos = inst_list.Count;
379                         LabelInfo label_info = new LabelInfo (null, inst_list.Count);
380                         label_list.Add (label_info);
381                         return label_info;
382                 }
383
384                 public PEAPI.CILLabel GetLabelDef (string name)
385                 {
386                         LabelInfo label_info = (LabelInfo) label_table[name];
387
388                         return label_info.Label;
389                 }
390
391                 public PEAPI.CILLabel GetLabelDef (int pos)
392                 {
393                         foreach (LabelInfo li in label_list) {
394                                 if (li.Pos == pos)
395                                         return li.Label;
396                         }
397                         return null;
398                 }
399
400                 private void CreateSignature ()
401                 {
402                         signature = CreateSignature (name, param_list);
403                 }
404
405                 public static string CreateSignature (string name, IList param_list)
406                 {
407                         StringBuilder builder = new StringBuilder ();
408
409                         builder.Append (name);
410                         builder.Append ('(');
411
412                         if (param_list != null) {
413                                 bool first = true;
414                                 foreach (ParamDef paramdef in param_list) {
415                                         if (ParamDef.Ellipsis == paramdef)
416                                                 break;
417                                         if (!first)
418                                                 builder.Append (',');
419                                         builder.Append (paramdef.TypeName);
420                                         first = false;
421                                 }
422                         }
423                         builder.Append (')');
424
425                         return builder.ToString ();
426                 }
427
428                 public static string CreateSignature (string name, ITypeRef[] param_list)
429                 {
430                         StringBuilder builder = new StringBuilder ();
431
432                         builder.Append (name);
433                         builder.Append ('(');
434
435                         if (param_list != null) {
436                                 bool first = true;
437                                 foreach (ITypeRef param in param_list) {
438                                         if (param == TypeRef.Ellipsis)
439                                                 break;
440                                         if (!first)
441                                                 builder.Append (',');
442                                         builder.Append (param.FullName);
443                                         first = false;
444                                 }
445                         }
446                         builder.Append (')');
447
448                         return builder.ToString ();
449                 }
450
451                 private void CreateNamedParamTable ()
452                 {
453                         if (param_list == null)
454                                 return;
455
456                         int count = 0;
457                         foreach (ParamDef param in param_list) {
458                                 if (param.Name != null)
459                                         named_param_table.Add (param.Name, count);
460                                 count++;
461                         }
462                 }
463
464                 private void FixAttributes ()
465                 {
466                         if (name == ".ctor" || name == ".cctor")
467                                 meth_attr |= PEAPI.MethAttr.SpecialName | PEAPI.MethAttr.RTSpecialName;
468                         // If methods aren't flagged as static they are instance
469                         if ((PEAPI.MethAttr.Static & meth_attr) == 0)
470                                 call_conv |= PEAPI.CallConv.Instance;
471                 }
472
473         }
474
475 }
476