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