* TypeDef.cs (TypeDef.AddMethodDef): Throw exception with proper message
[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
470                                 foreach (Local local in local_list)
471                                         local_array[local.Slot]  = local.GetPeapiLocal (code_gen);
472
473                                 if (code_gen.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                                         code_gen.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                                 return label_info;
563                         label_info = new LabelInfo (name, inst_list.Count);
564                         label_table.Add (name, label_info);
565                         return label_info;
566                 }
567
568                 public LabelInfo AddLabelRef (string name)
569                 {
570                         LabelInfo label_info = (LabelInfo) label_table[name];
571                         if (label_info != null)
572                                 return label_info;
573                         label_info = (LabelInfo) labelref_table[name];
574                         if (label_info != null)
575                                 return label_info;
576                         label_info = new LabelInfo (name, -1);
577                         labelref_table.Add (name, label_info);
578                         return label_info;
579                 }
580
581                 public LabelInfo AddLabel (int offset)
582                 {
583                         // We go pos + 1 so this line is not counted
584                         LabelInfo label_info = new LabelInfo (null, inst_list.Count+1, (uint) offset);
585                         label_list.Add (label_info);
586                         return label_info;
587                 }
588
589                 public LabelInfo AddLabel ()
590                 {
591                         LabelInfo label_info = new LabelInfo (null, inst_list.Count);
592                         label_list.Add (label_info);
593                         return label_info;
594                 }
595
596                 public PEAPI.CILLabel GetLabelDef (string name)
597                 {
598                         LabelInfo label_info = (LabelInfo) label_table[name];
599
600                         return label_info.Label;
601                 }
602
603                 public PEAPI.CILLabel GetLabelDef (int pos)
604                 {
605                         foreach (LabelInfo li in label_list) {
606                                 if (li.Pos == pos)
607                                         return li.Label;
608                         }
609                         return null;
610                 }
611
612                 private void CreateSignature ()
613                 {
614                         if (IsVararg)
615                                 signature = CreateVarargSignature (RetType, name, param_list);
616                         else
617                                 signature = CreateSignature (RetType, name, param_list, GenParamCount);
618                 }
619
620                 public static string CreateSignature (BaseTypeRef RetType, string name, IList param_list, int gen_param_count)
621                 {
622                         StringBuilder builder = new StringBuilder ();
623
624                         builder.Append (RetType.FullName);
625                         builder.Append (" ");
626                         builder.Append (name);
627                         if (gen_param_count > 0)
628                                 builder.AppendFormat ("`{0}", gen_param_count);
629                         builder.Append ('(');
630
631                         if (param_list != null) {
632                                 bool first = true;
633                                 foreach (ParamDef paramdef in param_list) {
634                                         if (!first)
635                                                 builder.Append (',');
636                                         builder.Append (paramdef.TypeName);
637                                         first = false;
638                                 }
639                         }
640                         builder.Append (')');
641
642                         return builder.ToString ();
643                 }
644
645                 public static string CreateVarargSignature (BaseTypeRef RetType, string name, IList param_list)
646                 {
647                         StringBuilder builder = new StringBuilder ();
648                         ParamDef last = null;
649
650                         builder.Append (RetType.FullName);
651                         builder.Append (" ");
652                         builder.Append (name);
653                         builder.Append ('(');
654
655                         bool first = true;
656                         if (param_list != null) {
657                                 foreach (ParamDef paramdef in param_list) {
658                                         if (!first)
659                                                 builder.Append (',');
660                                         builder.Append (paramdef.TypeName);
661                                         first = false;
662                                 }
663                                 last = (ParamDef) param_list[param_list.Count - 1];
664                         }
665
666                         
667                         if (last == null || !last.IsSentinel ()) {
668                                 if (!first)
669                                         builder.Append (',');
670                                 builder.Append ("...");
671                         }
672
673                         builder.Append (')');
674
675                         return builder.ToString ();
676                 }
677
678                 public static string CreateVarargSignature (BaseTypeRef RetType, string name, BaseTypeRef [] param_list)
679                 {
680                         StringBuilder builder = new StringBuilder ();
681                         BaseTypeRef last = null;
682
683                         builder.Append (RetType.FullName);
684                         builder.Append (" ");
685                         builder.Append (name);
686                         builder.Append ('(');
687
688                         bool first = true;
689                         if (param_list != null && param_list.Length > 0) {
690                                 foreach (BaseTypeRef param in param_list) {
691                                         if (!first)
692                                                 builder.Append (',');
693                                         builder.Append (param.FullName);
694                                         first = false;
695                                         last = param;
696                                         if (param is SentinelTypeRef)
697                                                 break;
698                                 }
699                                 
700                         }
701                         
702                         if (last == null || !(last is SentinelTypeRef)) {
703                                 if (!first)
704                                         builder.Append (',');
705                                 builder.Append ("...");
706                         }
707
708                         builder.Append (')');
709
710                         return builder.ToString ();
711                 }
712
713                 public static string CreateSignature (BaseTypeRef RetType, string name, BaseTypeRef[] param_list, int gen_param_count)
714                 {
715                         StringBuilder builder = new StringBuilder ();
716
717                         builder.Append (RetType.FullName);
718                         builder.Append (" ");
719                         builder.Append (name);
720                         if (gen_param_count > 0)
721                                 builder.AppendFormat ("`{0}", gen_param_count);
722                         builder.Append ('(');
723
724                         if (param_list != null) {
725                                 bool first = true;
726                                 foreach (BaseTypeRef param in param_list) {
727                                         if (!first)
728                                                 builder.Append (',');
729                                         builder.Append (param.FullName);
730                                         first = false;
731                                         if (param is SentinelTypeRef)
732                                                 break;
733                                 }
734                         }
735                         builder.Append (')');
736
737                         return builder.ToString ();
738                 }
739
740                 private void FixAttributes ()
741                 {
742                         if (name == ".ctor" || name == ".cctor")
743                                 meth_attr |= PEAPI.MethAttr.SpecialName | PEAPI.MethAttr.RTSpecialName;
744                         // If methods aren't flagged as static they are instance
745                         if ((PEAPI.MethAttr.Static & meth_attr) == 0)
746                                 call_conv |= PEAPI.CallConv.Instance;
747                 }
748
749         }
750
751 }
752