* TypeDef.cs (GenericInfo):
[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 BaseTypeRef ret_type;
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 PEAPI.NativeType ret_native_type;
49                 private TypeDef type_def;
50                 private GenericParameters gen_params;
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.ret_type = ret_type;
62                         this.param_list = param_list;
63                         this.type_def = type_def;
64                         this.gen_params = gen_params;
65
66                         inst_list = new ArrayList ();
67                         label_table = new Hashtable ();
68                         labelref_table = new Hashtable ();
69                         label_list = new ArrayList ();
70                         local_list = new ArrayList ();
71                         named_local_table = new Hashtable ();
72
73                         entry_point = false;
74                         zero_init = false;
75                         init_locals = false;
76                         max_stack = -1;
77                         pinvoke_info = false;
78
79                         is_defined = false;
80                         is_resolved = false;
81                         ResolveGenParams ();
82                         CreateSignature ();
83
84                         codegen.BeginMethodDef (this);
85
86                         if (codegen.SymbolWriter != null)
87                                 source = codegen.SymbolWriter.BeginMethod (this, start);
88                 }
89
90                 public string Name {
91                         get { return name; }
92                 }
93
94                 public string Signature {
95                         get { return signature; }
96                 }
97
98                 public BaseTypeRef RetType {
99                         get { return ret_type; }
100                 }
101
102                 public PEAPI.CallConv CallConv {
103                         get { return call_conv; }
104                 }
105
106                 public PEAPI.MethodDef PeapiMethodDef {
107                         get { return methoddef; }
108                 }
109
110                 public PEAPI.MethAttr Attributes {
111                         get { return meth_attr; }
112                         set { meth_attr = value; }
113                 }
114
115                 public bool IsVararg {
116                         get { return (call_conv & PEAPI.CallConv.Vararg) != 0; }
117                 }
118
119                 public bool IsStatic {
120                         get { return (meth_attr & PEAPI.MethAttr.Static) != 0; }
121                 }
122
123                 public bool IsVirtual {
124                         get { return (meth_attr & PEAPI.MethAttr.Virtual) != 0; }
125                 }
126
127                 public bool IsAbstract {
128                         get { return (meth_attr & PEAPI.MethAttr.Abstract) != 0; }
129                 }
130
131                 public BaseTypeRef[] ParamTypeList () {
132
133                         if (param_list == null)
134                                 return new BaseTypeRef[0];
135                         int count = 0;
136                         BaseTypeRef[] type_list = new BaseTypeRef[param_list.Count];
137                         foreach (ParamDef param in param_list) {
138                                 type_list[count++] = param.Type;
139                         }
140                         return type_list;
141                 }
142
143                 public void AddPInvokeInfo (PEAPI.PInvokeAttr pinvoke_attr, ExternModule pinvoke_mod,
144                                 string pinvoke_name)
145                 {
146                         this.pinvoke_attr = pinvoke_attr;
147                         this.pinvoke_mod = pinvoke_mod;
148                         this.pinvoke_name = pinvoke_name;
149                         pinvoke_info = true;
150                 }
151
152                 public int GenParamCount {
153                         get { return (gen_params != null ? gen_params.Count : 0); }
154                 }
155
156                 public GenericParameter GetGenericParam (string id)
157                 {
158                         if (gen_params == null)
159                                 return null;
160                         
161                         return gen_params.GetGenericParam (id);
162                 }
163
164                 public GenericParameter GetGenericParam (int index)
165                 {
166                         if (gen_params == null || index < 0 || index >= gen_params.Count)
167                                 return null;
168                         
169                         return gen_params [index];
170                 }
171
172                 public int GetGenericParamNum (string id)
173                 {
174                         if (gen_params == null)
175                                 return -1;
176                         
177                         return gen_params.GetGenericParamNum (id);
178                 }
179
180                 public void AddParamDefaultValue (int index, PEAPI.Constant defval)
181                 {
182                         if (param_list [index] != null) {
183                                 ((ParamDef)param_list [index]).AddDefaultValue (defval);
184                         }
185                 }
186
187                 public void AddCustomAttribute (CustomAttr customattr)
188                 {
189                         if (customattr_list == null)
190                                 customattr_list = new ArrayList ();
191
192                         customattr_list.Add (customattr);
193                 }
194
195                 public void AddPermissionSet (PEAPI.SecurityAction sec_action, PermissionSet ps)
196                 {
197                         if (decl_sec == null)
198                                 decl_sec = new DeclSecurity ();
199                         
200                         decl_sec.AddPermissionSet (sec_action, ps);
201                 }
202                 
203                 public void AddPermission (PEAPI.SecurityAction sec_action, IPermission iper)
204                 {
205                         if (decl_sec == null)
206                                 decl_sec = new DeclSecurity ();
207                         
208                         decl_sec.AddPermission (sec_action, iper);
209                 }
210
211                 public void AddRetTypeMarshalInfo (PEAPI.NativeType native_type)
212                 {
213                         this.ret_native_type = native_type;
214                 }
215
216                 public void AddLocals (ArrayList local_list)
217                 {
218                         int slot_pos = this.local_list.Count;
219
220                         foreach (Local local in local_list) {
221                                 if (local.Slot == -1) {
222                                         local.Slot = slot_pos;
223                                 }
224                                 slot_pos++;
225                                 if (local.Name == null)
226                                         continue;
227                                 if(!named_local_table.Contains(local.Name))
228                                   named_local_table.Add (local.Name, local);
229                         }
230
231                         this.local_list.AddRange (local_list);
232                 }
233
234                 public Local GetNamedLocal (string name)
235                 {
236                         return (Local) named_local_table[name];
237                 }
238
239                 public int GetNamedLocalSlot (string name)
240                 {
241                         Local local = (Local) named_local_table[name];
242
243                         return local.Slot;
244                 }
245
246                 public int GetNamedParamPos (string name)
247                 {
248                         int pos = -1;
249                         if (!IsStatic)
250                                 pos ++;
251                         foreach (ParamDef param in param_list) {
252                                 pos ++;
253                                 if (param.Name.CompareTo (name) == 0)
254                                         return pos;
255                         }
256
257                         return pos;
258                 }
259
260                 public ParamDef GetParam (int index)
261                 {
262                         if (param_list [index] != null)
263                                 return (ParamDef)param_list [index];
264                         else
265                                 return null;
266                 }
267
268                 public void InitLocals ()
269                 {
270                         init_locals = true;
271                 }
272
273                 public void EntryPoint ()
274                 {
275                         if (!IsStatic)
276                                 throw new Exception ("Non-static method as entrypoint.");
277                         entry_point = true;
278                 }
279
280                 public void ZeroInit ()
281                 {
282                         zero_init = true;
283                 }
284                 
285                 public void SetMaxStack (int max_stack)
286                 {
287                         this.max_stack = max_stack;
288                 }
289
290                 public void ResolveGenParam (PEAPI.GenParam gpar)
291                 {
292                         if (gpar.Index != -1)
293                                 return;
294         
295                         if (gpar.Type == PEAPI.GenParamType.MVar)
296                                 gpar.Index = GetGenericParamNum (gpar.Name); 
297                         else
298                                 gpar.Index = type_def.GetGenericParamNum (gpar.Name);
299
300                         if (gpar.Index < 0)
301                                 /* TODO: Report error */
302                                 throw new Exception (String.Format ("Invalid {0}type parameter '{1}'", 
303                                                         (gpar.Type == PEAPI.GenParamType.MVar ? "method " : ""),
304                                                          gpar.Name));
305                 }
306
307                 public void ResolveGenParams ()
308                 {
309                         GenericParameters type_params = (type_def != null) ? type_def.TypeParameters : null;
310
311                         if (gen_params == null && type_params == null)
312                                 return;
313
314                         if (gen_params != null)
315                                 gen_params.ResolveConstraints (type_params, gen_params);
316                         
317                         BaseGenericTypeRef gtr = ret_type as BaseGenericTypeRef;
318                         if (gtr != null)
319                                 gtr.Resolve (type_params, gen_params);
320
321                         if (param_list == null)
322                                 return;
323
324                         foreach (ParamDef param in param_list) {
325                                 gtr = param.Type as BaseGenericTypeRef;
326                                 if (gtr != null)
327                                         gtr.Resolve (type_params, gen_params);
328                         }        
329                 }
330
331                 public PEAPI.MethodDef Resolve (CodeGen code_gen)
332                 {
333                         return Resolve (code_gen, null);
334                 }
335
336                 public PEAPI.MethodDef Resolve (CodeGen code_gen, PEAPI.ClassDef classdef)
337                 {
338                         if (is_resolved)
339                                 return methoddef;
340
341                         PEAPI.Param [] param_array = GenerateParams (code_gen);
342                         FixAttributes ();
343                         ret_type.Resolve (code_gen);
344
345                         if (classdef == null)
346                                 methoddef = code_gen.PEFile.AddMethod (meth_attr, impl_attr,
347                                                 name, ret_type.PeapiType, param_array);
348                         else                    
349                                 methoddef = classdef.AddMethod (meth_attr, impl_attr,
350                                                 name, ret_type.PeapiType, param_array);
351
352                         methoddef.AddCallConv (call_conv);
353
354                         if (ret_native_type != null)
355                                 methoddef.AddRetTypeMarshallInfo (ret_native_type);
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 Exception ("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 = code_gen.Report.ErrorCount;
468                                 PEAPI.Local[] local_array = new PEAPI.Local[local_list.Count];
469                                 int i = 0;
470
471                                 foreach (Local local in local_list)
472                                         local_array[local.Slot]  = local.GetPeapiLocal (code_gen);
473
474                                 if (code_gen.Report.ErrorCount > ec)
475                                         return;
476
477                                 if (zero_init)
478                                         init_locals = true;
479                                 
480                                 methoddef.AddLocals (local_array, init_locals);
481                         }
482
483                         /// Nothing seems to work if maxstack is not set,
484                         /// i need to find out if this NEEDs to be set
485                         /// and what its default value should be
486                         if (max_stack < 0)
487                                 max_stack = 8;
488                         methoddef.SetMaxStack (max_stack);
489
490                         if (pinvoke_info) {
491                                 methoddef.AddPInvokeInfo (pinvoke_mod.ModuleRef,
492                                                 (pinvoke_name != null ? pinvoke_name : name), pinvoke_attr);
493                         }
494
495                         if (inst_list.Count < 1)
496                                 return;
497
498                         PEAPI.CILInstructions cil = methoddef.CreateCodeBuffer ();
499                         /// Create all the labels
500                         /// TODO: Most labels don't actually need to be created so we could
501                         /// probably only create the ones that need to be
502                         LabelInfo[] label_info = new LabelInfo[label_table.Count + label_list.Count];
503                         label_table.Values.CopyTo (label_info, 0);
504                         label_list.CopyTo (label_info, label_table.Count);
505                         int previous_pos = -1;
506                         LabelInfo previous_label = null;
507                         Array.Sort (label_info);
508
509                         foreach (LabelInfo label in label_info) {
510                                 if (label.UseOffset) {
511                                         label.Define (new PEAPI.CILLabel (label.Offset));
512                                         continue;
513                                 }
514                                 if (label.Pos == previous_pos)
515                                         label.Label = previous_label.Label;
516                                 else
517                                         label.Define (cil.NewLabel ());
518
519                                 previous_label = label;
520                                 previous_pos = label.Pos;
521                         }
522
523                         // Set all the label refs
524                         foreach (LabelInfo label in labelref_table.Values) {
525                                 LabelInfo def = (LabelInfo) label_table[label.Name];
526                                 if (def == null) {
527                                         code_gen.Report.Error ("Undefined Label:  " + label);
528                                         return;
529                                 }
530                                 label.Label = def.Label;
531                         }
532
533                         int label_pos = 0;
534                         int next_label_pos = (label_info.Length > 0 ? label_info[0].Pos : -1);
535
536                         for (int i=0; i<inst_list.Count; i++) {
537                                 IInstr instr = (IInstr) inst_list[i];
538                                 if (next_label_pos == i) {
539                                         cil.CodeLabel (label_info[label_pos].Label);
540                                         if (label_pos < label_info.Length) {
541                                                 while (next_label_pos == i && ++label_pos < label_info.Length) {
542                                                         if (label_info[label_pos].UseOffset)
543                                                                 cil.CodeLabel (label_info[label_pos].Label);
544                                                        next_label_pos = label_info[label_pos].Pos;
545                                                 }
546                                         }
547                                         if (label_pos >= label_info.Length)
548                                                 next_label_pos = -1;
549                                 }
550                                 if (source != null)
551                                         source.MarkLocation (instr.Location.line, cil.Offset);
552                                 instr.Emit (code_gen, this, cil);
553                         }
554
555                         if (source != null)
556                                 source.MarkLocation (source.EndLine, cil.Offset);
557                 }
558
559                 public LabelInfo AddLabel (string name)
560                 {
561                         LabelInfo label_info = (LabelInfo) label_table[name];
562                         if (label_info != null)
563                                 return label_info;
564                         label_info = new LabelInfo (name, inst_list.Count);
565                         label_table.Add (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                         int pos = inst_list.Count;
593                         LabelInfo label_info = new LabelInfo (null, inst_list.Count);
594                         label_list.Add (label_info);
595                         return label_info;
596                 }
597
598                 public PEAPI.CILLabel GetLabelDef (string name)
599                 {
600                         LabelInfo label_info = (LabelInfo) label_table[name];
601
602                         return label_info.Label;
603                 }
604
605                 public PEAPI.CILLabel GetLabelDef (int pos)
606                 {
607                         foreach (LabelInfo li in label_list) {
608                                 if (li.Pos == pos)
609                                         return li.Label;
610                         }
611                         return null;
612                 }
613
614                 private void CreateSignature ()
615                 {
616                         if (IsVararg)
617                                 signature = CreateVarargSignature (RetType, name, param_list);
618                         else
619                                 signature = CreateSignature (RetType, name, param_list, GenParamCount);
620                 }
621
622                 public static string CreateSignature (BaseTypeRef RetType, string name, IList param_list, int gen_param_count)
623                 {
624                         StringBuilder builder = new StringBuilder ();
625
626                         builder.Append (RetType.FullName);
627                         builder.Append (" ");
628                         builder.Append (name);
629                         if (gen_param_count > 0)
630                                 builder.AppendFormat ("`{0}", gen_param_count);
631                         builder.Append ('(');
632
633                         if (param_list != null) {
634                                 bool first = true;
635                                 foreach (ParamDef paramdef in param_list) {
636                                         if (!first)
637                                                 builder.Append (',');
638                                         builder.Append (paramdef.TypeName);
639                                         first = false;
640                                 }
641                         }
642                         builder.Append (')');
643
644                         return builder.ToString ();
645                 }
646
647                 public static string CreateVarargSignature (BaseTypeRef RetType, string name, IList param_list)
648                 {
649                         StringBuilder builder = new StringBuilder ();
650                         ParamDef last = null;
651
652                         builder.Append (RetType.FullName);
653                         builder.Append (" ");
654                         builder.Append (name);
655                         builder.Append ('(');
656
657                         bool first = true;
658                         if (param_list != null) {
659                                 foreach (ParamDef paramdef in param_list) {
660                                         if (!first)
661                                                 builder.Append (',');
662                                         builder.Append (paramdef.TypeName);
663                                         first = false;
664                                 }
665                                 last = (ParamDef) param_list[param_list.Count - 1];
666                         }
667
668                         
669                         if (last == null || !last.IsSentinel ()) {
670                                 if (!first)
671                                         builder.Append (',');
672                                 builder.Append ("...");
673                         }
674
675                         builder.Append (')');
676
677                         return builder.ToString ();
678                 }
679
680                 public static string CreateVarargSignature (BaseTypeRef RetType, string name, BaseTypeRef [] param_list)
681                 {
682                         StringBuilder builder = new StringBuilder ();
683                         BaseTypeRef last = null;
684
685                         builder.Append (RetType.FullName);
686                         builder.Append (" ");
687                         builder.Append (name);
688                         builder.Append ('(');
689
690                         bool first = true;
691                         if (param_list != null && param_list.Length > 0) {
692                                 foreach (BaseTypeRef param in param_list) {
693                                         if (!first)
694                                                 builder.Append (',');
695                                         builder.Append (param.FullName);
696                                         first = false;
697                                         last = param;
698                                         if (param is SentinelTypeRef)
699                                                 break;
700                                 }
701                                 
702                         }
703                         
704                         if (last == null || !(last is SentinelTypeRef)) {
705                                 if (!first)
706                                         builder.Append (',');
707                                 builder.Append ("...");
708                         }
709
710                         builder.Append (')');
711
712                         return builder.ToString ();
713                 }
714
715                 public static string CreateSignature (BaseTypeRef RetType, string name, BaseTypeRef[] param_list, int gen_param_count)
716                 {
717                         StringBuilder builder = new StringBuilder ();
718
719                         builder.Append (RetType.FullName);
720                         builder.Append (" ");
721                         builder.Append (name);
722                         if (gen_param_count > 0)
723                                 builder.AppendFormat ("`{0}", gen_param_count);
724                         builder.Append ('(');
725
726                         if (param_list != null) {
727                                 bool first = true;
728                                 foreach (BaseTypeRef param in param_list) {
729                                         if (!first)
730                                                 builder.Append (',');
731                                         builder.Append (param.FullName);
732                                         first = false;
733                                         if (param is SentinelTypeRef)
734                                                 break;
735                                 }
736                         }
737                         builder.Append (')');
738
739                         return builder.ToString ();
740                 }
741
742                 private void FixAttributes ()
743                 {
744                         if (name == ".ctor" || name == ".cctor")
745                                 meth_attr |= PEAPI.MethAttr.SpecialName | PEAPI.MethAttr.RTSpecialName;
746                         // If methods aren't flagged as static they are instance
747                         if ((PEAPI.MethAttr.Static & meth_attr) == 0)
748                                 call_conv |= PEAPI.CallConv.Instance;
749                 }
750
751         }
752
753 }
754