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 {
37 public readonly Block Parent;
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 void AddChild (Block b)
161 if (children == null)
162 children = new ArrayList ();
167 public void SetEndLocation (Location loc)
173 /// Adds a label to the current block.
177 /// false if the name already exists in this block. true
182 public bool AddLabel (string name, LabeledStatement target)
185 labels = new CaseInsensitiveHashtable ();
186 if (labels.Contains (name))
189 labels.Add (name, target);
195 public bool AddLabel (string name, LabeledStatement target, Location loc)
198 if (switch_block != null)
199 return switch_block.AddLabel (name, target, loc);
202 while (cur != null) {
204 if (cur.DoLookupLabel (name) != null) {
206 140, loc, "The label '" + name +"' is a duplicate");
216 while (cur != null) {
217 if (cur.DoLookupLabel (name) != null) {
220 "The label '"+ name +"' shadows another label " +
221 "by the same name in a containing scope.");
225 if (children != null) {
226 foreach (Block b in children) {
227 LabeledStatement s = b.DoLookupLabel (name);
232 "The label '"+ name +"' shadows another " +
233 "label by the same name in a " +
234 "containing scope.");
241 labels = new CaseInsensitiveHashtable ();
242 if (labels.Contains (name))
245 labels.Add (name, target);
250 public LabeledStatement LookupLabel (string name)
252 LabeledStatement s = DoLookupLabel (name);
256 if (children == null)
259 foreach (Block child in children) {
263 s = child.LookupLabel (name);
271 public LabeledStatement DoLookupLabel (string name)
274 if (labels.Contains (name))
275 return ((LabeledStatement) labels [name]);
279 return Parent.LookupLabel (name);
284 VariableInfo this_variable = null;
287 // Returns the "this" instance variable of this block.
288 // See AddThisVariable() for more information.
290 public VariableInfo ThisVariable {
292 if (this_variable != null)
293 return this_variable;
294 else if (Parent != null)
295 return Parent.ThisVariable;
301 Hashtable child_variable_names;
304 // Marks a variable with name @name as being used in a child block.
305 // If a variable name has been used in a child block, it's illegal to
306 // declare a variable with the same name in the current block.
308 public void AddChildVariableName (string name)
310 if (child_variable_names == null)
311 child_variable_names = new CaseInsensitiveHashtable ();
313 if (!child_variable_names.Contains (name))
314 child_variable_names.Add (name, true);
318 // Marks all variables from block @block and all its children as being
319 // used in a child block.
321 public void AddChildVariableNames (Block block)
323 if (block.Variables != null) {
324 foreach (string name in block.Variables.Keys)
325 AddChildVariableName (name);
328 foreach (Block child in block.children) {
329 if (child.Variables != null) {
330 foreach (string name in child.Variables.Keys)
331 AddChildVariableName (name);
337 // Checks whether a variable name has already been used in a child block.
339 public bool IsVariableNameUsedInChildBlock (string name)
341 if (child_variable_names == null)
344 return child_variable_names.Contains (name);
348 // This is used by non-static 'struct' constructors which do not have an
349 // initializer - in this case, the constructor must initialize all of the
350 // struct's fields. To do this, we add a "this" variable and use the flow
351 // analysis code to ensure that it's been fully initialized before control
352 // leaves the constructor.
354 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
356 if (this_variable != null)
357 return this_variable;
359 this_variable = new VariableInfo (tc, ID, l);
361 if (variables == null)
362 variables = new CaseInsensitiveHashtable ();
363 variables.Add ("this", this_variable);
365 return this_variable;
368 public VariableInfo AddVariable (EmitContext ec, Expression type, string name, Location l)
370 if (!variables_initialized)
371 throw new InvalidOperationException();
373 VariableInfo vi = AddVariable(type, name, null, loc);
375 int priorCount = count_variables;
376 DeclSpace ds = ec.DeclSpace;
378 if (!vi.Resolve (ds)) {
381 vi.Number = ++count_variables;
382 if (vi.StructInfo != null)
383 count_variables += vi.StructInfo.Count;
385 if (priorCount < count_variables)
386 ec.CurrentBranching.CurrentUsageVector.AddExtraLocals(count_variables - priorCount);
391 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
393 if (variables == null)
394 variables = new CaseInsensitiveHashtable ();
396 VariableInfo vi = GetVariableInfo (name);
399 Report.Error (30616, l, "A local variable named '" + name + "' " +
400 "cannot be declared in this scope since it would " +
401 "give a different meaning to '" + name + "', which " +
402 "is already used in a 'parent or current' scope to " +
403 "denote something else");
405 Report.Error (30290, l, "A local variable '" + name + "' is already " +
406 "defined in this scope");
410 if (IsVariableNameUsedInChildBlock (name)) {
411 Report.Error (136, l, "A local variable named '" + name + "' " +
412 "cannot be declared in this scope since it would " +
413 "give a different meaning to '" + name + "', which " +
414 "is already used in a 'child' scope to denote something " +
421 Parameter p = pars.GetParameterByName (name, out idx);
423 Report.Error (30616, l, "A local variable named '" + name + "' " +
424 "cannot be declared in this scope since it would " +
425 "give a different meaning to '" + name + "', which " +
426 "is already used in a 'parent or current' scope to " +
427 "denote something else");
432 vi = new VariableInfo (type, name, ID, l);
434 variables.Add (name, vi);
439 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
441 if (AddVariable (type, name, pars, l) == null)
444 if (constants == null)
445 constants = new CaseInsensitiveHashtable ();
447 constants.Add (name, value);
451 public Hashtable Variables {
457 public VariableInfo GetVariableInfo (string name)
459 if (variables != null) {
461 temp = variables [name];
464 return (VariableInfo) temp;
469 return Parent.GetVariableInfo (name);
474 public Expression GetVariableType (string name)
476 VariableInfo vi = GetVariableInfo (name);
484 public Expression GetConstantExpression (string name)
486 if (constants != null) {
488 temp = constants [name];
491 return (Expression) temp;
495 return Parent.GetConstantExpression (name);
501 /// True if the variable named @name has been defined
504 public bool IsVariableDefined (string name)
506 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
507 if (variables != null) {
508 if (variables.Contains (name))
513 return Parent.IsVariableDefined (name);
519 /// True if the variable named @name is a constant
521 public bool IsConstant (string name)
525 e = GetConstantExpression (name);
531 /// Use to fetch the statement associated with this label
533 public Statement this [string name] {
535 return (Statement) labels [name];
539 Parameters parameters = null;
540 public Parameters Parameters {
543 return Parent.Parameters;
550 /// A list of labels that were not used within this block
552 public string [] GetUnreferenced ()
554 // FIXME: Implement me
558 public void AddStatement (Statement s)
575 bool variables_initialized = false;
576 int count_variables = 0, first_variable = 0;
578 void UpdateVariableInfo (EmitContext ec)
580 DeclSpace ds = ec.DeclSpace;
585 first_variable += Parent.CountVariables;
587 count_variables = first_variable;
588 if (variables != null) {
589 foreach (VariableInfo vi in variables.Values) {
590 if (!vi.Resolve (ds)) {
595 vi.Number = ++count_variables;
597 if (vi.StructInfo != null)
598 count_variables += vi.StructInfo.Count;
602 variables_initialized = true;
607 // The number of local variables in this block
609 public int CountVariables
612 if (!variables_initialized)
613 throw new Exception ();
615 return count_variables;
620 /// Emits the variable declarations and labels.
623 /// tc: is our typecontainer (to resolve type references)
624 /// ig: is the code generator:
625 /// toplevel: the toplevel block. This is used for checking
626 /// that no two labels with the same name are used.
628 public void EmitMeta (EmitContext ec, Block toplevel)
630 //DeclSpace ds = ec.DeclSpace;
631 ILGenerator ig = ec.ig;
633 if (!variables_initialized)
634 UpdateVariableInfo (ec);
637 // Process this block variables
639 if (variables != null){
640 //local_builders = new CaseInsensitiveHashtable ();
642 foreach (DictionaryEntry de in variables){
643 string name = (string) de.Key;
645 if (!isLateBindingRequired) {
646 if (name.Equals (Block.lateBindingArgs) ||
647 name.Equals (Block.lateBindingArgNames) ||
648 name.Equals (Block.lateBindingCopyBack))
652 VariableInfo vi = (VariableInfo) de.Value;
654 if (vi.VariableType == null)
657 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
659 if (CodeGen.SymbolWriter != null)
660 vi.LocalBuilder.SetLocalSymInfo (name);
662 if (constants == null)
665 Expression cv = (Expression) constants [name];
669 Expression e = cv.Resolve (ec);
673 if (!(e is Constant)){
674 Report.Error (133, vi.Location,
675 "The expression being assigned to '" +
676 name + "' must be constant (" + e + ")");
680 constants.Remove (name);
681 constants.Add (name, e);
686 // Now, handle the children
688 if (children != null){
689 foreach (Block b in children)
690 b.EmitMeta (ec, toplevel);
694 public void UsageWarning ()
698 if (variables != null){
699 foreach (DictionaryEntry de in variables){
700 VariableInfo vi = (VariableInfo) de.Value;
705 name = (string) de.Key;
709 219, vi.Location, "The variable '" + name +
710 "' is assigned but its value is never used");
712 if (!(name.Equals(lateBindingArgs)||name.Equals(lateBindingArgNames)||name.Equals(lateBindingCopyBack)))
714 168, vi.Location, "The variable '" +
715 name +"' is declared but never used");
720 if (children != null)
721 foreach (Block b in children)
725 bool has_ret = false;
727 public override bool Resolve (EmitContext ec)
729 Block prev_block = ec.CurrentBlock;
732 ec.CurrentBlock = this;
734 if (!variables_initialized)
735 UpdateVariableInfo (ec);
737 ec.StartFlowBranching (this);
739 Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
741 ArrayList new_statements = new ArrayList ();
742 bool unreachable = false, warning_shown = false;
744 foreach (Statement s in statements){
746 if (unreachable && !(s is LabeledStatement)) {
747 if (!warning_shown && !(s is EmptyStatement)) {
748 warning_shown = true;
749 Warning_DeadCodeFound (s.loc);
754 if (s.Resolve (ec) == false) {
759 if (s is LabeledStatement)
762 unreachable = ! ec.CurrentBranching.IsReachable ();
764 new_statements.Add (s);
767 statements = new_statements;
769 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
771 FlowReturns returns = ec.EndFlowBranching ();
772 ec.CurrentBlock = prev_block;
774 // If we're a non-static 'struct' constructor which doesn't have an
775 // initializer, then we must initialize all of the struct's fields.
776 if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
777 !this_variable.IsAssigned (ec, loc))
780 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
781 foreach (LabeledStatement label in labels.Values)
782 if (!label.HasBeenReferenced)
783 Report.Warning (164, label.Location,
784 "This label has not been referenced");
787 if ((returns == FlowReturns.ALWAYS) ||
788 (returns == FlowReturns.EXCEPTION) ||
789 (returns == FlowReturns.UNREACHABLE))
795 protected override bool DoEmit (EmitContext ec)
797 Block prev_block = ec.CurrentBlock;
799 ec.CurrentBlock = this;
801 ec.Mark (StartLocation);
802 foreach (Statement s in statements)
805 ec.Mark (EndLocation);
807 ec.CurrentBlock = prev_block;
811 public override string ToString ()
813 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
818 } // namespace Mono.MonoBASIC