in System.Runtime.CompilerServices:
[mcs.git] / ilasm / codegen / MethodDef.cs
blob529a886ca8536a7b6c45c8ea65f1abcbbeb240e3
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 //
11 using System;
12 using System.Text;
13 using System.Collections;
14 using System.Security;
16 using Mono.CompilerServices.SymbolWriter;
18 namespace Mono.ILASM {
20 public class MethodDef : ICustomAttrTarget, IDeclSecurityTarget {
22 private PEAPI.MethAttr meth_attr;
23 private PEAPI.CallConv call_conv;
24 private PEAPI.ImplAttr impl_attr;
25 private string name;
26 private string signature;
27 private Hashtable vararg_sig_table;
28 private ParamDef ret_param;
29 private ArrayList param_list;
30 private ArrayList inst_list;
31 private ArrayList customattr_list;
32 private DeclSecurity decl_sec;
33 private Hashtable label_table;
34 private Hashtable labelref_table;
35 private ArrayList label_list;
36 private PEAPI.MethodDef methoddef;
37 private bool entry_point;
38 private bool zero_init;
39 private bool is_resolved;
40 private bool is_defined;
41 private ArrayList local_list;
42 private ArrayList named_local_tables;
43 private int current_scope_depth;
44 private bool init_locals;
45 private int max_stack;
46 private bool pinvoke_info;
47 private ExternModule pinvoke_mod;
48 private string pinvoke_name;
49 private PEAPI.PInvokeAttr pinvoke_attr;
50 private SourceMethod source;
51 private TypeDef type_def;
52 private GenericParameters gen_params;
53 private Location start;
54 private CodeGen codegen;
56 public MethodDef (CodeGen codegen, PEAPI.MethAttr meth_attr,
57 PEAPI.CallConv call_conv, PEAPI.ImplAttr impl_attr,
58 string name, BaseTypeRef ret_type, ArrayList param_list,
59 Location start, GenericParameters gen_params, TypeDef type_def)
61 this.codegen = codegen;
62 this.meth_attr = meth_attr;
63 this.call_conv = call_conv;
64 this.impl_attr = impl_attr;
65 this.name = name;
66 this.param_list = param_list;
67 this.type_def = type_def;
68 this.gen_params = gen_params;
69 this.ret_param = new ParamDef (PEAPI.ParamAttr.Default, "", ret_type);
70 this.start = (Location) start.Clone ();
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_tables = new ArrayList ();
78 named_local_tables.Add (new Hashtable ());
79 current_scope_depth = 0;
81 entry_point = false;
82 zero_init = false;
83 init_locals = false;
84 max_stack = -1;
85 pinvoke_info = false;
87 is_defined = false;
88 is_resolved = false;
89 ResolveGenParams ();
90 CreateSignature ();
92 codegen.BeginMethodDef (this);
94 if (codegen.SymbolWriter != null)
95 source = codegen.SymbolWriter.BeginMethod (this, start);
98 public string Name {
99 get { return name; }
102 public string Signature {
103 get { return signature; }
106 public BaseTypeRef RetType {
107 get { return ret_param.Type; }
110 public PEAPI.CallConv CallConv {
111 get { return call_conv; }
114 public PEAPI.MethodDef PeapiMethodDef {
115 get { return methoddef; }
118 public PEAPI.MethAttr Attributes {
119 get { return meth_attr; }
120 set { meth_attr = value; }
123 public bool IsVararg {
124 get { return (call_conv & PEAPI.CallConv.Vararg) != 0; }
127 public bool IsStatic {
128 get { return (meth_attr & PEAPI.MethAttr.Static) != 0; }
131 public bool IsVirtual {
132 get { return (meth_attr & PEAPI.MethAttr.Virtual) != 0; }
135 public bool IsAbstract {
136 get { return (meth_attr & PEAPI.MethAttr.Abstract) != 0; }
139 public Location StartLocation {
140 get { return start; }
143 public DeclSecurity DeclSecurity {
144 get {
145 if (decl_sec == null)
146 decl_sec = new DeclSecurity ();
147 return decl_sec;
151 public string FullName {
152 get {
153 if (type_def == null)
154 return Name;
155 return type_def.FullName + "." + Name;
159 public BaseTypeRef[] ParamTypeList () {
161 if (param_list == null)
162 return new BaseTypeRef[0];
163 int count = 0;
164 BaseTypeRef[] type_list = new BaseTypeRef[param_list.Count];
165 foreach (ParamDef param in param_list) {
166 type_list[count++] = param.Type;
168 return type_list;
171 public void AddPInvokeInfo (PEAPI.PInvokeAttr pinvoke_attr, ExternModule pinvoke_mod,
172 string pinvoke_name)
174 this.pinvoke_attr = pinvoke_attr;
175 this.pinvoke_mod = pinvoke_mod;
176 this.pinvoke_name = pinvoke_name;
177 pinvoke_info = true;
180 public int GenParamCount {
181 get { return (gen_params != null ? gen_params.Count : 0); }
184 public GenericParameter GetGenericParam (string id)
186 if (gen_params == null)
187 return null;
189 return gen_params.GetGenericParam (id);
192 public GenericParameter GetGenericParam (int index)
194 if (gen_params == null || index < 0 || index >= gen_params.Count)
195 return null;
197 return gen_params [index];
200 public int GetGenericParamNum (string id)
202 if (gen_params == null)
203 return -1;
205 return gen_params.GetGenericParamNum (id);
208 public void AddCustomAttribute (CustomAttr customattr)
210 if (customattr_list == null)
211 customattr_list = new ArrayList ();
213 customattr_list.Add (customattr);
216 public void AddRetTypeMarshalInfo (PEAPI.NativeType native_type)
218 this.ret_param.AddMarshalInfo (native_type);
221 //try/catch scope, used to scope local vars
222 public void BeginLocalsScope ()
224 current_scope_depth ++;
225 named_local_tables.Add (new Hashtable ());
228 public void EndLocalsScope ()
230 named_local_tables.RemoveAt (current_scope_depth);
231 current_scope_depth --;
234 public void AddLocals (ArrayList local_list)
236 int slot_pos = this.local_list.Count;
238 Hashtable current_named_table = null;
239 current_named_table = (Hashtable) named_local_tables [current_scope_depth];
241 foreach (Local local in local_list) {
242 if (local.Slot == -1) {
243 local.Slot = slot_pos;
245 slot_pos++;
246 if (local.Name == null)
247 continue;
249 if (!current_named_table.Contains (local.Name))
250 current_named_table.Add (local.Name, local);
253 this.local_list.AddRange (local_list);
256 public Local GetNamedLocal (string name)
258 Local ret = null;
259 int i = current_scope_depth;
260 while (ret == null && i >= 0) {
261 Hashtable current_named_table = (Hashtable) named_local_tables [i];
262 ret = (Local) current_named_table [name];
264 i --;
267 return ret;
270 public int GetNamedLocalSlot (string name)
272 Local local = GetNamedLocal (name);
273 if (local == null)
274 return -1;
276 return local.Slot;
279 public int GetNamedParamPos (string name)
281 int pos = -1;
282 if (param_list == null)
283 return -1;
285 if (!IsStatic)
286 pos ++;
287 foreach (ParamDef param in param_list) {
288 pos ++;
289 if (param.Name.CompareTo (name) == 0)
290 return pos;
293 return pos;
296 public LocalVariableEntry[] GetLocalVars()
298 System.IO.MemoryStream str = new System.IO.MemoryStream();
299 int i = 0;
300 int num_locals = ((Hashtable) named_local_tables [current_scope_depth]).Count;
301 LocalVariableEntry[] locals = new LocalVariableEntry[num_locals];
303 foreach (Local local in local_list) {
304 if (local.Name != null) { // only named variables
305 locals[i++] = new LocalVariableEntry(local.Slot, local.Name, 0);
308 return locals;
312 /* index - 0: return type
313 * 1: params start from this
315 public ParamDef GetParam (int index)
317 if (index == 0)
318 return ret_param;
320 if ((param_list == null) || (index < 0) || (index > param_list.Count))
321 return null;
323 index --; /* param_list has params zero-based */
325 if (param_list [index] != null)
326 return (ParamDef)param_list [index];
327 else
328 return null;
331 public void InitLocals ()
333 init_locals = true;
336 public void EntryPoint ()
338 if (!IsStatic)
339 Report.Error ("Non-static method as entrypoint.");
340 entry_point = true;
343 public void ZeroInit ()
345 zero_init = true;
348 public void SetMaxStack (int max_stack)
350 this.max_stack = max_stack;
353 public void ResolveGenParam (PEAPI.GenParam gpar)
355 if (gpar.Index != -1)
356 return;
358 if (gpar.Type == PEAPI.GenParamType.MVar)
359 gpar.Index = GetGenericParamNum (gpar.Name);
360 else
361 gpar.Index = type_def.GetGenericParamNum (gpar.Name);
363 if (gpar.Index < 0)
364 Report.Error (String.Format ("Invalid {0}type parameter '{1}'",
365 (gpar.Type == PEAPI.GenParamType.MVar ? "method " : ""),
366 gpar.Name));
369 public void ResolveGenParams ()
371 GenericParameters type_params = (type_def != null) ? type_def.TypeParameters : null;
373 if (gen_params == null && type_params == null)
374 return;
376 if (gen_params != null)
377 gen_params.ResolveConstraints (type_params, gen_params);
379 BaseGenericTypeRef gtr = RetType as BaseGenericTypeRef;
380 if (gtr != null)
381 gtr.Resolve (type_params, gen_params);
383 if (param_list == null)
384 return;
386 foreach (ParamDef param in param_list) {
387 gtr = param.Type as BaseGenericTypeRef;
388 if (gtr != null)
389 gtr.Resolve (type_params, gen_params);
393 public PEAPI.MethodDef Resolve (CodeGen code_gen)
395 return Resolve (code_gen, null);
398 public PEAPI.MethodDef Resolve (CodeGen code_gen, PEAPI.ClassDef classdef)
400 if (is_resolved)
401 return methoddef;
403 PEAPI.Param [] param_array = GenerateParams (code_gen);
404 FixAttributes ();
405 ret_param.Define (code_gen);
407 if (classdef == null)
408 methoddef = code_gen.PEFile.AddMethod (meth_attr, impl_attr,
409 name, ret_param.PeapiParam, param_array);
410 else
411 methoddef = classdef.AddMethod (meth_attr, impl_attr,
412 name, ret_param.PeapiParam, param_array);
414 methoddef.AddCallConv (call_conv);
416 is_resolved = true;
418 return methoddef;
421 private PEAPI.Param [] GenerateParams (CodeGen code_gen)
423 PEAPI.Param[] param_array;
425 if (param_list != null && param_list.Count > 0) {
426 int param_count = param_list.Count;
428 // Remove the last param if its the sentinel, not sure what
429 // should happen with more then one sentinel
430 ParamDef last = (ParamDef) param_list [param_count-1];
431 if (last.IsSentinel ())
432 param_count--;
434 param_array = new PEAPI.Param [param_count];
435 for (int i = 0; i < param_count; i++) {
436 ParamDef paramdef = (ParamDef) param_list [i];
437 paramdef.Define (code_gen);
438 param_array [i] = paramdef.PeapiParam;
441 } else {
442 param_array = new PEAPI.Param [0];
445 return param_array;
448 public PEAPI.MethodRef GetVarargSig (PEAPI.Type[] opt, string full_signature)
450 if (!is_resolved)
451 throw new InternalErrorException ("Methods must be resolved before a vararg sig can be created.");
453 PEAPI.MethodRef methref = null;
454 if (vararg_sig_table == null) {
455 vararg_sig_table = new Hashtable ();
456 } else {
457 methref = vararg_sig_table [full_signature] as PEAPI.MethodRef;
460 if (methref == null) {
461 methref = methoddef.MakeVarArgSignature (opt);
462 vararg_sig_table [full_signature] = methref;
465 return methref;
468 /// <summary>
469 /// Define a member method
470 /// </summary>
471 public void Define (CodeGen code_gen)
473 if (is_defined)
474 return;
476 if (type_def == null)
477 /* Global method */
478 Resolve (code_gen, null);
479 else
480 Resolve (code_gen, (PEAPI.ClassDef) type_def.ClassDef);
482 WriteCode (code_gen, methoddef);
484 //code_gen.Report.Message (String.Format ("Assembled method {0}::{1}", typedef.FullName, name));
485 is_defined = true;
488 public void AddInstr (IInstr instr)
490 inst_list.Add (instr);
493 protected void WriteCode (CodeGen code_gen, PEAPI.MethodDef methoddef)
495 /// Add the custrom attributes to this method
496 if (customattr_list != null)
497 foreach (CustomAttr customattr in customattr_list) {
498 customattr.AddTo (code_gen, methoddef);
499 if (customattr.IsSuppressUnmanaged (code_gen))
500 methoddef.AddMethAttribute (PEAPI.MethAttr.HasSecurity);
503 /// Add declarative security to this method
504 if (decl_sec != null) {
505 decl_sec.AddTo (code_gen, methoddef);
506 methoddef.AddMethAttribute (PEAPI.MethAttr.HasSecurity);
509 // Generic type parameters
510 if (gen_params != null)
511 gen_params.Resolve (code_gen, methoddef);
513 if (type_def == null) {
514 //Global method
515 meth_attr &= ~PEAPI.MethAttr.Abstract;
516 meth_attr |= PEAPI.MethAttr.Static;
517 } else {
518 if ((inst_list.Count > 0) && type_def.IsInterface && !IsStatic)
519 Report.Error (start, "Method cannot have body if it is non-static declared in an interface");
521 if (IsAbstract) {
522 if (!type_def.IsAbstract)
523 Report.Error (start, String.Format ("Abstract method '{0}' in non-abstract class '{1}'",
524 Name, type_def.FullName));
525 if (inst_list.Count > 0)
526 Report.Error (start, "Method cannot have body if it is abstract.");
527 return;
531 if (entry_point)
532 methoddef.DeclareEntryPoint ();
534 if (local_list.Count > 0) {
535 int ec = Report.ErrorCount;
536 PEAPI.Local[] local_array = new PEAPI.Local[local_list.Count];
538 foreach (Local local in local_list)
539 local_array[local.Slot] = local.GetPeapiLocal (code_gen);
541 if (Report.ErrorCount > ec)
542 return;
544 if (zero_init)
545 init_locals = true;
547 methoddef.AddLocals (local_array, init_locals);
550 /// Nothing seems to work if maxstack is not set,
551 /// i need to find out if this NEEDs to be set
552 /// and what its default value should be
553 if (max_stack < 0)
554 max_stack = 8;
555 methoddef.SetMaxStack (max_stack);
557 if (pinvoke_info) {
558 methoddef.AddPInvokeInfo (pinvoke_mod.ModuleRef,
559 (pinvoke_name != null ? pinvoke_name : name), pinvoke_attr);
562 if ((impl_attr & PEAPI.ImplAttr.Runtime) == PEAPI.ImplAttr.Runtime) {
563 if (inst_list.Count > 0)
564 Report.Error (start, String.Format ("Method cannot have body if it is non-IL runtime-supplied, '{0}'",
565 FullName));
566 } else {
567 if (((impl_attr & PEAPI.ImplAttr.Native) != 0) ||
568 ((impl_attr & PEAPI.ImplAttr.Unmanaged) != 0))
569 Report.Error (start, String.Format ("Cannot compile native/unmanaged method, '{0}'",
570 FullName));
573 if (inst_list.Count > 0) {
574 /* Has body */
575 if ((impl_attr & PEAPI.ImplAttr.InternalCall) != 0)
576 Report.Error (start, String.Format ("Method cannot have body if it is an internal call, '{0}'",
577 FullName));
579 if (pinvoke_info)
580 Report.Error (start, String.Format ("Method cannot have body if it is pinvoke, '{0}'",
581 FullName));
582 } else {
583 if (pinvoke_info ||
584 ((impl_attr & PEAPI.ImplAttr.Runtime) != 0) ||
585 ((impl_attr & PEAPI.ImplAttr.InternalCall) != 0))
586 /* No body required */
587 return;
589 Report.Warning (start, "Method has no body, 'ret' emitted.");
590 AddInstr (new SimpInstr (PEAPI.Op.ret, start));
593 PEAPI.CILInstructions cil = methoddef.CreateCodeBuffer ();
594 /// Create all the labels
595 /// TODO: Most labels don't actually need to be created so we could
596 /// probably only create the ones that need to be
597 LabelInfo[] label_info = new LabelInfo[label_table.Count + label_list.Count];
598 label_table.Values.CopyTo (label_info, 0);
599 label_list.CopyTo (label_info, label_table.Count);
600 int previous_pos = -1;
601 LabelInfo previous_label = null;
602 Array.Sort (label_info);
604 foreach (LabelInfo label in label_info) {
605 if (label.UseOffset) {
606 label.Define (new PEAPI.CILLabel (label.Offset, true));
607 continue;
609 if (label.Pos == previous_pos)
610 label.Label = previous_label.Label;
611 else
612 label.Define (cil.NewLabel ());
614 previous_label = label;
615 previous_pos = label.Pos;
618 // Set all the label refs
619 foreach (LabelInfo label in labelref_table.Values) {
620 LabelInfo def = (LabelInfo) label_table[label.Name];
621 if (def == null) {
622 Report.Error ("Undefined Label: " + label);
623 return;
625 label.Label = def.Label;
628 int label_pos = 0;
629 int next_label_pos = (label_info.Length > 0 ? label_info[0].Pos : -1);
631 for (int i=0; i<inst_list.Count; i++) {
632 IInstr instr = (IInstr) inst_list[i];
633 if (next_label_pos == i) {
634 cil.CodeLabel (label_info[label_pos].Label);
635 if (label_pos < label_info.Length) {
636 while (next_label_pos == i && ++label_pos < label_info.Length) {
637 if (label_info[label_pos].UseOffset)
638 cil.CodeLabel (label_info[label_pos].Label);
639 next_label_pos = label_info[label_pos].Pos;
642 if (label_pos >= label_info.Length)
643 next_label_pos = -1;
645 if (source != null)
646 source.MarkLocation (instr.Location.line, cil.Offset);
647 instr.Emit (code_gen, this, cil);
650 if (source != null)
651 source.MarkLocation (source.EndLine, cil.Offset);
654 public LabelInfo AddLabel (string name)
656 LabelInfo label_info = (LabelInfo) label_table[name];
657 if (label_info != null)
658 Report.Error ("Duplicate label '" + name + "'");
660 label_info = new LabelInfo (name, inst_list.Count);
661 label_table [name] = label_info;
662 return label_info;
665 public LabelInfo AddLabelRef (string name)
667 LabelInfo label_info = (LabelInfo) label_table[name];
668 if (label_info != null)
669 return label_info;
670 label_info = (LabelInfo) labelref_table[name];
671 if (label_info != null)
672 return label_info;
673 label_info = new LabelInfo (name, -1);
674 labelref_table.Add (name, label_info);
675 return label_info;
678 public LabelInfo AddLabel (int offset)
680 // We go pos + 1 so this line is not counted
681 LabelInfo label_info = new LabelInfo (null, inst_list.Count+1, (uint) offset);
682 label_list.Add (label_info);
683 return label_info;
686 public LabelInfo AddLabel ()
688 LabelInfo label_info = new LabelInfo (null, inst_list.Count);
689 label_list.Add (label_info);
690 return label_info;
693 public PEAPI.CILLabel GetLabelDef (string name)
695 LabelInfo label_info = (LabelInfo) label_table[name];
697 return label_info.Label;
700 public PEAPI.CILLabel GetLabelDef (int pos)
702 foreach (LabelInfo li in label_list) {
703 if (li.Pos == pos)
704 return li.Label;
706 return null;
709 private void CreateSignature ()
711 if (IsVararg)
712 signature = CreateVarargSignature (RetType, name, param_list);
713 else
714 signature = CreateSignature (RetType, name, param_list, GenParamCount);
717 static string CreateSignature (BaseTypeRef RetType, string name, IList param_list, int gen_param_count)
719 StringBuilder builder = new StringBuilder ();
721 builder.Append (RetType.FullName);
722 builder.Append (" ");
723 builder.Append (name);
724 if (gen_param_count > 0)
725 builder.AppendFormat ("`{0}", gen_param_count);
726 builder.Append ('(');
728 if (param_list != null) {
729 bool first = true;
730 foreach (ParamDef paramdef in param_list) {
731 if (!first)
732 builder.Append (',');
733 builder.Append (paramdef.TypeName);
734 first = false;
737 builder.Append (')');
739 return builder.ToString ();
742 static string CreateVarargSignature (BaseTypeRef RetType, string name, IList param_list)
744 StringBuilder builder = new StringBuilder ();
745 ParamDef last = null;
747 builder.Append (RetType.FullName);
748 builder.Append (" ");
749 builder.Append (name);
750 builder.Append ('(');
752 bool first = true;
753 if (param_list != null) {
754 foreach (ParamDef paramdef in param_list) {
755 if (!first)
756 builder.Append (',');
757 builder.Append (paramdef.TypeName);
758 first = false;
760 last = (ParamDef) param_list[param_list.Count - 1];
764 if (last == null || !last.IsSentinel ()) {
765 if (!first)
766 builder.Append (',');
767 builder.Append ("...");
770 builder.Append (')');
772 return builder.ToString ();
775 // @include_optional: include optional parameters for vararg methods
776 // This will be true mostly for *Ref use, eg. methodrefs at call sites
777 // and false for *Def (include only the required params)
778 public static string CreateSignature (BaseTypeRef RetType, PEAPI.CallConv call_conv, string name,
779 BaseTypeRef[] param_list, int gen_param_count, bool include_optional)
781 if ((call_conv & PEAPI.CallConv.Vararg) != 0)
782 return CreateVarargSignature (RetType, name, param_list, include_optional);
783 else
784 return CreateSignature (RetType, name, param_list, gen_param_count, include_optional);
787 static string CreateVarargSignature (BaseTypeRef RetType, string name, BaseTypeRef [] param_list, bool include_optional)
789 StringBuilder builder = new StringBuilder ();
790 BaseTypeRef last = null;
792 builder.Append (RetType.FullName);
793 builder.Append (" ");
794 builder.Append (name);
795 builder.Append ('(');
797 bool first = true;
798 if (param_list != null && param_list.Length > 0) {
799 foreach (BaseTypeRef param in param_list) {
800 if (!first)
801 builder.Append (',');
802 builder.Append (param.FullName);
803 first = false;
804 last = param;
805 if (!include_optional && param is SentinelTypeRef)
806 break;
811 if (!include_optional && (last == null || !(last is SentinelTypeRef))) {
812 if (!first)
813 builder.Append (',');
814 builder.Append ("...");
817 builder.Append (')');
819 return builder.ToString ();
822 static string CreateSignature (BaseTypeRef RetType, string name, BaseTypeRef[] param_list, int gen_param_count, bool include_optional)
824 StringBuilder builder = new StringBuilder ();
826 builder.Append (RetType.FullName);
827 builder.Append (" ");
828 builder.Append (name);
829 if (gen_param_count > 0)
830 builder.AppendFormat ("`{0}", gen_param_count);
831 builder.Append ('(');
833 if (param_list != null) {
834 bool first = true;
835 foreach (BaseTypeRef param in param_list) {
836 if (!first)
837 builder.Append (',');
838 builder.Append (param.FullName);
839 first = false;
840 if (!include_optional && param is SentinelTypeRef)
841 break;
844 builder.Append (')');
846 return builder.ToString ();
849 private void FixAttributes ()
851 if (name == ".ctor" || name == ".cctor")
852 meth_attr |= PEAPI.MethAttr.SpecialName | PEAPI.MethAttr.RTSpecialName;
853 // If methods aren't flagged as static they are instance
854 if ((PEAPI.MethAttr.Static & meth_attr) == 0)
855 call_conv |= PEAPI.CallConv.Instance;