2 // codegen.cs: The code generator
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
11 using System.Collections;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Diagnostics.SymbolStore;
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 ISymbolWriter 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 // This routine initializes the Mono runtime SymbolWriter.
68 static bool InitMonoSymbolWriter (string basename, string output_file,
71 string symbol_output = basename + ".dbg";
73 Type itype = SymbolWriter.GetType ();
77 Type[] arg_types = new Type [3];
78 arg_types [0] = typeof (string);
79 arg_types [1] = typeof (string);
80 arg_types [2] = typeof (string[]);
82 MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
83 if (initialize == null)
86 object[] args = new object [3];
87 args [0] = output_file;
88 args [1] = symbol_output;
89 args [2] = debug_args;
91 initialize.Invoke (SymbolWriter, args);
96 // Initializes the symbol writer
98 static void InitializeSymbolWriter (string basename, string output_file,
101 SymbolWriter = ModuleBuilder.GetSymWriter ();
104 // If we got an ISymbolWriter instance, initialize it.
106 if (SymbolWriter == null) {
108 -18, "Cannot find any symbol writer");
113 // Due to lacking documentation about the first argument of the
114 // Initialize method, we cannot use Microsoft's default symbol
117 // If we're using the mono symbol writer, the SymbolWriter object
118 // is of type MonoSymbolWriter - but that's defined in a dynamically
119 // loaded DLL - that's why we're doing a comparision based on the type
120 // name here instead of using `SymbolWriter is MonoSymbolWriter'.
122 Type sym_type = ((object) SymbolWriter).GetType ();
124 switch (sym_type.Name){
125 case "MonoSymbolWriter":
126 if (!InitMonoSymbolWriter (basename, output_file, args))
128 -18, "Cannot initialize the symbol writer");
133 -18, "Cannot generate debugging information on this platform");
139 // Initializes the code generator variables
141 static public void Init (string name, string output, bool want_debugging_support,
147 an = new AssemblyName ();
148 an.Name = TrimExt (Basename (name));
149 current_domain = AppDomain.CurrentDomain;
150 AssemblyBuilder = current_domain.DefineDynamicAssembly (
151 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
154 // Pass a path-less name to DefineDynamicModule. Wonder how
155 // this copes with output in different directories then.
156 // FIXME: figure out how this copes with --output /tmp/blah
158 // If the third argument is true, the ModuleBuilder will dynamically
159 // load the default symbol writer.
161 ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
162 Basename (name), Basename (output), want_debugging_support);
164 if (want_debugging_support) {
165 int pos = output.LastIndexOf (".");
169 basename = output.Substring (0, pos);
173 InitializeSymbolWriter (basename, output, debug_args);
177 static public void Save (string name)
180 AssemblyBuilder.Save (Basename (name));
181 } catch (System.IO.IOException io){
182 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
186 static public void SaveSymbols ()
188 if (SymbolWriter != null) {
189 // If we have a symbol writer, call its Close() method to write
190 // the symbol file to disk.
192 // When using Mono's default symbol writer, the Close() method must
193 // be called after the assembly has already been written to disk since
194 // it opens the assembly and reads its metadata.
195 SymbolWriter.Close ();
201 /// An Emit Context is created for each body of code (from methods,
202 /// properties bodies, indexer bodies or constructor bodies)
204 public class EmitContext {
205 public DeclSpace DeclSpace;
206 public TypeContainer TypeContainer;
207 public ILGenerator ig;
210 /// This variable tracks the `checked' state of the compilation,
211 /// it controls whether we should generate code that does overflow
212 /// checking, or if we generate code that ignores overflows.
214 /// The default setting comes from the command line option to generate
215 /// checked or unchecked code plus any source code changes using the
216 /// checked/unchecked statements or expressions. Contrast this with
217 /// the ConstantCheckState flag.
220 public bool CheckState;
223 /// The constant check state is always set to `true' and cant be changed
224 /// from the command line. The source code can change this setting with
225 /// the `checked' and `unchecked' statements and expressions.
227 public bool ConstantCheckState;
230 /// Whether we are emitting code inside a static or instance method
232 public bool IsStatic;
235 /// Whether we are emitting a field initializer
237 public bool IsFieldInitializer;
240 /// The value that is allowed to be returned or NULL if there is no
243 public Type ReturnType;
246 /// Points to the Type (extracted from the TypeContainer) that
247 /// declares this body of code
249 public Type ContainerType;
252 /// Whether this is generating code for a constructor
254 public bool IsConstructor;
257 /// Whether we're control flow analysis enabled
259 public bool DoFlowAnalysis;
262 /// Keeps track of the Type to LocalBuilder temporary storage created
263 /// to store structures (used to compute the address of the structure
264 /// value on structure method invocations)
266 public Hashtable temporary_storage;
268 public Block CurrentBlock;
271 /// The location where we store the return value.
273 LocalBuilder return_value;
276 /// The location where return has to jump to return the
279 public Label ReturnLabel;
282 /// If we already defined the ReturnLabel
284 public bool HasReturnLabel;
287 /// Whether we are in a Finally block
289 public bool InFinally;
292 /// Whether we are in a Try block
297 /// Whether we are in a Catch block
302 /// Whether we are inside an unsafe block
304 public bool InUnsafe;
307 /// Whether we break from a loop or not
312 /// Location for this EmitContext
317 /// Used to flag that it is ok to define types recursively, as the
318 /// expressions are being evaluated as part of the type lookup
319 /// during the type resolution process
321 public bool ResolvingTypeTree;
324 /// Inside an enum definition, we do not resolve enumeration values
325 /// to their enumerations, but rather to the underlying type/value
326 /// This is so EnumVal + EnumValB can be evaluated.
328 /// There is no "E operator + (E x, E y)", so during an enum evaluation
329 /// we relax the rules
331 public bool InEnumContext;
333 protected Stack FlowStack;
335 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
336 Type return_type, int code_flags, bool is_constructor)
340 TypeContainer = parent;
342 CheckState = RootContext.Checked;
343 ConstantCheckState = true;
345 IsStatic = (code_flags & Modifiers.STATIC) != 0;
346 ReturnType = return_type;
347 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 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
367 Type return_type, int code_flags, bool is_constructor)
368 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
372 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
373 Type return_type, int code_flags)
374 : this (tc, tc, l, ig, return_type, code_flags, false)
378 public FlowBranching CurrentBranching {
380 return (FlowBranching) FlowStack.Peek ();
385 // Starts a new code branching. This inherits the state of all local
386 // variables and parameters from the current branching.
388 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
390 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
392 FlowStack.Push (cfb);
398 // Starts a new code branching for block `block'.
400 public FlowBranching StartFlowBranching (Block block)
403 FlowBranchingType type;
405 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
406 type = FlowBranchingType.SWITCH_SECTION;
408 type = FlowBranchingType.BLOCK;
410 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
412 FlowStack.Push (cfb);
418 // Ends a code branching. Merges the state of locals and parameters
419 // from all the children of the ending branching.
421 public FlowReturns EndFlowBranching ()
423 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
425 return CurrentBranching.MergeChild (cfb);
429 // Kills the current code branching. This throws away any changed state
430 // information and should only be used in case of an error.
432 public void KillFlowBranching ()
434 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
438 // Checks whether the local variable `vi' is already initialized
439 // at the current point of the method's control flow.
440 // If this method returns false, the caller must report an
443 public bool IsVariableAssigned (VariableInfo vi)
446 return CurrentBranching.IsVariableAssigned (vi);
452 // Marks the local variable `vi' as being initialized at the current
453 // current point of the method's control flow.
455 public void SetVariableAssigned (VariableInfo vi)
458 CurrentBranching.SetVariableAssigned (vi);
462 // Checks whether the parameter `number' is already initialized
463 // at the current point of the method's control flow.
464 // If this method returns false, the caller must report an
465 // error 165. This is only necessary for `out' parameters and the
466 // call will always succeed for non-`out' parameters.
468 public bool IsParameterAssigned (int number)
471 return CurrentBranching.IsParameterAssigned (number);
477 // Marks the parameter `number' as being initialized at the current
478 // current point of the method's control flow. This is only necessary
479 // for `out' parameters.
481 public void SetParameterAssigned (int number)
484 CurrentBranching.SetParameterAssigned (number);
487 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
489 bool has_ret = false;
491 // Console.WriteLine ("Emitting: " + loc);
493 if (CodeGen.SymbolWriter != null)
497 int errors = Report.Errors;
499 block.EmitMeta (this, block);
501 if (Report.Errors == errors){
502 bool old_do_flow_analysis = DoFlowAnalysis;
503 DoFlowAnalysis = true;
505 FlowBranching cfb = new FlowBranching (block, ip, loc);
506 FlowStack.Push (cfb);
508 if (!block.Resolve (this)) {
510 DoFlowAnalysis = old_do_flow_analysis;
514 cfb = (FlowBranching) FlowStack.Pop ();
515 cfb.MergeTopBlock ();
517 DoFlowAnalysis = old_do_flow_analysis;
519 has_ret = block.Emit (this);
521 if (Report.Errors == errors){
522 if (RootContext.WarningLevel >= 3)
523 block.UsageWarning ();
528 if (ReturnType != null && !has_ret){
530 // FIXME: we need full flow analysis to implement this
531 // correctly and emit an error instead of a warning.
534 Report.Error (161, loc, "Not all code paths return a value");
539 ig.MarkLabel (ReturnLabel);
540 if (return_value != null){
541 ig.Emit (OpCodes.Ldloc, return_value);
542 ig.Emit (OpCodes.Ret);
545 if (!has_ret || HasReturnLabel)
546 ig.Emit (OpCodes.Ret);
552 /// This is called immediately before emitting an IL opcode to tell the symbol
553 /// writer to which source line this opcode belongs.
555 public void Mark (Location loc)
557 if (!Location.IsNull (loc)) {
558 ISymbolDocumentWriter doc = loc.SymbolDocument;
561 ig.MarkSequencePoint (doc, loc.Row, 0, loc.Row, 0);
566 /// Returns a temporary storage for a variable of type t as
567 /// a local variable in the current body.
569 public LocalBuilder GetTemporaryStorage (Type t)
571 LocalBuilder location;
573 if (temporary_storage != null){
574 location = (LocalBuilder) temporary_storage [t];
575 if (location != null)
579 location = ig.DeclareLocal (t);
584 public void FreeTemporaryStorage (LocalBuilder b)
590 /// Current loop begin and end labels.
592 public Label LoopBegin, LoopEnd;
595 /// Whether we are inside a loop and break/continue are possible.
600 /// This is incremented each time we enter a try/catch block and
601 /// decremented if we leave it.
603 public int TryCatchLevel;
606 /// The TryCatchLevel at the begin of the current loop.
608 public int LoopBeginTryCatchLevel;
611 /// Default target in a switch statement. Only valid if
614 public Label DefaultTarget;
617 /// If this is non-null, points to the current switch statement
619 public Switch Switch;
622 /// ReturnValue creates on demand the LocalBuilder for the
623 /// return value from the function. By default this is not
624 /// used. This is only required when returns are found inside
625 /// Try or Catch statements.
627 public LocalBuilder TemporaryReturn ()
629 if (return_value == null){
630 return_value = ig.DeclareLocal (ReturnType);
631 ReturnLabel = ig.DefineLabel ();
632 HasReturnLabel = true;
639 /// A dynamic This that is shared by all variables in a emitcontext.
640 /// Created on demand.
642 public Expression my_this;
643 public Expression This {
645 if (my_this == null) {
646 if (CurrentBlock != null)
647 my_this = new This (CurrentBlock, loc);
649 my_this = new This (loc);
651 my_this = my_this.Resolve (this);