// Miguel de Icaza (miguel@ximian.com)
// Martin Baulig (martin@gnome.org)
//
-// (C) 2001, 2002 Ximian, Inc.
+// (C) 2001, 2002, 2003 Ximian, Inc.
//
using System;
public abstract class Statement {
public Location loc;
- ///
- /// Resolves the statement, true means that all sub-statements
- /// did resolve ok.
- //
+ /// <summary>
+ /// Resolves the statement, true means that all sub-statements
+ /// did resolve ok.
+ // </summary>
public virtual bool Resolve (EmitContext ec)
{
return true;
}
-
- /// <summary>
- /// Return value indicates whether all code paths emitted return.
- /// </summary>
- protected abstract bool DoEmit (EmitContext ec);
/// <summary>
- /// Return value indicates whether all code paths emitted return.
+ /// We already know that the statement is unreachable, but we still
+ /// need to resolve it to catch errors.
/// </summary>
- public virtual bool Emit (EmitContext ec)
+ public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
{
- ec.Mark (loc);
- Report.Debug (8, "MARK", this, loc);
- return DoEmit (ec);
- }
-
- public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
- {
- e = e.Resolve (ec);
- if (e == null)
- return null;
-
- if (e.Type != TypeManager.bool_type){
- e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
- new Location (-1));
- }
+ //
+ // This conflicts with csc's way of doing this, but IMHO it's
+ // the right thing to do.
+ //
+ // If something is unreachable, we still check whether it's
+ // correct. This means that you cannot use unassigned variables
+ // in unreachable code, for instance.
+ //
- if (e == null){
- Report.Error (
- 31, loc, "Can not convert the expression to a boolean");
- }
+ ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
+ bool ok = Resolve (ec);
+ ec.KillFlowBranching ();
- ec.Mark (loc);
+ if (!ok)
+ return false;
- return e;
+ if (warn)
+ Report.Warning (162, loc, "Unreachable code detected");
+ return true;
}
- /// <remarks>
- /// Encapsulates the emission of a boolean test and jumping to a
- /// destination.
- ///
- /// This will emit the bool expression in `bool_expr' and if
- /// `target_is_for_true' is true, then the code will generate a
- /// brtrue to the target. Otherwise a brfalse.
- /// </remarks>
- public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
- Label target, bool target_is_for_true)
- {
- ILGenerator ig = ec.ig;
-
- bool invert = false;
- if (bool_expr is Unary){
- Unary u = (Unary) bool_expr;
-
- if (u.Oper == Unary.Operator.LogicalNot){
- invert = true;
-
- u.EmitLogicalNot (ec);
- }
- } else if (bool_expr is Binary){
- Binary b = (Binary) bool_expr;
-
- if (b.EmitBranchable (ec, target, target_is_for_true))
- return;
- }
-
- if (!invert)
- bool_expr.Emit (ec);
+ /// <summary>
+ /// Return value indicates whether all code paths emitted return.
+ /// </summary>
+ protected abstract void DoEmit (EmitContext ec);
- if (target_is_for_true){
- if (invert)
- ig.Emit (OpCodes.Brfalse, target);
- else
- ig.Emit (OpCodes.Brtrue, target);
- } else {
- if (invert)
- ig.Emit (OpCodes.Brtrue, target);
- else
- ig.Emit (OpCodes.Brfalse, target);
- }
+ /// <summary>
+ /// Utility wrapper routine for Error, just to beautify the code
+ /// </summary>
+ public void Error (int error, string format, params object[] args)
+ {
+ Error (error, String.Format (format, args));
}
- public static void Warning_DeadCodeFound (Location loc)
+ public void Error (int error, string s)
{
- Report.Warning (162, loc, "Unreachable code detected");
+ if (!Location.IsNull (loc))
+ Report.Error (error, loc, s);
+ else
+ Report.Error (error, s);
}
+
+ /// <summary>
+ /// Return value indicates whether all code paths emitted return.
+ /// </summary>
+ public virtual void Emit (EmitContext ec)
+ {
+ ec.Mark (loc, true);
+ DoEmit (ec);
+ }
}
- public class EmptyStatement : Statement {
+ public sealed class EmptyStatement : Statement {
+
+ private EmptyStatement () {}
+
+ public static readonly EmptyStatement Value = new EmptyStatement ();
+
public override bool Resolve (EmitContext ec)
{
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
- return false;
}
}
Expression expr;
public Statement TrueStatement;
public Statement FalseStatement;
+
+ bool is_true_ret;
public If (Expression expr, Statement trueStatement, Location l)
{
{
Report.Debug (1, "START IF BLOCK", loc);
- expr = ResolveBoolean (ec, expr, loc);
+ expr = Expression.ResolveBoolean (ec, expr, loc);
if (expr == null){
return false;
}
+
+ //
+ // Dead code elimination
+ //
+ if (expr is BoolConstant){
+ bool take = ((BoolConstant) expr).Value;
+
+ if (take){
+ if (!TrueStatement.Resolve (ec))
+ return false;
+
+ if ((FalseStatement != null) &&
+ !FalseStatement.ResolveUnreachable (ec, true))
+ return false;
+ FalseStatement = null;
+ } else {
+ if (!TrueStatement.ResolveUnreachable (ec, true))
+ return false;
+ TrueStatement = null;
+
+ if ((FalseStatement != null) &&
+ !FalseStatement.Resolve (ec))
+ return false;
+ }
+
+ return true;
+ }
- ec.StartFlowBranching (FlowBranchingType.BLOCK, loc);
+ ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
- if (!TrueStatement.Resolve (ec)) {
- ec.KillFlowBranching ();
- return false;
- }
+ bool ok = TrueStatement.Resolve (ec);
+
+ is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
ec.CurrentBranching.CreateSibling ();
- if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
- ec.KillFlowBranching ();
- return false;
- }
+ if ((FalseStatement != null) && !FalseStatement.Resolve (ec))
+ ok = false;
ec.EndFlowBranching ();
Report.Debug (1, "END IF BLOCK", loc);
- return true;
+ return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
Label false_target = ig.DefineLabel ();
Label end;
- bool is_true_ret, is_false_ret;
//
- // Dead code elimination
+ // If we're a boolean expression, Resolve() already
+ // eliminated dead code for us.
//
if (expr is BoolConstant){
bool take = ((BoolConstant) expr).Value;
- if (take){
- if (FalseStatement != null){
- Warning_DeadCodeFound (FalseStatement.loc);
- }
- return TrueStatement.Emit (ec);
- } else {
- Warning_DeadCodeFound (TrueStatement.loc);
- if (FalseStatement != null)
- return FalseStatement.Emit (ec);
- }
+ if (take)
+ TrueStatement.Emit (ec);
+ else if (FalseStatement != null)
+ FalseStatement.Emit (ec);
+
+ return;
}
- EmitBoolExpression (ec, expr, false_target, false);
-
- is_true_ret = TrueStatement.Emit (ec);
- is_false_ret = is_true_ret;
+ expr.EmitBranchable (ec, false_target, false);
+
+ TrueStatement.Emit (ec);
if (FalseStatement != null){
bool branch_emitted = false;
}
ig.MarkLabel (false_target);
- is_false_ret = FalseStatement.Emit (ec);
+ FalseStatement.Emit (ec);
if (branch_emitted)
ig.MarkLabel (end);
} else {
ig.MarkLabel (false_target);
- is_false_ret = false;
}
-
- return is_true_ret && is_false_ret;
}
}
public class Do : Statement {
public Expression expr;
public readonly Statement EmbeddedStatement;
- bool infinite, may_return;
+ bool infinite;
public Do (Statement statement, Expression boolExpr, Location l)
{
{
bool ok = true;
- ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
if (!EmbeddedStatement.Resolve (ec))
ok = false;
- expr = ResolveBoolean (ec, expr, loc);
+ expr = Expression.ResolveBoolean (ec, expr, loc);
if (expr == null)
ok = false;
else if (expr is BoolConstant){
}
ec.CurrentBranching.Infinite = infinite;
- FlowReturns returns = ec.EndFlowBranching ();
- may_return = returns != FlowReturns.NEVER;
+ ec.EndFlowBranching ();
return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
Label loop = ig.DefineLabel ();
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
- bool old_inloop = ec.InLoop;
- int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
- ec.InLoop = true;
- ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
ig.MarkLabel (loop);
EmbeddedStatement.Emit (ec);
if (res)
ec.ig.Emit (OpCodes.Br, loop);
} else
- EmitBoolExpression (ec, expr, loop, true);
+ expr.EmitBranchable (ec, loop, true);
ig.MarkLabel (ec.LoopEnd);
- ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
- ec.InLoop = old_inloop;
-
- if (infinite)
- return may_return == false;
- else
- return false;
}
}
public class While : Statement {
public Expression expr;
public readonly Statement Statement;
- bool may_return, empty, infinite;
+ bool infinite, empty;
public While (Expression boolExpr, Statement statement, Location l)
{
{
bool ok = true;
- expr = ResolveBoolean (ec, expr, loc);
+ expr = Expression.ResolveBoolean (ec, expr, loc);
if (expr == null)
return false;
- ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
-
//
// Inform whether we are infinite or not
//
BoolConstant bc = (BoolConstant) expr;
if (bc.Value == false){
- Warning_DeadCodeFound (Statement.loc);
+ if (!Statement.ResolveUnreachable (ec, true))
+ return false;
empty = true;
+ return true;
} else
infinite = true;
- } else {
- //
- // We are not infinite, so the loop may or may not be executed.
- //
- ec.CurrentBranching.CreateSibling ();
}
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
+
if (!Statement.Resolve (ec))
ok = false;
- if (empty)
- ec.KillFlowBranching ();
- else {
- ec.CurrentBranching.Infinite = infinite;
- FlowReturns returns = ec.EndFlowBranching ();
- may_return = returns != FlowReturns.NEVER;
- }
+ ec.CurrentBranching.Infinite = infinite;
+ ec.EndFlowBranching ();
return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
if (empty)
- return false;
+ return;
ILGenerator ig = ec.ig;
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
- bool old_inloop = ec.InLoop;
- int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
- bool ret;
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
- ec.InLoop = true;
- ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
//
// Inform whether we are infinite or not
//
if (expr is BoolConstant){
- BoolConstant bc = (BoolConstant) expr;
-
ig.MarkLabel (ec.LoopBegin);
Statement.Emit (ec);
ig.Emit (OpCodes.Br, ec.LoopBegin);
// Inform that we are infinite (ie, `we return'), only
// if we do not `break' inside the code.
//
- ret = may_return == false;
ig.MarkLabel (ec.LoopEnd);
} else {
Label while_loop = ig.DefineLabel ();
ig.MarkLabel (ec.LoopBegin);
- EmitBoolExpression (ec, expr, while_loop, true);
+ expr.EmitBranchable (ec, while_loop, true);
+
ig.MarkLabel (ec.LoopEnd);
-
- ret = false;
}
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
- ec.InLoop = old_inloop;
- ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
-
- return ret;
}
}
readonly Statement InitStatement;
readonly Statement Increment;
readonly Statement Statement;
- bool may_return, infinite, empty;
+ bool infinite, empty;
public For (Statement initStatement,
Expression test,
}
if (Test != null){
- Test = ResolveBoolean (ec, Test, loc);
+ Test = Expression.ResolveBoolean (ec, Test, loc);
if (Test == null)
ok = false;
else if (Test is BoolConstant){
BoolConstant bc = (BoolConstant) Test;
if (bc.Value == false){
- Warning_DeadCodeFound (Statement.loc);
+ if (!Statement.ResolveUnreachable (ec, true))
+ return false;
+ if ((Increment != null) &&
+ !Increment.ResolveUnreachable (ec, false))
+ return false;
empty = true;
+ return true;
} else
infinite = true;
}
} else
infinite = true;
- if (Increment != null){
- if (!Increment.Resolve (ec))
- ok = false;
- }
-
- ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
if (!infinite)
ec.CurrentBranching.CreateSibling ();
if (!Statement.Resolve (ec))
ok = false;
- if (empty)
- ec.KillFlowBranching ();
- else {
- ec.CurrentBranching.Infinite = infinite;
- FlowReturns returns = ec.EndFlowBranching ();
- may_return = returns != FlowReturns.NEVER;
+ if (Increment != null){
+ if (!Increment.Resolve (ec))
+ ok = false;
}
+ ec.CurrentBranching.Infinite = infinite;
+ ec.EndFlowBranching ();
+
return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
if (empty)
- return false;
+ return;
ILGenerator ig = ec.ig;
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
- bool old_inloop = ec.InLoop;
- int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
Label loop = ig.DefineLabel ();
Label test = ig.DefineLabel ();
- if (InitStatement != null)
- if (! (InitStatement is EmptyStatement))
- InitStatement.Emit (ec);
+ if (InitStatement != null && InitStatement != EmptyStatement.Value)
+ InitStatement.Emit (ec);
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
- ec.InLoop = true;
- ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
ig.Emit (OpCodes.Br, test);
ig.MarkLabel (loop);
Statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
- if (!(Increment is EmptyStatement))
+ if (Increment != EmptyStatement.Value)
Increment.Emit (ec);
ig.MarkLabel (test);
//
if (Test != null){
//
- // The Resolve code already catches the case for Test == BoolConstant (false)
- // so we know that this is true
+ // The Resolve code already catches the case for
+ // Test == BoolConstant (false) so we know that
+ // this is true
//
if (Test is BoolConstant)
ig.Emit (OpCodes.Br, loop);
else
- EmitBoolExpression (ec, Test, loop, true);
+ Test.EmitBranchable (ec, loop, true);
+
} else
ig.Emit (OpCodes.Br, loop);
ig.MarkLabel (ec.LoopEnd);
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
- ec.InLoop = old_inloop;
- ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
-
- //
- // Inform whether we are infinite or not
- //
- if (Test != null){
- if (Test is BoolConstant){
- BoolConstant bc = (BoolConstant) Test;
-
- if (bc.Value)
- return may_return == false;
- }
- return false;
- } else
- return may_return == false;
}
}
public class StatementExpression : Statement {
- Expression expr;
+ ExpressionStatement expr;
public StatementExpression (ExpressionStatement expr, Location l)
{
public override bool Resolve (EmitContext ec)
{
- expr = (Expression) expr.Resolve (ec);
+ expr = expr.ResolveStatement (ec);
return expr != null;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
- ILGenerator ig = ec.ig;
-
- if (expr is ExpressionStatement)
- ((ExpressionStatement) expr).EmitStatement (ec);
- else {
- expr.Emit (ec);
- ig.Emit (OpCodes.Pop);
- }
-
- return false;
+ expr.EmitStatement (ec);
}
public override string ToString ()
loc = l;
}
+ bool in_exc;
+
public override bool Resolve (EmitContext ec)
{
- if (Expr != null){
+ if (ec.ReturnType == null){
+ if (Expr != null){
+ Error (127, "Return with a value not allowed here");
+ return false;
+ }
+ } else {
+ if (Expr == null){
+ Error (126, "An object of type `{0}' is expected " +
+ "for the return statement",
+ TypeManager.CSharpName (ec.ReturnType));
+ return false;
+ }
+
Expr = Expr.Resolve (ec);
if (Expr == null)
return false;
+
+ if (Expr.Type != ec.ReturnType) {
+ Expr = Convert.ImplicitConversionRequired (
+ ec, Expr, ec.ReturnType, loc);
+ if (Expr == null)
+ return false;
+ }
}
+ if (ec.InIterator){
+ Error (-206, "Return statement not allowed inside iterators");
+ return false;
+ }
+
FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
- if (ec.CurrentBranching.InTryBlock ())
+ if (ec.CurrentBranching.InTryOrCatch (true)) {
ec.CurrentBranching.AddFinallyVector (vector);
- else
+ in_exc = true;
+ } else if (ec.CurrentBranching.InFinally (true)) {
+ Error (157, "Control can not leave the body of the finally block");
+ return false;
+ } else
vector.CheckOutParameters (ec.CurrentBranching);
- vector.Returns = FlowReturns.ALWAYS;
- vector.Breaks = FlowReturns.ALWAYS;
+ ec.CurrentBranching.CurrentUsageVector.Return ();
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
- if (ec.InFinally){
- Report.Error (157,loc,"Control can not leave the body of the finally block");
- return false;
- }
-
- if (ec.ReturnType == null){
- if (Expr != null){
- Report.Error (127, loc, "Return with a value not allowed here");
- return true;
- }
- } else {
- if (Expr == null){
- Report.Error (126, loc, "An object of type `" +
- TypeManager.CSharpName (ec.ReturnType) + "' is " +
- "expected for the return statement");
- return true;
- }
-
- if (Expr.Type != ec.ReturnType)
- Expr = Expression.ConvertImplicitRequired (
- ec, Expr, ec.ReturnType, loc);
-
- if (Expr == null)
- return true;
-
+ if (Expr != null) {
Expr.Emit (ec);
- if (ec.InTry || ec.InCatch)
+ if (in_exc)
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (ec.InTry || ec.InCatch) {
- if (!ec.HasReturnLabel) {
- ec.ReturnLabel = ec.ig.DefineLabel ();
- ec.HasReturnLabel = true;
- }
+ if (in_exc) {
+ ec.NeedReturnLabel ();
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
- } else
+ } else {
ec.ig.Emit (OpCodes.Ret);
-
- return true;
+ }
}
}
public override bool Resolve (EmitContext ec)
{
- label = block.LookupLabel (target);
- if (label == null){
- Report.Error (
- 159, loc,
- "No such label `" + target + "' in this scope");
+ label = ec.CurrentBranching.LookupLabel (target, loc);
+ if (label == null)
return false;
- }
// If this is a forward goto.
if (!label.IsDefined)
label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
- ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
}
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
Label l = label.LabelTarget (ec);
ec.ig.Emit (OpCodes.Br, l);
-
- return false;
}
}
public class LabeledStatement : Statement {
public readonly Location Location;
- string label_name;
bool defined;
bool referenced;
Label label;
- ArrayList vectors;
+ FlowBranching.UsageVector vectors;
public LabeledStatement (string label_name, Location l)
{
- this.label_name = label_name;
this.Location = l;
}
public void AddUsageVector (FlowBranching.UsageVector vector)
{
- if (vectors == null)
- vectors = new ArrayList ();
-
- vectors.Add (vector.Clone ());
+ vector = vector.Clone ();
+ vector.Next = vectors;
+ vectors = vector;
}
public override bool Resolve (EmitContext ec)
{
- if (vectors != null)
- ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors);
- else {
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER;
- ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER;
- }
+ ec.CurrentBranching.Label (vectors);
referenced = true;
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
LabelTarget (ec);
ec.ig.MarkLabel (label);
-
- return false;
}
}
public override bool Resolve (EmitContext ec)
{
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
- ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
if (ec.Switch == null){
Report.Error (153, loc, "goto default is only valid in a switch statement");
- return false;
+ return;
}
if (!ec.Switch.GotDefault){
Report.Error (159, loc, "No default target on switch statement");
- return false;
+ return;
}
ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
- return false;
}
}
Report.Error (
159, loc,
"No such label 'case " + val + "': for the goto case");
+ return false;
}
label = sl.ILLabelCode;
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE;
- ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ec.ig.Emit (OpCodes.Br, label);
- return true;
}
}
public override bool Resolve (EmitContext ec)
{
+ bool in_catch = ec.CurrentBranching.InCatch ();
+ ec.CurrentBranching.CurrentUsageVector.Throw ();
+
if (expr != null){
expr = expr.Resolve (ec);
if (expr == null)
if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
- expr.Error118 ("value, variable, property or indexer access ");
+ expr.Error_UnexpectedKind ("value, variable, property or indexer access ");
return false;
}
if ((t != TypeManager.exception_type) &&
!t.IsSubclassOf (TypeManager.exception_type) &&
!(expr is NullLiteral)) {
- Report.Error (155, loc,
- "The type caught or thrown must be derived " +
- "from System.Exception");
+ Error (155,
+ "The type caught or thrown must be derived " +
+ "from System.Exception");
return false;
}
+ } else if (!in_catch) {
+ Error (156,
+ "A throw statement with no argument is only " +
+ "allowed in a catch clause");
+ return false;
}
- ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION;
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION;
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
- if (expr == null){
- if (ec.InCatch)
- ec.ig.Emit (OpCodes.Rethrow);
- else {
- Report.Error (
- 156, loc,
- "A throw statement with no argument is only " +
- "allowed in a catch clause");
- }
- return false;
- }
-
- expr.Emit (ec);
-
- ec.ig.Emit (OpCodes.Throw);
+ if (expr == null)
+ ec.ig.Emit (OpCodes.Rethrow);
+ else {
+ expr.Emit (ec);
- return true;
+ ec.ig.Emit (OpCodes.Throw);
+ }
}
}
loc = l;
}
+ bool crossing_exc;
+
public override bool Resolve (EmitContext ec)
{
- ec.CurrentBranching.MayLeaveLoop = true;
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
+ if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
+ Error (139, "No enclosing loop or switch to continue to");
+ return false;
+ } else if (ec.CurrentBranching.InFinally (false)) {
+ Error (157, "Control can not leave the body of the finally block");
+ return false;
+ } else if (ec.CurrentBranching.InTryOrCatch (false))
+ ec.CurrentBranching.AddFinallyVector (
+ ec.CurrentBranching.CurrentUsageVector);
+ else if (ec.CurrentBranching.InLoop ())
+ ec.CurrentBranching.AddBreakVector (
+ ec.CurrentBranching.CurrentUsageVector);
+
+ crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
+
+ ec.CurrentBranching.CurrentUsageVector.Break ();
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
- if (ec.InLoop == false && ec.Switch == null){
- Report.Error (139, loc, "No enclosing loop or switch to continue to");
- return false;
- }
-
- if (ec.InTry || ec.InCatch)
+ if (crossing_exc)
ig.Emit (OpCodes.Leave, ec.LoopEnd);
- else
+ else {
+ ec.NeedReturnLabel ();
ig.Emit (OpCodes.Br, ec.LoopEnd);
-
- return false;
+ }
}
}
loc = l;
}
+ bool crossing_exc;
+
public override bool Resolve (EmitContext ec)
{
- ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
+ if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
+ Error (139, "No enclosing loop to continue to");
+ return false;
+ } else if (ec.CurrentBranching.InFinally (false)) {
+ Error (157, "Control can not leave the body of the finally block");
+ return false;
+ } else if (ec.CurrentBranching.InTryOrCatch (false))
+ ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector);
+
+ crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
+
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
Label begin = ec.LoopBegin;
- if (!ec.InLoop){
- Report.Error (139, loc, "No enclosing loop to continue to");
- return false;
- }
-
- //
- // UGH: Non trivial. This Br might cross a try/catch boundary
- // How can we tell?
- //
- // while () {
- // try { ... } catch { continue; }
- // }
- //
- // From:
- // try {} catch { while () { continue; }}
- //
- if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
+ if (crossing_exc)
ec.ig.Emit (OpCodes.Leave, begin);
- else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
- throw new Exception ("Should never happen");
else
ec.ig.Emit (OpCodes.Br, begin);
- return false;
- }
- }
-
- // <summary>
- // This is used in the control flow analysis code to specify whether the
- // current code block may return to its enclosing block before reaching
- // its end.
- // </summary>
- public enum FlowReturns {
- // It can never return.
- NEVER,
-
- // This means that the block contains a conditional return statement
- // somewhere.
- SOMETIMES,
-
- // The code always returns, ie. there's an unconditional return / break
- // statement in it.
- ALWAYS,
-
- // The code always throws an exception.
- EXCEPTION,
-
- // The current code block is unreachable. This happens if it's immediately
- // following a FlowReturns.ALWAYS block.
- UNREACHABLE
- }
-
- // <summary>
- // This is a special bit vector which can inherit from another bit vector doing a
- // copy-on-write strategy. The inherited vector may have a smaller size than the
- // current one.
- // </summary>
- public class MyBitVector {
- public readonly int Count;
- public readonly MyBitVector InheritsFrom;
-
- bool is_dirty;
- BitArray vector;
-
- public MyBitVector (int Count)
- : this (null, Count)
- { }
-
- public MyBitVector (MyBitVector InheritsFrom, int Count)
- {
- this.InheritsFrom = InheritsFrom;
- this.Count = Count;
- }
-
- // <summary>
- // Checks whether this bit vector has been modified. After setting this to true,
- // we won't use the inherited vector anymore, but our own copy of it.
- // </summary>
- public bool IsDirty {
- get {
- return is_dirty;
- }
-
- set {
- if (!is_dirty)
- initialize_vector ();
- }
- }
-
- // <summary>
- // Get/set bit `index' in the bit vector.
- // </summary>
- public bool this [int index]
- {
- get {
- if (index > Count)
- throw new ArgumentOutOfRangeException ();
-
- // We're doing a "copy-on-write" strategy here; as long
- // as nobody writes to the array, we can use our parent's
- // copy instead of duplicating the vector.
-
- if (vector != null)
- return vector [index];
- else if (InheritsFrom != null) {
- BitArray inherited = InheritsFrom.Vector;
-
- if (index < inherited.Count)
- return inherited [index];
- else
- return false;
- } else
- return false;
- }
-
- set {
- if (index > Count)
- throw new ArgumentOutOfRangeException ();
-
- // Only copy the vector if we're actually modifying it.
-
- if (this [index] != value) {
- initialize_vector ();
-
- vector [index] = value;
- }
- }
- }
-
- // <summary>
- // If you explicitly convert the MyBitVector to a BitArray, you will get a deep
- // copy of the bit vector.
- // </summary>
- public static explicit operator BitArray (MyBitVector vector)
- {
- vector.initialize_vector ();
- return vector.Vector;
- }
-
- // <summary>
- // Performs an `or' operation on the bit vector. The `new_vector' may have a
- // different size than the current one.
- // </summary>
- public void Or (MyBitVector new_vector)
- {
- BitArray new_array = new_vector.Vector;
-
- initialize_vector ();
-
- int upper;
- if (vector.Count < new_array.Count)
- upper = vector.Count;
- else
- upper = new_array.Count;
-
- for (int i = 0; i < upper; i++)
- vector [i] = vector [i] | new_array [i];
- }
-
- // <summary>
- // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
- // a different size than the current one.
- // </summary>
- public void And (MyBitVector new_vector)
- {
- BitArray new_array = new_vector.Vector;
-
- initialize_vector ();
-
- int lower, upper;
- if (vector.Count < new_array.Count)
- lower = upper = vector.Count;
- else {
- lower = new_array.Count;
- upper = vector.Count;
- }
-
- for (int i = 0; i < lower; i++)
- vector [i] = vector [i] & new_array [i];
-
- for (int i = lower; i < upper; i++)
- vector [i] = false;
- }
-
- // <summary>
- // This does a deep copy of the bit vector.
- // </summary>
- public MyBitVector Clone ()
- {
- MyBitVector retval = new MyBitVector (Count);
-
- retval.Vector = Vector;
-
- return retval;
- }
-
- BitArray Vector {
- get {
- if (vector != null)
- return vector;
- else if (!is_dirty && (InheritsFrom != null))
- return InheritsFrom.Vector;
-
- initialize_vector ();
-
- return vector;
- }
-
- set {
- initialize_vector ();
-
- for (int i = 0; i < Math.Min (vector.Count, value.Count); i++)
- vector [i] = value [i];
- }
- }
-
- void initialize_vector ()
- {
- if (vector != null)
- return;
-
- vector = new BitArray (Count, false);
- if (InheritsFrom != null)
- Vector = InheritsFrom.Vector;
-
- is_dirty = true;
- }
-
- public override string ToString ()
- {
- StringBuilder sb = new StringBuilder ("MyBitVector (");
-
- BitArray vector = Vector;
- sb.Append (Count);
- sb.Append (",");
- if (!IsDirty)
- sb.Append ("INHERITED - ");
- for (int i = 0; i < vector.Count; i++) {
- if (i > 0)
- sb.Append (",");
- sb.Append (vector [i]);
- }
-
- sb.Append (")");
- return sb.ToString ();
- }
- }
-
- // <summary>
- // The type of a FlowBranching.
- // </summary>
- public enum FlowBranchingType {
- // Normal (conditional or toplevel) block.
- BLOCK,
-
- // A loop block.
- LOOP_BLOCK,
-
- // Try/Catch block.
- EXCEPTION,
-
- // Switch block.
- SWITCH,
-
- // Switch section.
- SWITCH_SECTION
- }
-
- // <summary>
- // A new instance of this class is created every time a new block is resolved
- // and if there's branching in the block's control flow.
- // </summary>
- public class FlowBranching {
- // <summary>
- // The type of this flow branching.
- // </summary>
- public readonly FlowBranchingType Type;
-
- // <summary>
- // The block this branching is contained in. This may be null if it's not
- // a top-level block and it doesn't declare any local variables.
- // </summary>
- public readonly Block Block;
-
- // <summary>
- // The parent of this branching or null if this is the top-block.
- // </summary>
- public readonly FlowBranching Parent;
-
- // <summary>
- // Start-Location of this flow branching.
- // </summary>
- public readonly Location Location;
-
- // <summary>
- // A list of UsageVectors. A new vector is added each time control flow may
- // take a different path.
- // </summary>
- public UsageVector[] Siblings;
-
- // <summary>
- // If this is an infinite loop.
- // </summary>
- public bool Infinite;
-
- // <summary>
- // If we may leave the current loop.
- // </summary>
- public bool MayLeaveLoop;
-
- //
- // Private
- //
- InternalParameters param_info;
- int[] param_map;
- MyStructInfo[] struct_params;
- int num_params;
- ArrayList finally_vectors;
-
- static int next_id = 0;
- int id;
-
- // <summary>
- // Performs an `And' operation on the FlowReturns status
- // (for instance, a block only returns ALWAYS if all its siblings
- // always return).
- // </summary>
- public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b)
- {
- if (b == FlowReturns.UNREACHABLE)
- return a;
-
- switch (a) {
- case FlowReturns.NEVER:
- if (b == FlowReturns.NEVER)
- return FlowReturns.NEVER;
- else
- return FlowReturns.SOMETIMES;
-
- case FlowReturns.SOMETIMES:
- return FlowReturns.SOMETIMES;
-
- case FlowReturns.ALWAYS:
- if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION))
- return FlowReturns.ALWAYS;
- else
- return FlowReturns.SOMETIMES;
-
- case FlowReturns.EXCEPTION:
- if (b == FlowReturns.EXCEPTION)
- return FlowReturns.EXCEPTION;
- else if (b == FlowReturns.ALWAYS)
- return FlowReturns.ALWAYS;
- else
- return FlowReturns.SOMETIMES;
- }
-
- return b;
- }
-
- // <summary>
- // The vector contains a BitArray with information about which local variables
- // and parameters are already initialized at the current code position.
- // </summary>
- public class UsageVector {
- // <summary>
- // If this is true, then the usage vector has been modified and must be
- // merged when we're done with this branching.
- // </summary>
- public bool IsDirty;
-
- // <summary>
- // The number of parameters in this block.
- // </summary>
- public readonly int CountParameters;
-
- // <summary>
- // The number of locals in this block.
- // </summary>
- public readonly int CountLocals;
-
- // <summary>
- // If not null, then we inherit our state from this vector and do a
- // copy-on-write. If null, then we're the first sibling in a top-level
- // block and inherit from the empty vector.
- // </summary>
- public readonly UsageVector InheritsFrom;
-
- //
- // Private.
- //
- MyBitVector locals, parameters;
- FlowReturns real_returns, real_breaks;
- bool is_finally;
-
- static int next_id = 0;
- int id;
-
- //
- // Normally, you should not use any of these constructors.
- //
- public UsageVector (UsageVector parent, int num_params, int num_locals)
- {
- this.InheritsFrom = parent;
- this.CountParameters = num_params;
- this.CountLocals = num_locals;
- this.real_returns = FlowReturns.NEVER;
- this.real_breaks = FlowReturns.NEVER;
-
- if (parent != null) {
- locals = new MyBitVector (parent.locals, CountLocals);
- if (num_params > 0)
- parameters = new MyBitVector (parent.parameters, num_params);
- real_returns = parent.Returns;
- real_breaks = parent.Breaks;
- } else {
- locals = new MyBitVector (null, CountLocals);
- if (num_params > 0)
- parameters = new MyBitVector (null, num_params);
- }
-
- id = ++next_id;
- }
-
- public UsageVector (UsageVector parent)
- : this (parent, parent.CountParameters, parent.CountLocals)
- { }
-
- // <summary>
- // This does a deep copy of the usage vector.
- // </summary>
- public UsageVector Clone ()
- {
- UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
-
- retval.locals = locals.Clone ();
- if (parameters != null)
- retval.parameters = parameters.Clone ();
- retval.real_returns = real_returns;
- retval.real_breaks = real_breaks;
-
- return retval;
- }
-
- //
- // State of parameter `number'.
- //
- public bool this [int number]
- {
- get {
- if (number == -1)
- return true;
- else if (number == 0)
- throw new ArgumentException ();
-
- return parameters [number - 1];
- }
-
- set {
- if (number == -1)
- return;
- else if (number == 0)
- throw new ArgumentException ();
-
- parameters [number - 1] = value;
- }
- }
-
- //
- // State of the local variable `vi'.
- // If the local variable is a struct, use a non-zero `field_idx'
- // to check an individual field in it.
- //
- public bool this [VariableInfo vi, int field_idx]
- {
- get {
- if (vi.Number == -1)
- return true;
- else if (vi.Number == 0)
- throw new ArgumentException ();
-
- return locals [vi.Number + field_idx - 1];
- }
-
- set {
- if (vi.Number == -1)
- return;
- else if (vi.Number == 0)
- throw new ArgumentException ();
-
- locals [vi.Number + field_idx - 1] = value;
- }
- }
-
- // <summary>
- // Specifies when the current block returns.
- // If this is FlowReturns.UNREACHABLE, then control can never reach the
- // end of the method (so that we don't need to emit a return statement).
- // The same applies for FlowReturns.EXCEPTION, but in this case the return
- // value will never be used.
- // </summary>
- public FlowReturns Returns {
- get {
- return real_returns;
- }
-
- set {
- real_returns = value;
- }
- }
-
- // <summary>
- // Specifies whether control may return to our containing block
- // before reaching the end of this block. This happens if there
- // is a break/continue/goto/return in it.
- // This can also be used to find out whether the statement immediately
- // following the current block may be reached or not.
- // </summary>
- public FlowReturns Breaks {
- get {
- return real_breaks;
- }
-
- set {
- real_breaks = value;
- }
- }
-
- public bool AlwaysBreaks {
- get {
- return (Breaks == FlowReturns.ALWAYS) ||
- (Breaks == FlowReturns.EXCEPTION) ||
- (Breaks == FlowReturns.UNREACHABLE);
- }
- }
-
- public bool MayBreak {
- get {
- return Breaks != FlowReturns.NEVER;
- }
- }
-
- public bool AlwaysReturns {
- get {
- return (Returns == FlowReturns.ALWAYS) ||
- (Returns == FlowReturns.EXCEPTION);
- }
- }
-
- public bool MayReturn {
- get {
- return (Returns == FlowReturns.SOMETIMES) ||
- (Returns == FlowReturns.ALWAYS);
- }
- }
-
- // <summary>
- // Merge a child branching.
- // </summary>
- public FlowReturns MergeChildren (FlowBranching branching, UsageVector[] children)
- {
- MyBitVector new_locals = null;
- MyBitVector new_params = null;
-
- FlowReturns new_returns = FlowReturns.NEVER;
- FlowReturns new_breaks = FlowReturns.NEVER;
- bool new_returns_set = false, new_breaks_set = false;
-
- Report.Debug (2, "MERGING CHILDREN", branching, branching.Type,
- this, children.Length);
-
- foreach (UsageVector child in children) {
- Report.Debug (2, " MERGING CHILD", child, child.is_finally);
-
- if (!child.is_finally) {
- if (child.Breaks != FlowReturns.UNREACHABLE) {
- // If Returns is already set, perform an
- // `And' operation on it, otherwise just set just.
- if (!new_returns_set) {
- new_returns = child.Returns;
- new_returns_set = true;
- } else
- new_returns = AndFlowReturns (
- new_returns, child.Returns);
- }
-
- // If Breaks is already set, perform an
- // `And' operation on it, otherwise just set just.
- if (!new_breaks_set) {
- new_breaks = child.Breaks;
- new_breaks_set = true;
- } else
- new_breaks = AndFlowReturns (
- new_breaks, child.Breaks);
- }
-
- // Ignore unreachable children.
- if (child.Returns == FlowReturns.UNREACHABLE)
- continue;
-
- // A local variable is initialized after a flow branching if it
- // has been initialized in all its branches which do neither
- // always return or always throw an exception.
- //
- // If a branch may return, but does not always return, then we
- // can treat it like a never-returning branch here: control will
- // only reach the code position after the branching if we did not
- // return here.
- //
- // It's important to distinguish between always and sometimes
- // returning branches here:
- //
- // 1 int a;
- // 2 if (something) {
- // 3 return;
- // 4 a = 5;
- // 5 }
- // 6 Console.WriteLine (a);
- //
- // The if block in lines 3-4 always returns, so we must not look
- // at the initialization of `a' in line 4 - thus it'll still be
- // uninitialized in line 6.
- //
- // On the other hand, the following is allowed:
- //
- // 1 int a;
- // 2 if (something)
- // 3 a = 5;
- // 4 else
- // 5 return;
- // 6 Console.WriteLine (a);
- //
- // Here, `a' is initialized in line 3 and we must not look at
- // line 5 since it always returns.
- //
- if (child.is_finally) {
- if (new_locals == null)
- new_locals = locals.Clone ();
- new_locals.Or (child.locals);
-
- if (parameters != null) {
- if (new_params == null)
- new_params = parameters.Clone ();
- new_params.Or (child.parameters);
- }
-
- } else {
- if (!child.AlwaysReturns && !child.AlwaysBreaks) {
- if (new_locals != null)
- new_locals.And (child.locals);
- else {
- new_locals = locals.Clone ();
- new_locals.Or (child.locals);
- }
- } else if (children.Length == 1) {
- new_locals = locals.Clone ();
- new_locals.Or (child.locals);
- }
-
- // An `out' parameter must be assigned in all branches which do
- // not always throw an exception.
- if (parameters != null) {
- if (child.Breaks != FlowReturns.EXCEPTION) {
- if (new_params != null)
- new_params.And (child.parameters);
- else {
- new_params = parameters.Clone ();
- new_params.Or (child.parameters);
- }
- } else if (children.Length == 1) {
- new_params = parameters.Clone ();
- new_params.Or (child.parameters);
- }
- }
- }
- }
-
- Returns = new_returns;
- if ((branching.Type == FlowBranchingType.BLOCK) ||
- (branching.Type == FlowBranchingType.EXCEPTION) ||
- (new_breaks == FlowReturns.UNREACHABLE) ||
- (new_breaks == FlowReturns.EXCEPTION))
- Breaks = new_breaks;
- else if (branching.Type == FlowBranchingType.SWITCH_SECTION)
- Breaks = new_returns;
- else if (branching.Type == FlowBranchingType.SWITCH){
- if (new_breaks == FlowReturns.ALWAYS)
- Breaks = FlowReturns.ALWAYS;
- }
-
- //
- // We've now either reached the point after the branching or we will
- // never get there since we always return or always throw an exception.
- //
- // If we can reach the point after the branching, mark all locals and
- // parameters as initialized which have been initialized in all branches
- // we need to look at (see above).
- //
-
- if (((new_breaks != FlowReturns.ALWAYS) &&
- (new_breaks != FlowReturns.EXCEPTION) &&
- (new_breaks != FlowReturns.UNREACHABLE)) ||
- (children.Length == 1)) {
- if (new_locals != null)
- locals.Or (new_locals);
-
- if (new_params != null)
- parameters.Or (new_params);
- }
-
- Report.Debug (2, "MERGING CHILDREN DONE", branching.Type,
- new_params, new_locals, new_returns, new_breaks,
- branching.Infinite, branching.MayLeaveLoop, this);
-
- if (branching.Type == FlowBranchingType.SWITCH_SECTION) {
- if ((new_breaks != FlowReturns.ALWAYS) &&
- (new_breaks != FlowReturns.EXCEPTION) &&
- (new_breaks != FlowReturns.UNREACHABLE))
- Report.Error (163, branching.Location,
- "Control cannot fall through from one " +
- "case label to another");
- }
-
- if (branching.Infinite && !branching.MayLeaveLoop) {
- Report.Debug (1, "INFINITE", new_returns, new_breaks,
- Returns, Breaks, this);
-
- // We're actually infinite.
- if (new_returns == FlowReturns.NEVER) {
- Breaks = FlowReturns.UNREACHABLE;
- return FlowReturns.UNREACHABLE;
- }
-
- // If we're an infinite loop and do not break, the code after
- // the loop can never be reached. However, if we may return
- // from the loop, then we do always return (or stay in the loop
- // forever).
- if ((new_returns == FlowReturns.SOMETIMES) ||
- (new_returns == FlowReturns.ALWAYS)) {
- Returns = FlowReturns.ALWAYS;
- return FlowReturns.ALWAYS;
- }
- }
-
- if ((branching.Type == FlowBranchingType.LOOP_BLOCK) &&
- branching.MayLeaveLoop && (new_returns == FlowReturns.ALWAYS)) {
- Returns = FlowReturns.SOMETIMES;
- return FlowReturns.SOMETIMES;
- }
-
- return new_returns;
- }
-
- // <summary>
- // Tells control flow analysis that the current code position may be reached with
- // a forward jump from any of the origins listed in `origin_vectors' which is a
- // list of UsageVectors.
- //
- // This is used when resolving forward gotos - in the following example, the
- // variable `a' is uninitialized in line 8 becase this line may be reached via
- // the goto in line 4:
- //
- // 1 int a;
- //
- // 3 if (something)
- // 4 goto World;
- //
- // 6 a = 5;
- //
- // 7 World:
- // 8 Console.WriteLine (a);
- //
- // </summary>
- public void MergeJumpOrigins (ICollection origin_vectors)
- {
- Report.Debug (1, "MERGING JUMP ORIGIN", this);
-
- real_breaks = FlowReturns.NEVER;
- real_returns = FlowReturns.NEVER;
-
- foreach (UsageVector vector in origin_vectors) {
- Report.Debug (1, " MERGING JUMP ORIGIN", vector);
-
- locals.And (vector.locals);
- if (parameters != null)
- parameters.And (vector.parameters);
- Breaks = AndFlowReturns (Breaks, vector.Breaks);
- Returns = AndFlowReturns (Returns, vector.Returns);
- }
-
- Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
- }
-
- // <summary>
- // This is used at the beginning of a finally block if there were
- // any return statements in the try block or one of the catch blocks.
- // </summary>
- public void MergeFinallyOrigins (ICollection finally_vectors)
- {
- Report.Debug (1, "MERGING FINALLY ORIGIN", this);
-
- real_breaks = FlowReturns.NEVER;
-
- foreach (UsageVector vector in finally_vectors) {
- Report.Debug (1, " MERGING FINALLY ORIGIN", vector);
-
- if (parameters != null)
- parameters.And (vector.parameters);
- Breaks = AndFlowReturns (Breaks, vector.Breaks);
- }
-
- is_finally = true;
-
- Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
- }
-
- public void CheckOutParameters (FlowBranching branching)
- {
- if (parameters != null)
- branching.CheckOutParameters (parameters, branching.Location);
- }
-
- // <summary>
- // Performs an `or' operation on the locals and the parameters.
- // </summary>
- public void Or (UsageVector new_vector)
- {
- locals.Or (new_vector.locals);
- if (parameters != null)
- parameters.Or (new_vector.parameters);
- }
-
- // <summary>
- // Performs an `and' operation on the locals.
- // </summary>
- public void AndLocals (UsageVector new_vector)
- {
- locals.And (new_vector.locals);
- }
-
- // <summary>
- // Returns a deep copy of the parameters.
- // </summary>
- public MyBitVector Parameters {
- get {
- if (parameters != null)
- return parameters.Clone ();
- else
- return null;
- }
- }
-
- // <summary>
- // Returns a deep copy of the locals.
- // </summary>
- public MyBitVector Locals {
- get {
- return locals.Clone ();
- }
- }
-
- //
- // Debugging stuff.
- //
-
- public override string ToString ()
- {
- StringBuilder sb = new StringBuilder ();
-
- sb.Append ("Vector (");
- sb.Append (id);
- sb.Append (",");
- sb.Append (Returns);
- sb.Append (",");
- sb.Append (Breaks);
- if (parameters != null) {
- sb.Append (" - ");
- sb.Append (parameters);
- }
- sb.Append (" - ");
- sb.Append (locals);
- sb.Append (")");
-
- return sb.ToString ();
- }
- }
-
- FlowBranching (FlowBranchingType type, Location loc)
- {
- this.Block = null;
- this.Location = loc;
- this.Type = type;
- id = ++next_id;
- }
-
- // <summary>
- // Creates a new flow branching for `block'.
- // This is used from Block.Resolve to create the top-level branching of
- // the block.
- // </summary>
- public FlowBranching (Block block, InternalParameters ip, Location loc)
- : this (FlowBranchingType.BLOCK, loc)
- {
- Block = block;
- Parent = null;
-
- int count = (ip != null) ? ip.Count : 0;
-
- param_info = ip;
- param_map = new int [count];
- struct_params = new MyStructInfo [count];
- num_params = 0;
-
- for (int i = 0; i < count; i++) {
- Parameter.Modifier mod = param_info.ParameterModifier (i);
-
- if ((mod & Parameter.Modifier.OUT) == 0)
- continue;
-
- param_map [i] = ++num_params;
-
- Type param_type = param_info.ParameterType (i);
-
- struct_params [i] = MyStructInfo.GetStructInfo (param_type);
- if (struct_params [i] != null)
- num_params += struct_params [i].Count;
- }
-
- AddSibling (new UsageVector (null, num_params, block.CountVariables));
- }
-
- // <summary>
- // Creates a new flow branching which is contained in `parent'.
- // You should only pass non-null for the `block' argument if this block
- // introduces any new variables - in this case, we need to create a new
- // usage vector with a different size than our parent's one.
- // </summary>
- public FlowBranching (FlowBranching parent, FlowBranchingType type,
- Block block, Location loc)
- : this (type, loc)
- {
- Parent = parent;
- Block = block;
-
- if (parent != null) {
- param_info = parent.param_info;
- param_map = parent.param_map;
- struct_params = parent.struct_params;
- num_params = parent.num_params;
- }
-
- UsageVector vector;
- if (Block != null)
- vector = new UsageVector (parent.CurrentUsageVector, num_params,
- Block.CountVariables);
- else
- vector = new UsageVector (Parent.CurrentUsageVector);
-
- AddSibling (vector);
-
- switch (Type) {
- case FlowBranchingType.EXCEPTION:
- finally_vectors = new ArrayList ();
- break;
-
- default:
- break;
- }
- }
-
- void AddSibling (UsageVector uv)
- {
- if (Siblings != null) {
- UsageVector[] ns = new UsageVector [Siblings.Length + 1];
- for (int i = 0; i < Siblings.Length; ++i)
- ns [i] = Siblings [i];
- Siblings = ns;
- } else {
- Siblings = new UsageVector [1];
- }
- Siblings [Siblings.Length - 1] = uv;
- }
-
- // <summary>
- // Returns the branching's current usage vector.
- // </summary>
- public UsageVector CurrentUsageVector
- {
- get {
- return Siblings [Siblings.Length - 1];
- }
- }
-
- // <summary>
- // Creates a sibling of the current usage vector.
- // </summary>
- public void CreateSibling ()
- {
- AddSibling (new UsageVector (Parent.CurrentUsageVector));
-
- Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
- }
-
- // <summary>
- // Creates a sibling for a `finally' block.
- // </summary>
- public void CreateSiblingForFinally ()
- {
- if (Type != FlowBranchingType.EXCEPTION)
- throw new NotSupportedException ();
-
- CreateSibling ();
-
- CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
- }
-
- // <summary>
- // Check whether all `out' parameters have been assigned.
- // </summary>
- public void CheckOutParameters (MyBitVector parameters, Location loc)
- {
- if (InTryBlock ())
- return;
-
- for (int i = 0; i < param_map.Length; i++) {
- int index = param_map [i];
-
- if (index == 0)
- continue;
-
- if (parameters [index - 1])
- continue;
-
- // If it's a struct, we must ensure that all its fields have
- // been assigned. If the struct has any non-public fields, this
- // can only be done by assigning the whole struct.
-
- MyStructInfo struct_info = struct_params [i];
- if ((struct_info == null) || struct_info.HasNonPublicFields) {
- Report.Error (
- 177, loc, "The out parameter `" +
- param_info.ParameterName (i) + "' must be " +
- "assigned before control leave the current method.");
- param_map [i] = 0;
- continue;
- }
-
-
- for (int j = 0; j < struct_info.Count; j++) {
- if (!parameters [index + j]) {
- Report.Error (
- 177, loc, "The out parameter `" +
- param_info.ParameterName (i) + "' must be " +
- "assigned before control leaves the current method.");
- param_map [i] = 0;
- break;
- }
- }
- }
- }
-
- // <summary>
- // Merge a child branching.
- // </summary>
- public FlowReturns MergeChild (FlowBranching child)
- {
- FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings);
-
- if ((child.Type != FlowBranchingType.LOOP_BLOCK) &&
- (child.Type != FlowBranchingType.SWITCH_SECTION))
- MayLeaveLoop |= child.MayLeaveLoop;
- else
- MayLeaveLoop = false;
-
- return returns;
- }
-
- // <summary>
- // Does the toplevel merging.
- // </summary>
- public FlowReturns MergeTopBlock ()
- {
- if ((Type != FlowBranchingType.BLOCK) || (Block == null))
- throw new NotSupportedException ();
-
- UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
-
- Report.Debug (1, "MERGING TOP BLOCK", Location, vector);
-
- vector.MergeChildren (this, Siblings);
-
- if (Siblings.Length == 1)
- Siblings [0] = vector;
- else {
- Siblings = null;
- AddSibling (vector);
- }
-
- Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector);
-
- if (vector.Breaks != FlowReturns.EXCEPTION) {
- if (!vector.AlwaysBreaks)
- CheckOutParameters (CurrentUsageVector.Parameters, Location);
- return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns;
- } else
- return FlowReturns.EXCEPTION;
- }
-
- public bool InTryBlock ()
- {
- if (finally_vectors != null)
- return true;
- else if (Parent != null)
- return Parent.InTryBlock ();
- else
- return false;
- }
-
- public void AddFinallyVector (UsageVector vector)
- {
- if (finally_vectors != null) {
- finally_vectors.Add (vector.Clone ());
- return;
- }
-
- if (Parent != null)
- Parent.AddFinallyVector (vector);
- else
- throw new NotSupportedException ();
- }
-
- public bool IsVariableAssigned (VariableInfo vi)
- {
- if (CurrentUsageVector.AlwaysBreaks)
- return true;
- else
- return CurrentUsageVector [vi, 0];
- }
-
- public bool IsVariableAssigned (VariableInfo vi, int field_idx)
- {
- if (CurrentUsageVector.AlwaysBreaks)
- return true;
- else
- return CurrentUsageVector [vi, field_idx];
- }
-
- public void SetVariableAssigned (VariableInfo vi)
- {
- if (CurrentUsageVector.AlwaysBreaks)
- return;
-
- CurrentUsageVector [vi, 0] = true;
- }
-
- public void SetVariableAssigned (VariableInfo vi, int field_idx)
- {
- if (CurrentUsageVector.AlwaysBreaks)
- return;
-
- CurrentUsageVector [vi, field_idx] = true;
- }
-
- public bool IsParameterAssigned (int number)
- {
- int index = param_map [number];
-
- if (index == 0)
- return true;
-
- if (CurrentUsageVector [index])
- return true;
-
- // Parameter is not assigned, so check whether it's a struct.
- // If it's either not a struct or a struct which non-public
- // fields, return false.
- MyStructInfo struct_info = struct_params [number];
- if ((struct_info == null) || struct_info.HasNonPublicFields)
- return false;
-
- // Ok, so each field must be assigned.
- for (int i = 0; i < struct_info.Count; i++)
- if (!CurrentUsageVector [index + i])
- return false;
-
- return true;
- }
-
- public bool IsParameterAssigned (int number, string field_name)
- {
- int index = param_map [number];
-
- if (index == 0)
- return true;
-
- MyStructInfo info = (MyStructInfo) struct_params [number];
- if (info == null)
- return true;
-
- int field_idx = info [field_name];
-
- return CurrentUsageVector [index + field_idx];
- }
-
- public void SetParameterAssigned (int number)
- {
- if (param_map [number] == 0)
- return;
-
- if (!CurrentUsageVector.AlwaysBreaks)
- CurrentUsageVector [param_map [number]] = true;
- }
-
- public void SetParameterAssigned (int number, string field_name)
- {
- int index = param_map [number];
-
- if (index == 0)
- return;
-
- MyStructInfo info = (MyStructInfo) struct_params [number];
- if (info == null)
- return;
-
- int field_idx = info [field_name];
-
- if (!CurrentUsageVector.AlwaysBreaks)
- CurrentUsageVector [index + field_idx] = true;
- }
-
- public bool IsReachable ()
- {
- bool reachable;
-
- switch (Type) {
- case FlowBranchingType.SWITCH_SECTION:
- // The code following a switch block is reachable unless the switch
- // block always returns.
- reachable = !CurrentUsageVector.AlwaysReturns;
- break;
-
- case FlowBranchingType.LOOP_BLOCK:
- // The code following a loop is reachable unless the loop always
- // returns or it's an infinite loop without any `break's in it.
- reachable = !CurrentUsageVector.AlwaysReturns &&
- (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE);
- break;
-
- default:
- // The code following a block or exception is reachable unless the
- // block either always returns or always breaks.
- if (MayLeaveLoop)
- reachable = true;
- else
- reachable = !CurrentUsageVector.AlwaysBreaks &&
- !CurrentUsageVector.AlwaysReturns;
- break;
- }
-
- Report.Debug (1, "REACHABLE", this, Type, CurrentUsageVector.Returns,
- CurrentUsageVector.Breaks, CurrentUsageVector, MayLeaveLoop,
- reachable);
-
- return reachable;
- }
-
- public override string ToString ()
- {
- StringBuilder sb = new StringBuilder ("FlowBranching (");
-
- sb.Append (id);
- sb.Append (",");
- sb.Append (Type);
- if (Block != null) {
- sb.Append (" - ");
- sb.Append (Block.ID);
- sb.Append (" - ");
- sb.Append (Block.StartLocation);
- }
- sb.Append (" - ");
- sb.Append (Siblings.Length);
- sb.Append (" - ");
- sb.Append (CurrentUsageVector);
- sb.Append (")");
- return sb.ToString ();
- }
- }
-
- public class MyStructInfo {
- public readonly Type Type;
- public readonly FieldInfo[] Fields;
- public readonly FieldInfo[] NonPublicFields;
- public readonly int Count;
- public readonly int CountNonPublic;
- public readonly bool HasNonPublicFields;
-
- private static Hashtable field_type_hash = new Hashtable ();
- private Hashtable field_hash;
-
- // Private constructor. To save memory usage, we only need to create one instance
- // of this class per struct type.
- private MyStructInfo (Type type)
- {
- this.Type = type;
-
- if (type is TypeBuilder) {
- TypeContainer tc = TypeManager.LookupTypeContainer (type);
-
- Field [] fields = tc.Fields;
- if (fields != null) {
- foreach (Field field in fields) {
- if ((field.ModFlags & Modifiers.STATIC) != 0)
- continue;
- if ((field.ModFlags & Modifiers.PUBLIC) != 0)
- ++Count;
- else
- ++CountNonPublic;
- }
- }
-
- Fields = new FieldInfo [Count];
- NonPublicFields = new FieldInfo [CountNonPublic];
-
- Count = CountNonPublic = 0;
- if (fields != null) {
- foreach (Field field in fields) {
- if ((field.ModFlags & Modifiers.STATIC) != 0)
- continue;
- if ((field.ModFlags & Modifiers.PUBLIC) != 0)
- Fields [Count++] = field.FieldBuilder;
- else
- NonPublicFields [CountNonPublic++] =
- field.FieldBuilder;
- }
- }
-
- } else {
- Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public);
- Count = Fields.Length;
-
- NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic);
- CountNonPublic = NonPublicFields.Length;
- }
-
- Count += NonPublicFields.Length;
-
- int number = 0;
- field_hash = new Hashtable ();
- foreach (FieldInfo field in Fields)
- field_hash.Add (field.Name, ++number);
-
- if (NonPublicFields.Length != 0)
- HasNonPublicFields = true;
-
- foreach (FieldInfo field in NonPublicFields)
- field_hash.Add (field.Name, ++number);
- }
-
- public int this [string name] {
- get {
- if (field_hash.Contains (name))
- return (int) field_hash [name];
- else
- return 0;
- }
- }
-
- public FieldInfo this [int index] {
- get {
- if (index >= Fields.Length)
- return NonPublicFields [index - Fields.Length];
- else
- return Fields [index];
- }
- }
-
- public static MyStructInfo GetStructInfo (Type type)
- {
- if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type))
- return null;
-
- if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type))
- return null;
-
- MyStructInfo info = (MyStructInfo) field_type_hash [type];
- if (info != null)
- return info;
-
- info = new MyStructInfo (type);
- field_type_hash.Add (type, info);
- return info;
- }
-
- public static MyStructInfo GetStructInfo (TypeContainer tc)
- {
- MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder];
- if (info != null)
- return info;
-
- info = new MyStructInfo (tc.TypeBuilder);
- field_type_hash.Add (tc.TypeBuilder, info);
- return info;
}
}
-
- public class VariableInfo : IVariable {
+
+ public class LocalInfo {
public Expression Type;
+
+ //
+ // Most of the time a variable will be stored in a LocalBuilder
+ //
+ // But sometimes, it will be stored in a field. The context of the field will
+ // be stored in the EmitContext
+ //
+ //
public LocalBuilder LocalBuilder;
+ public FieldBuilder FieldBuilder;
+
public Type VariableType;
public readonly string Name;
public readonly Location Location;
- public readonly int Block;
+ public readonly Block Block;
- public int Number;
-
- public bool Used;
- public bool Assigned;
- public bool ReadOnly;
+ public VariableInfo VariableInfo;
+
+ enum Flags : byte {
+ Used = 1,
+ ReadOnly = 2,
+ Fixed = 4
+ }
+
+ Flags flags;
- public VariableInfo (Expression type, string name, int block, Location l)
+ public LocalInfo (Expression type, string name, Block block, Location l)
{
Type = type;
Name = name;
Block = block;
- LocalBuilder = null;
Location = l;
}
- public VariableInfo (TypeContainer tc, int block, Location l)
+ public LocalInfo (TypeContainer tc, Block block, Location l)
{
VariableType = tc.TypeBuilder;
- struct_info = MyStructInfo.GetStructInfo (tc);
Block = block;
- LocalBuilder = null;
Location = l;
}
- MyStructInfo struct_info;
- public MyStructInfo StructInfo {
- get {
- return struct_info;
- }
- }
-
- public bool IsAssigned (EmitContext ec, Location loc)
- {
- if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
- return true;
-
- MyStructInfo struct_info = StructInfo;
- if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
- Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
- ec.CurrentBranching.SetVariableAssigned (this);
- return false;
- }
-
- int count = struct_info.Count;
-
- for (int i = 0; i < count; i++) {
- if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
- if (Name != null) {
- Report.Error (165, loc,
- "Use of unassigned local variable `" +
- Name + "'");
- ec.CurrentBranching.SetVariableAssigned (this);
- return false;
- }
-
- FieldInfo field = struct_info [i];
- Report.Error (171, loc,
- "Field `" + TypeManager.CSharpName (VariableType) +
- "." + field.Name + "' must be fully initialized " +
- "before control leaves the constructor");
- return false;
- }
- }
-
- return true;
- }
-
- public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
+ public bool IsThisAssigned (EmitContext ec, Location loc)
{
- if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) ||
- (struct_info == null))
- return true;
+ if (VariableInfo == null)
+ throw new Exception ();
- int field_idx = StructInfo [name];
- if (field_idx == 0)
+ if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
return true;
- if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) {
- Report.Error (170, loc,
- "Use of possibly unassigned field `" + name + "'");
- ec.CurrentBranching.SetVariableAssigned (this, field_idx);
- return false;
- }
-
- return true;
+ return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
}
- public void SetAssigned (EmitContext ec)
+ public bool IsAssigned (EmitContext ec)
{
- if (ec.DoFlowAnalysis)
- ec.CurrentBranching.SetVariableAssigned (this);
- }
+ if (VariableInfo == null)
+ throw new Exception ();
- public void SetFieldAssigned (EmitContext ec, string name)
- {
- if (ec.DoFlowAnalysis && (struct_info != null))
- ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]);
+ return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
}
public bool Resolve (DeclSpace decl)
{
- if (struct_info != null)
- return true;
-
if (VariableType == null)
VariableType = decl.ResolveType (Type, false, Location);
- if (VariableType == null)
+ if (VariableType == TypeManager.void_type) {
+ Report.Error (1547, Location,
+ "Keyword 'void' cannot be used in this context");
return false;
+ }
- struct_info = MyStructInfo.GetStructInfo (VariableType);
+ if (VariableType == null)
+ return false;
return true;
}
public void MakePinned ()
{
TypeManager.MakePinned (LocalBuilder);
+ flags |= Flags.Fixed;
+ }
+
+ public bool IsFixed {
+ get {
+ if (((flags & Flags.Fixed) != 0) || TypeManager.IsValueType (VariableType))
+ return true;
+
+ return false;
+ }
}
public override string ToString ()
{
- return "VariableInfo (" + Number + "," + Type + "," + Location + ")";
+ return String.Format ("LocalInfo ({0},{1},{2},{3})",
+ Name, Type, VariableInfo, Location);
+ }
+
+ public bool Used {
+ get {
+ return (flags & Flags.Used) != 0;
+ }
+ set {
+ flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
+ }
+ }
+
+ public bool ReadOnly {
+ get {
+ return (flags & Flags.ReadOnly) != 0;
+ }
+ set {
+ flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
+ }
}
+
+
+
}
/// <summary>
///
/// Implicit blocks are used as labels or to introduce variable
/// declarations.
+ ///
+ /// Top-level blocks derive from Block, and they are called ToplevelBlock
+ /// they contain extra information that is not necessary on normal blocks.
/// </remarks>
public class Block : Statement {
public readonly Block Parent;
- public readonly bool Implicit;
public readonly Location StartLocation;
public Location EndLocation = Location.Null;
+ [Flags]
+ public enum Flags : byte {
+ Implicit = 1,
+ Unchecked = 2,
+ BlockUsed = 4,
+ VariablesInitialized = 8,
+ HasRet = 16,
+ IsDestructor = 32
+ }
+ Flags flags;
+
+ public bool Implicit {
+ get {
+ return (flags & Flags.Implicit) != 0;
+ }
+ }
+
+ public bool Unchecked {
+ get {
+ return (flags & Flags.Unchecked) != 0;
+ }
+ set {
+ flags |= Flags.Unchecked;
+ }
+ }
+
//
// The statements in this block
//
ArrayList statements;
+ int num_statements;
//
// An array of Blocks. We keep track of children just
Hashtable constants;
//
- // Maps variable names to ILGenerator.LocalBuilders
+ // If this is a switch section, the enclosing switch block.
//
- Hashtable local_builders;
-
- bool used = false;
+ Block switch_block;
static int id;
int this_id;
public Block (Block parent)
- : this (parent, false, Location.Null, Location.Null)
+ : this (parent, (Flags) 0, Location.Null, Location.Null)
{ }
- public Block (Block parent, bool implicit_block)
- : this (parent, implicit_block, Location.Null, Location.Null)
+ public Block (Block parent, Flags flags)
+ : this (parent, flags, 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, Flags flags, Parameters parameters)
+ : this (parent, flags, parameters, Location.Null, Location.Null)
{ }
public Block (Block parent, Location start, Location end)
- : this (parent, false, start, end)
+ : this (parent, (Flags) 0, start, end)
{ }
public Block (Block parent, Parameters parameters, Location start, Location end)
- : this (parent, false, parameters, start, end)
+ : this (parent, (Flags) 0, 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, Flags flags, Location start, Location end)
+ : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
{ }
- public Block (Block parent, bool implicit_block, Parameters parameters,
+ public Block (Block parent, Flags flags, Parameters parameters,
Location start, Location end)
{
if (parent != null)
parent.AddChild (this);
this.Parent = parent;
- this.Implicit = implicit_block;
+ this.flags = flags;
this.parameters = parameters;
this.StartLocation = start;
this.EndLocation = end;
this.loc = start;
this_id = id++;
statements = new ArrayList ();
+
+ if (parent != null && Implicit) {
+ if (parent.child_variable_names == null)
+ parent.child_variable_names = new Hashtable();
+ // share with parent
+ child_variable_names = parent.child_variable_names;
+ }
+
+ }
+
+ public Block CreateSwitchBlock (Location start)
+ {
+ Block new_block = new Block (this, start, start);
+ new_block.switch_block = this;
+ return new_block;
}
public int ID {
/// otherwise.
/// </returns>
///
- public bool AddLabel (string name, LabeledStatement target)
+ 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 '{0}' is a duplicate",
+ name);
+ return false;
+ }
+
+ if (!Implicit)
+ break;
+
+ cur = cur.Parent;
+ }
+
+ while (cur != null) {
+ if (cur.DoLookupLabel (name) != null) {
+ Report.Error (
+ 158, loc,
+ "The label '{0}' shadows another label " +
+ "by the same name in a containing scope.",
+ name);
+ 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 '{0}' shadows another " +
+ "label by the same name in a " +
+ "containing scope.",
+ name);
+ return false;
+ }
+ }
+
+
+ cur = cur.Parent;
+ }
+
if (labels == null)
labels = new Hashtable ();
- if (labels.Contains (name))
- return false;
-
+
labels.Add (name, target);
return true;
}
public LabeledStatement LookupLabel (string name)
{
- if (labels != null){
- if (labels.Contains (name))
- return ((LabeledStatement) labels [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;
}
- if (Parent != null)
- return Parent.LookupLabel (name);
+ return null;
+ }
+
+ LabeledStatement DoLookupLabel (string name)
+ {
+ if (switch_block != null)
+ return switch_block.LookupLabel (name);
+
+ if (labels != null)
+ if (labels.Contains (name))
+ return ((LabeledStatement) labels [name]);
return null;
}
- VariableInfo this_variable = null;
+ LocalInfo this_variable = null;
// <summary>
// Returns the "this" instance variable of this block.
// See AddThisVariable() for more information.
// </summary>
- public VariableInfo ThisVariable {
+ public LocalInfo ThisVariable {
get {
if (this_variable != null)
return this_variable;
child_variable_names.Add (name, true);
}
- // <summary>
- // Marks all variables from block @block and all its children as being
- // used in a child block.
- // </summary>
- public void AddChildVariableNames (Block block)
- {
- if (block.Variables != null) {
- foreach (string name in block.Variables.Keys)
- AddChildVariableName (name);
- }
-
- if (block.children != null) {
- foreach (Block child in block.children)
- AddChildVariableNames (child);
- }
- }
-
// <summary>
// Checks whether a variable name has already been used in a child block.
// </summary>
// analysis code to ensure that it's been fully initialized before control
// leaves the constructor.
// </summary>
- public VariableInfo AddThisVariable (TypeContainer tc, Location l)
+ public LocalInfo AddThisVariable (TypeContainer tc, Location l)
{
if (this_variable != null)
return this_variable;
- this_variable = new VariableInfo (tc, ID, l);
-
if (variables == null)
variables = new Hashtable ();
+
+ this_variable = new LocalInfo (tc, this, l);
+ this_variable.Used = true;
+
variables.Add ("this", this_variable);
return this_variable;
}
- public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
+ public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
{
if (variables == null)
variables = new Hashtable ();
- VariableInfo vi = GetVariableInfo (name);
+ LocalInfo vi = GetLocalInfo (name);
if (vi != null) {
- if (vi.Block != ID)
+ if (vi.Block != this)
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 " +
}
if (pars != null) {
- int idx = 0;
+ int idx;
Parameter p = pars.GetParameterByName (name, out idx);
if (p != null) {
Report.Error (136, l, "A local variable named `" + name + "' " +
return null;
}
}
-
- vi = new VariableInfo (type, name, ID, l);
+
+ vi = new LocalInfo (type, name, this, l);
variables.Add (name, vi);
- if (variables_initialized)
+ // Mark 'name' as "used by a child block" in every surrounding block
+ Block cur = this;
+ while (cur != null && cur.Implicit)
+ cur = cur.Parent;
+ if (cur != null)
+ for (Block par = cur.Parent; par != null; par = par.Parent)
+ par.AddChildVariableName (name);
+
+ if ((flags & Flags.VariablesInitialized) != 0)
throw new Exception ();
// Console.WriteLine ("Adding {0} to {1}", name, ID);
}
}
- public VariableInfo GetVariableInfo (string name)
+ public LocalInfo GetLocalInfo (string name)
{
- if (variables != null) {
- object temp;
- temp = variables [name];
-
- if (temp != null){
- return (VariableInfo) temp;
+ for (Block b = this; b != null; b = b.Parent) {
+ if (b.variables != null) {
+ LocalInfo ret = b.variables [name] as LocalInfo;
+ if (ret != null)
+ return ret;
}
}
-
- if (Parent != null)
- return Parent.GetVariableInfo (name);
-
return null;
}
-
+
public Expression GetVariableType (string name)
{
- VariableInfo vi = GetVariableInfo (name);
+ LocalInfo vi = GetLocalInfo (name);
if (vi != null)
return vi.Type;
public Expression GetConstantExpression (string name)
{
- if (constants != null) {
- object temp;
- temp = constants [name];
-
- if (temp != null)
- return (Expression) temp;
+ for (Block b = this; b != null; b = b.Parent) {
+ if (b.constants != null) {
+ Expression ret = b.constants [name] as Expression;
+ if (ret != null)
+ return ret;
+ }
}
-
- if (Parent != null)
- return Parent.GetConstantExpression (name);
-
return null;
}
return e != null;
}
- /// <summary>
- /// Use to fetch the statement associated with this label
- /// </summary>
- 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;
+ Block b = this;
+ while (b.Parent != null)
+ b = b.Parent;
+ return b.parameters;
}
}
public void AddStatement (Statement s)
{
statements.Add (s);
- used = true;
+ flags |= Flags.BlockUsed;
}
public bool Used {
get {
- return used;
+ return (flags & Flags.BlockUsed) != 0;
}
}
public void Use ()
{
- used = true;
+ flags |= Flags.BlockUsed;
}
- bool variables_initialized = false;
- int count_variables = 0, first_variable = 0;
-
- void UpdateVariableInfo (EmitContext ec)
- {
- DeclSpace ds = ec.DeclSpace;
+ public bool HasRet {
+ get {
+ return (flags & Flags.HasRet) != 0;
+ }
+ }
- first_variable = 0;
+ public bool IsDestructor {
+ get {
+ return (flags & Flags.IsDestructor) != 0;
+ }
+ }
- if (Parent != null)
- first_variable += Parent.CountVariables;
+ public void SetDestructor ()
+ {
+ flags |= Flags.IsDestructor;
+ }
- count_variables = first_variable;
- if (variables != null) {
- foreach (VariableInfo vi in variables.Values) {
- if (!vi.Resolve (ds)) {
- vi.Number = -1;
- continue;
- }
+ VariableMap param_map, local_map;
- vi.Number = ++count_variables;
+ public VariableMap ParameterMap {
+ get {
+ if ((flags & Flags.VariablesInitialized) == 0)
+ throw new Exception ();
- if (vi.StructInfo != null)
- count_variables += vi.StructInfo.Count;
- }
+ return param_map;
}
-
- variables_initialized = true;
}
- //
- // <returns>
- // The number of local variables in this block
- // </returns>
- public int CountVariables
- {
+ public VariableMap LocalMap {
get {
- if (!variables_initialized)
+ if ((flags & Flags.VariablesInitialized) == 0)
throw new Exception ();
- return count_variables;
+ return local_map;
}
}
+ public bool LiftVariable (LocalInfo local_info)
+ {
+ return false;
+ }
+
/// <summary>
/// Emits the variable declarations and labels.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
- public void EmitMeta (EmitContext ec, Block toplevel)
+ public void EmitMeta (EmitContext ec, InternalParameters ip)
{
- DeclSpace ds = ec.DeclSpace;
ILGenerator ig = ec.ig;
- if (!variables_initialized)
- UpdateVariableInfo (ec);
+ //
+ // Compute the VariableMap's.
+ //
+ // Unfortunately, we don't know the type when adding variables with
+ // AddVariable(), so we need to compute this info here.
+ //
+
+ LocalInfo[] locals;
+ if (variables != null) {
+ foreach (LocalInfo li in variables.Values)
+ li.Resolve (ec.DeclSpace);
+
+ locals = new LocalInfo [variables.Count];
+ variables.Values.CopyTo (locals, 0);
+ } else
+ locals = new LocalInfo [0];
+
+ if (Parent != null)
+ local_map = new VariableMap (Parent.LocalMap, locals);
+ else
+ local_map = new VariableMap (locals);
+
+ param_map = new VariableMap (ip);
+ flags |= Flags.VariablesInitialized;
+ bool old_check_state = ec.ConstantCheckState;
+ ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
+ bool remap_locals = ec.RemapToProxy;
+
//
// Process this block variables
//
if (variables != null){
- local_builders = new Hashtable ();
-
foreach (DictionaryEntry de in variables){
string name = (string) de.Key;
- VariableInfo vi = (VariableInfo) de.Value;
-
+ LocalInfo vi = (LocalInfo) de.Value;
+
if (vi.VariableType == null)
continue;
- vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+ Type variable_type = vi.VariableType;
- if (CodeGen.SymbolWriter != null)
- vi.LocalBuilder.SetLocalSymInfo (name);
+ if (variable_type.IsPointer){
+ //
+ // Am not really convinced that this test is required (Microsoft does it)
+ // but the fact is that you would not be able to use the pointer variable
+ // *anyways*
+ //
+ if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
+ vi.Location))
+ continue;
+ }
+
+ if (remap_locals)
+ vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
+ else
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
if (constants == null)
continue;
if (cv == null)
continue;
+ ec.CurrentBlock = this;
Expression e = cv.Resolve (ec);
if (e == null)
continue;
- if (!(e is Constant)){
+ Constant ce = e as Constant;
+ if (ce == null){
Report.Error (133, vi.Location,
"The expression being assigned to `" +
name + "' must be constant (" + e + ")");
continue;
}
+ if (e.Type != variable_type){
+ e = Const.ChangeType (vi.Location, ce, variable_type);
+ if (e == null)
+ continue;
+ }
+
constants.Remove (name);
constants.Add (name, e);
}
}
+ ec.ConstantCheckState = old_check_state;
//
// Now, handle the children
//
if (children != null){
foreach (Block b in children)
- b.EmitMeta (ec, toplevel);
+ b.EmitMeta (ec, ip);
}
}
- public void UsageWarning ()
+ void UsageWarning (FlowBranching.UsageVector vector)
{
string name;
-
+
if (variables != null){
foreach (DictionaryEntry de in variables){
- VariableInfo vi = (VariableInfo) de.Value;
+ LocalInfo vi = (LocalInfo) de.Value;
if (vi.Used)
continue;
name = (string) de.Key;
-
- if (vi.Assigned){
+
+ if (vector.IsAssigned (vi.VariableInfo)){
Report.Warning (
219, vi.Location, "The variable `" + name +
"' is assigned but its value is 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;
+ int errors = Report.Errors;
+
ec.CurrentBlock = this;
ec.StartFlowBranching (this);
- Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
+ Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
- if (!variables_initialized)
- UpdateVariableInfo (ec);
-
- ArrayList new_statements = new ArrayList ();
bool unreachable = false, warning_shown = false;
- foreach (Statement s in statements){
+ int statement_count = statements.Count;
+ for (int ix = 0; ix < statement_count; ix++){
+ Statement s = (Statement) statements [ix];
+
if (unreachable && !(s is LabeledStatement)) {
- if (!warning_shown && !(s is EmptyStatement)) {
+ if (s == EmptyStatement.Value)
+ s.loc = EndLocation;
+
+ if (!s.ResolveUnreachable (ec, !warning_shown))
+ ok = false;
+
+ if (s != EmptyStatement.Value)
warning_shown = true;
- Warning_DeadCodeFound (s.loc);
- }
+ else
+ s.loc = Location.Null;
+ statements [ix] = EmptyStatement.Value;
continue;
}
if (s.Resolve (ec) == false) {
ok = false;
+ statements [ix] = EmptyStatement.Value;
continue;
}
+ num_statements = ix + 1;
+
if (s is LabeledStatement)
unreachable = false;
else
- unreachable = ! ec.CurrentBranching.IsReachable ();
-
- new_statements.Add (s);
+ unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
}
- statements = new_statements;
+ Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
+ ec.CurrentBranching, statement_count, num_statements);
- Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
- FlowReturns returns = ec.EndFlowBranching ();
+ FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
+
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))
+ if ((this_variable != null) &&
+ (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) &&
+ !this_variable.IsThisAssigned (ec, loc))
ok = false;
if ((labels != null) && (RootContext.WarningLevel >= 2)) {
"This label has not been referenced");
}
- if ((returns == FlowReturns.ALWAYS) ||
- (returns == FlowReturns.EXCEPTION) ||
- (returns == FlowReturns.UNREACHABLE))
- has_ret = true;
+ Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
+
+ if ((vector.Reachability.Returns == FlowBranching.FlowReturns.Always) ||
+ (vector.Reachability.Throws == FlowBranching.FlowReturns.Always) ||
+ (vector.Reachability.Reachable == FlowBranching.FlowReturns.Never))
+ flags |= Flags.HasRet;
+
+ if (ok && (errors == Report.Errors)) {
+ if (RootContext.WarningLevel >= 3)
+ UsageWarning (vector);
+ }
return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
+ {
+ for (int ix = 0; ix < num_statements; ix++){
+ Statement s = (Statement) statements [ix];
+
+ // Check whether we are the last statement in a
+ // top-level block.
+
+ if ((Parent == null) && (ix+1 == num_statements))
+ ec.IsLastStatement = true;
+ else
+ ec.IsLastStatement = false;
+
+ s.Emit (ec);
+ }
+ }
+
+ public override void Emit (EmitContext ec)
{
Block prev_block = ec.CurrentBlock;
ec.CurrentBlock = this;
- ec.Mark (StartLocation);
- foreach (Statement s in statements)
- s.Emit (ec);
- ec.Mark (EndLocation);
-
+ bool emit_debug_info = (CodeGen.SymbolWriter != null);
+ bool is_lexical_block = !Implicit && (Parent != null);
+
+ if (emit_debug_info) {
+ if (is_lexical_block)
+ ec.ig.BeginScope ();
+
+ if (variables != null) {
+ foreach (DictionaryEntry de in variables) {
+ string name = (string) de.Key;
+ LocalInfo vi = (LocalInfo) de.Value;
+
+ if (vi.LocalBuilder == null)
+ continue;
+
+ vi.LocalBuilder.SetLocalSymInfo (name);
+ }
+ }
+ }
+
+ ec.Mark (StartLocation, true);
+ DoEmit (ec);
+ ec.Mark (EndLocation, true);
+
+ if (emit_debug_info && is_lexical_block)
+ ec.ig.EndScope ();
+
ec.CurrentBlock = prev_block;
- return has_ret;
}
}
+ //
+ //
+ public class ToplevelBlock : Block {
+ public ToplevelBlock (Parameters parameters, Location start) :
+ base (null, parameters, start, Location.Null)
+ {
+ }
+
+ public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
+ base (null, flags, parameters, start, Location.Null)
+ {
+ }
+ }
+
public class SwitchLabel {
Expression label;
object converted;
foreach (Type tt in allowed_types){
Expression e;
- e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
+ e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
if (e == null)
continue;
}
if (error)
return false;
-
+
return true;
}
public long nFirst;
public long nLast;
public ArrayList rgKeys = null;
+ // how many items are in the bucket
+ public int Size = 1;
public int Length
{
get { return (int) (nLast - nFirst + 1); }
/// <param name="ec"></param>
/// <param name="val"></param>
/// <returns></returns>
- bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
+ void TableSwitchEmit (EmitContext ec, LocalBuilder val)
{
int cElements = Elements.Count;
object [] rgKeys = new object [cElements];
// initialize the block list with one element per key
ArrayList rgKeyBlocks = new ArrayList ();
foreach (object key in rgKeys)
- rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
+ rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
KeyBlock kbCurr;
// iteratively merge the blocks while they are at least half full
for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
{
KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
- if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
+ if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
{
// merge blocks
kbCurr.nLast = kb.nLast;
+ kbCurr.Size += kb.Size;
}
else
{
kbCurr = (KeyBlock) rgKeyBlocks [0];
foreach (object key in rgKeys)
{
- bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
+ bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
+ System.Convert.ToInt64 (key) > kbCurr.nLast;
if (fNextBlock)
kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
kbCurr.rgKeys.Add (key);
if (rgKeys.Length > 0)
typeKeys = rgKeys [0].GetType (); // used for conversions
+ Type compare_type;
+
+ if (TypeManager.IsEnumType (SwitchType))
+ compare_type = TypeManager.EnumToUnderlying (SwitchType);
+ else
+ compare_type = SwitchType;
+
for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
{
KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
{
// TODO: if all the keys in the block are the same and there are
// no gaps/defaults then just use a range-check.
- if (SwitchType == TypeManager.int64_type ||
- SwitchType == TypeManager.uint64_type)
+ if (compare_type == TypeManager.int64_type ||
+ compare_type == TypeManager.uint64_type)
{
// TODO: optimize constant/I4 cases
// check block range (could be > 2^31)
ig.Emit (OpCodes.Ldloc, val);
- EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
+ EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
ig.Emit (OpCodes.Blt, lblDefault);
ig.Emit (OpCodes.Ldloc, val);
- EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
+ EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
ig.Emit (OpCodes.Bgt, lblDefault);
// normalize range
ig.Emit (OpCodes.Ldloc, val);
if (kb.nFirst != 0)
{
- EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
+ EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
ig.Emit (OpCodes.Sub);
}
ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
for (int iJump = 0; iJump < cJumps; iJump++)
{
object key = kb.rgKeys [iKey];
- if (Convert.ToInt64 (key) == kb.nFirst + iJump)
+ if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
{
SwitchLabel sl = (SwitchLabel) Elements [key];
rgLabels [iJump] = sl.ILLabel;
// now emit the code for the sections
bool fFoundDefault = false;
- bool fAllReturn = true;
foreach (SwitchSection ss in Sections)
{
foreach (SwitchLabel sl in ss.Labels)
fFoundDefault = true;
}
}
- bool returns = ss.Block.Emit (ec);
- fAllReturn &= returns;
+ ss.Block.Emit (ec);
//ig.Emit (OpCodes.Br, lblEnd);
}
if (!fFoundDefault) {
ig.MarkLabel (lblDefault);
- fAllReturn = false;
}
ig.MarkLabel (lblEnd);
-
- return fAllReturn;
}
//
// This simple emit switch works, but does not take advantage of the
// TODO: remove non-string logic from here
// TODO: binary search strings?
//
- bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
+ void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
{
ILGenerator ig = ec.ig;
Label end_of_switch = ig.DefineLabel ();
bool default_found = false;
bool first_test = true;
bool pending_goto_end = false;
- bool all_return = true;
- bool is_string = false;
bool null_found;
+ bool default_at_end = false;
- //
- // Special processing for strings: we cant compare
- // against null.
- //
- if (SwitchType == TypeManager.string_type){
- ig.Emit (OpCodes.Ldloc, val);
- is_string = true;
-
- if (Elements.Contains (NullLiteral.Null)){
- ig.Emit (OpCodes.Brfalse, null_target);
- } else
- ig.Emit (OpCodes.Brfalse, default_target);
-
- ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
- ig.Emit (OpCodes.Stloc, val);
- }
+ ig.Emit (OpCodes.Ldloc, val);
- foreach (SwitchSection ss in Sections){
+ if (Elements.Contains (NullLiteral.Null)){
+ ig.Emit (OpCodes.Brfalse, null_target);
+ } else
+ ig.Emit (OpCodes.Brfalse, default_target);
+
+ ig.Emit (OpCodes.Ldloc, val);
+ ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
+ ig.Emit (OpCodes.Stloc, val);
+
+ int section_count = Sections.Count;
+ for (int section = 0; section < section_count; section++){
+ SwitchSection ss = (SwitchSection) Sections [section];
Label sec_begin = ig.DefineLabel ();
if (pending_goto_end)
ig.Emit (OpCodes.Br, end_of_switch);
int label_count = ss.Labels.Count;
+ bool mark_default = false;
null_found = false;
- foreach (SwitchLabel sl in ss.Labels){
+ for (int label = 0; label < label_count; label++){
+ SwitchLabel sl = (SwitchLabel) ss.Labels [label];
ig.MarkLabel (sl.ILLabel);
if (!first_test){
// If we are the default target
//
if (sl.Label == null){
- ig.MarkLabel (default_target);
+ if (label+1 == label_count)
+ default_at_end = true;
+ mark_default = true;
default_found = true;
} else {
object lit = sl.Converted;
continue;
}
- if (is_string){
- StringConstant str = (StringConstant) lit;
-
- ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Ldstr, str.Value);
- if (label_count == 1)
+ StringConstant str = (StringConstant) lit;
+
+ ig.Emit (OpCodes.Ldloc, val);
+ ig.Emit (OpCodes.Ldstr, str.Value);
+ if (label_count == 1)
+ ig.Emit (OpCodes.Bne_Un, next_test);
+ else {
+ if (label+1 == label_count)
ig.Emit (OpCodes.Bne_Un, next_test);
else
ig.Emit (OpCodes.Beq, sec_begin);
- } else {
- ig.Emit (OpCodes.Ldloc, val);
- EmitObjectInteger (ig, lit);
- ig.Emit (OpCodes.Ceq);
- if (label_count == 1)
- ig.Emit (OpCodes.Brfalse, next_test);
- else
- ig.Emit (OpCodes.Brtrue, sec_begin);
}
}
}
- if (label_count != 1)
- ig.Emit (OpCodes.Br, next_test);
-
if (null_found)
ig.MarkLabel (null_target);
ig.MarkLabel (sec_begin);
foreach (SwitchLabel sl in ss.Labels)
ig.MarkLabel (sl.ILLabelCode);
- bool returns = ss.Block.Emit (ec);
- if (returns)
- pending_goto_end = false;
- else {
- all_return = false;
- pending_goto_end = true;
- }
+ if (mark_default)
+ ig.MarkLabel (default_target);
+ ss.Block.Emit (ec);
+ pending_goto_end = !ss.Block.HasRet;
first_test = false;
}
- if (!default_found){
- ig.MarkLabel (default_target);
- all_return = false;
- }
ig.MarkLabel (next_test);
+ if (default_found){
+ if (!default_at_end)
+ ig.Emit (OpCodes.Br, default_target);
+ } else
+ ig.MarkLabel (default_target);
ig.MarkLabel (end_of_switch);
-
- return all_return;
}
public override bool Resolve (EmitContext ec)
ec.Switch = this;
ec.Switch.SwitchType = SwitchType;
- ec.StartFlowBranching (FlowBranchingType.SWITCH, loc);
+ Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
+ ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
bool first = true;
foreach (SwitchSection ss in Sections){
if (!first)
- ec.CurrentBranching.CreateSibling ();
+ ec.CurrentBranching.CreateSibling (
+ null, FlowBranching.SiblingType.SwitchSection);
else
first = false;
if (!got_default)
- ec.CurrentBranching.CreateSibling ();
+ ec.CurrentBranching.CreateSibling (
+ null, FlowBranching.SiblingType.SwitchSection);
- ec.EndFlowBranching ();
+ FlowBranching.Reachability reachability = ec.EndFlowBranching ();
ec.Switch = old_switch;
+ Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
+ reachability);
+
return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
// Store variable for comparission purposes
LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
ec.Switch = this;
// Emit Code.
- bool all_return;
if (SwitchType == TypeManager.string_type)
- all_return = SimpleSwitchEmit (ec, value);
+ SimpleSwitchEmit (ec, value);
else
- all_return = TableSwitchEmit (ec, value);
+ TableSwitchEmit (ec, value);
// Restore context state.
ig.MarkLabel (ec.LoopEnd);
//
ec.LoopEnd = old_end;
ec.Switch = old_switch;
-
- return all_return;
}
}
public override bool Resolve (EmitContext ec)
{
expr = expr.Resolve (ec);
- return Statement.Resolve (ec) && expr != null;
+ if (expr == null)
+ return false;
+
+ if (expr.Type.IsValueType){
+ Error (185, "lock statement requires the expression to be " +
+ " a reference type (type is: `{0}'",
+ TypeManager.CSharpName (expr.Type));
+ return false;
+ }
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
+ bool ok = Statement.Resolve (ec);
+ ec.EndFlowBranching ();
+
+ return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
Type type = expr.Type;
- bool val;
- if (type.IsValueType){
- Report.Error (185, loc, "lock statement requires the expression to be " +
- " a reference type (type is: `" +
- TypeManager.CSharpName (type) + "'");
- return false;
- }
-
ILGenerator ig = ec.ig;
LocalBuilder temp = ig.DeclareLocal (type);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
// try
- Label end = ig.BeginExceptionBlock ();
- bool old_in_try = ec.InTry;
- ec.InTry = true;
+ ig.BeginExceptionBlock ();
Label finish = ig.DefineLabel ();
- val = Statement.Emit (ec);
- ec.InTry = old_in_try;
+ Statement.Emit (ec);
// ig.Emit (OpCodes.Leave, finish);
ig.MarkLabel (finish);
ig.Emit (OpCodes.Ldloc, temp);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
ig.EndExceptionBlock ();
-
- return val;
}
}
public Unchecked (Block b)
{
Block = b;
+ b.Unchecked = true;
}
public override bool Resolve (EmitContext ec)
{
- return Block.Resolve (ec);
+ bool previous_state = ec.CheckState;
+ bool previous_state_const = ec.ConstantCheckState;
+
+ ec.CheckState = false;
+ ec.ConstantCheckState = false;
+ bool ret = Block.Resolve (ec);
+ ec.CheckState = previous_state;
+ ec.ConstantCheckState = previous_state_const;
+
+ return ret;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
bool previous_state = ec.CheckState;
bool previous_state_const = ec.ConstantCheckState;
- bool val;
ec.CheckState = false;
ec.ConstantCheckState = false;
- val = Block.Emit (ec);
+ Block.Emit (ec);
ec.CheckState = previous_state;
ec.ConstantCheckState = previous_state_const;
-
- return val;
}
}
public Checked (Block b)
{
Block = b;
+ b.Unchecked = false;
}
public override bool Resolve (EmitContext ec)
return ret;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
bool previous_state = ec.CheckState;
bool previous_state_const = ec.ConstantCheckState;
- bool val;
ec.CheckState = true;
ec.ConstantCheckState = true;
- val = Block.Emit (ec);
+ Block.Emit (ec);
ec.CheckState = previous_state;
ec.ConstantCheckState = previous_state_const;
-
- return val;
}
}
return val;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
bool previous_state = ec.InUnsafe;
- bool val;
ec.InUnsafe = true;
- val = Block.Emit (ec);
+ Block.Emit (ec);
ec.InUnsafe = previous_state;
-
- return val;
}
}
Statement statement;
Type expr_type;
FixedData[] data;
+ bool has_ret;
struct FixedData {
public bool is_object;
- public VariableInfo vi;
+ public LocalInfo vi;
public Expression expr;
public Expression converted;
}
public override bool Resolve (EmitContext ec)
{
+ if (!ec.InUnsafe){
+ Expression.UnsafeError (loc);
+ return false;
+ }
+
expr_type = ec.DeclSpace.ResolveType (type, false, loc);
if (expr_type == null)
return false;
+ if (ec.RemapToProxy){
+ Report.Error (-210, loc, "Fixed statement not allowed in iterators");
+ return false;
+ }
+
data = new FixedData [declarators.Count];
if (!expr_type.IsPointer){
int i = 0;
foreach (Pair p in declarators){
- VariableInfo vi = (VariableInfo) p.First;
+ LocalInfo vi = (LocalInfo) p.First;
Expression e = (Expression) p.Second;
- vi.Number = -1;
+ vi.VariableInfo = null;
+ vi.ReadOnly = true;
//
// The rules for the possible declarators are pretty wise,
// is present, so we need to test for this particular case.
//
+ if (e is Cast){
+ Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement");
+ return false;
+ }
+
//
// Case 1: & object.
//
"fixed)");
return false;
}
-
+
+ ec.InFixedInitializer = true;
e = e.Resolve (ec);
+ ec.InFixedInitializer = false;
if (e == null)
return false;
continue;
}
+ ec.InFixedInitializer = true;
e = e.Resolve (ec);
+ ec.InFixedInitializer = false;
if (e == null)
return false;
// Case 2: Array
//
if (e.Type.IsArray){
- Type array_type = e.Type.GetElementType ();
+ Type array_type = TypeManager.GetElementType (e.Type);
vi.MakePinned ();
//
//
ArrayPtr array_ptr = new ArrayPtr (e, loc);
- Expression converted = Expression.ConvertImplicitRequired (
+ Expression converted = Convert.ImplicitConversionRequired (
ec, array_ptr, vi.VariableType, loc);
if (converted == null)
return false;
data [i].converted = null;
data [i].vi = vi;
i++;
+ continue;
}
+
+ //
+ // For other cases, flag a `this is already fixed expression'
+ //
+ if (e is LocalVariableReference || e is ParameterReference ||
+ Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
+
+ Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
+ return false;
+ }
+
+ Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
+ return false;
+ }
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
+
+ if (!statement.Resolve (ec)) {
+ ec.KillFlowBranching ();
+ return false;
}
- return statement.Resolve (ec);
+ FlowBranching.Reachability reachability = ec.EndFlowBranching ();
+ has_ret = reachability.IsUnreachable;
+
+ return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
- bool is_ret = false;
LocalBuilder [] clear_list = new LocalBuilder [data.Length];
for (int i = 0; i < data.Length; i++) {
- VariableInfo vi = data [i].vi;
+ LocalInfo vi = data [i].vi;
//
// Case 1: & object.
ig.Emit (OpCodes.Stloc, pinned_string);
Expression sptr = new StringPtr (pinned_string, loc);
- Expression converted = Expression.ConvertImplicitRequired (
+ Expression converted = Convert.ImplicitConversionRequired (
ec, sptr, vi.VariableType, loc);
if (converted == null)
}
}
- is_ret = statement.Emit (ec);
+ statement.Emit (ec);
+
+ if (has_ret)
+ return;
- if (is_ret)
- return is_ret;
//
// Clear the pinned variable
//
for (int i = 0; i < data.Length; i++) {
- VariableInfo vi = data [i].vi;
-
if (data [i].is_object || data [i].expr.Type.IsArray) {
ig.Emit (OpCodes.Ldc_I4_0);
ig.Emit (OpCodes.Conv_U);
ig.Emit (OpCodes.Stloc, clear_list [i]);
}
}
-
- return is_ret;
}
}
{
bool ok = true;
- ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation);
+ ec.StartFlowBranching (FlowBranching.BranchingType.Exception, Block.StartLocation);
Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
- bool old_in_try = ec.InTry;
- ec.InTry = true;
-
if (!Block.Resolve (ec))
ok = false;
- ec.InTry = old_in_try;
-
FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
Report.Debug (1, "START OF CATCH BLOCKS", vector);
foreach (Catch c in Specific){
- ec.CurrentBranching.CreateSibling ();
+ ec.CurrentBranching.CreateSibling (
+ c.Block, FlowBranching.SiblingType.Catch);
+
Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
if (c.Name != null) {
- VariableInfo vi = c.Block.GetVariableInfo (c.Name);
+ LocalInfo vi = c.Block.GetLocalInfo (c.Name);
if (vi == null)
throw new Exception ();
- vi.Number = -1;
+ vi.VariableInfo = null;
}
- bool old_in_catch = ec.InCatch;
- ec.InCatch = true;
-
if (!c.Resolve (ec))
ok = false;
-
- ec.InCatch = old_in_catch;
-
- FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
-
- if (!current.AlwaysReturns && !current.AlwaysBreaks)
- vector.AndLocals (current);
- else
- vector.Or (current);
}
Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
if (General != null){
- ec.CurrentBranching.CreateSibling ();
- Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
+ ec.CurrentBranching.CreateSibling (
+ General.Block, FlowBranching.SiblingType.Catch);
- bool old_in_catch = ec.InCatch;
- ec.InCatch = true;
+ Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
if (!General.Resolve (ec))
ok = false;
-
- ec.InCatch = old_in_catch;
-
- FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
-
- if (!current.AlwaysReturns && !current.AlwaysBreaks)
- vector.AndLocals (current);
- else
- vector.Or (current);
}
Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
if (Fini != null) {
- ec.CurrentBranching.CreateSiblingForFinally ();
- Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
+ if (ok)
+ ec.CurrentBranching.CreateSibling (
+ Fini, FlowBranching.SiblingType.Finally);
- bool old_in_finally = ec.InFinally;
- ec.InFinally = true;
+ Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
if (!Fini.Resolve (ec))
ok = false;
-
- ec.InFinally = old_in_finally;
}
- FlowReturns returns = ec.EndFlowBranching ();
+ FlowBranching.Reachability reachability = ec.EndFlowBranching ();
FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
- Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector);
+ Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
- if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) {
- ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc);
+ if (reachability.Returns != FlowBranching.FlowReturns.Always) {
+ // Unfortunately, System.Reflection.Emit automatically emits a leave
+ // to the end of the finally block. This is a problem if `returns'
+ // is true since we may jump to a point after the end of the method.
+ // As a workaround, emit an explicit ret here.
+ ec.NeedReturnLabel ();
}
- ec.CurrentBranching.CurrentUsageVector.Or (vector);
-
- Report.Debug (1, "END OF TRY", ec.CurrentBranching);
-
return ok;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
- Label end;
Label finish = ig.DefineLabel ();;
- bool returns;
- ec.TryCatchLevel++;
- end = ig.BeginExceptionBlock ();
- bool old_in_try = ec.InTry;
- ec.InTry = true;
- returns = Block.Emit (ec);
- ec.InTry = old_in_try;
+ ig.BeginExceptionBlock ();
+ Block.Emit (ec);
//
// System.Reflection.Emit provides this automatically:
// ig.Emit (OpCodes.Leave, finish);
- bool old_in_catch = ec.InCatch;
- ec.InCatch = true;
- DeclSpace ds = ec.DeclSpace;
-
foreach (Catch c in Specific){
- VariableInfo vi;
+ LocalInfo vi;
ig.BeginCatchBlock (c.CatchType);
if (c.Name != null){
- vi = c.Block.GetVariableInfo (c.Name);
+ vi = c.Block.GetLocalInfo (c.Name);
if (vi == null)
throw new Exception ("Variable does not exist in this block");
} else
ig.Emit (OpCodes.Pop);
- if (!c.Block.Emit (ec))
- returns = false;
+ c.Block.Emit (ec);
}
if (General != null){
ig.BeginCatchBlock (TypeManager.object_type);
ig.Emit (OpCodes.Pop);
- if (!General.Block.Emit (ec))
- returns = false;
+ General.Block.Emit (ec);
}
- ec.InCatch = old_in_catch;
ig.MarkLabel (finish);
if (Fini != null){
ig.BeginFinallyBlock ();
- bool old_in_finally = ec.InFinally;
- ec.InFinally = true;
Fini.Emit (ec);
- ec.InFinally = old_in_finally;
}
ig.EndExceptionBlock ();
- ec.TryCatchLevel--;
-
- if (!returns || ec.InTry || ec.InCatch)
- return returns;
-
- // Unfortunately, System.Reflection.Emit automatically emits a leave
- // to the end of the finally block. This is a problem if `returns'
- // is true since we may jump to a point after the end of the method.
- // As a workaround, emit an explicit ret here.
-
- if (ec.ReturnType != null)
- ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
- ec.ig.Emit (OpCodes.Ret);
-
- return true;
}
}
if (var == null)
return false;
- converted_vars [i] = Expression.ConvertImplicitRequired (
+ converted_vars [i] = Convert.ImplicitConversionRequired (
ec, var, TypeManager.idisposable_type, loc);
if (converted_vars [i] == null)
bool ResolveExpression (EmitContext ec)
{
if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
- conv = Expression.ConvertImplicitRequired (
+ conv = Convert.ImplicitConversionRequired (
ec, expr, TypeManager.idisposable_type, loc);
if (conv == null)
ILGenerator ig = ec.ig;
int i = 0;
- bool old_in_try = ec.InTry;
- ec.InTry = true;
for (i = 0; i < assign.Length; i++) {
assign [i].EmitStatement (ec);
ig.BeginExceptionBlock ();
}
Statement.Emit (ec);
- ec.InTry = old_in_try;
- bool old_in_finally = ec.InFinally;
- ec.InFinally = true;
var_list.Reverse ();
foreach (DictionaryEntry e in var_list){
LocalVariableReference var = (LocalVariableReference) e.Key;
i--;
ig.BeginFinallyBlock ();
-
- var.Emit (ec);
- ig.Emit (OpCodes.Brfalse, skip);
- converted_vars [i].Emit (ec);
- ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+
+ if (!var.Type.IsValueType) {
+ var.Emit (ec);
+ ig.Emit (OpCodes.Brfalse, skip);
+ converted_vars [i].Emit (ec);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ } else {
+ Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
+
+ if (!(ml is MethodGroupExpr)) {
+ var.Emit (ec);
+ ig.Emit (OpCodes.Box, var.Type);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ } else {
+ MethodInfo mi = null;
+
+ foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
+ if (mk.GetParameters().Length == 0) {
+ mi = mk;
+ break;
+ }
+ }
+
+ if (mi == null) {
+ Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
+ return false;
+ }
+
+ var.AddressOf (ec, AddressOp.Load);
+ ig.Emit (OpCodes.Call, mi);
+ }
+ }
+
ig.MarkLabel (skip);
ig.EndExceptionBlock ();
}
- ec.InFinally = old_in_finally;
return false;
}
expr.Emit (ec);
ig.Emit (OpCodes.Stloc, local_copy);
- bool old_in_try = ec.InTry;
- ec.InTry = true;
ig.BeginExceptionBlock ();
Statement.Emit (ec);
- ec.InTry = old_in_try;
Label skip = ig.DefineLabel ();
- bool old_in_finally = ec.InFinally;
ig.BeginFinallyBlock ();
ig.Emit (OpCodes.Ldloc, local_copy);
ig.Emit (OpCodes.Brfalse, skip);
ig.Emit (OpCodes.Ldloc, local_copy);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
ig.MarkLabel (skip);
- ec.InFinally = old_in_finally;
ig.EndExceptionBlock ();
return false;
if (!ResolveExpression (ec))
return false;
- }
+ }
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
- return Statement.Resolve (ec);
+ bool ok = Statement.Resolve (ec);
+
+ if (!ok) {
+ ec.KillFlowBranching ();
+ return false;
+ }
+
+ FlowBranching.Reachability reachability = ec.EndFlowBranching ();
+
+ if (reachability.Returns != FlowBranching.FlowReturns.Always) {
+ // Unfortunately, System.Reflection.Emit automatically emits a leave
+ // to the end of the finally block. This is a problem if `returns'
+ // is true since we may jump to a point after the end of the method.
+ // As a workaround, emit an explicit ret here.
+ ec.NeedReturnLabel ();
+ }
+
+ return true;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
if (expression_or_block is DictionaryEntry)
- return EmitLocalVariableDecls (ec);
+ EmitLocalVariableDecls (ec);
else if (expression_or_block is Expression)
- return EmitExpression (ec);
-
- return false;
+ EmitExpression (ec);
}
}
/// </summary>
public class Foreach : Statement {
Expression type;
- LocalVariableReference variable;
+ Expression variable;
Expression expr;
Statement statement;
ForeachHelperMethods hm;
if (expr.Type.IsArray) {
array_type = expr.Type;
- element_type = array_type.GetElementType ();
+ element_type = TypeManager.GetElementType (array_type);
empty = new EmptyExpression (element_type);
} else {
if (hm == null){
error1579 (expr.Type);
return false;
- }
+ }
array_type = expr.Type;
element_type = hm.element_type;
empty = new EmptyExpression (hm.element_type);
}
- ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
+ bool ok = true;
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
ec.CurrentBranching.CreateSibling ();
//
// Although it is not as important in this case, as the type
// will not likely be object (what the enumerator will return).
//
- conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
+ conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
if (conv == null)
- return false;
+ ok = false;
- if (variable.ResolveLValue (ec, empty) == null)
- return false;
+ variable = variable.ResolveLValue (ec, empty);
+ if (variable == null)
+ ok = false;
+
+ bool disposable = (hm != null) && hm.is_disposable;
+ if (disposable)
+ ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
if (!statement.Resolve (ec))
- return false;
+ ok = false;
- FlowReturns returns = ec.EndFlowBranching ();
+ if (disposable)
+ ec.EndFlowBranching ();
- return true;
+ ec.EndFlowBranching ();
+
+ return ok;
}
//
//
static MethodInfo FetchMethodGetCurrent (Type t)
{
- MemberList move_next_list;
-
- move_next_list = TypeContainer.FindMembers (
+ MemberList get_current_list;
+
+ get_current_list = TypeContainer.FindMembers (
t, MemberTypes.Method,
BindingFlags.Public | BindingFlags.Instance,
Type.FilterName, "get_Current");
- if (move_next_list.Count == 0)
+ if (get_current_list.Count == 0)
return null;
- foreach (MemberInfo m in move_next_list){
+ foreach (MemberInfo m in get_current_list){
MethodInfo mi = (MethodInfo) m;
Type [] args;
return false;
}
+ if ((mi.ReturnType == TypeManager.ienumerator_type) && (declaring == TypeManager.string_type))
+ //
+ // Apply the same optimization as MS: skip the GetEnumerator
+ // returning an IEnumerator, and use the one returning a
+ // CharEnumerator instead. This allows us to avoid the
+ // try-finally block and the boxing.
+ //
+ return false;
+
//
// Ok, we can access it, now make sure that we can do something
// with this `GetEnumerator'
//
-
- if (mi.ReturnType == TypeManager.ienumerator_type ||
- TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
- (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
- hm.move_next = TypeManager.bool_movenext_void;
- hm.get_current = TypeManager.object_getcurrent_void;
- return true;
- }
-
- //
- // Ok, so they dont return an IEnumerable, we will have to
- // find if they support the GetEnumerator pattern.
- //
+
Type return_type = mi.ReturnType;
+ if (mi.ReturnType == TypeManager.ienumerator_type ||
+ TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
+ (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
+
+ //
+ // If it is not an interface, lets try to find the methods ourselves.
+ // For example, if we have:
+ // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
+ // We can avoid the iface call. This is a runtime perf boost.
+ // even bigger if we have a ValueType, because we avoid the cost
+ // of boxing.
+ //
+ // We have to make sure that both methods exist for us to take
+ // this path. If one of the methods does not exist, we will just
+ // use the interface. Sadly, this complex if statement is the only
+ // way I could do this without a goto
+ //
+
+ if (return_type.IsInterface ||
+ (hm.move_next = FetchMethodMoveNext (return_type)) == null ||
+ (hm.get_current = FetchMethodGetCurrent (return_type)) == null) {
+
+ hm.move_next = TypeManager.bool_movenext_void;
+ hm.get_current = TypeManager.object_getcurrent_void;
+ return true;
+ }
- hm.move_next = FetchMethodMoveNext (return_type);
- if (hm.move_next == null)
- return false;
- hm.get_current = FetchMethodGetCurrent (return_type);
- if (hm.get_current == null)
- return false;
+ } else {
+ //
+ // Ok, so they dont return an IEnumerable, we will have to
+ // find if they support the GetEnumerator pattern.
+ //
+
+ hm.move_next = FetchMethodMoveNext (return_type);
+ if (hm.move_next == null)
+ return false;
+
+ hm.get_current = FetchMethodGetCurrent (return_type);
+ if (hm.get_current == null)
+ return false;
+ }
+
hm.element_type = hm.get_current.ReturnType;
hm.enumerator_type = return_type;
- hm.is_disposable = TypeManager.ImplementsInterface (
- hm.enumerator_type, TypeManager.idisposable_type);
+ hm.is_disposable = !hm.enumerator_type.IsSealed ||
+ TypeManager.ImplementsInterface (
+ hm.enumerator_type, TypeManager.idisposable_type);
return true;
}
mi = TypeContainer.FindMembers (t, MemberTypes.Method,
BindingFlags.Public | BindingFlags.NonPublic |
- BindingFlags.Instance,
+ BindingFlags.Instance | BindingFlags.DeclaredOnly,
FilterEnumerator, hm);
if (mi.Count == 0)
{
ForeachHelperMethods hm = new ForeachHelperMethods (ec);
- if (TryType (t, hm))
- return hm;
+ for (Type tt = t; tt != null && tt != TypeManager.object_type;){
+ if (TryType (tt, hm))
+ return hm;
+ tt = tt.BaseType;
+ }
//
// Now try to find the method in the interfaces
bool EmitCollectionForeach (EmitContext ec)
{
ILGenerator ig = ec.ig;
- LocalBuilder enumerator, disposable;
+ VariableStorage enumerator;
- enumerator = ig.DeclareLocal (hm.enumerator_type);
- if (hm.is_disposable)
- disposable = ig.DeclareLocal (TypeManager.idisposable_type);
- else
- disposable = null;
-
+ enumerator = new VariableStorage (ec, hm.enumerator_type);
+ enumerator.EmitThis ();
//
// Instantiate the enumerator
//
if (expr is IMemoryLocation){
IMemoryLocation ml = (IMemoryLocation) expr;
- ml.AddressOf (ec, AddressOp.Load);
+ Expression ml1 = Expression.MemberLookup(ec, TypeManager.ienumerator_type, expr.Type, "GetEnumerator", Mono.CSharp.Location.Null);
+
+ if (!(ml1 is MethodGroupExpr)) {
+ expr.Emit(ec);
+ ec.ig.Emit(OpCodes.Box, expr.Type);
+ } else {
+ ml.AddressOf (ec, AddressOp.Load);
+ }
} else
throw new Exception ("Expr " + expr + " of type " + expr.Type +
" does not implement IMemoryLocation");
- ig.Emit (OpCodes.Call, hm.get_enumerator);
+ ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
} else {
expr.Emit (ec);
ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
}
- ig.Emit (OpCodes.Stloc, enumerator);
+ enumerator.EmitStore ();
//
// Protect the code in a try/finalize block, so that
// if the beast implement IDisposable, we get rid of it
//
- Label l;
- bool old_in_try = ec.InTry;
-
- if (hm.is_disposable) {
- l = ig.BeginExceptionBlock ();
- ec.InTry = true;
- }
+ if (hm.is_disposable)
+ ig.BeginExceptionBlock ();
Label end_try = ig.DefineLabel ();
ig.MarkLabel (ec.LoopBegin);
- ig.Emit (OpCodes.Ldloc, enumerator);
- ig.Emit (OpCodes.Callvirt, hm.move_next);
+
+ enumerator.EmitCall (hm.move_next);
+
ig.Emit (OpCodes.Brfalse, end_try);
- ig.Emit (OpCodes.Ldloc, enumerator);
- ig.Emit (OpCodes.Callvirt, hm.get_current);
- variable.EmitAssign (ec, conv);
+ if (ec.InIterator)
+ ec.EmitThis ();
+
+ enumerator.EmitCall (hm.get_current);
+
+ if (ec.InIterator){
+ conv.Emit (ec);
+ ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ } else
+ ((IAssignMethod)variable).EmitAssign (ec, conv);
+
statement.Emit (ec);
ig.Emit (OpCodes.Br, ec.LoopBegin);
ig.MarkLabel (end_try);
- ec.InTry = old_in_try;
// The runtime provides this for us.
// ig.Emit (OpCodes.Leave, end);
// Now the finally block
//
if (hm.is_disposable) {
- Label end_finally = ig.DefineLabel ();
- bool old_in_finally = ec.InFinally;
- ec.InFinally = true;
+ Label call_dispose = ig.DefineLabel ();
ig.BeginFinallyBlock ();
-
- ig.Emit (OpCodes.Ldloc, enumerator);
+
+ enumerator.EmitThis ();
+ enumerator.EmitLoad ();
ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
- ig.Emit (OpCodes.Stloc, disposable);
- ig.Emit (OpCodes.Ldloc, disposable);
- ig.Emit (OpCodes.Brfalse, end_finally);
- ig.Emit (OpCodes.Ldloc, disposable);
+ ig.Emit (OpCodes.Dup);
+ ig.Emit (OpCodes.Brtrue_S, call_dispose);
+ ig.Emit (OpCodes.Pop);
+ ig.Emit (OpCodes.Endfinally);
+
+ ig.MarkLabel (call_dispose);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
- ig.MarkLabel (end_finally);
- ec.InFinally = old_in_finally;
+
// The runtime generates this anyways.
// ig.Emit (OpCodes.Endfinally);
int rank = array_type.GetArrayRank ();
ILGenerator ig = ec.ig;
- LocalBuilder copy = ig.DeclareLocal (array_type);
+ VariableStorage copy = new VariableStorage (ec, array_type);
//
// Make our copy of the array
//
+ copy.EmitThis ();
expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, copy);
+ copy.EmitStore ();
if (rank == 1){
- LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
+ VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
Label loop, test;
-
+
+ counter.EmitThis ();
ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Stloc, counter);
+ counter.EmitStore ();
test = ig.DefineLabel ();
ig.Emit (OpCodes.Br, test);
loop = ig.DefineLabel ();
ig.MarkLabel (loop);
- ig.Emit (OpCodes.Ldloc, copy);
- ig.Emit (OpCodes.Ldloc, counter);
- ArrayAccess.EmitLoadOpcode (ig, var_type);
+ if (ec.InIterator)
+ ec.EmitThis ();
+
+ copy.EmitThis ();
+ copy.EmitLoad ();
+ counter.EmitThis ();
+ counter.EmitLoad ();
- variable.EmitAssign (ec, conv);
+ //
+ // Load the value, we load the value using the underlying type,
+ // then we use the variable.EmitAssign to load using the proper cast.
+ //
+ ArrayAccess.EmitLoadOpcode (ig, element_type);
+ if (ec.InIterator){
+ conv.Emit (ec);
+ ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ } else
+ ((IAssignMethod)variable).EmitAssign (ec, conv);
statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
- ig.Emit (OpCodes.Ldloc, counter);
+ counter.EmitThis ();
+ counter.EmitThis ();
+ counter.EmitLoad ();
ig.Emit (OpCodes.Ldc_I4_1);
ig.Emit (OpCodes.Add);
- ig.Emit (OpCodes.Stloc, counter);
+ counter.EmitStore ();
ig.MarkLabel (test);
- ig.Emit (OpCodes.Ldloc, counter);
- ig.Emit (OpCodes.Ldloc, copy);
+ counter.EmitThis ();
+ counter.EmitLoad ();
+ copy.EmitThis ();
+ copy.EmitLoad ();
ig.Emit (OpCodes.Ldlen);
ig.Emit (OpCodes.Conv_I4);
ig.Emit (OpCodes.Blt, loop);
} else {
- LocalBuilder [] dim_len = new LocalBuilder [rank];
- LocalBuilder [] dim_count = new LocalBuilder [rank];
+ VariableStorage [] dim_len = new VariableStorage [rank];
+ VariableStorage [] dim_count = new VariableStorage [rank];
Label [] loop = new Label [rank];
Label [] test = new Label [rank];
int dim;
for (dim = 0; dim < rank; dim++){
- dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
- dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
+ dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
+ dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
test [dim] = ig.DefineLabel ();
loop [dim] = ig.DefineLabel ();
}
for (dim = 0; dim < rank; dim++){
- ig.Emit (OpCodes.Ldloc, copy);
+ dim_len [dim].EmitThis ();
+ copy.EmitThis ();
+ copy.EmitLoad ();
IntLiteral.EmitInt (ig, dim);
ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
- ig.Emit (OpCodes.Stloc, dim_len [dim]);
+ dim_len [dim].EmitStore ();
+
}
for (dim = 0; dim < rank; dim++){
+ dim_count [dim].EmitThis ();
ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Stloc, dim_count [dim]);
+ dim_count [dim].EmitStore ();
ig.Emit (OpCodes.Br, test [dim]);
ig.MarkLabel (loop [dim]);
}
- ig.Emit (OpCodes.Ldloc, copy);
- for (dim = 0; dim < rank; dim++)
- ig.Emit (OpCodes.Ldloc, dim_count [dim]);
+ if (ec.InIterator)
+ ec.EmitThis ();
+ copy.EmitThis ();
+ copy.EmitLoad ();
+ for (dim = 0; dim < rank; dim++){
+ dim_count [dim].EmitThis ();
+ dim_count [dim].EmitLoad ();
+ }
//
// FIXME: Maybe we can cache the computation of `get'?
for (int i = 0; i < rank; i++)
args [i] = TypeManager.int32_type;
- ModuleBuilder mb = CodeGen.ModuleBuilder;
+ ModuleBuilder mb = CodeGen.Module.Builder;
get = mb.GetArrayMethod (
array_type, "Get",
CallingConventions.HasThis| CallingConventions.Standard,
var_type, args);
ig.Emit (OpCodes.Call, get);
- variable.EmitAssign (ec, conv);
+ if (ec.InIterator){
+ conv.Emit (ec);
+ ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ } else
+ ((IAssignMethod)variable).EmitAssign (ec, conv);
statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
for (dim = rank - 1; dim >= 0; dim--){
- ig.Emit (OpCodes.Ldloc, dim_count [dim]);
+ dim_count [dim].EmitThis ();
+ dim_count [dim].EmitThis ();
+ dim_count [dim].EmitLoad ();
ig.Emit (OpCodes.Ldc_I4_1);
ig.Emit (OpCodes.Add);
- ig.Emit (OpCodes.Stloc, dim_count [dim]);
+ dim_count [dim].EmitStore ();
ig.MarkLabel (test [dim]);
- ig.Emit (OpCodes.Ldloc, dim_count [dim]);
- ig.Emit (OpCodes.Ldloc, dim_len [dim]);
+ dim_count [dim].EmitThis ();
+ dim_count [dim].EmitLoad ();
+ dim_len [dim].EmitThis ();
+ dim_len [dim].EmitLoad ();
ig.Emit (OpCodes.Blt, loop [dim]);
}
}
return false;
}
- protected override bool DoEmit (EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
- bool ret_val;
-
ILGenerator ig = ec.ig;
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
- bool old_inloop = ec.InLoop;
- int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
- ec.InLoop = true;
- ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
if (hm != null)
- ret_val = EmitCollectionForeach (ec);
+ EmitCollectionForeach (ec);
else
- ret_val = EmitArrayForeach (ec);
+ EmitArrayForeach (ec);
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
- ec.InLoop = old_inloop;
- ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
-
- return ret_val;
}
}
}