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.Save, 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;
324 protected Stack FlowStack;
326 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
327 Type return_type, int code_flags, bool is_constructor)
331 TypeContainer = parent;
333 CheckState = RootContext.Checked;
334 ConstantCheckState = true;
336 IsStatic = (code_flags & Modifiers.STATIC) != 0;
337 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
338 RemapToProxy = InIterator;
339 ReturnType = return_type;
340 IsConstructor = is_constructor;
345 // Can only be null for the ResolveType contexts.
346 ContainerType = parent.TypeBuilder;
347 if (parent.UnsafeContext)
350 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
354 FlowStack = new Stack ();
356 if (ReturnType == TypeManager.void_type)
360 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
361 Type return_type, int code_flags, bool is_constructor)
362 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
366 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
367 Type return_type, int code_flags)
368 : this (tc, tc, l, ig, return_type, code_flags, false)
372 public FlowBranching CurrentBranching {
374 return (FlowBranching) FlowStack.Peek ();
379 // Starts a new code branching. This inherits the state of all local
380 // variables and parameters from the current branching.
382 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
384 FlowBranching cfb = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
386 FlowStack.Push (cfb);
392 // Starts a new code branching for block `block'.
394 public FlowBranching StartFlowBranching (Block block)
397 FlowBranching.BranchingType type;
399 if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
400 type = FlowBranching.BranchingType.SwitchSection;
402 type = FlowBranching.BranchingType.Block;
404 cfb = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
406 FlowStack.Push (cfb);
412 // Ends a code branching. Merges the state of locals and parameters
413 // from all the children of the ending branching.
415 public FlowBranching.UsageVector DoEndFlowBranching ()
417 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
419 return CurrentBranching.MergeChild (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.Reachability EndFlowBranching ()
428 FlowBranching.UsageVector vector = DoEndFlowBranching ();
430 return vector.Reachability;
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 ()
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.Reachability reachability = cfb.MergeTopBlock ();
472 DoFlowAnalysis = old_do_flow_analysis;
476 if ((reachability.Returns == FlowBranching.FlowReturns.Always) ||
477 (reachability.Throws == FlowBranching.FlowReturns.Always) ||
478 (reachability.Reachable == FlowBranching.FlowReturns.Never))
482 Console.WriteLine ("Exception caught by the compiler while compiling:");
483 Console.WriteLine (" Block that caused the problem begin at: " + loc);
485 if (CurrentBlock != null){
486 Console.WriteLine (" Block being compiled: [{0},{1}]",
487 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
493 if (ReturnType != null && !has_ret){
495 // FIXME: we need full flow analysis to implement this
496 // correctly and emit an error instead of a warning.
500 Report.Error (161, loc, "Not all code paths return a value");
506 ig.MarkLabel (ReturnLabel);
507 if (return_value != null){
508 ig.Emit (OpCodes.Ldloc, return_value);
509 ig.Emit (OpCodes.Ret);
515 if (!has_ret || HasReturnLabel) {
516 ig.Emit (OpCodes.Ret);
517 NeedExplicitReturn = false;
521 // Unfortunately, System.Reflection.Emit automatically emits a leave
522 // to the end of a finally block. This is a problem if no code is
523 // following the try/finally block since we may jump to a point after
524 // the end of the method. As a workaround, emit an explicit ret here.
526 if (NeedExplicitReturn) {
527 if (ReturnType != null)
528 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
529 ig.Emit (OpCodes.Ret);
535 /// This is called immediately before emitting an IL opcode to tell the symbol
536 /// writer to which source line this opcode belongs.
538 public void Mark (Location loc, bool check_file)
540 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
543 if (check_file && (CurrentFile != loc.File))
546 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
550 /// Returns a temporary storage for a variable of type t as
551 /// a local variable in the current body.
553 public LocalBuilder GetTemporaryLocal (Type t)
555 LocalBuilder location = null;
557 if (temporary_storage != null){
558 object o = temporary_storage [t];
561 ArrayList al = (ArrayList) o;
563 for (int i = 0; i < al.Count; i++){
565 location = (LocalBuilder) al [i];
571 location = (LocalBuilder) o;
572 if (location != null)
577 return ig.DeclareLocal (t);
580 public void FreeTemporaryLocal (LocalBuilder b, Type t)
582 if (temporary_storage == null){
583 temporary_storage = new Hashtable ();
584 temporary_storage [t] = b;
587 object o = temporary_storage [t];
589 temporary_storage [t] = b;
593 ArrayList al = (ArrayList) o;
594 for (int i = 0; i < al.Count; i++){
603 ArrayList replacement = new ArrayList ();
605 temporary_storage.Remove (t);
606 temporary_storage [t] = replacement;
610 /// Current loop begin and end labels.
612 public Label LoopBegin, LoopEnd;
615 /// Whether we are inside a loop and break/continue are possible.
620 /// This is incremented each time we enter a try/catch block and
621 /// decremented if we leave it.
623 public int TryCatchLevel;
626 /// The TryCatchLevel at the begin of the current loop.
628 public int LoopBeginTryCatchLevel;
631 /// Default target in a switch statement. Only valid if
634 public Label DefaultTarget;
637 /// If this is non-null, points to the current switch statement
639 public Switch Switch;
642 /// ReturnValue creates on demand the LocalBuilder for the
643 /// return value from the function. By default this is not
644 /// used. This is only required when returns are found inside
645 /// Try or Catch statements.
647 public LocalBuilder TemporaryReturn ()
649 if (return_value == null){
650 return_value = ig.DeclareLocal (ReturnType);
651 ReturnLabel = ig.DefineLabel ();
652 HasReturnLabel = true;
659 // Creates a field `name' with the type `t' on the proxy class
661 public FieldBuilder MapVariable (string name, Type t)
664 return IteratorHandler.Current.MapVariable ("v_", name, t);
667 throw new Exception ("MapVariable for an unknown state");
671 // Invoke this routine to remap a VariableInfo into the
672 // proper MemberAccess expression
674 public Expression RemapLocal (LocalInfo local_info)
676 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
677 fe.InstanceExpression = new ProxyInstance ();
678 return fe.DoResolve (this);
681 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
683 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
684 fe.InstanceExpression = new ProxyInstance ();
685 return fe.DoResolveLValue (this, right_side);
688 public Expression RemapParameter (int idx)
690 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
691 fe.InstanceExpression = new ProxyInstance ();
692 return fe.DoResolve (this);
695 public Expression RemapParameterLValue (int idx, Expression right_side)
697 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
698 fe.InstanceExpression = new ProxyInstance ();
699 return fe.DoResolveLValue (this, right_side);
703 // Emits the proper object to address fields on a remapped
704 // variable/parameter to field in anonymous-method/iterator proxy classes.
706 public void EmitThis ()
708 ig.Emit (OpCodes.Ldarg_0);
712 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
714 throw new Exception ("EmitThis for an unknown state");
718 public Expression GetThis (Location loc)
721 if (CurrentBlock != null)
722 my_this = new This (CurrentBlock, loc);
724 my_this = new This (loc);
726 if (!my_this.ResolveBase (this))