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