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.MonoBASIC {
20 /// Code generator class.
22 public class CodeGen {
23 static AppDomain current_domain;
25 public static AssemblyBuilder AssemblyBuilder {
27 return Assembly.Builder;
31 Assembly.Builder = value;
35 public static ModuleBuilder ModuleBuilder {
37 return Module.Builder;
41 Module.Builder = value;
45 public static AssemblyClass Assembly;
46 public static ModuleClass Module;
48 static public ISymbolWriter SymbolWriter;
52 Assembly = new AssemblyClass ();
53 Module = new ModuleClass ();
56 public static string Basename (string name)
58 int pos = name.LastIndexOf ("/");
61 return name.Substring (pos + 1);
63 pos = name.LastIndexOf ("\\");
65 return name.Substring (pos + 1);
70 public static string Dirname (string name)
72 int pos = name.LastIndexOf ("/");
75 return name.Substring (0, pos);
77 pos = name.LastIndexOf ("\\");
79 return name.Substring (0, pos);
84 static string TrimExt (string name)
86 int pos = name.LastIndexOf (".");
88 return name.Substring (0, pos);
91 static public string FileName;
94 // This routine initializes the Mono runtime SymbolWriter.
96 static bool InitMonoSymbolWriter (string basename, string symbol_output,
97 string exe_output_file, string[] debug_args)
99 Type itype = SymbolWriter.GetType ();
103 Type[] arg_types = new Type [3];
104 arg_types [0] = typeof (string);
105 arg_types [1] = typeof (string);
106 arg_types [2] = typeof (string[]);
108 MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
109 if (initialize == null)
112 object[] args = new object [3];
113 args [0] = exe_output_file;
114 args [1] = symbol_output;
115 args [2] = debug_args;
117 initialize.Invoke (SymbolWriter, args);
122 // Initializes the symbol writer
124 static void InitializeSymbolWriter (string basename, string symbol_output,
125 string exe_output_file, string[] args)
127 SymbolWriter = ModuleBuilder.GetSymWriter ();
130 // If we got an ISymbolWriter instance, initialize it.
132 if (SymbolWriter == null) {
134 -18, "Cannot find any symbol writer");
139 // Due to lacking documentation about the first argument of the
140 // Initialize method, we cannot use Microsoft's default symbol
143 // If we're using the mono symbol writer, the SymbolWriter object
144 // is of type MonoSymbolWriter - but that's defined in a dynamically
145 // loaded DLL - that's why we're doing a comparision based on the type
146 // name here instead of using `SymbolWriter is MonoSymbolWriter'.
148 Type sym_type = ((object) SymbolWriter).GetType ();
150 switch (sym_type.Name){
151 case "MonoSymbolWriter":
152 if (!InitMonoSymbolWriter (basename, symbol_output,
153 exe_output_file, args))
155 -18, "Cannot initialize the symbol writer");
160 -18, "Cannot generate debugging information on this platform");
166 // Initializes the code generator variables
168 static public void Init (string name, string output, bool want_debugging_support,
174 an = new AssemblyName ();
175 an.Name = TrimExt (Basename (name));
176 current_domain = AppDomain.CurrentDomain;
177 AssemblyBuilder = current_domain.DefineDynamicAssembly (
178 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
181 // Pass a path-less name to DefineDynamicModule. Wonder how
182 // this copes with output in different directories then.
183 // FIXME: figure out how this copes with --output /tmp/blah
185 // If the third argument is true, the ModuleBuilder will dynamically
186 // load the default symbol writer.
188 ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
189 Basename (name), Basename (output), want_debugging_support);
191 int pos = output.LastIndexOf (".");
195 basename = output.Substring (0, pos);
199 string symbol_output = basename + ".dbg";
201 if (want_debugging_support)
202 InitializeSymbolWriter (basename, symbol_output, output, debug_args);
205 File.Delete (symbol_output);
212 static public void Save (string name)
215 AssemblyBuilder.Save (Basename (name));
216 } catch (System.IO.IOException io){
217 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
221 static public void SaveSymbols ()
223 if (SymbolWriter != null) {
224 // If we have a symbol writer, call its Close() method to write
225 // the symbol file to disk.
227 // When using Mono's default symbol writer, the Close() method must
228 // be called after the assembly has already been written to disk since
229 // it opens the assembly and reads its metadata.
230 SymbolWriter.Close ();
234 public static void AddGlobalAttributes (ArrayList attrs)
236 foreach (Attribute attr in attrs) {
237 if (attr.IsAssemblyAttribute)
238 Assembly.AddAttribute (attr);
239 else if (attr.IsModuleAttribute)
240 Module.AddAttribute (attr);
244 public static void EmitGlobalAttributes ()
246 //Assembly.Emit (Tree.Types);
247 //Module.Emit (Tree.Types);
253 /// An Emit Context is created for each body of code (from methods,
254 /// properties bodies, indexer bodies or constructor bodies)
256 public class EmitContext {
257 public DeclSpace DeclSpace;
258 public TypeContainer TypeContainer;
259 public ILGenerator ig;
262 /// This variable tracks the `checked' state of the compilation,
263 /// it controls whether we should generate code that does overflow
264 /// checking, or if we generate code that ignores overflows.
266 /// The default setting comes from the command line option to generate
267 /// checked or unchecked code plus any source code changes using the
268 /// checked/unchecked statements or expressions. Contrast this with
269 /// the ConstantCheckState flag.
272 public bool CheckState;
275 /// The constant check state is always set to `true' and cant be changed
276 /// from the command line. The source code can change this setting with
277 /// the `checked' and `unchecked' statements and expressions.
279 public bool ConstantCheckState;
282 /// Whether we are emitting code inside a static or instance method
284 public bool IsStatic;
287 /// Whether we are emitting a field initializer
289 public bool IsFieldInitializer;
292 /// The value that is allowed to be returned or NULL if there is no
295 public Type ReturnType;
298 /// Points to the Type (extracted from the TypeContainer) that
299 /// declares this body of code
301 public Type ContainerType;
304 /// Whether this is generating code for a constructor
306 public bool IsConstructor;
309 /// Whether we're control flow analysis enabled
311 public bool DoFlowAnalysis;
314 /// Keeps track of the Type to LocalBuilder temporary storage created
315 /// to store structures (used to compute the address of the structure
316 /// value on structure method invocations)
318 public Hashtable temporary_storage;
320 public Block CurrentBlock;
323 /// The location where we store the return value.
325 LocalBuilder return_value;
328 /// The location where return has to jump to return the
331 public Label ReturnLabel;
334 /// If we already defined the ReturnLabel
336 public bool HasReturnLabel;
339 /// The location where to exit
341 public Label ExitLabel;
344 /// If we already defined the ExitLabel
346 public bool HasExitLabel;
349 /// Whether we are in a Finally block
351 public bool InFinally;
354 /// Whether we are in a Try block
359 /// Whether we are in a Catch block
364 /// Whether we are inside an unsafe block
366 public bool InUnsafe;
369 /// Whether we are inside an unsafe block
371 public bool InvokingOwnOverload;
374 /// Location for this EmitContext
379 /// Used to flag that it is ok to define types recursively, as the
380 /// expressions are being evaluated as part of the type lookup
381 /// during the type resolution process
383 public bool ResolvingTypeTree;
386 /// Inside an enum definition, we do not resolve enumeration values
387 /// to their enumerations, but rather to the underlying type/value
388 /// This is so EnumVal + EnumValB can be evaluated.
390 /// There is no "E operator + (E x, E y)", so during an enum evaluation
391 /// we relax the rules
393 public bool InEnumContext;
395 public string BlockName;
397 protected Stack FlowStack;
399 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
400 Type return_type, int code_flags, bool is_constructor)
404 TypeContainer = parent;
406 CheckState = RootContext.Checked;
407 ConstantCheckState = true;
409 IsStatic = (code_flags & Modifiers.STATIC) != 0;
410 ReturnType = return_type;
411 IsConstructor = is_constructor;
414 InvokingOwnOverload = false;
417 // Can only be null for the ResolveType contexts.
418 ContainerType = parent.TypeBuilder;
419 if (parent.UnsafeContext)
422 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
426 FlowStack = new Stack ();
428 if (ReturnType == TypeManager.void_type)
432 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
433 Type return_type, int code_flags, bool is_constructor)
434 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
438 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
439 Type return_type, int code_flags)
440 : this (tc, tc, l, ig, return_type, code_flags, false)
444 public FlowBranching CurrentBranching {
446 return (FlowBranching) FlowStack.Peek ();
451 // Starts a new code branching. This inherits the state of all local
452 // variables and parameters from the current branching.
454 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
456 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
458 FlowStack.Push (cfb);
464 // Starts a new code branching for block `block'.
466 public FlowBranching StartFlowBranching (Block block)
469 FlowBranchingType type;
471 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
472 type = FlowBranchingType.SWITCH_SECTION;
474 type = FlowBranchingType.BLOCK;
476 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
478 FlowStack.Push (cfb);
484 // Ends a code branching. Merges the state of locals and parameters
485 // from all the children of the ending branching.
487 public FlowReturns EndFlowBranching ()
489 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
491 return CurrentBranching.MergeChild (cfb);
495 // Kills the current code branching. This throws away any changed state
496 // information and should only be used in case of an error.
498 public void KillFlowBranching ()
500 /*FlowBranching cfb = (FlowBranching)*/ FlowStack.Pop ();
504 // Checks whether the local variable `vi' is already initialized
505 // at the current point of the method's control flow.
506 // If this method returns false, the caller must report an
509 public bool IsVariableAssigned (VariableInfo vi)
512 return CurrentBranching.IsVariableAssigned (vi);
518 // Marks the local variable `vi' as being initialized at the current
519 // current point of the method's control flow.
521 public void SetVariableAssigned (VariableInfo vi)
524 CurrentBranching.SetVariableAssigned (vi);
528 // Checks whether the parameter `number' is already initialized
529 // at the current point of the method's control flow.
530 // If this method returns false, the caller must report an
531 // error 165. This is only necessary for `out' parameters and the
532 // call will always succeed for non-`out' parameters.
534 public bool IsParameterAssigned (int number)
537 return CurrentBranching.IsParameterAssigned (number);
543 // Marks the parameter `number' as being initialized at the current
544 // current point of the method's control flow. This is only necessary
545 // for `out' parameters.
547 public void SetParameterAssigned (int number)
550 CurrentBranching.SetParameterAssigned (number);
553 // These are two overloaded methods for EmitTopBlock
554 // since in MonoBasic functions we need the Function name
555 // along with its top block, in order to be able to
556 // retrieve the return value when there is no explicit
557 // 'Return' statement
558 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
560 EmitTopBlock (block, "", ip, loc);
563 public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc)
565 bool has_ret = false;
567 //Console.WriteLine ("Emitting: '{0}", bname);
569 if (CodeGen.SymbolWriter != null)
573 int errors = Report.Errors;
575 block.EmitMeta (this, block);
577 if (Report.Errors == errors){
578 bool old_do_flow_analysis = DoFlowAnalysis;
579 DoFlowAnalysis = true;
581 FlowBranching cfb = new FlowBranching (block, ip, loc);
582 FlowStack.Push (cfb);
584 if (!block.Resolve (this)) {
586 DoFlowAnalysis = old_do_flow_analysis;
590 cfb = (FlowBranching) FlowStack.Pop ();
591 FlowReturns returns = cfb.MergeTopBlock ();
593 DoFlowAnalysis = old_do_flow_analysis;
595 has_ret = block.Emit (this);
597 if ((returns == FlowReturns.ALWAYS) ||
598 (returns == FlowReturns.EXCEPTION) ||
599 (returns == FlowReturns.UNREACHABLE))
602 if (Report.Errors == errors){
603 if (RootContext.WarningLevel >= 3)
604 block.UsageWarning ();
610 ig.MarkLabel (ReturnLabel);
611 if (return_value != null){
612 ig.Emit (OpCodes.Ldloc, return_value);
613 ig.Emit (OpCodes.Ret);
617 if (ReturnType != null && !has_ret){
619 // mcs here would report an error (and justly so), but functions without
620 // an explicit return value are perfectly legal in MonoBasic
623 VariableInfo vi = block.GetVariableInfo (bname);
626 ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
627 ig.Emit (OpCodes.Ret);
630 Report.Error (-200, "This is not supposed to happen !");
636 ig.Emit (OpCodes.Ret);
637 else if (ReturnType == null)
638 ig.Emit (OpCodes.Ret);
643 /// This is called immediately before emitting an IL opcode to tell the symbol
644 /// writer to which source line this opcode belongs.
646 public void Mark (Location loc)
648 if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
649 ISymbolDocumentWriter doc = loc.SymbolDocument;
652 ig.MarkSequencePoint (doc, loc.Row, 0, loc.Row, 0);
657 /// Returns a temporary storage for a variable of type t as
658 /// a local variable in the current body.
660 public LocalBuilder GetTemporaryStorage (Type t)
662 LocalBuilder location;
664 if (temporary_storage != null){
665 location = (LocalBuilder) temporary_storage [t];
666 if (location != null)
670 location = ig.DeclareLocal (t);
675 public void FreeTemporaryStorage (LocalBuilder b)
681 /// Current loop begin and end labels.
683 public Label LoopBegin, LoopEnd;
686 /// Whether we are inside a loop and break/continue are possible.
691 /// This is incremented each time we enter a try/catch block and
692 /// decremented if we leave it.
694 public int TryCatchLevel;
697 /// The TryCatchLevel at the begin of the current loop.
699 public int LoopBeginTryCatchLevel;
702 /// Default target in a switch statement. Only valid if
705 public Label DefaultTarget;
708 /// If this is non-null, points to the current switch statement
710 public Switch Switch;
713 /// ReturnValue creates on demand the LocalBuilder for the
714 /// return value from the function. By default this is not
715 /// used. This is only required when returns are found inside
716 /// Try or Catch statements.
718 public LocalBuilder TemporaryReturn ()
720 if (return_value == null){
721 return_value = ig.DeclareLocal (ReturnType);
722 ReturnLabel = ig.DefineLabel ();
723 HasReturnLabel = true;
730 /// A dynamic This that is shared by all variables in a emitcontext.
731 /// Created on demand.
733 public Expression my_this;
734 public Expression This {
736 if (my_this == null) {
737 if (CurrentBlock != null)
738 my_this = new This (CurrentBlock, loc);
740 my_this = new This (loc);
742 my_this = my_this.Resolve (this);
751 public abstract class CommonAssemblyModulClass: Attributable {
752 protected CommonAssemblyModulClass ():
757 public void AddAttribute (Attribute attr)
759 if (OptAttributes == null) {
760 OptAttributes = new Attributes (attr);
762 OptAttributes.Add (attr);
766 public virtual void Emit (TypeContainer tc)
768 if (OptAttributes == null)
770 EmitContext ec = new EmitContext (tc, Location.Null, null, null, 0, false);
772 if (OptAttributes != null)
773 OptAttributes.Emit (ec, this);
779 public class AssemblyClass: CommonAssemblyModulClass
781 public AssemblyBuilder Builder;
783 public override AttributeTargets AttributeTargets {
785 return AttributeTargets.Assembly;
789 public override void Emit (TypeContainer tc)
794 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
796 Builder.SetCustomAttribute (customBuilder);
801 public class ModuleClass: CommonAssemblyModulClass
803 public ModuleBuilder Builder;
805 public ModuleClass ()
809 public override void Emit (TypeContainer tc)
814 public override AttributeTargets AttributeTargets {
816 return AttributeTargets.Module;
820 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
822 Builder.SetCustomAttribute (customBuilder);