2 // block.cs: Block representation for the IL tree.
5 // Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 // Miguel de Icaza (miguel@ximian.com)
7 // Martin Baulig (martin@gnome.org)
8 // Anirban Bhattacharjee (banirban@novell.com)
9 // Manjula GHM (mmanjula@novell.com)
10 // Satya Sudha K (ksathyasudha@novell.com)
12 // (C) 2001, 2002, 2003, 2004, 2005 Ximian, Inc.
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Diagnostics;
21 namespace Mono.MonoBASIC {
23 using System.Collections;
26 /// Block represents a VB.NET block.
30 /// This class is used in a number of places: either to represent
31 /// explicit blocks that the programmer places or implicit blocks.
33 /// Implicit blocks are used as labels or to introduce variable
36 public class Block : Statement {
38 public readonly bool Implicit;
39 public readonly Location StartLocation;
40 public Location EndLocation;
43 // The statements in this block
45 public ArrayList statements;
48 // An array of Blocks. We keep track of children just
49 // to generate the local variable declarations.
51 // Statements and child statements are handled through the
57 // Labels. (label, block) pairs.
59 CaseInsensitiveHashtable labels;
62 // Keeps track of (name, type) pairs
64 CaseInsensitiveHashtable variables;
67 // Keeps track of constants
68 CaseInsensitiveHashtable constants;
71 // Maps variable names to ILGenerator.LocalBuilders
73 //CaseInsensitiveHashtable local_builders;
75 // to hold names of variables required for late binding
76 public const string lateBindingArgs = "1_LBArgs";
77 public const string lateBindingArgNames = "1_LBArgsNames";
78 public const string lateBindingCopyBack = "1_LBCopyBack";
80 bool isLateBindingRequired = false;
88 public Block (Block parent)
89 : this (parent, false, Location.Null, Location.Null)
92 public Block (Block parent, bool implicit_block)
93 : this (parent, implicit_block, Location.Null, Location.Null)
96 public Block (Block parent, bool implicit_block, Parameters parameters)
97 : this (parent, implicit_block, parameters, Location.Null, Location.Null)
100 public Block (Block parent, Location start, Location end)
101 : this (parent, false, start, end)
104 public Block (Block parent, Parameters parameters, Location start, Location end)
105 : this (parent, false, parameters, start, end)
108 public Block (Block parent, bool implicit_block, Location start, Location end)
109 : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
113 public Block (Block parent, bool implicit_block, Parameters parameters,
114 Location start, Location end)
117 parent.AddChild (this);
120 // Add variables that may be required for late binding
121 variables = new CaseInsensitiveHashtable ();
122 ArrayList rank_specifier = new ArrayList ();
123 ArrayList element = new ArrayList ();
124 element.Add (new EmptyExpression ());
125 rank_specifier.Add (element);
126 Expression e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Object[]", start);
127 AddVariable (e, Block.lateBindingArgs, null, start);
128 e = Mono.MonoBASIC.Parser.DecomposeQI ("System.String[]", start);
129 AddVariable (e, Block.lateBindingArgNames, null, start);
130 e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Boolean[]", start);
131 AddVariable (e, Block.lateBindingCopyBack, null, start);
134 this.Parent = parent;
135 this.Implicit = implicit_block;
136 this.parameters = parameters;
137 this.StartLocation = start;
138 this.EndLocation = end;
141 statements = new ArrayList ();
144 public bool IsLateBindingRequired {
146 return isLateBindingRequired;
149 isLateBindingRequired = value;
159 public void AddChild (Block b)
161 if (children == null)
162 children = new ArrayList ();
167 public void SetEndLocation (Location loc)
173 /// Verify if the current block has a labeled statement.
177 /// false if desn't exist a labeled statement in this block or in its children. true
180 public bool HasLabeledStatement {
182 foreach( Statement s in statements ) {
183 if( s is LabeledStatement )
185 else if (s is Block )
186 return ( ((Block) s).HasLabeledStatement);
192 public bool HasGotoStatement {
194 foreach( Statement s in statements ) {
197 else if (s is Block )
198 return ( ((Block) s).HasLabeledStatement);
204 public string LabelName {
206 foreach( Statement s in statements ) {
207 if( s is LabeledStatement )
208 return ((LabeledStatement)s).LabelName;
209 else if (s is Block )
210 return ( ((Block) s).LabelName);
217 /// Adds a label to the current block.
221 /// false if the name already exists in this block. true
226 public bool AddLabel (string name, LabeledStatement target)
229 labels = new CaseInsensitiveHashtable ();
230 if (labels.Contains (name))
233 labels.Add (name, target);
239 public bool AddLabel (string name, LabeledStatement target, Location loc)
242 if (switch_block != null)
243 return switch_block.AddLabel (name, target, loc);
246 while (cur != null) {
248 if (cur.DoLookupLabel (name) != null) {
250 140, loc, "The label '" + name +"' is a duplicate");
260 while (cur != null) {
261 if (cur.DoLookupLabel (name) != null) {
264 "The label '"+ name +"' shadows another label " +
265 "by the same name in a containing scope.");
269 if (children != null) {
270 foreach (Block b in children) {
271 LabeledStatement s = b.DoLookupLabel (name);
276 "The label '"+ name +"' shadows another " +
277 "label by the same name in a " +
278 "containing scope.");
285 labels = new CaseInsensitiveHashtable ();
286 if (labels.Contains (name))
289 labels.Add (name, target);
294 public LabeledStatement LookupLabel (string name)
296 LabeledStatement s = DoLookupLabel (name);
300 if (children == null)
303 foreach (Block child in children) {
304 // if (!child.Implicit)
307 s = child.LookupLabel (name);
315 public LabeledStatement DoLookupLabel (string name)
318 if (labels.Contains (name))
319 return ((LabeledStatement) labels [name]);
323 return Parent.LookupLabel (name);
328 VariableInfo this_variable = null;
331 // Returns the "this" instance variable of this block.
332 // See AddThisVariable() for more information.
334 public VariableInfo ThisVariable {
336 if (this_variable != null)
337 return this_variable;
338 else if (Parent != null)
339 return Parent.ThisVariable;
345 Hashtable child_variable_names;
348 // Marks a variable with name @name as being used in a child block.
349 // If a variable name has been used in a child block, it's illegal to
350 // declare a variable with the same name in the current block.
352 public void AddChildVariableName (string name)
354 if (child_variable_names == null)
355 child_variable_names = new CaseInsensitiveHashtable ();
357 if (!child_variable_names.Contains (name))
358 child_variable_names.Add (name, true);
362 // Marks all variables from block @block and all its children as being
363 // used in a child block.
365 public void AddChildVariableNames (Block block)
367 if (block.Variables != null) {
368 foreach (string name in block.Variables.Keys)
369 AddChildVariableName (name);
372 foreach (Block child in block.children) {
373 if (child.Variables != null) {
374 foreach (string name in child.Variables.Keys)
375 AddChildVariableName (name);
381 // Checks whether a variable name has already been used in a child block.
383 public bool IsVariableNameUsedInChildBlock (string name)
385 if (child_variable_names == null)
388 return child_variable_names.Contains (name);
392 // This is used by non-static 'struct' constructors which do not have an
393 // initializer - in this case, the constructor must initialize all of the
394 // struct's fields. To do this, we add a "this" variable and use the flow
395 // analysis code to ensure that it's been fully initialized before control
396 // leaves the constructor.
398 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
400 if (this_variable != null)
401 return this_variable;
403 this_variable = new VariableInfo (tc, ID, l);
405 if (variables == null)
406 variables = new CaseInsensitiveHashtable ();
407 variables.Add ("this", this_variable);
409 return this_variable;
412 public VariableInfo AddVariable (EmitContext ec, Expression type, string name, Location l)
414 if (!variables_initialized)
415 throw new InvalidOperationException();
417 VariableInfo vi = AddVariable(type, name, null, loc);
419 int priorCount = count_variables;
420 DeclSpace ds = ec.DeclSpace;
422 if (!vi.Resolve (ds)) {
425 vi.Number = ++count_variables;
426 if (vi.StructInfo != null)
427 count_variables += vi.StructInfo.Count;
429 if (priorCount < count_variables)
430 ec.CurrentBranching.CurrentUsageVector.AddExtraLocals(count_variables - priorCount);
437 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l, string Alias)
439 VariableInfo vi = AddVariable (type, name, pars, l);
446 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
448 if (variables == null)
449 variables = new CaseInsensitiveHashtable ();
451 VariableInfo vi = GetVariableInfo (name);
454 Report.Error (30616, l, "A local variable named '" + name + "' " +
455 "cannot be declared in this scope since it would " +
456 "give a different meaning to '" + name + "', which " +
457 "is already used in a 'parent or current' scope to " +
458 "denote something else");
460 Report.Error (30290, l, "A local variable '" + name + "' is already " +
461 "defined in this scope");
465 if (IsVariableNameUsedInChildBlock (name)) {
466 Report.Error (136, l, "A local variable named '" + name + "' " +
467 "cannot be declared in this scope since it would " +
468 "give a different meaning to '" + name + "', which " +
469 "is already used in a 'child' scope to denote something " +
476 Parameter p = pars.GetParameterByName (name, out idx);
478 Report.Error (30616, l, "A local variable named '" + name + "' " +
479 "cannot be declared in this scope since it would " +
480 "give a different meaning to '" + name + "', which " +
481 "is already used in a 'parent or current' scope to " +
482 "denote something else");
487 vi = new VariableInfo (type, name, ID, l);
489 variables.Add (name, vi);
494 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
496 if (AddVariable (type, name, pars, l) == null)
499 if (constants == null)
500 constants = new CaseInsensitiveHashtable ();
502 constants.Add (name, value);
506 public Hashtable Variables {
512 public VariableInfo GetVariableInfo (string name)
514 if (variables != null) {
516 temp = variables [name];
519 return (VariableInfo) temp;
524 return Parent.GetVariableInfo (name);
529 public Expression GetVariableType (string name)
531 VariableInfo vi = GetVariableInfo (name);
539 public Expression GetConstantExpression (string name)
541 if (constants != null) {
543 temp = constants [name];
546 return (Expression) temp;
550 return Parent.GetConstantExpression (name);
556 /// True if the variable named @name has been defined
559 public bool IsVariableDefined (string name)
561 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
562 if (variables != null) {
563 if (variables.Contains (name))
568 return Parent.IsVariableDefined (name);
574 /// True if the variable named @name is a constant
576 public bool IsConstant (string name)
580 e = GetConstantExpression (name);
586 /// Use to fetch the statement associated with this label
588 public Statement this [string name] {
590 return (Statement) labels [name];
594 Parameters parameters = null;
595 public Parameters Parameters {
598 return Parent.Parameters;
605 /// A list of labels that were not used within this block
607 public string [] GetUnreferenced ()
609 // FIXME: Implement me
613 public void AddStatement (Statement s)
630 bool variables_initialized = false;
631 int count_variables = 0, first_variable = 0;
633 void UpdateVariableInfo (EmitContext ec)
635 DeclSpace ds = ec.DeclSpace;
640 first_variable += Parent.CountVariables;
642 count_variables = first_variable;
643 if (variables != null) {
644 foreach (VariableInfo vi in variables.Values) {
645 if (!vi.Resolve (ds)) {
650 vi.Number = ++count_variables;
652 if (vi.StructInfo != null)
653 count_variables += vi.StructInfo.Count;
657 variables_initialized = true;
662 // The number of local variables in this block
664 public int CountVariables
667 if (!variables_initialized)
668 throw new Exception ();
670 return count_variables;
675 /// Emits the variable declarations and labels.
678 /// tc: is our typecontainer (to resolve type references)
679 /// ig: is the code generator:
680 /// toplevel: the toplevel block. This is used for checking
681 /// that no two labels with the same name are used.
683 public void EmitMeta (EmitContext ec, Block toplevel)
685 //DeclSpace ds = ec.DeclSpace;
686 ILGenerator ig = ec.ig;
688 if (!variables_initialized)
689 UpdateVariableInfo (ec);
692 // Process this block variables
694 if (variables != null){
695 //local_builders = new CaseInsensitiveHashtable ();
697 foreach (DictionaryEntry de in variables){
698 string name = (string) de.Key;
700 if (!isLateBindingRequired) {
701 if (name.Equals (Block.lateBindingArgs) ||
702 name.Equals (Block.lateBindingArgNames) ||
703 name.Equals (Block.lateBindingCopyBack))
707 VariableInfo vi = (VariableInfo) de.Value;
709 if (vi.VariableType == null)
712 if (vi.Alias == null)
713 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
715 if (CodeGen.SymbolWriter != null && vi.LocalBuilder != null)
716 vi.LocalBuilder.SetLocalSymInfo (name);
718 if (constants == null)
721 Expression cv = (Expression) constants [name];
725 Expression e = cv.Resolve (ec);
729 if (!(e is Constant)){
730 Report.Error (133, vi.Location,
731 "The expression being assigned to '" +
732 name + "' must be constant (" + e + ")");
736 constants.Remove (name);
737 constants.Add (name, e);
742 // Now, handle the children
744 if (children != null){
745 foreach (Block b in children)
746 b.EmitMeta (ec, toplevel);
750 public void UsageWarning ()
754 if (variables != null){
755 foreach (DictionaryEntry de in variables){
756 VariableInfo vi = (VariableInfo) de.Value;
761 name = (string) de.Key;
765 219, vi.Location, "The variable '" + name +
766 "' is assigned but its value is never used");
768 if (!(name.Equals(lateBindingArgs)||name.Equals(lateBindingArgNames)||name.Equals(lateBindingCopyBack)))
770 168, vi.Location, "The variable '" +
771 name +"' is declared but never used");
776 if (children != null)
777 foreach (Block b in children)
781 bool has_ret = false;
783 public override bool Resolve (EmitContext ec)
785 Block prev_block = ec.CurrentBlock;
788 ec.CurrentBlock = this;
790 if (!variables_initialized)
791 UpdateVariableInfo (ec);
793 ec.StartFlowBranching (this);
795 Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
797 ArrayList new_statements = new ArrayList ();
798 bool unreachable = false, warning_shown = false;
800 foreach (Statement s in statements) {
802 if (unreachable && !(s is LabeledStatement)) {
803 if ( !(s is Block && ((Block)s).HasLabeledStatement) ) {
804 if (!warning_shown && !(s is EmptyStatement)) {
805 warning_shown = true;
806 Warning_DeadCodeFound (s.loc);
812 if (s.Resolve (ec) == false) {
817 if (s is LabeledStatement)
820 unreachable = ! ec.CurrentBranching.IsReachable ();
822 new_statements.Add (s);
825 statements = new_statements;
827 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
829 FlowReturns returns = ec.EndFlowBranching ();
830 ec.CurrentBlock = prev_block;
832 // If we're a non-static 'struct' constructor which doesn't have an
833 // initializer, then we must initialize all of the struct's fields.
834 if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
835 !this_variable.IsAssigned (ec, loc))
838 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
839 foreach (LabeledStatement label in labels.Values)
840 if (!label.HasBeenReferenced)
841 Report.Warning (164, label.Location,
842 "This label has not been referenced");
845 if ((returns == FlowReturns.ALWAYS) ||
846 (returns == FlowReturns.EXCEPTION) ||
847 (returns == FlowReturns.UNREACHABLE))
853 protected override bool DoEmit (EmitContext ec)
855 Block prev_block = ec.CurrentBlock;
857 ec.CurrentBlock = this;
859 ec.Mark (StartLocation);
860 foreach (Statement s in statements)
863 ec.Mark (EndLocation);
865 ec.CurrentBlock = prev_block;
869 public override string ToString ()
871 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
877 /// Block represents a VB.NET method block.
881 /// This class is used in a number of places: either to represent
882 /// explicit blocks that the programmer places or implicit blocks.
884 /// Implicit blocks are used as labels or to introduce variable
887 public class MethodBlock : Block {
888 public readonly string MethodName;
889 public Block onerror;
890 public ArrayList Pending_Assigns;
892 public MethodBlock (Block parent, string MethodName)
893 : base (parent, false, Location.Null, Location.Null)
895 this.MethodName = MethodName;
898 public MethodBlock (Block parent, bool implicit_block, string MethodName)
899 : base (parent, implicit_block, Location.Null, Location.Null)
901 this.MethodName = MethodName;
904 public MethodBlock (Block parent, bool implicit_block, Parameters parameters, string MethodName)
905 : base (parent, implicit_block, parameters, Location.Null, Location.Null)
907 this.MethodName = MethodName;
910 public MethodBlock (Block parent, Location start, Location end, String MethodName)
911 : base (parent, false, start, end)
913 this.MethodName = MethodName;
916 public MethodBlock (Block parent, Parameters parameters, Location start, Location end, string MethodName)
917 : base (parent, false, parameters, start, end)
919 this.MethodName = MethodName;
922 public MethodBlock (Block parent, bool implicit_block, Location start, Location end, string MethodName)
923 : base (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
926 this.MethodName = MethodName;
929 public MethodBlock (Block parent, bool implicit_block, Parameters parameters,
930 Location start, Location end, string MethodName)
931 : base (parent, implicit_block, parameters, start, end)
933 this.MethodName = MethodName;
938 } // namespace Mono.MonoBASIC