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 remapping of locals, parameters and fields is turned on.
227 /// Used by iterators and anonymous methods.
229 public bool RemapToProxy;
232 /// Whether we are in a Catch block
237 /// Whether we are inside an unsafe block
239 public bool InUnsafe;
242 /// Whether we are inside an anonymous method.
244 public bool InAnonymousMethod;
247 /// Location for this EmitContext
252 /// Used to flag that it is ok to define types recursively, as the
253 /// expressions are being evaluated as part of the type lookup
254 /// during the type resolution process
256 public bool ResolvingTypeTree;
259 /// Inside an enum definition, we do not resolve enumeration values
260 /// to their enumerations, but rather to the underlying type/value
261 /// This is so EnumVal + EnumValB can be evaluated.
263 /// There is no "E operator + (E x, E y)", so during an enum evaluation
264 /// we relax the rules
266 public bool InEnumContext;
268 protected Stack FlowStack;
270 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
271 Type return_type, int code_flags, bool is_constructor)
275 TypeContainer = parent;
277 CheckState = RootContext.Checked;
278 ConstantCheckState = true;
280 IsStatic = (code_flags & Modifiers.STATIC) != 0;
281 InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
282 RemapToProxy = InIterator;
283 ReturnType = return_type;
284 IsConstructor = is_constructor;
289 // Can only be null for the ResolveType contexts.
290 ContainerType = parent.TypeBuilder;
291 if (parent.UnsafeContext)
294 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
298 FlowStack = new Stack ();
300 if (ReturnType == TypeManager.void_type)
304 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
305 Type return_type, int code_flags, bool is_constructor)
306 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
310 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
311 Type return_type, int code_flags)
312 : this (tc, tc, l, ig, return_type, code_flags, false)
316 public FlowBranching CurrentBranching {
318 return (FlowBranching) FlowStack.Peek ();
323 // Starts a new code branching. This inherits the state of all local
324 // variables and parameters from the current branching.
326 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
328 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
330 FlowStack.Push (cfb);
336 // Starts a new code branching for block `block'.
338 public FlowBranching StartFlowBranching (Block block)
341 FlowBranchingType type;
343 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
344 type = FlowBranchingType.SWITCH_SECTION;
346 type = FlowBranchingType.BLOCK;
348 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
350 FlowStack.Push (cfb);
356 // Ends a code branching. Merges the state of locals and parameters
357 // from all the children of the ending branching.
359 public FlowReturns EndFlowBranching ()
361 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
363 return CurrentBranching.MergeChild (cfb);
367 // Kills the current code branching. This throws away any changed state
368 // information and should only be used in case of an error.
370 public void KillFlowBranching ()
372 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
375 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
377 bool has_ret = false;
379 if (!Location.IsNull (loc))
380 CurrentFile = loc.File;
384 int errors = Report.Errors;
386 block.EmitMeta (this, ip, block);
388 if (Report.Errors == errors){
389 bool old_do_flow_analysis = DoFlowAnalysis;
390 DoFlowAnalysis = true;
392 FlowBranching cfb = new FlowBranching (block, loc);
393 FlowStack.Push (cfb);
395 if (!block.Resolve (this)) {
397 DoFlowAnalysis = old_do_flow_analysis;
401 cfb = (FlowBranching) FlowStack.Pop ();
402 FlowReturns returns = cfb.MergeTopBlock ();
404 DoFlowAnalysis = old_do_flow_analysis;
406 has_ret = block.Emit (this);
408 if ((returns == FlowReturns.ALWAYS) ||
409 (returns == FlowReturns.EXCEPTION) ||
410 (returns == FlowReturns.UNREACHABLE))
413 if (Report.Errors == errors){
414 if (RootContext.WarningLevel >= 3)
415 block.UsageWarning ();
419 Console.WriteLine ("Exception caught by the compiler while compiling:");
420 Console.WriteLine (" Block that caused the problem begin at: " + loc);
422 if (CurrentBlock != null){
423 Console.WriteLine (" Block being compiled: [{0},{1}]",
424 CurrentBlock.StartLocation, CurrentBlock.EndLocation);
430 if (ReturnType != null && !has_ret){
432 // FIXME: we need full flow analysis to implement this
433 // correctly and emit an error instead of a warning.
437 Report.Error (161, loc, "Not all code paths return a value");
443 ig.MarkLabel (ReturnLabel);
444 if (return_value != null){
445 ig.Emit (OpCodes.Ldloc, return_value);
446 ig.Emit (OpCodes.Ret);
452 if (!has_ret || HasReturnLabel)
453 ig.Emit (OpCodes.Ret);
459 /// This is called immediately before emitting an IL opcode to tell the symbol
460 /// writer to which source line this opcode belongs.
462 public void Mark (Location loc, bool check_file)
464 if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
467 if (check_file && (CurrentFile != loc.File))
470 ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
474 /// Returns a temporary storage for a variable of type t as
475 /// a local variable in the current body.
477 public LocalBuilder GetTemporaryStorage (Type t)
479 LocalBuilder location;
481 if (temporary_storage != null){
482 location = (LocalBuilder) temporary_storage [t];
483 if (location != null)
487 location = ig.DeclareLocal (t);
492 public void FreeTemporaryStorage (LocalBuilder b)
498 /// Current loop begin and end labels.
500 public Label LoopBegin, LoopEnd;
503 /// Whether we are inside a loop and break/continue are possible.
508 /// This is incremented each time we enter a try/catch block and
509 /// decremented if we leave it.
511 public int TryCatchLevel;
514 /// The TryCatchLevel at the begin of the current loop.
516 public int LoopBeginTryCatchLevel;
519 /// Default target in a switch statement. Only valid if
522 public Label DefaultTarget;
525 /// If this is non-null, points to the current switch statement
527 public Switch Switch;
530 /// ReturnValue creates on demand the LocalBuilder for the
531 /// return value from the function. By default this is not
532 /// used. This is only required when returns are found inside
533 /// Try or Catch statements.
535 public LocalBuilder TemporaryReturn ()
537 if (return_value == null){
538 return_value = ig.DeclareLocal (ReturnType);
539 ReturnLabel = ig.DefineLabel ();
540 HasReturnLabel = true;
547 // Creates a field `name' with the type `t' on the proxy class
549 public FieldBuilder MapVariable (string name, Type t)
552 return IteratorHandler.Current.MapVariable (name, t);
555 throw new Exception ("MapVariable for an unknown state");
559 // Emits the proper object to address fields on a remapped
560 // variable/parameter to field in anonymous-method/iterator proxy classes.
562 public void EmitThis ()
564 ig.Emit (OpCodes.Ldarg_0);
568 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
570 throw new Exception ("EmitThis for an unknown state");
574 public void EmitArgument (int idx)
577 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.parameter_fields [idx]);
579 throw new Exception ("EmitStoreArgument for an unknown state");
582 public void EmitStoreArgument (int idx)
585 ig.Emit (OpCodes.Stfld, IteratorHandler.Current.parameter_fields [idx]);
587 throw new Exception ("EmitStoreArgument for an unknown state");
591 /// A dynamic This that is shared by all variables in a emitcontext.
592 /// Created on demand.
594 public Expression my_this;
595 public Expression This {
597 if (my_this == null) {
598 if (CurrentBlock != null)
599 my_this = new This (CurrentBlock, loc);
601 my_this = new This (loc);
603 my_this = my_this.Resolve (this);