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;
47 public static ArrayList ArrListVersion = new ArrayList();//keeps the version's 4 numbers
49 static public ISymbolWriter SymbolWriter;
53 Assembly = new AssemblyClass ();
54 Module = new ModuleClass ();
57 public static string Basename (string name)
59 int pos = name.LastIndexOf ("/");
62 return name.Substring (pos + 1);
64 pos = name.LastIndexOf ("\\");
66 return name.Substring (pos + 1);
71 public static string Dirname (string name)
73 int pos = name.LastIndexOf ("/");
76 return name.Substring (0, pos);
78 pos = name.LastIndexOf ("\\");
80 return name.Substring (0, pos);
85 static string TrimExt (string name)
87 int pos = name.LastIndexOf (".");
89 return name.Substring (0, pos);
92 static public string FileName;
95 // This routine initializes the Mono runtime SymbolWriter.
97 static bool InitMonoSymbolWriter (string basename, string symbol_output,
98 string exe_output_file, string[] debug_args)
100 Type itype = SymbolWriter.GetType ();
104 Type[] arg_types = new Type [3];
105 arg_types [0] = typeof (string);
106 arg_types [1] = typeof (string);
107 arg_types [2] = typeof (string[]);
109 MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
110 if (initialize == null)
113 object[] args = new object [3];
114 args [0] = exe_output_file;
115 args [1] = symbol_output;
116 args [2] = debug_args;
118 initialize.Invoke (SymbolWriter, args);
123 // Initializes the symbol writer
125 static void InitializeSymbolWriter (string basename, string symbol_output,
126 string exe_output_file, string[] args)
128 SymbolWriter = ModuleBuilder.GetSymWriter ();
131 // If we got an ISymbolWriter instance, initialize it.
133 if (SymbolWriter == null) {
135 -18, "Cannot find any symbol writer");
140 // Due to lacking documentation about the first argument of the
141 // Initialize method, we cannot use Microsoft's default symbol
144 // If we're using the mono symbol writer, the SymbolWriter object
145 // is of type MonoSymbolWriter - but that's defined in a dynamically
146 // loaded DLL - that's why we're doing a comparision based on the type
147 // name here instead of using `SymbolWriter is MonoSymbolWriter'.
149 Type sym_type = ((object) SymbolWriter).GetType ();
151 switch (sym_type.Name){
152 case "MonoSymbolWriter":
153 if (!InitMonoSymbolWriter (basename, symbol_output,
154 exe_output_file, args))
156 -18, "Cannot initialize the symbol writer");
161 -18, "Cannot generate debugging information on this platform");
167 // Initializes the code generator variables
169 static public void Init (string name, string output, bool want_debugging_support,
175 an = new AssemblyName ();
176 an.Name = TrimExt (Basename (name));
177 current_domain = AppDomain.CurrentDomain;
179 if (ArrListVersion.Count < 4)//4 -> Major, Minor,Version, Build
180 for(int i=ArrListVersion.Count-1;i<4;i++)
181 ArrListVersion.Add(0);
182 an.Version = new Version (Convert.ToInt32(ArrListVersion[0]), Convert.ToInt32(ArrListVersion[1]), Convert.ToInt32(ArrListVersion[2]), Convert.ToInt32(ArrListVersion[3]));
184 AssemblyBuilder = current_domain.DefineDynamicAssembly (
185 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
188 // Pass a path-less name to DefineDynamicModule. Wonder how
189 // this copes with output in different directories then.
190 // FIXME: figure out how this copes with --output /tmp/blah
192 // If the third argument is true, the ModuleBuilder will dynamically
193 // load the default symbol writer.
196 ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
197 Basename (name), Basename (output), want_debugging_support);
199 int pos = output.LastIndexOf (".");
203 basename = output.Substring (0, pos);
207 string symbol_output = basename + ".dbg";
209 if (want_debugging_support)
210 InitializeSymbolWriter (basename, symbol_output, output, debug_args);
213 File.Delete (symbol_output);
220 static public void Save (string name)
223 AssemblyBuilder.Save (Basename (name));
224 } catch (System.IO.IOException io){
225 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
229 static public void SaveSymbols ()
231 if (SymbolWriter != null) {
232 // If we have a symbol writer, call its Close() method to write
233 // the symbol file to disk.
235 // When using Mono's default symbol writer, the Close() method must
236 // be called after the assembly has already been written to disk since
237 // it opens the assembly and reads its metadata.
238 SymbolWriter.Close ();
242 public static void AddGlobalAttributes (ArrayList attrs)
244 foreach (Attribute attr in attrs) {
245 if (attr.IsAssemblyAttribute)
247 Assembly.AddAttribute (attr);
249 else if (attr.IsModuleAttribute)
251 Module.AddAttribute (attr);
256 public static void EmitGlobalAttributes ()
258 //Assembly.Emit (Tree.Types);
259 //Module.Emit (Tree.Types);
266 /// An Emit Context is created for each body of code (from methods,
267 /// properties bodies, indexer bodies or constructor bodies)
269 public class EmitContext {
271 public DeclSpace DeclSpace;
272 public TypeContainer TypeContainer;
273 public ILGenerator ig;
277 /// This variable tracks the `checked' state of the compilation,
278 /// it controls whether we should generate code that does overflow
279 /// checking, or if we generate code that ignores overflows.
281 /// The default setting comes from the command line option to generate
282 /// checked or unchecked code plus any source code changes using the
283 /// checked/unchecked statements or expressions. Contrast this with
284 /// the ConstantCheckState flag.
287 public bool CheckState;
290 /// The constant check state is always set to `true' and cant be changed
291 /// from the command line. The source code can change this setting with
292 /// the `checked' and `unchecked' statements and expressions.
294 public bool ConstantCheckState;
297 /// Whether we are emitting code inside a static or instance method
299 public bool IsStatic;
302 /// Whether we are emitting a field initializer
304 public bool IsFieldInitializer;
307 /// The value that is allowed to be returned or NULL if there is no
310 public Type ReturnType;
313 /// Points to the Type (extracted from the TypeContainer) that
314 /// declares this body of code
316 public Type ContainerType;
319 /// Whether this is generating code for a constructor
321 public bool IsConstructor;
324 /// Whether we're control flow analysis enabled
326 public bool DoFlowAnalysis;
329 /// Keeps track of the Type to LocalBuilder temporary storage created
330 /// to store structures (used to compute the address of the structure
331 /// value on structure method invocations)
333 public Hashtable temporary_storage;
335 public Block CurrentBlock;
338 /// The location where we store the return value.
340 LocalBuilder return_value;
343 /// The location where return has to jump to return the
346 public Label ReturnLabel;
349 /// If we already defined the ReturnLabel
351 public bool HasReturnLabel;
354 /// The location where to exit
356 public Label ExitLabel;
359 /// If we already defined the ExitLabel
361 public bool HasExitLabel;
364 /// Whether we are in a Finally block
366 public bool InFinally;
369 /// Whether we are in a Try block
374 /// Whether we are in a Catch block
379 /// Whether we are inside an unsafe block
381 public bool InUnsafe;
384 /// Whether we are inside an unsafe block
386 public bool InvokingOwnOverload;
389 /// Location for this EmitContext
394 /// Used to flag that it is ok to define types recursively, as the
395 /// expressions are being evaluated as part of the type lookup
396 /// during the type resolution process
398 public bool ResolvingTypeTree;
401 /// Inside an enum definition, we do not resolve enumeration values
402 /// to their enumerations, but rather to the underlying type/value
403 /// This is so EnumVal + EnumValB can be evaluated.
405 /// There is no "E operator + (E x, E y)", so during an enum evaluation
406 /// we relax the rules
408 public bool InEnumContext;
410 public string BlockName;
412 protected Stack FlowStack;
414 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
415 Type return_type, int code_flags, bool is_constructor)
418 TypeContainer = parent;
420 CheckState = RootContext.Checked;
421 ConstantCheckState = true;
423 IsStatic = (code_flags & Modifiers.STATIC) != 0;
424 ReturnType = return_type;
425 IsConstructor = is_constructor;
428 InvokingOwnOverload = false;
431 // Can only be null for the ResolveType contexts.
432 ContainerType = parent.TypeBuilder;
433 if (parent.UnsafeContext)
436 InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
440 FlowStack = new Stack ();
442 if (ReturnType == TypeManager.void_type)
446 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
447 Type return_type, int code_flags, bool is_constructor)
448 : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
452 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
453 Type return_type, int code_flags)
454 : this (tc, tc, l, ig, return_type, code_flags, false)
458 public FlowBranching CurrentBranching {
460 return (FlowBranching) FlowStack.Peek ();
465 // Starts a new code branching. This inherits the state of all local
466 // variables and parameters from the current branching.
468 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
470 FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
472 FlowStack.Push (cfb);
478 // Starts a new code branching for block `block'.
480 public FlowBranching StartFlowBranching (Block block)
483 FlowBranchingType type;
485 if (CurrentBranching.Type == FlowBranchingType.SWITCH)
486 type = FlowBranchingType.SWITCH_SECTION;
488 type = FlowBranchingType.BLOCK;
490 cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
492 FlowStack.Push (cfb);
498 // Ends a code branching. Merges the state of locals and parameters
499 // from all the children of the ending branching.
501 public FlowReturns EndFlowBranching ()
503 FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
505 return CurrentBranching.MergeChild (cfb);
509 // Kills the current code branching. This throws away any changed state
510 // information and should only be used in case of an error.
512 public void KillFlowBranching ()
514 /*FlowBranching cfb = (FlowBranching)*/ FlowStack.Pop ();
518 // Checks whether the local variable `vi' is already initialized
519 // at the current point of the method's control flow.
520 // If this method returns false, the caller must report an
523 public bool IsVariableAssigned (VariableInfo vi)
526 return CurrentBranching.IsVariableAssigned (vi);
532 // Marks the local variable `vi' as being initialized at the current
533 // current point of the method's control flow.
535 public void SetVariableAssigned (VariableInfo vi)
538 CurrentBranching.SetVariableAssigned (vi);
542 // Checks whether the parameter `number' is already initialized
543 // at the current point of the method's control flow.
544 // If this method returns false, the caller must report an
545 // error 165. This is only necessary for `out' parameters and the
546 // call will always succeed for non-`out' parameters.
548 public bool IsParameterAssigned (int number)
551 return CurrentBranching.IsParameterAssigned (number);
557 // Marks the parameter `number' as being initialized at the current
558 // current point of the method's control flow. This is only necessary
559 // for `out' parameters.
561 public void SetParameterAssigned (int number)
564 CurrentBranching.SetParameterAssigned (number);
567 // These are two overloaded methods for EmitTopBlock
568 // since in MonoBasic functions we need the Function name
569 // along with its top block, in order to be able to
570 // retrieve the return value when there is no explicit
571 // 'Return' statement
572 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
574 EmitTopBlock (block, "", ip, loc);
577 public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc)
579 bool has_ret = false;
581 //Console.WriteLine ("Emitting: '{0}", bname);
583 if (CodeGen.SymbolWriter != null)
587 int errors = Report.Errors;
589 block.EmitMeta (this, block);
591 if (Report.Errors == errors){
592 bool old_do_flow_analysis = DoFlowAnalysis;
593 DoFlowAnalysis = true;
595 FlowBranching cfb = new FlowBranching (block, ip, loc);
596 FlowStack.Push (cfb);
598 if (!block.Resolve (this)) {
600 DoFlowAnalysis = old_do_flow_analysis;
604 cfb = (FlowBranching) FlowStack.Pop ();
605 FlowReturns returns = cfb.MergeTopBlock ();
607 DoFlowAnalysis = old_do_flow_analysis;
609 has_ret = block.Emit (this);
611 if ((returns == FlowReturns.ALWAYS) ||
612 (returns == FlowReturns.EXCEPTION) ||
613 (returns == FlowReturns.UNREACHABLE))
616 if (Report.Errors == errors){
617 if (RootContext.WarningLevel >= 3)
618 block.UsageWarning ();
624 ig.MarkLabel (ReturnLabel);
625 if (return_value != null){
626 ig.Emit (OpCodes.Ldloc, return_value);
627 ig.Emit (OpCodes.Ret);
631 if (ReturnType != null && !has_ret){
633 // mcs here would report an error (and justly so), but functions without
634 // an explicit return value are perfectly legal in MonoBasic
637 VariableInfo vi = block.GetVariableInfo (bname);
640 ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
641 ig.Emit (OpCodes.Ret);
644 Report.Error (-200, "This is not supposed to happen !");
650 ig.Emit (OpCodes.Ret);
651 else if (ReturnType == null)
652 ig.Emit (OpCodes.Ret);
657 /// This is called immediately before emitting an IL opcode to tell the symbol
658 /// writer to which source line this opcode belongs.
660 public void Mark (Location loc)
662 if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
663 ISymbolDocumentWriter doc = loc.SymbolDocument;
666 ig.MarkSequencePoint (doc, loc.Row, 0, loc.Row, 0);
671 /// Returns a temporary storage for a variable of type t as
672 /// a local variable in the current body.
674 public LocalBuilder GetTemporaryStorage (Type t)
676 LocalBuilder location;
678 if (temporary_storage != null){
679 location = (LocalBuilder) temporary_storage [t];
680 if (location != null)
684 location = ig.DeclareLocal (t);
689 public void FreeTemporaryStorage (LocalBuilder b)
695 /// Current loop begin and end labels.
697 public Label LoopBegin, LoopEnd;
700 /// Whether we are inside a loop and break/continue are possible.
705 /// This is incremented each time we enter a try/catch block and
706 /// decremented if we leave it.
708 public int TryCatchLevel;
711 /// The TryCatchLevel at the begin of the current loop.
713 public int LoopBeginTryCatchLevel;
716 /// Default target in a switch statement. Only valid if
719 public Label DefaultTarget;
722 /// If this is non-null, points to the current switch statement
724 public Switch Switch;
727 /// ReturnValue creates on demand the LocalBuilder for the
728 /// return value from the function. By default this is not
729 /// used. This is only required when returns are found inside
730 /// Try or Catch statements.
732 public LocalBuilder TemporaryReturn ()
734 if (return_value == null){
735 return_value = ig.DeclareLocal (ReturnType);
736 ReturnLabel = ig.DefineLabel ();
737 HasReturnLabel = true;
744 /// A dynamic This that is shared by all variables in a emitcontext.
745 /// Created on demand.
747 public Expression my_this;
748 public Expression This {
750 if (my_this == null) {
751 if (CurrentBlock != null)
752 my_this = new This (CurrentBlock, loc);
754 my_this = new This (loc);
756 my_this = my_this.Resolve (this);
765 public abstract class CommonAssemblyModulClass: Attributable {
766 protected CommonAssemblyModulClass ():
771 public void AddAttribute (Attribute attr)
773 if (OptAttributes == null) {
774 OptAttributes = new Attributes (attr);
776 OptAttributes.Add (attr);
780 public virtual void Emit (TypeContainer tc)
782 if (OptAttributes == null)
784 EmitContext ec = new EmitContext (tc, Location.Null, null, null, 0, false);
786 if (OptAttributes != null)
787 OptAttributes.Emit (ec, this);
793 public class AssemblyClass: CommonAssemblyModulClass
795 public AssemblyBuilder Builder;
797 public override AttributeTargets AttributeTargets {
799 return AttributeTargets.Assembly;
803 public override void Emit (TypeContainer tc)
808 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
810 Builder.SetCustomAttribute (customBuilder);
815 public class ModuleClass: CommonAssemblyModulClass
817 public ModuleBuilder Builder;
819 public ModuleClass ()
823 public override void Emit (TypeContainer tc)
828 public override AttributeTargets AttributeTargets {
830 return AttributeTargets.Module;
834 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder customBuilder)
836 Builder.SetCustomAttribute (customBuilder);