2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Security.Cryptography;
17 using Mono.Security.Cryptography;
19 namespace Mono.CSharp {
22 /// Code generator class.
24 public class CodeGen {
25 static AppDomain current_domain;
26 static public SymbolWriter SymbolWriter;
28 public static AssemblyClass Assembly;
29 public static ModuleClass Module;
33 Assembly = new AssemblyClass ();
34 Module = new ModuleClass (RootContext.Unsafe);
37 public static string Basename (string name)
39 int pos = name.LastIndexOf ('/');
42 return name.Substring (pos + 1);
44 pos = name.LastIndexOf ('\\');
46 return name.Substring (pos + 1);
51 public static string Dirname (string name)
53 int pos = name.LastIndexOf ('/');
56 return name.Substring (0, pos);
58 pos = name.LastIndexOf ('\\');
60 return name.Substring (0, pos);
65 static string TrimExt (string name)
67 int pos = name.LastIndexOf ('.');
69 return name.Substring (0, pos);
72 static public string FileName;
75 // Initializes the symbol writer
77 static void InitializeSymbolWriter ()
79 SymbolWriter = SymbolWriter.GetSymbolWriter (Module.Builder);
82 // If we got an ISymbolWriter instance, initialize it.
84 if (SymbolWriter == null) {
86 -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
92 // Initializes the code generator variables
94 static public void Init (string name, string output, bool want_debugging_support)
97 AssemblyName an = Assembly.GetAssemblyName (name, output);
99 current_domain = AppDomain.CurrentDomain;
102 Assembly.Builder = current_domain.DefineDynamicAssembly (an,
103 AssemblyBuilderAccess.Save, Dirname (name));
105 catch (ArgumentException) {
106 // specified key may not be exportable outside it's container
107 if (RootContext.StrongNameKeyContainer != null) {
108 Report.Error (1548, "Could not access the key inside the container `" +
109 RootContext.StrongNameKeyContainer + "'.");
110 Environment.Exit (1);
114 catch (CryptographicException) {
115 if ((RootContext.StrongNameKeyContainer != null) || (RootContext.StrongNameKeyFile != null)) {
116 Report.Error (1548, "Could not use the specified key to strongname the assembly.");
117 Environment.Exit (1);
123 // Pass a path-less name to DefineDynamicModule. Wonder how
124 // this copes with output in different directories then.
125 // FIXME: figure out how this copes with --output /tmp/blah
127 // If the third argument is true, the ModuleBuilder will dynamically
128 // load the default symbol writer.
130 Module.Builder = Assembly.Builder.DefineDynamicModule (
131 Basename (name), Basename (output), want_debugging_support);
133 if (want_debugging_support)
134 InitializeSymbolWriter ();
137 static public void Save (string name)
140 Assembly.Builder.Save (Basename (name));
141 } catch (System.IO.IOException io){
142 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
148 // Provides "local" store across code that can yield: locals
149 // or fields, notice that this should not be used by anonymous
150 // methods to create local storage, those only require
153 public class VariableStorage {
160 public VariableStorage (EmitContext ec, Type t)
164 fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t);
166 local = ec.ig.DeclareLocal (t);
170 public void EmitThis ()
173 ig.Emit (OpCodes.Ldarg_0);
176 public void EmitStore ()
179 ig.Emit (OpCodes.Stloc, local);
181 ig.Emit (OpCodes.Stfld, fb);
184 public void EmitLoad ()
187 ig.Emit (OpCodes.Ldloc, local);
189 ig.Emit (OpCodes.Ldfld, fb);
192 public void EmitCall (MethodInfo mi)
194 // FIXME : we should handle a call like tostring
195 // here, where boxing is needed. However, we will
196 // never encounter that with the current usage.
198 bool value_type_call;
201 value_type_call = local.LocalType.IsValueType;
204 ig.Emit (OpCodes.Ldloca, local);
206 ig.Emit (OpCodes.Ldloc, local);
208 value_type_call = fb.FieldType.IsValueType;
211 ig.Emit (OpCodes.Ldflda, fb);
213 ig.Emit (OpCodes.Ldfld, fb);
216 ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
221 /// An Emit Context is created for each body of code (from methods,
222 /// properties bodies, indexer bodies or constructor bodies)
224 public class EmitContext {
225 public DeclSpace DeclSpace;
226 public DeclSpace TypeContainer;
227 public ILGenerator ig;
230 /// This variable tracks the `checked' state of the compilation,
231 /// it controls whether we should generate code that does overflow
232 /// checking, or if we generate code that ignores overflows.
234 /// The default setting comes from the command line option to generate
235 /// checked or unchecked code plus any source code changes using the
236 /// checked/unchecked statements or expressions. Contrast this with
237 /// the ConstantCheckState flag.
240 public bool CheckState;
243 /// The constant check state is always set to `true' and cant be changed
244 /// from the command line. The source code can change this setting with
245 /// the `checked' and `unchecked' statements and expressions.
247 public bool ConstantCheckState;
250 /// Whether we are emitting code inside a static or instance method
252 public bool IsStatic;
255 /// Whether we are emitting a field initializer
257 public bool IsFieldInitializer;
260 /// The value that is allowed to be returned or NULL if there is no
263 public Type ReturnType;
266 /// Points to the Type (extracted from the TypeContainer) that
267 /// declares this body of code
269 public Type ContainerType;
272 /// Whether this is generating code for a constructor
274 public bool IsConstructor;
277 /// Whether we're control flow analysis enabled
279 public bool DoFlowAnalysis;
282 /// Keeps track of the Type to LocalBuilder temporary storage created
283 /// to store structures (used to compute the address of the structure
284 /// value on structure method invocations)
286 public Hashtable temporary_storage;
288 public Block CurrentBlock;
290 public int CurrentFile;
293 /// The location where we store the return value.
295 LocalBuilder return_value;
298 /// The location where return has to jump to return the
301 public Label ReturnLabel;
304 /// If we already defined the ReturnLabel
306 public bool HasReturnLabel;
309 /// Whether we are inside an iterator block.
311 public bool InIterator;
313 public bool IsLastStatement;
316 /// Whether remapping of locals, parameters and fields is turned on.
317 /// Used by iterators and anonymous methods.
319 public bool RemapToProxy;
322 /// Whether we are inside an unsafe block
324 public bool InUnsafe;
327 /// Whether we are in a `fixed' initialization
329 public bool InFixedInitializer;
332 /// Whether we are inside an anonymous method.
334 public bool InAnonymousMethod;
337 /// Location for this EmitContext
342 /// Used to flag that it is ok to define types recursively, as the
343 /// expressions are being evaluated as part of the type lookup
344 /// during the type resolution process
346 public bool ResolvingTypeTree;
349 /// Inside an enum definition, we do not resolve enumeration values
350 /// to their enumerations, but rather to the underlying type/value
351 /// This is so EnumVal + EnumValB can be evaluated.
353 /// There is no "E operator + (E x, E y)", so during an enum evaluation
354 /// we relax the rules
356 public bool InEnumContext;
358 FlowBranching current_flow_branching;
360 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
361 Type return_type, int code_flags, bool is_constructor)
365 TypeContainer = parent;
367 CheckState = RootContext.Checked;
368 ConstantCheckState = true;
370 IsStatic = (code_flags & Modifiers.STATIC) != 0;
371 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
372 RemapToProxy = InIterator;
373 ReturnType = return_type;
374 IsConstructor = is_constructor;
379 // Can only be null for the ResolveType contexts.
380 ContainerType = parent.TypeBuilder;
381 if (parent.UnsafeContext)
384 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
388 if (ReturnType == TypeManager.void_type)
392 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
393 Type return_type, int code_flags, bool is_constructor)
394 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
398 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
399 Type return_type, int code_flags)
400 : this (tc, tc, l, ig, return_type, code_flags, false)
404 public FlowBranching CurrentBranching {
406 return current_flow_branching;
411 // Starts a new code branching. This inherits the state of all local
412 // variables and parameters from the current branching.
414 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
416 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
417 return current_flow_branching;
421 // Starts a new code branching for block `block'.
423 public FlowBranching StartFlowBranching (Block block)
425 FlowBranching.BranchingType type;
427 if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
428 type = FlowBranching.BranchingType.SwitchSection;
430 type = FlowBranching.BranchingType.Block;
432 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
433 return current_flow_branching;
437 // Ends a code branching. Merges the state of locals and parameters
438 // from all the children of the ending branching.
440 public FlowBranching.UsageVector DoEndFlowBranching ()
442 FlowBranching old = current_flow_branching;
443 current_flow_branching = current_flow_branching.Parent;
445 return current_flow_branching.MergeChild (old);
449 // Ends a code branching. Merges the state of locals and parameters
450 // from all the children of the ending branching.
452 public FlowBranching.Reachability EndFlowBranching ()
454 FlowBranching.UsageVector vector = DoEndFlowBranching ();
456 return vector.Reachability;
460 // Kills the current code branching. This throws away any changed state
461 // information and should only be used in case of an error.
463 public void KillFlowBranching ()
465 current_flow_branching = current_flow_branching.Parent;
468 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
470 bool unreachable = false;
472 if (!Location.IsNull (loc))
473 CurrentFile = loc.File;
477 int errors = Report.Errors;
479 block.EmitMeta (this, ip);
481 if (Report.Errors == errors){
482 bool old_do_flow_analysis = DoFlowAnalysis;
483 DoFlowAnalysis = true;
485 current_flow_branching = FlowBranching.CreateBranching (
486 null, FlowBranching.BranchingType.Block, block, loc);
488 if (!block.Resolve (this)) {
489 current_flow_branching = null;
490 DoFlowAnalysis = old_do_flow_analysis;
494 FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
495 current_flow_branching = null;
497 DoFlowAnalysis = old_do_flow_analysis;
501 if (reachability.AlwaysReturns ||
502 reachability.AlwaysThrows ||
503 reachability.IsUnreachable)
506 } catch (Exception e) {
507 Console.WriteLine ("Exception caught by the compiler while compiling:");
508 Console.WriteLine (" Block that caused the problem begin at: " + loc);
510 if (CurrentBlock != null){
511 Console.WriteLine (" Block being compiled: [{0},{1}]",
512 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
514 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
515 Console.WriteLine (Report.FriendlyStackTrace (e));
517 Environment.Exit (1);
521 if (ReturnType != null && !unreachable){
523 Report.Error (161, loc, "Not all code paths return a value");
529 ig.MarkLabel (ReturnLabel);
530 if (return_value != null){
531 ig.Emit (OpCodes.Ldloc, return_value);
532 ig.Emit (OpCodes.Ret);
535 // If `HasReturnLabel' is set, then we already emitted a
536 // jump to the end of the method, so we must emit a `ret'
539 // Unfortunately, System.Reflection.Emit automatically emits
540 // a leave to the end of a finally block. This is a problem
541 // if no code is following the try/finally block since we may
542 // jump to a point after the end of the method.
543 // As a workaround, we're always creating a return label in
547 if ((block != null) && block.IsDestructor) {
548 // Nothing to do; S.R.E automatically emits a leave.
549 } else if (HasReturnLabel || (!unreachable && !InIterator)) {
550 if (ReturnType != null)
551 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
552 ig.Emit (OpCodes.Ret);
558 /// This is called immediately before emitting an IL opcode to tell the symbol
559 /// writer to which source line this opcode belongs.
561 public void Mark (Location loc, bool check_file)
563 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
566 if (check_file && (CurrentFile != loc.File))
569 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
573 /// Returns a temporary storage for a variable of type t as
574 /// a local variable in the current body.
576 public LocalBuilder GetTemporaryLocal (Type t)
578 LocalBuilder location = null;
580 if (temporary_storage != null){
581 object o = temporary_storage [t];
584 ArrayList al = (ArrayList) o;
586 for (int i = 0; i < al.Count; i++){
588 location = (LocalBuilder) al [i];
594 location = (LocalBuilder) o;
595 if (location != null)
600 return ig.DeclareLocal (t);
603 public void FreeTemporaryLocal (LocalBuilder b, Type t)
605 if (temporary_storage == null){
606 temporary_storage = new Hashtable ();
607 temporary_storage [t] = b;
610 object o = temporary_storage [t];
612 temporary_storage [t] = b;
616 ArrayList al = (ArrayList) o;
617 for (int i = 0; i < al.Count; i++){
626 ArrayList replacement = new ArrayList ();
628 temporary_storage.Remove (t);
629 temporary_storage [t] = replacement;
633 /// Current loop begin and end labels.
635 public Label LoopBegin, LoopEnd;
638 /// Default target in a switch statement. Only valid if
641 public Label DefaultTarget;
644 /// If this is non-null, points to the current switch statement
646 public Switch Switch;
649 /// ReturnValue creates on demand the LocalBuilder for the
650 /// return value from the function. By default this is not
651 /// used. This is only required when returns are found inside
652 /// Try or Catch statements.
654 public LocalBuilder TemporaryReturn ()
656 if (return_value == null){
657 return_value = ig.DeclareLocal (ReturnType);
658 ReturnLabel = ig.DefineLabel ();
659 HasReturnLabel = true;
665 public void NeedReturnLabel ()
667 if (!HasReturnLabel) {
668 ReturnLabel = ig.DefineLabel ();
669 HasReturnLabel = true;
674 // Creates a field `name' with the type `t' on the proxy class
676 public FieldBuilder MapVariable (string name, Type t)
679 return IteratorHandler.Current.MapVariable ("v_", name, t);
682 throw new Exception ("MapVariable for an unknown state");
686 // Invoke this routine to remap a VariableInfo into the
687 // proper MemberAccess expression
689 public Expression RemapLocal (LocalInfo local_info)
691 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
692 fe.InstanceExpression = new ProxyInstance ();
693 return fe.DoResolve (this);
696 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
698 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
699 fe.InstanceExpression = new ProxyInstance ();
700 return fe.DoResolveLValue (this, right_side);
703 public Expression RemapParameter (int idx)
705 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
706 fe.InstanceExpression = new ProxyInstance ();
707 return fe.DoResolve (this);
710 public Expression RemapParameterLValue (int idx, Expression right_side)
712 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
713 fe.InstanceExpression = new ProxyInstance ();
714 return fe.DoResolveLValue (this, right_side);
718 // Emits the proper object to address fields on a remapped
719 // variable/parameter to field in anonymous-method/iterator proxy classes.
721 public void EmitThis ()
723 ig.Emit (OpCodes.Ldarg_0);
727 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
729 throw new Exception ("EmitThis for an unknown state");
733 public Expression GetThis (Location loc)
736 if (CurrentBlock != null)
737 my_this = new This (CurrentBlock, loc);
739 my_this = new This (loc);
741 if (!my_this.ResolveBase (this))
749 public abstract class CommonAssemblyModulClass: IAttributeSupport {
750 protected Hashtable m_attributes;
752 protected CommonAssemblyModulClass ()
754 m_attributes = new Hashtable ();
758 // Adds a global attribute that was declared in `container',
759 // the attribute is in `attr', and it was defined at `loc'
761 public void AddAttribute (TypeContainer container, AttributeSection attr)
763 NamespaceEntry ns = container.NamespaceEntry;
764 Attributes a = (Attributes) m_attributes [ns];
767 m_attributes [ns] = new Attributes (attr);
771 a.AddAttributeSection (attr);
774 public virtual void Emit ()
776 if (m_attributes.Count < 1)
779 TypeContainer dummy = new TypeContainer ();
780 EmitContext temp_ec = new EmitContext (dummy, Mono.CSharp.Location.Null, null, null, 0, false);
782 foreach (DictionaryEntry de in m_attributes)
784 NamespaceEntry ns = (NamespaceEntry) de.Key;
785 Attributes attrs = (Attributes) de.Value;
787 dummy.NamespaceEntry = ns;
788 Attribute.ApplyAttributes (temp_ec, null, this, attrs);
792 #region IAttributeSupport Members
793 public abstract void SetCustomAttribute(CustomAttributeBuilder customBuilder);
799 public class AssemblyClass: CommonAssemblyModulClass {
800 // TODO: make it private and move all builder based methods here
801 public AssemblyBuilder Builder;
803 bool m_is_cls_compliant;
805 public AssemblyClass (): base ()
807 m_is_cls_compliant = false;
810 public bool IsClsCompliant {
812 return m_is_cls_compliant;
816 public AssemblyName GetAssemblyName (string name, string output)
818 // scan assembly attributes for strongname related attr
819 foreach (DictionaryEntry nsattr in m_attributes) {
820 ArrayList list = ((Attributes)nsattr.Value).AttributeSections;
821 for (int i=0; i < list.Count; i++) {
822 AttributeSection asect = (AttributeSection) list [i];
823 if (asect.Target != "assembly")
825 // strongname attributes don't support AllowMultiple
826 Attribute a = (Attribute) asect.Attributes [0];
828 case "AssemblyKeyFile":
829 if (RootContext.StrongNameKeyFile != null) {
830 Report.Warning (1616, "Compiler option -keyfile overrides " +
831 "AssemblyKeyFileAttribute");
834 string value = a.GetString ();
835 if (value != String.Empty)
836 RootContext.StrongNameKeyFile = value;
839 case "AssemblyKeyName":
840 if (RootContext.StrongNameKeyContainer != null) {
841 Report.Warning (1616, "Compiler option -keycontainer overrides " +
842 "AssemblyKeyNameAttribute");
845 string value = a.GetString ();
846 if (value != String.Empty)
847 RootContext.StrongNameKeyContainer = value;
850 case "AssemblyDelaySign":
851 RootContext.StrongNameDelaySign = a.GetBoolean ();
857 AssemblyName an = new AssemblyName ();
858 an.Name = Path.GetFileNameWithoutExtension (name);
860 // note: delay doesn't apply when using a key container
861 if (RootContext.StrongNameKeyContainer != null) {
862 an.KeyPair = new StrongNameKeyPair (RootContext.StrongNameKeyContainer);
866 // strongname is optional
867 if (RootContext.StrongNameKeyFile == null)
870 string AssemblyDir = Path.GetDirectoryName (output);
872 // the StrongName key file may be relative to (a) the compiled
873 // file or (b) to the output assembly. See bugzilla #55320
874 // http://bugzilla.ximian.com/show_bug.cgi?id=55320
876 // (a) relative to the compiled file
877 string filename = Path.GetFullPath (RootContext.StrongNameKeyFile);
878 bool exist = File.Exists (filename);
879 if ((!exist) && (AssemblyDir != null) && (AssemblyDir != String.Empty)) {
880 // (b) relative to the outputed assembly
881 filename = Path.GetFullPath (Path.Combine (AssemblyDir, RootContext.StrongNameKeyFile));
882 exist = File.Exists (filename);
886 using (FileStream fs = new FileStream (filename, FileMode.Open)) {
887 byte[] snkeypair = new byte [fs.Length];
888 fs.Read (snkeypair, 0, snkeypair.Length);
891 // this will import public or private/public keys
892 RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair);
893 // only the public part must be supplied if signature is delayed
894 byte[] key = CryptoConvert.ToCapiKeyBlob (rsa, !RootContext.StrongNameDelaySign);
895 an.KeyPair = new StrongNameKeyPair (key);
897 catch (CryptographicException) {
898 Report.Error (1548, "Could not strongname the assembly. File `" +
899 RootContext.StrongNameKeyFile + "' incorrectly encoded.");
900 Environment.Exit (1);
905 Report.Error (1548, "Could not strongname the assembly. File `" +
906 RootContext.StrongNameKeyFile + "' not found.");
907 Environment.Exit (1);
912 public override void SetCustomAttribute(CustomAttributeBuilder customBuilder)
914 Builder.SetCustomAttribute (customBuilder);
918 public class ModuleClass: CommonAssemblyModulClass {
919 // TODO: make it private and move all builder based methods here
920 public ModuleBuilder Builder;
922 bool m_module_is_unsafe;
924 public ModuleClass (bool is_unsafe)
926 m_module_is_unsafe = is_unsafe;
929 public override void Emit ()
933 if (!m_module_is_unsafe)
936 if (TypeManager.unverifiable_code_ctor == null) {
937 Console.WriteLine ("Internal error ! Cannot set unverifiable code attribute.");
941 SetCustomAttribute (new CustomAttributeBuilder (TypeManager.unverifiable_code_ctor, new object [0]));
944 public override void SetCustomAttribute(CustomAttributeBuilder customBuilder)
946 Builder.SetCustomAttribute (customBuilder);