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