2 // Mono.ILASM.MethodDef
5 // Jackson Harper (Jackson@LatitudeGeo.com)
7 // (C) 2003 Jackson Harper, All rights reserved
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
;
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
;
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;
92 codegen
.BeginMethodDef (this);
94 if (codegen
.SymbolWriter
!= null)
95 source
= codegen
.SymbolWriter
.BeginMethod (this, start
);
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
{
145 if (decl_sec
== null)
146 decl_sec
= new DeclSecurity ();
151 public string FullName
{
153 if (type_def
== null)
155 return type_def
.FullName
+ "." + Name
;
159 public BaseTypeRef
[] ParamTypeList () {
161 if (param_list
== null)
162 return new BaseTypeRef
[0];
164 BaseTypeRef
[] type_list
= new BaseTypeRef
[param_list
.Count
];
165 foreach (ParamDef param
in param_list
) {
166 type_list
[count
++] = param
.Type
;
171 public void AddPInvokeInfo (PEAPI
.PInvokeAttr pinvoke_attr
, ExternModule pinvoke_mod
,
174 this.pinvoke_attr
= pinvoke_attr
;
175 this.pinvoke_mod
= pinvoke_mod
;
176 this.pinvoke_name
= pinvoke_name
;
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)
189 return gen_params
.GetGenericParam (id
);
192 public GenericParameter
GetGenericParam (int index
)
194 if (gen_params
== null || index
< 0 || index
>= gen_params
.Count
)
197 return gen_params
[index
];
200 public int GetGenericParamNum (string id
)
202 if (gen_params
== null)
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
;
246 if (local
.Name
== null)
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
)
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
];
270 public int GetNamedLocalSlot (string name
)
272 Local local
= GetNamedLocal (name
);
279 public int GetNamedParamPos (string name
)
282 if (param_list
== null)
287 foreach (ParamDef param
in param_list
) {
289 if (param
.Name
.CompareTo (name
) == 0)
296 public LocalVariableEntry
[] GetLocalVars()
298 System
.IO
.MemoryStream str
= new System
.IO
.MemoryStream();
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);
312 /* index - 0: return type
313 * 1: params start from this
315 public ParamDef
GetParam (int index
)
320 if ((param_list
== null) || (index
< 0) || (index
> param_list
.Count
))
323 index
--; /* param_list has params zero-based */
325 if (param_list
[index
] != null)
326 return (ParamDef
)param_list
[index
];
331 public void InitLocals ()
336 public void EntryPoint ()
339 Report
.Error ("Non-static method as entrypoint.");
343 public void ZeroInit ()
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)
358 if (gpar
.Type
== PEAPI
.GenParamType
.MVar
)
359 gpar
.Index
= GetGenericParamNum (gpar
.Name
);
361 gpar
.Index
= type_def
.GetGenericParamNum (gpar
.Name
);
364 Report
.Error (String
.Format ("Invalid {0}type parameter '{1}'",
365 (gpar
.Type
== PEAPI
.GenParamType
.MVar
? "method " : ""),
369 public void ResolveGenParams ()
371 GenericParameters type_params
= (type_def
!= null) ? type_def
.TypeParameters
: null;
373 if (gen_params
== null && type_params
== null)
376 if (gen_params
!= null)
377 gen_params
.ResolveConstraints (type_params
, gen_params
);
379 BaseGenericTypeRef gtr
= RetType
as BaseGenericTypeRef
;
381 gtr
.Resolve (type_params
, gen_params
);
383 if (param_list
== null)
386 foreach (ParamDef param
in param_list
) {
387 gtr
= param
.Type
as BaseGenericTypeRef
;
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
)
403 PEAPI
.Param
[] param_array
= GenerateParams (code_gen
);
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
);
411 methoddef
= classdef
.AddMethod (meth_attr
, impl_attr
,
412 name
, ret_param
.PeapiParam
, param_array
);
414 methoddef
.AddCallConv (call_conv
);
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 ())
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
;
442 param_array
= new PEAPI
.Param
[0];
448 public PEAPI
.MethodRef
GetVarargSig (PEAPI
.Type
[] opt
, string full_signature
)
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 ();
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
;
469 /// Define a member method
471 public void Define (CodeGen code_gen
)
476 if (type_def
== null)
478 Resolve (code_gen
, null);
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));
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) {
515 meth_attr
&= ~PEAPI
.MethAttr
.Abstract
;
516 meth_attr
|= PEAPI
.MethAttr
.Static
;
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");
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.");
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
)
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
555 methoddef
.SetMaxStack (max_stack
);
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}'",
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}'",
573 if (inst_list
.Count
> 0) {
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}'",
580 Report
.Error (start
, String
.Format ("Method cannot have body if it is pinvoke, '{0}'",
584 ((impl_attr
& PEAPI
.ImplAttr
.Runtime
) != 0) ||
585 ((impl_attr
& PEAPI
.ImplAttr
.InternalCall
) != 0))
586 /* No body required */
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));
609 if (label
.Pos
== previous_pos
)
610 label
.Label
= previous_label
.Label
;
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
];
622 Report
.Error ("Undefined Label: " + label
);
625 label
.Label
= def
.Label
;
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
)
646 source
.MarkLocation (instr
.Location
.line
, cil
.Offset
);
647 instr
.Emit (code_gen
, this, cil
);
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
;
665 public LabelInfo
AddLabelRef (string name
)
667 LabelInfo label_info
= (LabelInfo
) label_table
[name
];
668 if (label_info
!= null)
670 label_info
= (LabelInfo
) labelref_table
[name
];
671 if (label_info
!= null)
673 label_info
= new LabelInfo (name
, -1);
674 labelref_table
.Add (name
, 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
);
686 public LabelInfo
AddLabel ()
688 LabelInfo label_info
= new LabelInfo (null, inst_list
.Count
);
689 label_list
.Add (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
) {
709 private void CreateSignature ()
712 signature
= CreateVarargSignature (RetType
, name
, param_list
);
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) {
730 foreach (ParamDef paramdef
in param_list
) {
732 builder
.Append (',');
733 builder
.Append (paramdef
.TypeName
);
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 ('(');
753 if (param_list
!= null) {
754 foreach (ParamDef paramdef
in param_list
) {
756 builder
.Append (',');
757 builder
.Append (paramdef
.TypeName
);
760 last
= (ParamDef
) param_list
[param_list
.Count
- 1];
764 if (last
== null || !last
.IsSentinel ()) {
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
);
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 ('(');
798 if (param_list
!= null && param_list
.Length
> 0) {
799 foreach (BaseTypeRef param
in param_list
) {
801 builder
.Append (',');
802 builder
.Append (param
.FullName
);
805 if (!include_optional
&& param
is SentinelTypeRef
)
811 if (!include_optional
&& (last
== null || !(last
is SentinelTypeRef
))) {
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) {
835 foreach (BaseTypeRef param
in param_list
) {
837 builder
.Append (',');
838 builder
.Append (param
.FullName
);
840 if (!include_optional
&& param
is SentinelTypeRef
)
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
;