//
// 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;
}
///
/// Verify if the current block has a labeled statement.
///
///
///
/// false if desn't exist a labeled statement in this block or in its children. true
/// otherwise.
///
public bool HasLabeledStatement {
get {
foreach( Statement s in statements ) {
if( s is LabeledStatement )
return true;
else if (s is Block )
return ( ((Block) s).HasLabeledStatement);
}
return false;
}
}
public bool HasGotoStatement {
get {
foreach( Statement s in statements ) {
if( s is Goto )
return true;
else if (s is Block )
return ( ((Block) s).HasLabeledStatement);
}
return false;
}
}
public string LabelName {
get {
foreach( Statement s in statements ) {
if( s is LabeledStatement )
return ((LabeledStatement)s).LabelName;
else if (s is Block )
return ( ((Block) s).LabelName);
}
return "";
}
}
///
/// 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, string Alias, bool Static)
{
VariableInfo vi = AddVariable (type, name, pars, l);
if (vi != null) {
vi.Alias = Alias;
vi.Static = Static;
}
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 ( !(s is Block && ((Block)s).HasLabeledStatement) ) {
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
///
/// Block represents a VB.NET method 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 MethodBlock : Block {
public readonly string MethodName;
public MethodBlock (Block parent, string MethodName)
: base (parent, false, Location.Null, Location.Null)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, bool implicit_block, string MethodName)
: base (parent, implicit_block, Location.Null, Location.Null)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, bool implicit_block, Parameters parameters, string MethodName)
: base (parent, implicit_block, parameters, Location.Null, Location.Null)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, Location start, Location end, String MethodName)
: base (parent, false, start, end)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, Parameters parameters, Location start, Location end, string MethodName)
: base (parent, false, parameters, start, end)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, bool implicit_block, Location start, Location end, string MethodName)
: base (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
start, end)
{
this.MethodName = MethodName;
}
public MethodBlock (Block parent, bool implicit_block, Parameters parameters,
Location start, Location end, string MethodName)
: base (parent, implicit_block, parameters, start, end)
{
this.MethodName = MethodName;
}
}
} // namespace Mono.MonoBASIC