// // block.cs: Block representation for the IL tree. // // Author: // Rafael Teixeira (rafaelteixeirabr@hotmail.com) // Miguel de Icaza (miguel@ximian.com) // Martin Baulig (martin@gnome.org) // Anirban Bhattacharjee (banirban@novell.com) // Manjula GHM (mmanjula@novell.com) // Satya Sudha K (ksathyasudha@novell.com) // // (C) 2001, 2002, 2003, 2004, 2005 Ximian, Inc. // using System; using System.Text; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; namespace Mono.MonoBASIC { using System.Collections; /// /// Block represents a VB.NET block. /// /// /// /// This class is used in a number of places: either to represent /// explicit blocks that the programmer places or implicit blocks. /// /// Implicit blocks are used as labels or to introduce variable /// declarations. /// public class Block : Statement { public readonly Block Parent; public readonly bool Implicit; public readonly Location StartLocation; public Location EndLocation; // // The statements in this block // public ArrayList statements; // // An array of Blocks. We keep track of children just // to generate the local variable declarations. // // Statements and child statements are handled through the // statements. // ArrayList children; // // Labels. (label, block) pairs. // CaseInsensitiveHashtable labels; // // Keeps track of (name, type) pairs // CaseInsensitiveHashtable variables; // // Keeps track of constants CaseInsensitiveHashtable constants; // // Maps variable names to ILGenerator.LocalBuilders // //CaseInsensitiveHashtable local_builders; // to hold names of variables required for late binding public const string lateBindingArgs = "1_LBArgs"; public const string lateBindingArgNames = "1_LBArgsNames"; public const string lateBindingCopyBack = "1_LBCopyBack"; bool isLateBindingRequired = false; bool used = false; static int id; int this_id; public Block (Block parent) : this (parent, false, Location.Null, Location.Null) { } public Block (Block parent, bool implicit_block) : this (parent, implicit_block, Location.Null, Location.Null) { } public Block (Block parent, bool implicit_block, Parameters parameters) : this (parent, implicit_block, parameters, Location.Null, Location.Null) { } public Block (Block parent, Location start, Location end) : this (parent, false, start, end) { } public Block (Block parent, Parameters parameters, Location start, Location end) : this (parent, false, parameters, start, end) { } public Block (Block parent, bool implicit_block, Location start, Location end) : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters, start, end) { } public Block (Block parent, bool implicit_block, Parameters parameters, Location start, Location end) { if (parent != null) parent.AddChild (this); else { // Top block // Add variables that may be required for late binding variables = new CaseInsensitiveHashtable (); ArrayList rank_specifier = new ArrayList (); ArrayList element = new ArrayList (); element.Add (new EmptyExpression ()); rank_specifier.Add (element); Expression e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Object[]", start); AddVariable (e, Block.lateBindingArgs, null, start); e = Mono.MonoBASIC.Parser.DecomposeQI ("System.String[]", start); AddVariable (e, Block.lateBindingArgNames, null, start); e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Boolean[]", start); AddVariable (e, Block.lateBindingCopyBack, null, start); } this.Parent = parent; this.Implicit = implicit_block; this.parameters = parameters; this.StartLocation = start; this.EndLocation = end; this.loc = start; this_id = id++; statements = new ArrayList (); } public bool IsLateBindingRequired { get { return isLateBindingRequired; } set { isLateBindingRequired = value; } } public int ID { get { return this_id; } } void AddChild (Block b) { if (children == null) children = new ArrayList (); children.Add (b); } public void SetEndLocation (Location loc) { EndLocation = loc; } /// /// Adds a label to the current block. /// /// /// /// false if the name already exists in this block. true /// otherwise. /// /// /** public bool AddLabel (string name, LabeledStatement target) { if (labels == null) labels = new CaseInsensitiveHashtable (); if (labels.Contains (name)) return false; labels.Add (name, target); return true; } **/ public bool AddLabel (string name, LabeledStatement target, Location loc) { /** if (switch_block != null) return switch_block.AddLabel (name, target, loc); **/ Block cur = this; while (cur != null) { if (cur.DoLookupLabel (name) != null) { Report.Error ( 140, loc, "The label '" + name +"' is a duplicate"); return false; } if (!Implicit) break; cur = cur.Parent; } while (cur != null) { if (cur.DoLookupLabel (name) != null) { Report.Error ( 158, loc, "The label '"+ name +"' shadows another label " + "by the same name in a containing scope."); return false; } if (children != null) { foreach (Block b in children) { LabeledStatement s = b.DoLookupLabel (name); if (s == null) continue; Report.Error ( 158, s.Location, "The label '"+ name +"' shadows another " + "label by the same name in a " + "containing scope."); return false; } } cur = cur.Parent; } if (labels == null) labels = new CaseInsensitiveHashtable (); if (labels.Contains (name)) return false; labels.Add (name, target); return true; } public LabeledStatement LookupLabel (string name) { LabeledStatement s = DoLookupLabel (name); if (s != null) return s; if (children == null) return null; foreach (Block child in children) { // if (!child.Implicit) // continue; s = child.LookupLabel (name); if (s != null) return s; } return null; } public LabeledStatement DoLookupLabel (string name) { if (labels != null){ if (labels.Contains (name)) return ((LabeledStatement) labels [name]); } /** if (Parent != null) return Parent.LookupLabel (name); **/ return null; } VariableInfo this_variable = null; // // Returns the "this" instance variable of this block. // See AddThisVariable() for more information. // public VariableInfo ThisVariable { get { if (this_variable != null) return this_variable; else if (Parent != null) return Parent.ThisVariable; else return null; } } Hashtable child_variable_names; // // Marks a variable with name @name as being used in a child block. // If a variable name has been used in a child block, it's illegal to // declare a variable with the same name in the current block. // public void AddChildVariableName (string name) { if (child_variable_names == null) child_variable_names = new CaseInsensitiveHashtable (); if (!child_variable_names.Contains (name)) child_variable_names.Add (name, true); } // // Marks all variables from block @block and all its children as being // used in a child block. // public void AddChildVariableNames (Block block) { if (block.Variables != null) { foreach (string name in block.Variables.Keys) AddChildVariableName (name); } foreach (Block child in block.children) { if (child.Variables != null) { foreach (string name in child.Variables.Keys) AddChildVariableName (name); } } } // // Checks whether a variable name has already been used in a child block. // public bool IsVariableNameUsedInChildBlock (string name) { if (child_variable_names == null) return false; return child_variable_names.Contains (name); } // // This is used by non-static 'struct' constructors which do not have an // initializer - in this case, the constructor must initialize all of the // struct's fields. To do this, we add a "this" variable and use the flow // analysis code to ensure that it's been fully initialized before control // leaves the constructor. // public VariableInfo AddThisVariable (TypeContainer tc, Location l) { if (this_variable != null) return this_variable; this_variable = new VariableInfo (tc, ID, l); if (variables == null) variables = new CaseInsensitiveHashtable (); variables.Add ("this", this_variable); return this_variable; } public VariableInfo AddVariable (EmitContext ec, Expression type, string name, Location l) { if (!variables_initialized) throw new InvalidOperationException(); VariableInfo vi = AddVariable(type, name, null, loc); int priorCount = count_variables; DeclSpace ds = ec.DeclSpace; if (!vi.Resolve (ds)) { vi.Number = -1; } else { vi.Number = ++count_variables; if (vi.StructInfo != null) count_variables += vi.StructInfo.Count; } if (priorCount < count_variables) ec.CurrentBranching.CurrentUsageVector.AddExtraLocals(count_variables - priorCount); return vi; } public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l) { if (variables == null) variables = new CaseInsensitiveHashtable (); VariableInfo vi = GetVariableInfo (name); if (vi != null) { if (vi.Block != ID) Report.Error (30616, l, "A local variable named '" + name + "' " + "cannot be declared in this scope since it would " + "give a different meaning to '" + name + "', which " + "is already used in a 'parent or current' scope to " + "denote something else"); else Report.Error (30290, l, "A local variable '" + name + "' is already " + "defined in this scope"); return null; } if (IsVariableNameUsedInChildBlock (name)) { Report.Error (136, l, "A local variable named '" + name + "' " + "cannot be declared in this scope since it would " + "give a different meaning to '" + name + "', which " + "is already used in a 'child' scope to denote something " + "else"); return null; } if (pars != null) { int idx = 0; Parameter p = pars.GetParameterByName (name, out idx); if (p != null) { Report.Error (30616, l, "A local variable named '" + name + "' " + "cannot be declared in this scope since it would " + "give a different meaning to '" + name + "', which " + "is already used in a 'parent or current' scope to " + "denote something else"); return null; } } vi = new VariableInfo (type, name, ID, l); variables.Add (name, vi); return vi; } public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l) { if (AddVariable (type, name, pars, l) == null) return false; if (constants == null) constants = new CaseInsensitiveHashtable (); constants.Add (name, value); return true; } public Hashtable Variables { get { return variables; } } public VariableInfo GetVariableInfo (string name) { if (variables != null) { object temp; temp = variables [name]; if (temp != null){ return (VariableInfo) temp; } } if (Parent != null) return Parent.GetVariableInfo (name); return null; } public Expression GetVariableType (string name) { VariableInfo vi = GetVariableInfo (name); if (vi != null) return vi.Type; return null; } public Expression GetConstantExpression (string name) { if (constants != null) { object temp; temp = constants [name]; if (temp != null) return (Expression) temp; } if (Parent != null) return Parent.GetConstantExpression (name); return null; } /// /// True if the variable named @name has been defined /// in this block /// public bool IsVariableDefined (string name) { // Console.WriteLine ("Looking up {0} in {1}", name, ID); if (variables != null) { if (variables.Contains (name)) return true; } if (Parent != null) return Parent.IsVariableDefined (name); return false; } /// /// True if the variable named @name is a constant /// public bool IsConstant (string name) { Expression e = null; e = GetConstantExpression (name); return e != null; } /// /// Use to fetch the statement associated with this label /// public Statement this [string name] { get { return (Statement) labels [name]; } } Parameters parameters = null; public Parameters Parameters { get { if (Parent != null) return Parent.Parameters; return parameters; } } /// /// A list of labels that were not used within this block /// public string [] GetUnreferenced () { // FIXME: Implement me return null; } public void AddStatement (Statement s) { statements.Add (s); used = true; } public bool Used { get { return used; } } public void Use () { used = true; } bool variables_initialized = false; int count_variables = 0, first_variable = 0; void UpdateVariableInfo (EmitContext ec) { DeclSpace ds = ec.DeclSpace; first_variable = 0; if (Parent != null) first_variable += Parent.CountVariables; count_variables = first_variable; if (variables != null) { foreach (VariableInfo vi in variables.Values) { if (!vi.Resolve (ds)) { vi.Number = -1; continue; } vi.Number = ++count_variables; if (vi.StructInfo != null) count_variables += vi.StructInfo.Count; } } variables_initialized = true; } // // // The number of local variables in this block // public int CountVariables { get { if (!variables_initialized) throw new Exception (); return count_variables; } } /// /// Emits the variable declarations and labels. /// /// /// tc: is our typecontainer (to resolve type references) /// ig: is the code generator: /// toplevel: the toplevel block. This is used for checking /// that no two labels with the same name are used. /// public void EmitMeta (EmitContext ec, Block toplevel) { //DeclSpace ds = ec.DeclSpace; ILGenerator ig = ec.ig; if (!variables_initialized) UpdateVariableInfo (ec); // // Process this block variables // if (variables != null){ //local_builders = new CaseInsensitiveHashtable (); foreach (DictionaryEntry de in variables){ string name = (string) de.Key; /* if (!isLateBindingRequired) { if (name.Equals (Block.lateBindingArgs) || name.Equals (Block.lateBindingArgNames) || name.Equals (Block.lateBindingCopyBack)) continue; } */ VariableInfo vi = (VariableInfo) de.Value; if (vi.VariableType == null) continue; vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); if (CodeGen.SymbolWriter != null) vi.LocalBuilder.SetLocalSymInfo (name); if (constants == null) continue; Expression cv = (Expression) constants [name]; if (cv == null) continue; Expression e = cv.Resolve (ec); if (e == null) continue; if (!(e is Constant)){ Report.Error (133, vi.Location, "The expression being assigned to '" + name + "' must be constant (" + e + ")"); continue; } constants.Remove (name); constants.Add (name, e); } } // // Now, handle the children // if (children != null){ foreach (Block b in children) b.EmitMeta (ec, toplevel); } } public void UsageWarning () { string name; if (variables != null){ foreach (DictionaryEntry de in variables){ VariableInfo vi = (VariableInfo) de.Value; if (vi.Used) continue; name = (string) de.Key; if (vi.Assigned){ Report.Warning ( 219, vi.Location, "The variable '" + name + "' is assigned but its value is never used"); } else { if (!(name.Equals(lateBindingArgs)||name.Equals(lateBindingArgNames)||name.Equals(lateBindingCopyBack))) Report.Warning ( 168, vi.Location, "The variable '" + name +"' is declared but never used"); } } } if (children != null) foreach (Block b in children) b.UsageWarning (); } bool has_ret = false; public override bool Resolve (EmitContext ec) { Block prev_block = ec.CurrentBlock; bool ok = true; ec.CurrentBlock = this; if (!variables_initialized) UpdateVariableInfo (ec); ec.StartFlowBranching (this); Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching); ArrayList new_statements = new ArrayList (); bool unreachable = false, warning_shown = false; foreach (Statement s in statements){ if (unreachable && !(s is LabeledStatement)) { if (!warning_shown && !(s is EmptyStatement)) { warning_shown = true; Warning_DeadCodeFound (s.loc); } continue; } if (s.Resolve (ec) == false) { ok = false; continue; } if (s is LabeledStatement) unreachable = false; else unreachable = ! ec.CurrentBranching.IsReachable (); new_statements.Add (s); } statements = new_statements; Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching); FlowReturns returns = ec.EndFlowBranching (); ec.CurrentBlock = prev_block; // If we're a non-static 'struct' constructor which doesn't have an // initializer, then we must initialize all of the struct's fields. if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) && !this_variable.IsAssigned (ec, loc)) ok = false; if ((labels != null) && (RootContext.WarningLevel >= 2)) { foreach (LabeledStatement label in labels.Values) if (!label.HasBeenReferenced) Report.Warning (164, label.Location, "This label has not been referenced"); } if ((returns == FlowReturns.ALWAYS) || (returns == FlowReturns.EXCEPTION) || (returns == FlowReturns.UNREACHABLE)) has_ret = true; return ok; } protected override bool DoEmit (EmitContext ec) { Block prev_block = ec.CurrentBlock; ec.CurrentBlock = this; ec.Mark (StartLocation); foreach (Statement s in statements) s.Emit (ec); ec.Mark (EndLocation); ec.CurrentBlock = prev_block; return has_ret; } public override string ToString () { return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation); } } // class Block } // namespace Mono.MonoBASIC