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 /// An Emit Context is created for each body of code (from methods,
124 /// properties bodies, indexer bodies or constructor bodies)
126 public class EmitContext {
127 public DeclSpace DeclSpace;
128 public DeclSpace TypeContainer;
129 public ILGenerator ig;
132 /// This variable tracks the `checked' state of the compilation,
133 /// it controls whether we should generate code that does overflow
134 /// checking, or if we generate code that ignores overflows.
136 /// The default setting comes from the command line option to generate
137 /// checked or unchecked code plus any source code changes using the
138 /// checked/unchecked statements or expressions. Contrast this with
139 /// the ConstantCheckState flag.
142 public bool CheckState;
145 /// The constant check state is always set to `true' and cant be changed
146 /// from the command line. The source code can change this setting with
147 /// the `checked' and `unchecked' statements and expressions.
149 public bool ConstantCheckState;
152 /// Whether we are emitting code inside a static or instance method
154 public bool IsStatic;
157 /// Whether we are emitting a field initializer
159 public bool IsFieldInitializer;
162 /// The value that is allowed to be returned or NULL if there is no
165 public Type ReturnType;
168 /// Points to the Type (extracted from the TypeContainer) that
169 /// declares this body of code
171 public Type ContainerType;
174 /// Whether this is generating code for a constructor
176 public bool IsConstructor;
179 /// Whether we're control flow analysis enabled
181 public bool DoFlowAnalysis;
184 /// Keeps track of the Type to LocalBuilder temporary storage created
185 /// to store structures (used to compute the address of the structure
186 /// value on structure method invocations)
188 public Hashtable temporary_storage;
190 public Block CurrentBlock;
192 public int CurrentFile;
195 /// The location where we store the return value.
197 LocalBuilder return_value;
200 /// The location where return has to jump to return the
203 public Label ReturnLabel;
206 /// If we already defined the ReturnLabel
208 public bool HasReturnLabel;
211 /// Whether we are in a Finally block
213 public bool InFinally;
216 /// Whether we are in a Try block
221 /// Whether we are inside an iterator block.
223 public bool InIterator;
226 /// Whether we need an explicit return statement at the end of the method.
228 public bool NeedExplicitReturn;
231 /// Whether remapping of locals, parameters and fields is turned on.
232 /// Used by iterators and anonymous methods.
234 public bool RemapToProxy;
237 /// Whether we are in a Catch block
242 /// Whether we are inside an unsafe block
244 public bool InUnsafe;
247 /// Whether we are in a `fixed' initialization
249 public bool InFixedInitializer;
252 /// Whether we are inside an anonymous method.
254 public bool InAnonymousMethod;
257 /// Location for this EmitContext
262 /// Used to flag that it is ok to define types recursively, as the
263 /// expressions are being evaluated as part of the type lookup
264 /// during the type resolution process
266 public bool ResolvingTypeTree;
269 /// Inside an enum definition, we do not resolve enumeration values
270 /// to their enumerations, but rather to the underlying type/value
271 /// This is so EnumVal + EnumValB can be evaluated.
273 /// There is no "E operator + (E x, E y)", so during an enum evaluation
274 /// we relax the rules
276 public bool InEnumContext;
279 /// If our type container, or the method we are generating code
282 public bool IsGeneric;
284 protected Stack FlowStack;
286 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
287 Type return_type, int code_flags, bool is_constructor)
291 TypeContainer = parent;
293 CheckState = RootContext.Checked;
294 ConstantCheckState = true;
296 IsStatic = (code_flags & Modifiers.STATIC) != 0;
297 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
298 RemapToProxy = InIterator;
299 ReturnType = return_type;
300 IsConstructor = is_constructor;
305 // Can only be null for the ResolveType contexts.
306 ContainerType = parent.TypeBuilder;
307 if (parent.UnsafeContext)
310 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
314 FlowStack = new Stack ();
316 if (ReturnType == TypeManager.void_type)
320 // We should also set it if the method is generic
322 IsGeneric = ds.IsGeneric || ((Modifiers.METHOD_GENERIC) != 0);
325 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
326 Type return_type, int code_flags, bool is_constructor)
327 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
331 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
332 Type return_type, int code_flags)
333 : this (tc, tc, l, ig, return_type, code_flags, false)
337 public FlowBranching CurrentBranching {
339 return (FlowBranching) FlowStack.Peek ();
344 // Starts a new code branching. This inherits the state of all local
345 // variables and parameters from the current branching.
347 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
349 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
351 FlowStack.Push (cfb);
357 // Starts a new code branching for block `block'.
359 public FlowBranching StartFlowBranching (Block block)
362 FlowBranchingType type;
364 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
365 type = FlowBranchingType.SWITCH_SECTION;
367 type = FlowBranchingType.BLOCK;
369 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
371 FlowStack.Push (cfb);
377 // Ends a code branching. Merges the state of locals and parameters
378 // from all the children of the ending branching.
380 public FlowReturns EndFlowBranching ()
382 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
384 return CurrentBranching.MergeChild (cfb);
388 // Kills the current code branching. This throws away any changed state
389 // information and should only be used in case of an error.
391 public void KillFlowBranching ()
393 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
396 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
398 bool has_ret = false;
400 if (!Location.IsNull (loc))
401 CurrentFile = loc.File;
405 int errors = Report.Errors;
407 block.EmitMeta (this, ip, block);
409 if (Report.Errors == errors){
410 bool old_do_flow_analysis = DoFlowAnalysis;
411 DoFlowAnalysis = true;
413 FlowBranching cfb = new FlowBranching (block, loc);
414 FlowStack.Push (cfb);
416 if (!block.Resolve (this)) {
418 DoFlowAnalysis = old_do_flow_analysis;
422 cfb = (FlowBranching) FlowStack.Pop ();
423 FlowReturns returns = cfb.MergeTopBlock ();
425 DoFlowAnalysis = old_do_flow_analysis;
427 has_ret = block.Emit (this);
429 if ((returns == FlowReturns.ALWAYS) ||
430 (returns == FlowReturns.EXCEPTION) ||
431 (returns == FlowReturns.UNREACHABLE))
434 if (Report.Errors == errors){
435 if (RootContext.WarningLevel >= 3)
436 block.UsageWarning ();
440 Console.WriteLine ("Exception caught by the compiler while compiling:");
441 Console.WriteLine (" Block that caused the problem begin at: " + loc);
443 if (CurrentBlock != null){
444 Console.WriteLine (" Block being compiled: [{0},{1}]",
445 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
451 if (ReturnType != null && !has_ret){
453 // FIXME: we need full flow analysis to implement this
454 // correctly and emit an error instead of a warning.
458 Report.Error (161, loc, "Not all code paths return a value");
464 ig.MarkLabel (ReturnLabel);
465 if (return_value != null){
466 ig.Emit (OpCodes.Ldloc, return_value);
467 ig.Emit (OpCodes.Ret);
473 if (!has_ret || HasReturnLabel) {
474 ig.Emit (OpCodes.Ret);
475 NeedExplicitReturn = false;
479 // Unfortunately, System.Reflection.Emit automatically emits a leave
480 // to the end of a finally block. This is a problem if no code is
481 // following the try/finally block since we may jump to a point after
482 // the end of the method. As a workaround, emit an explicit ret here.
484 if (NeedExplicitReturn) {
485 if (ReturnType != null)
486 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
487 ig.Emit (OpCodes.Ret);
493 /// This is called immediately before emitting an IL opcode to tell the symbol
494 /// writer to which source line this opcode belongs.
496 public void Mark (Location loc, bool check_file)
498 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
501 if (check_file && (CurrentFile != loc.File))
504 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
508 /// Returns a temporary storage for a variable of type t as
509 /// a local variable in the current body.
511 public LocalBuilder GetTemporaryStorage (Type t)
513 LocalBuilder location;
515 if (temporary_storage != null){
516 location = (LocalBuilder) temporary_storage [t];
517 if (location != null)
521 location = ig.DeclareLocal (t);
526 public void FreeTemporaryStorage (LocalBuilder b)
532 /// Current loop begin and end labels.
534 public Label LoopBegin, LoopEnd;
537 /// Whether we are inside a loop and break/continue are possible.
542 /// This is incremented each time we enter a try/catch block and
543 /// decremented if we leave it.
545 public int TryCatchLevel;
548 /// The TryCatchLevel at the begin of the current loop.
550 public int LoopBeginTryCatchLevel;
553 /// Default target in a switch statement. Only valid if
556 public Label DefaultTarget;
559 /// If this is non-null, points to the current switch statement
561 public Switch Switch;
564 /// ReturnValue creates on demand the LocalBuilder for the
565 /// return value from the function. By default this is not
566 /// used. This is only required when returns are found inside
567 /// Try or Catch statements.
569 public LocalBuilder TemporaryReturn ()
571 if (return_value == null){
572 return_value = ig.DeclareLocal (ReturnType);
573 ReturnLabel = ig.DefineLabel ();
574 HasReturnLabel = true;
581 // Creates a field `name' with the type `t' on the proxy class
583 public FieldBuilder MapVariable (string name, Type t)
586 return IteratorHandler.Current.MapVariable (name, t);
589 throw new Exception ("MapVariable for an unknown state");
593 // Emits the proper object to address fields on a remapped
594 // variable/parameter to field in anonymous-method/iterator proxy classes.
596 public void EmitThis ()
598 ig.Emit (OpCodes.Ldarg_0);
602 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
604 throw new Exception ("EmitThis for an unknown state");
608 public void EmitArgument (int idx)
611 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.parameter_fields [idx]);
613 throw new Exception ("EmitStoreArgument for an unknown state");
616 public void EmitStoreArgument (int idx)
619 ig.Emit (OpCodes.Stfld, IteratorHandler.Current.parameter_fields [idx]);
621 throw new Exception ("EmitStoreArgument for an unknown state");
624 public Expression GetThis (Location loc)
627 if (CurrentBlock != null)
628 my_this = new This (CurrentBlock, loc);
630 my_this = new This (loc);
632 if (!my_this.ResolveBase (this))