* ExternTable.cs (ExternTable.GetModuleTypeRef): 'module_table' can be
[mcs.git] / mbas / block.cs
blob6d76661e6226f2c56f530b83c8721a21df2f37ea
1 //
2 // block.cs: Block representation for the IL tree.
3 //
4 // Author:
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.
15 using System;
16 using System.Text;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Diagnostics;
21 namespace Mono.MonoBASIC {
23 using System.Collections;
25 /// <summary>
26 /// Block represents a VB.NET block.
27 /// </summary>
28 ///
29 /// <remarks>
30 /// This class is used in a number of places: either to represent
31 /// explicit blocks that the programmer places or implicit blocks.
32 ///
33 /// Implicit blocks are used as labels or to introduce variable
34 /// declarations.
35 /// </remarks>
36 public class Block : Statement {
37 public 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
52 // statements.
54 ArrayList children;
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;
82 bool used = false;
84 static int id;
86 int this_id;
88 public Block (Block parent)
89 : this (parent, false, Location.Null, Location.Null)
90 { }
92 public Block (Block parent, bool implicit_block)
93 : this (parent, implicit_block, Location.Null, Location.Null)
94 { }
96 public Block (Block parent, bool implicit_block, Parameters parameters)
97 : this (parent, implicit_block, parameters, Location.Null, Location.Null)
98 { }
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,
110 start, end)
113 public Block (Block parent, bool implicit_block, Parameters parameters,
114 Location start, Location end)
116 if (parent != null)
117 parent.AddChild (this);
118 else {
119 // Top block
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;
139 this.loc = start;
140 this_id = id++;
141 statements = new ArrayList ();
144 public bool IsLateBindingRequired {
145 get {
146 return isLateBindingRequired;
148 set {
149 isLateBindingRequired = value;
153 public int ID {
154 get {
155 return this_id;
159 public void AddChild (Block b)
161 if (children == null)
162 children = new ArrayList ();
164 children.Add (b);
167 public void SetEndLocation (Location loc)
169 EndLocation = loc;
172 /// <summary>
173 /// Verify if the current block has a labeled statement.
174 /// </summary>
176 /// <returns>
177 /// false if desn't exist a labeled statement in this block or in its children. true
178 /// otherwise.
179 /// </returns>
180 public bool HasLabeledStatement {
181 get {
182 foreach( Statement s in statements ) {
183 if( s is LabeledStatement )
184 return true;
185 else if (s is Block )
186 return ( ((Block) s).HasLabeledStatement);
188 return false;
192 public bool HasGotoStatement {
193 get {
194 foreach( Statement s in statements ) {
195 if( s is Goto )
196 return true;
197 else if (s is Block )
198 return ( ((Block) s).HasLabeledStatement);
200 return false;
204 public string LabelName {
205 get {
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);
212 return "";
216 /// <summary>
217 /// Adds a label to the current block.
218 /// </summary>
220 /// <returns>
221 /// false if the name already exists in this block. true
222 /// otherwise.
223 /// </returns>
226 public bool AddLabel (string name, LabeledStatement target)
228 if (labels == null)
229 labels = new CaseInsensitiveHashtable ();
230 if (labels.Contains (name))
231 return false;
233 labels.Add (name, target);
234 return true;
239 public bool AddLabel (string name, LabeledStatement target, Location loc)
242 if (switch_block != null)
243 return switch_block.AddLabel (name, target, loc);
245 Block cur = this;
246 while (cur != null) {
248 if (cur.DoLookupLabel (name) != null) {
249 Report.Error (
250 140, loc, "The label '" + name +"' is a duplicate");
251 return false;
254 if (!Implicit)
255 break;
257 cur = cur.Parent;
260 while (cur != null) {
261 if (cur.DoLookupLabel (name) != null) {
262 Report.Error (
263 158, loc,
264 "The label '"+ name +"' shadows another label " +
265 "by the same name in a containing scope.");
266 return false;
269 if (children != null) {
270 foreach (Block b in children) {
271 LabeledStatement s = b.DoLookupLabel (name);
272 if (s == null)
273 continue;
274 Report.Error (
275 158, s.Location,
276 "The label '"+ name +"' shadows another " +
277 "label by the same name in a " +
278 "containing scope.");
279 return false;
282 cur = cur.Parent;
284 if (labels == null)
285 labels = new CaseInsensitiveHashtable ();
286 if (labels.Contains (name))
287 return false;
289 labels.Add (name, target);
290 return true;
294 public LabeledStatement LookupLabel (string name)
296 LabeledStatement s = DoLookupLabel (name);
297 if (s != null)
298 return s;
300 if (children == null)
301 return null;
303 foreach (Block child in children) {
304 // if (!child.Implicit)
305 // continue;
307 s = child.LookupLabel (name);
308 if (s != null)
309 return s;
312 return null;
315 public LabeledStatement DoLookupLabel (string name)
317 if (labels != null){
318 if (labels.Contains (name))
319 return ((LabeledStatement) labels [name]);
322 if (Parent != null)
323 return Parent.LookupLabel (name);
325 return null;
328 VariableInfo this_variable = null;
330 // <summary>
331 // Returns the "this" instance variable of this block.
332 // See AddThisVariable() for more information.
333 // </summary>
334 public VariableInfo ThisVariable {
335 get {
336 if (this_variable != null)
337 return this_variable;
338 else if (Parent != null)
339 return Parent.ThisVariable;
340 else
341 return null;
345 Hashtable child_variable_names;
347 // <summary>
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.
351 // </summary>
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);
361 // <summary>
362 // Marks all variables from block @block and all its children as being
363 // used in a child block.
364 // </summary>
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);
380 // <summary>
381 // Checks whether a variable name has already been used in a child block.
382 // </summary>
383 public bool IsVariableNameUsedInChildBlock (string name)
385 if (child_variable_names == null)
386 return false;
388 return child_variable_names.Contains (name);
391 // <summary>
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.
397 // </summary>
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)) {
423 vi.Number = -1;
424 } else {
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);
432 return vi;
437 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l, string Alias)
439 VariableInfo vi = AddVariable (type, name, pars, l);
440 if (vi != null)
441 vi.Alias = Alias;
443 return vi;
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);
452 if (vi != null) {
453 if (vi.Block != ID)
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");
459 else
460 Report.Error (30290, l, "A local variable '" + name + "' is already " +
461 "defined in this scope");
462 return null;
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 " +
470 "else");
471 return null;
474 if (pars != null) {
475 int idx = 0;
476 Parameter p = pars.GetParameterByName (name, out idx);
477 if (p != null) {
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");
483 return null;
487 vi = new VariableInfo (type, name, ID, l);
489 variables.Add (name, vi);
491 return vi;
494 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
496 if (AddVariable (type, name, pars, l) == null)
497 return false;
499 if (constants == null)
500 constants = new CaseInsensitiveHashtable ();
502 constants.Add (name, value);
503 return true;
506 public Hashtable Variables {
507 get {
508 return variables;
512 public VariableInfo GetVariableInfo (string name)
514 if (variables != null) {
515 object temp;
516 temp = variables [name];
518 if (temp != null){
519 return (VariableInfo) temp;
523 if (Parent != null)
524 return Parent.GetVariableInfo (name);
526 return null;
529 public Expression GetVariableType (string name)
531 VariableInfo vi = GetVariableInfo (name);
533 if (vi != null)
534 return vi.Type;
536 return null;
539 public Expression GetConstantExpression (string name)
541 if (constants != null) {
542 object temp;
543 temp = constants [name];
545 if (temp != null)
546 return (Expression) temp;
549 if (Parent != null)
550 return Parent.GetConstantExpression (name);
552 return null;
555 /// <summary>
556 /// True if the variable named @name has been defined
557 /// in this block
558 /// </summary>
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))
564 return true;
567 if (Parent != null)
568 return Parent.IsVariableDefined (name);
570 return false;
573 /// <summary>
574 /// True if the variable named @name is a constant
575 /// </summary>
576 public bool IsConstant (string name)
578 Expression e = null;
580 e = GetConstantExpression (name);
582 return e != null;
585 /// <summary>
586 /// Use to fetch the statement associated with this label
587 /// </summary>
588 public Statement this [string name] {
589 get {
590 return (Statement) labels [name];
594 Parameters parameters = null;
595 public Parameters Parameters {
596 get {
597 if (Parent != null)
598 return Parent.Parameters;
600 return parameters;
604 /// <returns>
605 /// A list of labels that were not used within this block
606 /// </returns>
607 public string [] GetUnreferenced ()
609 // FIXME: Implement me
610 return null;
613 public void AddStatement (Statement s)
615 statements.Add (s);
616 used = true;
619 public bool Used {
620 get {
621 return used;
625 public void Use ()
627 used = true;
630 bool variables_initialized = false;
631 int count_variables = 0, first_variable = 0;
633 void UpdateVariableInfo (EmitContext ec)
635 DeclSpace ds = ec.DeclSpace;
637 first_variable = 0;
639 if (Parent != null)
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)) {
646 vi.Number = -1;
647 continue;
650 vi.Number = ++count_variables;
652 if (vi.StructInfo != null)
653 count_variables += vi.StructInfo.Count;
657 variables_initialized = true;
661 // <returns>
662 // The number of local variables in this block
663 // </returns>
664 public int CountVariables
666 get {
667 if (!variables_initialized)
668 throw new Exception ();
670 return count_variables;
674 /// <summary>
675 /// Emits the variable declarations and labels.
676 /// </summary>
677 /// <remarks>
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.
682 /// </remarks>
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))
704 continue;
707 VariableInfo vi = (VariableInfo) de.Value;
709 if (vi.VariableType == null)
710 continue;
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)
719 continue;
721 Expression cv = (Expression) constants [name];
722 if (cv == null)
723 continue;
725 Expression e = cv.Resolve (ec);
726 if (e == null)
727 continue;
729 if (!(e is Constant)){
730 Report.Error (133, vi.Location,
731 "The expression being assigned to '" +
732 name + "' must be constant (" + e + ")");
733 continue;
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 ()
752 string name;
754 if (variables != null){
755 foreach (DictionaryEntry de in variables){
756 VariableInfo vi = (VariableInfo) de.Value;
758 if (vi.Used)
759 continue;
761 name = (string) de.Key;
763 if (vi.Assigned){
764 Report.Warning (
765 219, vi.Location, "The variable '" + name +
766 "' is assigned but its value is never used");
767 } else {
768 if (!(name.Equals(lateBindingArgs)||name.Equals(lateBindingArgNames)||name.Equals(lateBindingCopyBack)))
769 Report.Warning (
770 168, vi.Location, "The variable '" +
771 name +"' is declared but never used");
776 if (children != null)
777 foreach (Block b in children)
778 b.UsageWarning ();
781 bool has_ret = false;
783 public override bool Resolve (EmitContext ec)
785 Block prev_block = ec.CurrentBlock;
786 bool ok = true;
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);
808 continue;
812 if (s.Resolve (ec) == false) {
813 ok = false;
814 continue;
817 if (s is LabeledStatement)
818 unreachable = false;
819 else
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))
836 ok = false;
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))
848 has_ret = true;
850 return ok;
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)
861 s.Emit (ec);
863 ec.Mark (EndLocation);
865 ec.CurrentBlock = prev_block;
866 return has_ret;
869 public override string ToString ()
871 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
874 } // class Block
876 /// <summary>
877 /// Block represents a VB.NET method block.
878 /// </summary>
880 /// <remarks>
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
885 /// declarations.
886 /// </remarks>
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,
924 start, end)
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