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