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;
16 namespace Mono.CSharp {
19 /// Code generator class.
21 public class CodeGen {
22 static AppDomain current_domain;
23 public static AssemblyBuilder AssemblyBuilder;
24 public static ModuleBuilder ModuleBuilder;
26 static public SymbolWriter SymbolWriter;
28 public static string Basename (string name)
30 int pos = name.LastIndexOf ("/");
33 return name.Substring (pos + 1);
35 pos = name.LastIndexOf ("\\");
37 return name.Substring (pos + 1);
42 public static string Dirname (string name)
44 int pos = name.LastIndexOf ("/");
47 return name.Substring (0, pos);
49 pos = name.LastIndexOf ("\\");
51 return name.Substring (0, pos);
56 static string TrimExt (string name)
58 int pos = name.LastIndexOf (".");
60 return name.Substring (0, pos);
63 static public string FileName;
66 // Initializes the symbol writer
68 static void InitializeSymbolWriter ()
70 SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
73 // If we got an ISymbolWriter instance, initialize it.
75 if (SymbolWriter == null) {
77 -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.");
83 // Initializes the code generator variables
85 static public void Init (string name, string output, bool want_debugging_support)
90 an = new AssemblyName ();
91 an.Name = Path.GetFileNameWithoutExtension (name);
93 current_domain = AppDomain.CurrentDomain;
94 AssemblyBuilder = current_domain.DefineDynamicAssembly (
95 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
98 // Pass a path-less name to DefineDynamicModule. Wonder how
99 // this copes with output in different directories then.
100 // FIXME: figure out how this copes with --output /tmp/blah
102 // If the third argument is true, the ModuleBuilder will dynamically
103 // load the default symbol writer.
105 ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
106 Basename (name), Basename (output), want_debugging_support);
108 if (want_debugging_support)
109 InitializeSymbolWriter ();
112 static public void Save (string name)
115 AssemblyBuilder.Save (Basename (name));
116 } catch (System.IO.IOException io){
117 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
123 // Provides "local" store across code that can yield: locals
124 // or fields, notice that this should not be used by anonymous
125 // methods to create local storage, those only require
128 public class VariableStorage {
135 public VariableStorage (EmitContext ec, Type t)
139 fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t);
141 local = ec.ig.DeclareLocal (t);
145 public void EmitThis ()
148 ig.Emit (OpCodes.Ldarg_0);
151 public void EmitStore ()
154 ig.Emit (OpCodes.Stloc, local);
156 ig.Emit (OpCodes.Stfld, fb);
159 public void EmitLoad ()
162 ig.Emit (OpCodes.Ldloc, local);
164 ig.Emit (OpCodes.Ldfld, fb);
169 /// An Emit Context is created for each body of code (from methods,
170 /// properties bodies, indexer bodies or constructor bodies)
172 public class EmitContext {
173 public DeclSpace DeclSpace;
174 public DeclSpace TypeContainer;
175 public ILGenerator ig;
178 /// This variable tracks the `checked' state of the compilation,
179 /// it controls whether we should generate code that does overflow
180 /// checking, or if we generate code that ignores overflows.
182 /// The default setting comes from the command line option to generate
183 /// checked or unchecked code plus any source code changes using the
184 /// checked/unchecked statements or expressions. Contrast this with
185 /// the ConstantCheckState flag.
188 public bool CheckState;
191 /// The constant check state is always set to `true' and cant be changed
192 /// from the command line. The source code can change this setting with
193 /// the `checked' and `unchecked' statements and expressions.
195 public bool ConstantCheckState;
198 /// Whether we are emitting code inside a static or instance method
200 public bool IsStatic;
203 /// Whether we are emitting a field initializer
205 public bool IsFieldInitializer;
208 /// The value that is allowed to be returned or NULL if there is no
211 public Type ReturnType;
214 /// Points to the Type (extracted from the TypeContainer) that
215 /// declares this body of code
217 public Type ContainerType;
220 /// Whether this is generating code for a constructor
222 public bool IsConstructor;
225 /// Whether we're control flow analysis enabled
227 public bool DoFlowAnalysis;
230 /// Keeps track of the Type to LocalBuilder temporary storage created
231 /// to store structures (used to compute the address of the structure
232 /// value on structure method invocations)
234 public Hashtable temporary_storage;
236 public Block CurrentBlock;
238 public int CurrentFile;
241 /// The location where we store the return value.
243 LocalBuilder return_value;
246 /// The location where return has to jump to return the
249 public Label ReturnLabel;
252 /// If we already defined the ReturnLabel
254 public bool HasReturnLabel;
257 /// Whether we are in a Finally block
259 public bool InFinally;
262 /// Whether we are in a Try block
267 /// Whether we are inside an iterator block.
269 public bool InIterator;
272 /// Whether we need an explicit return statement at the end of the method.
274 public bool NeedExplicitReturn;
277 /// Whether remapping of locals, parameters and fields is turned on.
278 /// Used by iterators and anonymous methods.
280 public bool RemapToProxy;
283 /// Whether we are in a Catch block
288 /// Whether we are inside an unsafe block
290 public bool InUnsafe;
293 /// Whether we are in a `fixed' initialization
295 public bool InFixedInitializer;
298 /// Whether we are inside an anonymous method.
300 public bool InAnonymousMethod;
303 /// Location for this EmitContext
308 /// Used to flag that it is ok to define types recursively, as the
309 /// expressions are being evaluated as part of the type lookup
310 /// during the type resolution process
312 public bool ResolvingTypeTree;
315 /// Inside an enum definition, we do not resolve enumeration values
316 /// to their enumerations, but rather to the underlying type/value
317 /// This is so EnumVal + EnumValB can be evaluated.
319 /// There is no "E operator + (E x, E y)", so during an enum evaluation
320 /// we relax the rules
322 public bool InEnumContext;
325 /// If our type container, or the method we are generating code
328 public bool IsGeneric;
330 protected Stack FlowStack;
332 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
333 Type return_type, int code_flags, bool is_constructor)
337 TypeContainer = parent;
339 CheckState = RootContext.Checked;
340 ConstantCheckState = true;
342 IsStatic = (code_flags & Modifiers.STATIC) != 0;
343 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
344 RemapToProxy = InIterator;
345 ReturnType = return_type;
346 IsConstructor = is_constructor;
351 // Can only be null for the ResolveType contexts.
352 ContainerType = parent.TypeBuilder;
353 if (parent.UnsafeContext)
356 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
360 FlowStack = new Stack ();
362 if (ReturnType == TypeManager.void_type)
366 // We should also set it if the method is generic
368 IsGeneric = ds.IsGeneric || ((code_flags & Modifiers.METHOD_GENERIC) != 0);
371 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
372 Type return_type, int code_flags, bool is_constructor)
373 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
377 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
378 Type return_type, int code_flags)
379 : this (tc, tc, l, ig, return_type, code_flags, false)
383 public FlowBranching CurrentBranching {
385 return (FlowBranching) FlowStack.Peek ();
390 // Starts a new code branching. This inherits the state of all local
391 // variables and parameters from the current branching.
393 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
395 FlowBranching cfb = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
397 FlowStack.Push (cfb);
403 // Starts a new code branching for block `block'.
405 public FlowBranching StartFlowBranching (Block block)
408 FlowBranching.BranchingType type;
410 if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
411 type = FlowBranching.BranchingType.SwitchSection;
413 type = FlowBranching.BranchingType.Block;
415 cfb = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
417 FlowStack.Push (cfb);
423 // Ends a code branching. Merges the state of locals and parameters
424 // from all the children of the ending branching.
426 public FlowBranching.FlowReturns EndFlowBranching ()
428 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
430 return CurrentBranching.MergeChild (cfb);
434 // Kills the current code branching. This throws away any changed state
435 // information and should only be used in case of an error.
437 public void KillFlowBranching ()
439 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
442 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
444 bool has_ret = false;
446 if (!Location.IsNull (loc))
447 CurrentFile = loc.File;
451 int errors = Report.Errors;
453 block.EmitMeta (this, ip);
455 if (Report.Errors == errors){
456 bool old_do_flow_analysis = DoFlowAnalysis;
457 DoFlowAnalysis = true;
459 FlowBranching cfb = FlowBranching.CreateBranching (
460 null, FlowBranching.BranchingType.Block, block, loc);
461 FlowStack.Push (cfb);
463 if (!block.Resolve (this)) {
465 DoFlowAnalysis = old_do_flow_analysis;
469 cfb = (FlowBranching) FlowStack.Pop ();
470 FlowBranching.FlowReturns returns = cfb.MergeTopBlock ();
472 DoFlowAnalysis = old_do_flow_analysis;
474 has_ret = block.Emit (this);
476 if ((returns == FlowBranching.FlowReturns.Always) ||
477 (returns == FlowBranching.FlowReturns.Exception) ||
478 (returns == FlowBranching.FlowReturns.Unreachable))
481 if (Report.Errors == errors){
482 if (RootContext.WarningLevel >= 3)
483 block.UsageWarning ();
487 Console.WriteLine ("Exception caught by the compiler while compiling:");
488 Console.WriteLine (" Block that caused the problem begin at: " + loc);
490 if (CurrentBlock != null){
491 Console.WriteLine (" Block being compiled: [{0},{1}]",
492 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
498 if (ReturnType != null && !has_ret){
500 // FIXME: we need full flow analysis to implement this
501 // correctly and emit an error instead of a warning.
505 Report.Error (161, loc, "Not all code paths return a value");
511 ig.MarkLabel (ReturnLabel);
512 if (return_value != null){
513 ig.Emit (OpCodes.Ldloc, return_value);
514 ig.Emit (OpCodes.Ret);
520 if (!has_ret || HasReturnLabel) {
521 ig.Emit (OpCodes.Ret);
522 NeedExplicitReturn = false;
526 // Unfortunately, System.Reflection.Emit automatically emits a leave
527 // to the end of a finally block. This is a problem if no code is
528 // following the try/finally block since we may jump to a point after
529 // the end of the method. As a workaround, emit an explicit ret here.
531 if (NeedExplicitReturn) {
532 if (ReturnType != null)
533 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
534 ig.Emit (OpCodes.Ret);
540 /// This is called immediately before emitting an IL opcode to tell the symbol
541 /// writer to which source line this opcode belongs.
543 public void Mark (Location loc, bool check_file)
545 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
548 if (check_file && (CurrentFile != loc.File))
551 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
555 /// Returns a temporary storage for a variable of type t as
556 /// a local variable in the current body.
558 public LocalBuilder GetTemporaryLocal (Type t)
560 LocalBuilder location = null;
562 if (temporary_storage != null){
563 object o = temporary_storage [t];
566 ArrayList al = (ArrayList) o;
568 for (int i = 0; i < al.Count; i++){
570 location = (LocalBuilder) al [i];
576 location = (LocalBuilder) o;
577 if (location != null)
582 return ig.DeclareLocal (t);
585 public void FreeTemporaryLocal (LocalBuilder b, Type t)
587 if (temporary_storage == null){
588 temporary_storage = new Hashtable ();
589 temporary_storage [t] = b;
592 object o = temporary_storage [t];
594 temporary_storage [t] = b;
598 ArrayList al = (ArrayList) o;
599 for (int i = 0; i < al.Count; i++){
608 ArrayList replacement = new ArrayList ();
610 temporary_storage.Remove (t);
611 temporary_storage [t] = replacement;
615 /// Current loop begin and end labels.
617 public Label LoopBegin, LoopEnd;
620 /// Whether we are inside a loop and break/continue are possible.
625 /// This is incremented each time we enter a try/catch block and
626 /// decremented if we leave it.
628 public int TryCatchLevel;
631 /// The TryCatchLevel at the begin of the current loop.
633 public int LoopBeginTryCatchLevel;
636 /// Default target in a switch statement. Only valid if
639 public Label DefaultTarget;
642 /// If this is non-null, points to the current switch statement
644 public Switch Switch;
647 /// ReturnValue creates on demand the LocalBuilder for the
648 /// return value from the function. By default this is not
649 /// used. This is only required when returns are found inside
650 /// Try or Catch statements.
652 public LocalBuilder TemporaryReturn ()
654 if (return_value == null){
655 return_value = ig.DeclareLocal (ReturnType);
656 ReturnLabel = ig.DefineLabel ();
657 HasReturnLabel = true;
664 // Creates a field `name' with the type `t' on the proxy class
666 public FieldBuilder MapVariable (string name, Type t)
669 return IteratorHandler.Current.MapVariable ("v_", name, t);
672 throw new Exception ("MapVariable for an unknown state");
676 // Invoke this routine to remap a VariableInfo into the
677 // proper MemberAccess expression
679 public Expression RemapLocal (LocalInfo local_info)
681 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
682 fe.InstanceExpression = new ProxyInstance ();
683 return fe.DoResolve (this);
686 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
688 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
689 fe.InstanceExpression = new ProxyInstance ();
690 return fe.DoResolveLValue (this, right_side);
693 public Expression RemapParameter (int idx)
695 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
696 fe.InstanceExpression = new ProxyInstance ();
697 return fe.DoResolve (this);
700 public Expression RemapParameterLValue (int idx, Expression right_side)
702 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
703 fe.InstanceExpression = new ProxyInstance ();
704 return fe.DoResolveLValue (this, right_side);
708 // Emits the proper object to address fields on a remapped
709 // variable/parameter to field in anonymous-method/iterator proxy classes.
711 public void EmitThis ()
713 ig.Emit (OpCodes.Ldarg_0);
717 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
719 throw new Exception ("EmitThis for an unknown state");
723 public Expression GetThis (Location loc)
726 if (CurrentBlock != null)
727 my_this = new This (CurrentBlock, loc);
729 my_this = new This (loc);
731 if (!my_this.ResolveBase (this))