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;
15 using System.Diagnostics.SymbolStore;
17 namespace Mono.CSharp {
20 /// Code generator class.
22 public class CodeGen {
23 static AppDomain current_domain;
24 public static AssemblyBuilder AssemblyBuilder;
25 public static ModuleBuilder ModuleBuilder;
27 static public ISymbolWriter SymbolWriter;
29 public static string Basename (string name)
31 int pos = name.LastIndexOf ("/");
34 return name.Substring (pos + 1);
36 pos = name.LastIndexOf ("\\");
38 return name.Substring (pos + 1);
43 public static string Dirname (string name)
45 int pos = name.LastIndexOf ("/");
48 return name.Substring (0, pos);
50 pos = name.LastIndexOf ("\\");
52 return name.Substring (0, pos);
57 static string TrimExt (string name)
59 int pos = name.LastIndexOf (".");
61 return name.Substring (0, pos);
64 static public string FileName;
67 // This routine initializes the Mono runtime SymbolWriter.
69 static bool InitMonoSymbolWriter (string basename, string symbol_output,
70 string exe_output_file, string[] debug_args)
72 Type itype = SymbolWriter.GetType ();
76 Type[] arg_types = new Type [3];
77 arg_types [0] = typeof (string);
78 arg_types [1] = typeof (string);
79 arg_types [2] = typeof (string[]);
81 MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
82 if (initialize == null)
85 object[] args = new object [3];
86 args [0] = exe_output_file;
87 args [1] = symbol_output;
88 args [2] = debug_args;
90 initialize.Invoke (SymbolWriter, args);
95 // Initializes the symbol writer
97 static void InitializeSymbolWriter (string basename, string symbol_output,
98 string exe_output_file, string[] args)
100 SymbolWriter = ModuleBuilder.GetSymWriter ();
103 // If we got an ISymbolWriter instance, initialize it.
105 if (SymbolWriter == null) {
107 -18, "Cannot find any symbol writer");
112 // Due to lacking documentation about the first argument of the
113 // Initialize method, we cannot use Microsoft's default symbol
116 // If we're using the mono symbol writer, the SymbolWriter object
117 // is of type MonoSymbolWriter - but that's defined in a dynamically
118 // loaded DLL - that's why we're doing a comparision based on the type
119 // name here instead of using `SymbolWriter is MonoSymbolWriter'.
121 Type sym_type = ((object) SymbolWriter).GetType ();
123 switch (sym_type.Name){
124 case "MonoSymbolWriter":
125 if (!InitMonoSymbolWriter (basename, symbol_output,
126 exe_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 int pos = output.LastIndexOf (".");
168 basename = output.Substring (0, pos);
172 string symbol_output = basename + ".dbg";
174 if (want_debugging_support)
175 InitializeSymbolWriter (basename, symbol_output, output, debug_args);
178 File.Delete (symbol_output);
185 static public void Save (string name)
188 AssemblyBuilder.Save (Basename (name));
189 } catch (System.IO.IOException io){
190 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
194 static public void SaveSymbols ()
196 if (SymbolWriter != null) {
197 // If we have a symbol writer, call its Close() method to write
198 // the symbol file to disk.
200 // When using Mono's default symbol writer, the Close() method must
201 // be called after the assembly has already been written to disk since
202 // it opens the assembly and reads its metadata.
203 SymbolWriter.Close ();
209 /// An Emit Context is created for each body of code (from methods,
210 /// properties bodies, indexer bodies or constructor bodies)
212 public class EmitContext {
213 public DeclSpace DeclSpace;
214 public TypeContainer TypeContainer;
215 public ILGenerator ig;
218 /// This variable tracks the `checked' state of the compilation,
219 /// it controls whether we should generate code that does overflow
220 /// checking, or if we generate code that ignores overflows.
222 /// The default setting comes from the command line option to generate
223 /// checked or unchecked code plus any source code changes using the
224 /// checked/unchecked statements or expressions. Contrast this with
225 /// the ConstantCheckState flag.
228 public bool CheckState;
231 /// The constant check state is always set to `true' and cant be changed
232 /// from the command line. The source code can change this setting with
233 /// the `checked' and `unchecked' statements and expressions.
235 public bool ConstantCheckState;
238 /// Whether we are emitting code inside a static or instance method
240 public bool IsStatic;
243 /// Whether we are emitting a field initializer
245 public bool IsFieldInitializer;
248 /// The value that is allowed to be returned or NULL if there is no
251 public Type ReturnType;
254 /// Points to the Type (extracted from the TypeContainer) that
255 /// declares this body of code
257 public Type ContainerType;
260 /// Whether this is generating code for a constructor
262 public bool IsConstructor;
265 /// Whether we're control flow analysis enabled
267 public bool DoFlowAnalysis;
270 /// Keeps track of the Type to LocalBuilder temporary storage created
271 /// to store structures (used to compute the address of the structure
272 /// value on structure method invocations)
274 public Hashtable temporary_storage;
276 public Block CurrentBlock;
279 /// The location where we store the return value.
281 LocalBuilder return_value;
284 /// The location where return has to jump to return the
287 public Label ReturnLabel;
290 /// If we already defined the ReturnLabel
292 public bool HasReturnLabel;
295 /// Whether we are in a Finally block
297 public bool InFinally;
300 /// Whether we are in a Try block
305 /// Whether we are in a Catch block
310 /// Whether we are inside an unsafe block
312 public bool InUnsafe;
315 /// Whether we are inside an unsafe block
317 public bool InvokingOwnOverload;
320 /// Location for this EmitContext
325 /// Used to flag that it is ok to define types recursively, as the
326 /// expressions are being evaluated as part of the type lookup
327 /// during the type resolution process
329 public bool ResolvingTypeTree;
332 /// Inside an enum definition, we do not resolve enumeration values
333 /// to their enumerations, but rather to the underlying type/value
334 /// This is so EnumVal + EnumValB can be evaluated.
336 /// There is no "E operator + (E x, E y)", so during an enum evaluation
337 /// we relax the rules
339 public bool InEnumContext;
341 public string BlockName;
343 protected Stack FlowStack;
345 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
346 Type return_type, int code_flags, bool is_constructor)
350 TypeContainer = parent;
352 CheckState = RootContext.Checked;
353 ConstantCheckState = true;
355 IsStatic = (code_flags & Modifiers.STATIC) != 0;
356 ReturnType = return_type;
357 IsConstructor = is_constructor;
360 InvokingOwnOverload = false;
363 // Can only be null for the ResolveType contexts.
364 ContainerType = parent.TypeBuilder;
365 if (parent.UnsafeContext)
368 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
372 FlowStack = new Stack ();
374 if (ReturnType == TypeManager.void_type)
378 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
379 Type return_type, int code_flags, bool is_constructor)
380 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
384 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
385 Type return_type, int code_flags)
386 : this (tc, tc, l, ig, return_type, code_flags, false)
390 public FlowBranching CurrentBranching {
392 return (FlowBranching) FlowStack.Peek ();
397 // Starts a new code branching. This inherits the state of all local
398 // variables and parameters from the current branching.
400 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
402 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
404 FlowStack.Push (cfb);
410 // Starts a new code branching for block `block'.
412 public FlowBranching StartFlowBranching (Block block)
415 FlowBranchingType type;
417 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
418 type = FlowBranchingType.SWITCH_SECTION;
420 type = FlowBranchingType.BLOCK;
422 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
424 FlowStack.Push (cfb);
430 // Ends a code branching. Merges the state of locals and parameters
431 // from all the children of the ending branching.
433 public FlowReturns EndFlowBranching ()
435 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
437 return CurrentBranching.MergeChild (cfb);
441 // Kills the current code branching. This throws away any changed state
442 // information and should only be used in case of an error.
444 public void KillFlowBranching ()
446 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
450 // Checks whether the local variable `vi' is already initialized
451 // at the current point of the method's control flow.
452 // If this method returns false, the caller must report an
455 public bool IsVariableAssigned (VariableInfo vi)
458 return CurrentBranching.IsVariableAssigned (vi);
464 // Marks the local variable `vi' as being initialized at the current
465 // current point of the method's control flow.
467 public void SetVariableAssigned (VariableInfo vi)
470 CurrentBranching.SetVariableAssigned (vi);
474 // Checks whether the parameter `number' is already initialized
475 // at the current point of the method's control flow.
476 // If this method returns false, the caller must report an
477 // error 165. This is only necessary for `out' parameters and the
478 // call will always succeed for non-`out' parameters.
480 public bool IsParameterAssigned (int number)
483 return CurrentBranching.IsParameterAssigned (number);
489 // Marks the parameter `number' as being initialized at the current
490 // current point of the method's control flow. This is only necessary
491 // for `out' parameters.
493 public void SetParameterAssigned (int number)
496 CurrentBranching.SetParameterAssigned (number);
499 // These are two overloaded methods for EmitTopBlock
500 // since in MonoBasic functions we need the Function name
501 // along with its top block, in order to be able to
502 // retrieve the return value when there is no explicit
503 // 'Return' statement
504 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
506 EmitTopBlock (block, "", ip, loc);
509 public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc)
511 bool has_ret = false;
513 //Console.WriteLine ("Emitting: '{0}", bname);
515 if (CodeGen.SymbolWriter != null)
519 int errors = Report.Errors;
521 block.EmitMeta (this, block);
523 if (Report.Errors == errors){
524 bool old_do_flow_analysis = DoFlowAnalysis;
525 DoFlowAnalysis = true;
527 FlowBranching cfb = new FlowBranching (block, ip, loc);
528 FlowStack.Push (cfb);
530 if (!block.Resolve (this)) {
532 DoFlowAnalysis = old_do_flow_analysis;
536 cfb = (FlowBranching) FlowStack.Pop ();
537 FlowReturns returns = cfb.MergeTopBlock ();
539 DoFlowAnalysis = old_do_flow_analysis;
541 has_ret = block.Emit (this);
543 if ((returns == FlowReturns.ALWAYS) ||
544 (returns == FlowReturns.EXCEPTION) ||
545 (returns == FlowReturns.UNREACHABLE))
548 if (Report.Errors == errors){
549 if (RootContext.WarningLevel >= 3)
550 block.UsageWarning ();
555 if (ReturnType != null && !has_ret){
557 // mcs here would report an error (and justly so), but functions without
558 // an explicit return value are perfectly legal in MonoBasic
561 VariableInfo vi = block.GetVariableInfo (bname);
564 ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
565 ig.Emit (OpCodes.Ret);
568 Report.Error (-200, "This is not supposed to happen !");
573 ig.MarkLabel (ReturnLabel);
574 if (return_value != null){
575 ig.Emit (OpCodes.Ldloc, return_value);
576 ig.Emit (OpCodes.Ret);
579 if (!has_ret || HasReturnLabel)
580 ig.Emit (OpCodes.Ret);
586 /// This is called immediately before emitting an IL opcode to tell the symbol
587 /// writer to which source line this opcode belongs.
589 public void Mark (Location loc)
591 if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
592 ISymbolDocumentWriter doc = loc.SymbolDocument;
595 ig.MarkSequencePoint (doc, loc.Row, 0, loc.Row, 0);
600 /// Returns a temporary storage for a variable of type t as
601 /// a local variable in the current body.
603 public LocalBuilder GetTemporaryStorage (Type t)
605 LocalBuilder location;
607 if (temporary_storage != null){
608 location = (LocalBuilder) temporary_storage [t];
609 if (location != null)
613 location = ig.DeclareLocal (t);
618 public void FreeTemporaryStorage (LocalBuilder b)
624 /// Current loop begin and end labels.
626 public Label LoopBegin, LoopEnd;
629 /// Whether we are inside a loop and break/continue are possible.
634 /// This is incremented each time we enter a try/catch block and
635 /// decremented if we leave it.
637 public int TryCatchLevel;
640 /// The TryCatchLevel at the begin of the current loop.
642 public int LoopBeginTryCatchLevel;
645 /// Default target in a switch statement. Only valid if
648 public Label DefaultTarget;
651 /// If this is non-null, points to the current switch statement
653 public Switch Switch;
656 /// ReturnValue creates on demand the LocalBuilder for the
657 /// return value from the function. By default this is not
658 /// used. This is only required when returns are found inside
659 /// Try or Catch statements.
661 public LocalBuilder TemporaryReturn ()
663 if (return_value == null){
664 return_value = ig.DeclareLocal (ReturnType);
665 ReturnLabel = ig.DefineLabel ();
666 HasReturnLabel = true;
673 /// A dynamic This that is shared by all variables in a emitcontext.
674 /// Created on demand.
676 public Expression my_this;
677 public Expression This {
679 if (my_this == null) {
680 if (CurrentBlock != null)
681 my_this = new This (CurrentBlock, loc);
683 my_this = new This (loc);
685 my_this = my_this.Resolve (this);