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 FlowBranching current_flow_branching;
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 if (ReturnType == TypeManager.void_type)
358 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
359 Type return_type, int code_flags, bool is_constructor)
360 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
364 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
365 Type return_type, int code_flags)
366 : this (tc, tc, l, ig, return_type, code_flags, false)
370 public FlowBranching CurrentBranching {
372 return current_flow_branching;
377 // Starts a new code branching. This inherits the state of all local
378 // variables and parameters from the current branching.
380 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
382 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
383 return current_flow_branching;
387 // Starts a new code branching for block `block'.
389 public FlowBranching StartFlowBranching (Block block)
391 FlowBranching.BranchingType type;
393 if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
394 type = FlowBranching.BranchingType.SwitchSection;
396 type = FlowBranching.BranchingType.Block;
398 current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
399 return current_flow_branching;
403 // Ends a code branching. Merges the state of locals and parameters
404 // from all the children of the ending branching.
406 public FlowBranching.UsageVector DoEndFlowBranching ()
408 FlowBranching old = current_flow_branching;
409 current_flow_branching = current_flow_branching.Parent;
411 return current_flow_branching.MergeChild (old);
415 // Ends a code branching. Merges the state of locals and parameters
416 // from all the children of the ending branching.
418 public FlowBranching.Reachability EndFlowBranching ()
420 FlowBranching.UsageVector vector = DoEndFlowBranching ();
422 return vector.Reachability;
426 // Kills the current code branching. This throws away any changed state
427 // information and should only be used in case of an error.
429 public void KillFlowBranching ()
431 current_flow_branching = current_flow_branching.Parent;
434 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
436 bool has_ret = false;
438 if (!Location.IsNull (loc))
439 CurrentFile = loc.File;
443 int errors = Report.Errors;
445 block.EmitMeta (this, ip);
447 if (Report.Errors == errors){
448 bool old_do_flow_analysis = DoFlowAnalysis;
449 DoFlowAnalysis = true;
451 current_flow_branching = FlowBranching.CreateBranching (
452 null, FlowBranching.BranchingType.Block, block, loc);
454 if (!block.Resolve (this)) {
455 current_flow_branching = null;
456 DoFlowAnalysis = old_do_flow_analysis;
460 FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
461 current_flow_branching = null;
463 DoFlowAnalysis = old_do_flow_analysis;
467 if ((reachability.Returns == FlowBranching.FlowReturns.Always) ||
468 (reachability.Throws == FlowBranching.FlowReturns.Always) ||
469 (reachability.Reachable == FlowBranching.FlowReturns.Never))
472 } catch (Exception e) {
473 Console.WriteLine ("Exception caught by the compiler while compiling:");
474 Console.WriteLine (" Block that caused the problem begin at: " + loc);
476 if (CurrentBlock != null){
477 Console.WriteLine (" Block being compiled: [{0},{1}]",
478 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
480 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
481 Console.WriteLine (Report.FriendlyStackTrace (e));
483 Environment.Exit (1);
487 if (ReturnType != null && !has_ret){
489 // FIXME: we need full flow analysis to implement this
490 // correctly and emit an error instead of a warning.
494 Report.Error (161, loc, "Not all code paths return a value");
500 ig.MarkLabel (ReturnLabel);
501 if (return_value != null){
502 ig.Emit (OpCodes.Ldloc, return_value);
503 ig.Emit (OpCodes.Ret);
509 if (!has_ret || HasReturnLabel) {
510 ig.Emit (OpCodes.Ret);
511 NeedExplicitReturn = false;
515 // Unfortunately, System.Reflection.Emit automatically emits a leave
516 // to the end of a finally block. This is a problem if no code is
517 // following the try/finally block since we may jump to a point after
518 // the end of the method. As a workaround, emit an explicit ret here.
520 if (NeedExplicitReturn) {
521 if (ReturnType != null)
522 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
523 ig.Emit (OpCodes.Ret);
529 /// This is called immediately before emitting an IL opcode to tell the symbol
530 /// writer to which source line this opcode belongs.
532 public void Mark (Location loc, bool check_file)
534 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
537 if (check_file && (CurrentFile != loc.File))
540 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
544 /// Returns a temporary storage for a variable of type t as
545 /// a local variable in the current body.
547 public LocalBuilder GetTemporaryLocal (Type t)
549 LocalBuilder location = null;
551 if (temporary_storage != null){
552 object o = temporary_storage [t];
555 ArrayList al = (ArrayList) o;
557 for (int i = 0; i < al.Count; i++){
559 location = (LocalBuilder) al [i];
565 location = (LocalBuilder) o;
566 if (location != null)
571 return ig.DeclareLocal (t);
574 public void FreeTemporaryLocal (LocalBuilder b, Type t)
576 if (temporary_storage == null){
577 temporary_storage = new Hashtable ();
578 temporary_storage [t] = b;
581 object o = temporary_storage [t];
583 temporary_storage [t] = b;
587 ArrayList al = (ArrayList) o;
588 for (int i = 0; i < al.Count; i++){
597 ArrayList replacement = new ArrayList ();
599 temporary_storage.Remove (t);
600 temporary_storage [t] = replacement;
604 /// Current loop begin and end labels.
606 public Label LoopBegin, LoopEnd;
609 /// Whether we are inside a loop and break/continue are possible.
614 /// This is incremented each time we enter a try/catch block and
615 /// decremented if we leave it.
617 public int TryCatchLevel;
620 /// The TryCatchLevel at the begin of the current loop.
622 public int LoopBeginTryCatchLevel;
625 /// Default target in a switch statement. Only valid if
628 public Label DefaultTarget;
631 /// If this is non-null, points to the current switch statement
633 public Switch Switch;
636 /// ReturnValue creates on demand the LocalBuilder for the
637 /// return value from the function. By default this is not
638 /// used. This is only required when returns are found inside
639 /// Try or Catch statements.
641 public LocalBuilder TemporaryReturn ()
643 if (return_value == null){
644 return_value = ig.DeclareLocal (ReturnType);
645 ReturnLabel = ig.DefineLabel ();
646 HasReturnLabel = true;
653 // Creates a field `name' with the type `t' on the proxy class
655 public FieldBuilder MapVariable (string name, Type t)
658 return IteratorHandler.Current.MapVariable ("v_", name, t);
661 throw new Exception ("MapVariable for an unknown state");
665 // Invoke this routine to remap a VariableInfo into the
666 // proper MemberAccess expression
668 public Expression RemapLocal (LocalInfo local_info)
670 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
671 fe.InstanceExpression = new ProxyInstance ();
672 return fe.DoResolve (this);
675 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
677 FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
678 fe.InstanceExpression = new ProxyInstance ();
679 return fe.DoResolveLValue (this, right_side);
682 public Expression RemapParameter (int idx)
684 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
685 fe.InstanceExpression = new ProxyInstance ();
686 return fe.DoResolve (this);
689 public Expression RemapParameterLValue (int idx, Expression right_side)
691 FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
692 fe.InstanceExpression = new ProxyInstance ();
693 return fe.DoResolveLValue (this, right_side);
697 // Emits the proper object to address fields on a remapped
698 // variable/parameter to field in anonymous-method/iterator proxy classes.
700 public void EmitThis ()
702 ig.Emit (OpCodes.Ldarg_0);
706 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
708 throw new Exception ("EmitThis for an unknown state");
712 public Expression GetThis (Location loc)
715 if (CurrentBlock != null)
716 my_this = new This (CurrentBlock, loc);
718 my_this = new This (loc);
720 if (!my_this.ResolveBase (this))