// Martin Baulig (martin@ximian.com)
// Marek Safar (marek.safar@seznam.cz)
//
-// (C) 2001, 2002, 2003 Ximian, Inc.
-// (C) 2003, 2004 Novell, Inc.
+// Copyright 2001, 2002, 2003 Ximian, Inc.
+// Copyright 2003, 2004 Novell, Inc.
//
using System;
/// </summary>
public virtual void Emit (EmitContext ec)
{
- ec.Mark (loc, true);
+ ec.Mark (loc);
DoEmit (ec);
}
// This routine must be overrided in derived classes and make copies
// of all the data that might be modified if resolved
//
- protected virtual void CloneTo (CloneContext clonectx, Statement target)
- {
- throw new InternalErrorException ("{0} does not implement Statement.CloneTo", this.GetType ());
- }
+ protected abstract void CloneTo (CloneContext clonectx, Statement target);
public Statement Clone (CloneContext clonectx)
{
return s;
}
+ public virtual Expression CreateExpressionTree (EmitContext ec)
+ {
+ Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
+ return null;
+ }
+
public Statement PerformClone ()
{
CloneContext clonectx = new CloneContext ();
return Clone (clonectx);
}
+ public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
}
//
{
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing needed.
loc = l;
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ TrueStatement.MutateHoistedGenericType (storey);
+ if (FalseStatement != null)
+ FalseStatement.MutateHoistedGenericType (storey);
+ }
+
public override bool Resolve (EmitContext ec)
{
bool ok = true;
//
// Dead code elimination
//
- if (expr is BoolConstant){
- bool take = ((BoolConstant) expr).Value;
+ if (expr is Constant){
+ bool take = !((Constant) expr).IsDefaultValue;
if (take){
if (!TrueStatement.Resolve (ec))
Label end;
//
- // If we're a boolean expression, Resolve() already
+ // If we're a boolean constant, Resolve() already
// eliminated dead code for us.
//
- if (expr is BoolConstant){
- bool take = ((BoolConstant) expr).Value;
+ Constant c = expr as Constant;
+ if (c != null){
+ c.EmitSideEffect (ec);
- if (take)
+ if (!c.IsDefaultValue)
TrueStatement.Emit (ec);
else if (FalseStatement != null)
FalseStatement.Emit (ec);
return;
- }
+ }
expr.EmitBranchable (ec, false_target, false);
public class Do : Statement {
public Expression expr;
public Statement EmbeddedStatement;
- bool infinite;
public Do (Statement statement, Expression bool_expr, Location l)
{
expr = Expression.ResolveBoolean (ec, expr, loc);
if (expr == null)
ok = false;
- else if (expr is BoolConstant){
- bool res = ((BoolConstant) expr).Value;
-
- if (res)
- infinite = true;
+ else if (expr is Constant){
+ bool infinite = !((Constant) expr).IsDefaultValue;
+ if (infinite)
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
}
- if (infinite)
- ec.CurrentBranching.CurrentUsageVector.Goto ();
ec.EndFlowBranching ();
//
// Dead code elimination
//
- if (expr is BoolConstant){
- bool res = ((BoolConstant) expr).Value;
+ if (expr is Constant){
+ bool res = !((Constant) expr).IsDefaultValue;
+ expr.EmitSideEffect (ec);
if (res)
ec.ig.Emit (OpCodes.Br, loop);
} else
ec.LoopEnd = old_end;
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ EmbeddedStatement.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Do target = (Do) t;
//
// Inform whether we are infinite or not
//
- if (expr is BoolConstant){
- BoolConstant bc = (BoolConstant) expr;
+ if (expr is Constant){
+ bool value = !((Constant) expr).IsDefaultValue;
- if (bc.Value == false){
+ if (value == false){
if (!Statement.ResolveUnreachable (ec, true))
return false;
empty = true;
protected override void DoEmit (EmitContext ec)
{
- if (empty)
+ if (empty) {
+ expr.EmitSideEffect (ec);
return;
+ }
ILGenerator ig = ec.ig;
Label old_begin = ec.LoopBegin;
//
// Inform whether we are infinite or not
//
- if (expr is BoolConstant){
+ if (expr is Constant){
+ // expr is 'true', since the 'empty' case above handles the 'false' case
ig.MarkLabel (ec.LoopBegin);
+ expr.EmitSideEffect (ec);
Statement.Emit (ec);
ig.Emit (OpCodes.Br, ec.LoopBegin);
Statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
+ ec.Mark (loc);
expr.EmitBranchable (ec, while_loop, true);
ec.LoopEnd = old_end;
}
+ public override void Emit (EmitContext ec)
+ {
+ DoEmit (ec);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
While target = (While) t;
target.expr = expr.Clone (clonectx);
target.Statement = Statement.Clone (clonectx);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ Statement.MutateHoistedGenericType (storey);
+ }
}
public class For : Statement {
Test = Expression.ResolveBoolean (ec, Test, loc);
if (Test == null)
ok = false;
- else if (Test is BoolConstant){
- BoolConstant bc = (BoolConstant) Test;
+ else if (Test is Constant){
+ bool value = !((Constant) Test).IsDefaultValue;
- if (bc.Value == false){
+ if (value == false){
if (!Statement.ResolveUnreachable (ec, true))
return false;
if ((Increment != null) &&
return ok;
}
-
+
protected override void DoEmit (EmitContext ec)
{
- if (empty)
+ if (InitStatement != null && InitStatement != EmptyStatement.Value)
+ InitStatement.Emit (ec);
+
+ if (empty) {
+ Test.EmitSideEffect (ec);
return;
+ }
ILGenerator ig = ec.ig;
Label old_begin = ec.LoopBegin;
Label old_end = ec.LoopEnd;
Label loop = ig.DefineLabel ();
Label test = ig.DefineLabel ();
-
- if (InitStatement != null && InitStatement != EmptyStatement.Value)
- InitStatement.Emit (ec);
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
if (Test != null){
//
// The Resolve code already catches the case for
- // Test == BoolConstant (false) so we know that
+ // Test == Constant (false) so we know that
// this is true
//
- if (Test is BoolConstant)
+ if (Test is Constant) {
+ Test.EmitSideEffect (ec);
ig.Emit (OpCodes.Br, loop);
- else
+ } else {
Test.EmitBranchable (ec, loop, true);
+ }
} else
ig.Emit (OpCodes.Br, loop);
ec.LoopEnd = old_end;
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ if (InitStatement != null)
+ InitStatement.MutateHoistedGenericType (storey);
+ if (Test != null)
+ Test.MutateHoistedGenericType (storey);
+ if (Increment != null)
+ Increment.MutateHoistedGenericType (storey);
+
+ Statement.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
For target = (For) t;
expr.EmitStatement (ec);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ }
+
public override string ToString ()
{
return "StatementExpression (" + expr + ")";
}
}
+ // A 'return' or a 'yield break'
+ public abstract class ExitStatement : Statement
+ {
+ protected bool unwind_protect;
+ protected abstract bool DoResolve (EmitContext ec);
+
+ public virtual void Error_FinallyClause ()
+ {
+ Report.Error (157, loc, "Control cannot leave the body of a finally clause");
+ }
+
+ public sealed override bool Resolve (EmitContext ec)
+ {
+ if (!DoResolve (ec))
+ return false;
+
+ unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
+ if (unwind_protect)
+ ec.NeedReturnLabel ();
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
+ return true;
+ }
+ }
+
/// <summary>
/// Implements the return statement
/// </summary>
- public class Return : Statement {
- Expression Expr;
- bool unwind_protect;
-
+ public class Return : ExitStatement {
+ protected Expression Expr;
public Return (Expression expr, Location l)
{
Expr = expr;
loc = l;
}
- bool DoResolve (EmitContext ec)
+ protected override bool DoResolve (EmitContext ec)
{
if (Expr == null) {
if (ec.ReturnType == TypeManager.void_type)
return false;
}
- AnonymousContainer am = ec.CurrentAnonymousMethod;
- if ((am != null) && am.IsIterator && ec.InIterator) {
+ if (ec.CurrentBlock.Toplevel.IsIterator) {
Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
"statement to return a value, or yield break to end the iteration");
}
+ AnonymousExpression am = ec.CurrentAnonymousMethod;
if (am == null && ec.ReturnType == TypeManager.void_type) {
MemberCore mc = ec.ResolveContext as MemberCore;
Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
if (Expr.Type != ec.ReturnType) {
if (ec.InferReturnType) {
+ //
+ // void cannot be used in contextual return
+ //
+ if (Expr.Type == TypeManager.void_type)
+ return false;
+
ec.ReturnType = Expr.Type;
} else {
Expr = Convert.ImplicitConversionRequired (
return true;
}
-
- public override bool Resolve (EmitContext ec)
- {
- if (!DoResolve (ec))
- return false;
-
- unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
- if (unwind_protect)
- ec.NeedReturnLabel ();
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return true;
- }
protected override void DoEmit (EmitContext ec)
{
ec.ig.Emit (OpCodes.Ret);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ if (Expr != null)
+ Expr.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Return target = (Return) t;
label.AddReference ();
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ // Nothing to clone
+ }
+
protected override void DoEmit (EmitContext ec)
{
if (label == null)
Label l = label.LabelTarget (ec);
ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
}
public class LabeledStatement : Statement {
vectors = vector;
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ // nothing to clone
+ }
+
public override bool Resolve (EmitContext ec)
{
// this flow-branching will be terminated when the surrounding block ends
ec.ig.MarkLabel (label);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
+
public void AddReference ()
{
referenced = true;
loc = l;
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ // nothing to clone
+ }
+
public override bool Resolve (EmitContext ec)
{
ec.CurrentBranching.CurrentUsageVector.Goto ();
}
ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
}
/// <summary>
ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
GotoCase target = (GotoCase) t;
target.expr = expr.Clone (clonectx);
- target.sl = sl.Clone (clonectx);
}
}
public override bool Resolve (EmitContext ec)
{
- ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- if (expr != null){
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
-
- ExprClass eclass = expr.eclass;
-
- if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
- eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
- expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
- return false;
- }
-
- Type t = expr.Type;
-
- if ((t != TypeManager.exception_type) &&
- !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
- !(expr is NullLiteral)) {
- Error (155,
- "The type caught or thrown must be derived " +
- "from System.Exception");
- return false;
- }
- return true;
+ if (expr == null) {
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
+ return ec.CurrentBranching.CheckRethrow (loc);
}
- if (!ec.InCatch) {
- Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
+ expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
+
+ if (expr == null)
return false;
- }
- if (ec.InFinally) {
- Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
+ Type t = expr.Type;
+
+ if ((t != TypeManager.exception_type) &&
+ !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
+ !(expr is NullLiteral)) {
+ Error (155, "The type caught or thrown must be derived from System.Exception");
return false;
}
return true;
}
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ if (expr != null)
+ expr.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Throw target = (Throw) t;
- target.expr = expr.Clone (clonectx);
+ if (expr != null)
+ target.expr = expr.Clone (clonectx);
}
}
{
ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
protected override void CloneTo (CloneContext clonectx, Statement t)
{
ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
// nothing needed.
}
}
- public abstract class Variable
+ public interface ILocalVariable
{
- public abstract Type Type {
- get;
- }
-
- public abstract bool HasInstance {
- get;
- }
-
- public abstract bool NeedsTemporary {
- get;
- }
-
- public abstract void EmitInstance (EmitContext ec);
-
- public abstract void Emit (EmitContext ec);
-
- public abstract void EmitAssign (EmitContext ec);
-
- public abstract void EmitAddressOf (EmitContext ec);
+ void Emit (EmitContext ec);
+ void EmitAssign (EmitContext ec);
+ void EmitAddressOf (EmitContext ec);
}
public interface IKnownVariable {
//
// The information about a user-perceived local variable
//
- public class LocalInfo : IKnownVariable {
- public Expression Type;
+ public class LocalInfo : IKnownVariable, ILocalVariable {
+ public readonly Expression Type;
public Type VariableType;
public readonly string Name;
public readonly Block Block;
public VariableInfo VariableInfo;
-
- Variable var;
- public Variable Variable {
- get { return var; }
- }
+ public HoistedVariable HoistedVariableReference;
[Flags]
enum Flags : byte {
ReadOnly = 2,
Pinned = 4,
IsThis = 8,
- Captured = 16,
AddressTaken = 32,
CompilerGenerated = 64,
IsConstant = 128
public void ResolveVariable (EmitContext ec)
{
- Block theblock = Block;
- if (theblock.ScopeInfo != null)
- var = theblock.ScopeInfo.GetCapturedVariable (this);
+ if (HoistedVariableReference != null)
+ return;
- if (var == null) {
+ if (builder == null) {
if (Pinned)
//
// This is needed to compile on both .NET 1.x and .NET 2.x
builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
else
builder = ec.ig.DeclareLocal (VariableType);
-
- var = new LocalVariable (this, builder);
}
}
- public void EmitSymbolInfo (EmitContext ec, string name)
+ public void Emit (EmitContext ec)
+ {
+ ec.ig.Emit (OpCodes.Ldloc, builder);
+ }
+
+ public void EmitAssign (EmitContext ec)
+ {
+ ec.ig.Emit (OpCodes.Stloc, builder);
+ }
+
+ public void EmitAddressOf (EmitContext ec)
+ {
+ ec.ig.Emit (OpCodes.Ldloca, builder);
+ }
+
+ public void EmitSymbolInfo (EmitContext ec)
{
if (builder != null)
- ec.DefineLocalVariable (name, builder);
+ ec.DefineLocalVariable (Name, builder);
}
public bool IsThisAssigned (EmitContext ec)
public bool Resolve (EmitContext ec)
{
- if (VariableType == null) {
- TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
- if (texpr == null)
- return false;
+ if (VariableType != null)
+ return true;
+
+ TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
+ if (texpr == null)
+ return false;
- VariableType = texpr.Type;
- }
+ VariableType = texpr.Type;
if (TypeManager.IsGenericParameter (VariableType))
return true;
- if (VariableType == TypeManager.void_type) {
- Expression.Error_VoidInvalidInTheContext (Location);
- return false;
- }
-
if (VariableType.IsAbstract && VariableType.IsSealed) {
FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
return false;
return true;
}
- public bool IsCaptured {
- get { return (flags & Flags.Captured) != 0; }
- set { flags |= Flags.Captured; }
- }
-
public bool IsConstant {
get { return (flags & Flags.IsConstant) != 0; }
set { flags |= Flags.IsConstant; }
get { return Location; }
}
- protected class LocalVariable : Variable
- {
- public readonly LocalInfo LocalInfo;
- LocalBuilder builder;
-
- public LocalVariable (LocalInfo local, LocalBuilder builder)
- {
- this.LocalInfo = local;
- this.builder = builder;
- }
-
- public override Type Type {
- get { return LocalInfo.VariableType; }
- }
-
- public override bool HasInstance {
- get { return false; }
- }
-
- public override bool NeedsTemporary {
- get { return false; }
- }
-
- public override void EmitInstance (EmitContext ec)
- {
- // Do nothing.
- }
-
- public override void Emit (EmitContext ec)
- {
- ec.ig.Emit (OpCodes.Ldloc, builder);
- }
-
- public override void EmitAssign (EmitContext ec)
- {
- ec.ig.Emit (OpCodes.Stloc, builder);
- }
-
- public override void EmitAddressOf (EmitContext ec)
- {
- ec.ig.Emit (OpCodes.Ldloca, builder);
- }
- }
-
public LocalInfo Clone (CloneContext clonectx)
{
//
public Location EndLocation = Location.Null;
public ExplicitBlock Explicit;
- public ToplevelBlock Toplevel;
+ public ToplevelBlock Toplevel; // TODO: Use Explicit
[Flags]
public enum Flags : byte {
HasRet = 8,
IsDestructor = 16,
Unsafe = 32,
- HasVarargs = 64, // Used in ToplevelBlock
- IsIterator = 128
+ IsIterator = 64,
+ HasStoreyAccess = 128
}
protected Flags flags;
public bool Unchecked {
get { return (flags & Flags.Unchecked) != 0; }
- set { flags |= Flags.Unchecked; }
+ set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
}
public bool Unsafe {
// The statements in this block
//
protected ArrayList statements;
- int num_statements;
//
// An array of Blocks. We keep track of children just
//
// Labels. (label, block) pairs.
//
- Hashtable labels;
+ protected HybridDictionary labels;
//
// Keeps track of (name, type) pairs
//
IDictionary variables;
+ protected IDictionary range_variables;
//
// Keeps track of constants
- Hashtable constants;
+ HybridDictionary constants;
//
// Temporary variables.
//
Block switch_block;
- ExpressionStatement scope_init;
+ ArrayList scope_initializers;
ArrayList anonymous_children;
int this_id;
int assignable_slots;
- protected ScopeInfo scope_info;
bool unreachable_shown;
bool unreachable;
this.EndLocation = end;
this.loc = start;
this_id = id++;
- statements = new ArrayList ();
+ statements = new ArrayList (4);
}
public Block CreateSwitchBlock (Location start)
void AddChild (Block b)
{
if (children == null)
- children = new ArrayList ();
+ children = new ArrayList (1);
children.Add (b);
}
Toplevel.CheckError158 (name, target.loc);
if (labels == null)
- labels = new Hashtable ();
+ labels = new HybridDictionary();
labels.Add (name, target);
return true;
if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
return true;
+ if (this is ToplevelBlock) {
+ Report.SymbolRelatedToPreviousError (kvi.Location, name);
+ e.Error_VariableIsUsedBeforeItIsDeclared (name);
+ return false;
+ }
+
//
// Even though we detected the error when the name is used, we
// treat it as if the variable declaration was in error.
LocalInfo vi = GetLocalInfo (name);
if (vi != null) {
Report.SymbolRelatedToPreviousError (vi.Location, name);
- if (Explicit == vi.Block.Explicit)
- Error_AlreadyDeclared (l, name, null);
- else
+ if (Explicit == vi.Block.Explicit) {
+ if (type == Linq.ImplicitQueryParameter.ImplicitType.Instance && type == vi.Type)
+ Error_AlreadyDeclared (l, name);
+ else
+ Error_AlreadyDeclared (l, name, null);
+ } else {
Error_AlreadyDeclared (l, name, "parent");
+ }
return null;
}
return false;
if (constants == null)
- constants = new Hashtable ();
+ constants = new HybridDictionary();
constants.Add (name, value);
public LocalInfo GetLocalInfo (string name)
{
+ LocalInfo ret;
for (Block b = this; b != null; b = b.Parent) {
if (b.variables != null) {
- LocalInfo ret = b.variables [name] as LocalInfo;
+ ret = (LocalInfo) b.variables [name];
+ if (ret != null)
+ return ret;
+ }
+
+ if (b.range_variables != null) {
+ ret = (LocalInfo) b.range_variables [name];
if (ret != null)
return ret;
}
}
+
return null;
}
}
return null;
}
+
+ //
+ // It should be used by expressions which require to
+ // register a statement during resolve process.
+ //
+ public void AddScopeStatement (StatementExpression s)
+ {
+ if (scope_initializers == null)
+ scope_initializers = new ArrayList ();
+
+ scope_initializers.Add (s);
+ }
public void AddStatement (Statement s)
{
public int AssignableSlots {
get {
- if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ("Variables have not been initialized yet");
+// TODO: Re-enable
+// if ((flags & Flags.VariablesInitialized) == 0)
+// throw new Exception ("Variables have not been initialized yet");
return assignable_slots;
}
}
- public ScopeInfo ScopeInfo {
- get { return scope_info; }
- }
-
- public ScopeInfo CreateScopeInfo ()
- {
- if (scope_info == null)
- scope_info = ScopeInfo.CreateScope (this);
-
- return scope_info;
- }
-
public ArrayList AnonymousChildren {
get { return anonymous_children; }
}
LocalInfo vi = (LocalInfo) de.Value;
Type variable_type = vi.VariableType;
- if (variable_type == null)
+ if (variable_type == null) {
+ if (vi.Type is VarExpr)
+ Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
+
continue;
+ }
Expression cv = (Expression) constants [name];
if (cv == null)
e = ce.ConvertImplicitly (variable_type);
if (e == null) {
- if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
+ if (TypeManager.IsReferenceType (variable_type))
Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
else
- ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
+ ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
continue;
}
//
public virtual void EmitMeta (EmitContext ec)
{
- Report.Debug (64, "BLOCK EMIT META", this, Parent, Toplevel, ScopeInfo, ec);
- if (ScopeInfo != null) {
- scope_init = ScopeInfo.GetScopeInitializer (ec);
- Report.Debug (64, "BLOCK EMIT META #1", this, Toplevel, ScopeInfo,
- ec, scope_init);
- }
-
if (variables != null){
foreach (LocalInfo vi in variables.Values)
vi.ResolveVariable (ec);
}
if (temporary_variables != null) {
- foreach (LocalInfo vi in temporary_variables)
- vi.ResolveVariable (ec);
+ for (int i = 0; i < temporary_variables.Count; i++)
+ ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
}
- if (children != null){
- foreach (Block b in children)
- b.EmitMeta (ec);
+ if (children != null) {
+ for (int i = 0; i < children.Count; i++)
+ ((Block)children[i]).EmitMeta(ec);
}
}
- void UsageWarning (FlowBranching.UsageVector vector)
+ void UsageWarning ()
{
- string name;
-
- if ((variables != null) && (Report.WarningLevel >= 3)) {
- foreach (DictionaryEntry de in variables){
- LocalInfo vi = (LocalInfo) de.Value;
+ if (variables == null || Report.WarningLevel < 3)
+ return;
- if (vi.Used)
- continue;
+ foreach (DictionaryEntry de in variables) {
+ LocalInfo vi = (LocalInfo) de.Value;
- name = (string) de.Key;
+ if (!vi.Used) {
+ string name = (string) de.Key;
// vi.VariableInfo can be null for 'catch' variables
- if (vi.VariableInfo != null && vector.IsAssigned (vi.VariableInfo, true)){
+ if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
- } else {
+ else
Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
- }
}
}
}
- private void CheckPossibleMistakenEmptyStatement (Statement s)
+ static void CheckPossibleMistakenEmptyStatement (Statement s)
{
Statement body;
body = ((Foreach) s).Statement;
else if (s is While)
body = ((While) s).Statement;
- else if (s is Using)
- body = ((Using) s).Statement;
else if (s is Fixed)
body = ((Fixed) s).Statement;
+ else if (s is Using)
+ body = ((Using) s).EmbeddedStatement;
+ else if (s is UsingTemporary)
+ body = ((UsingTemporary) s).Statement;
else
return;
if (s is EmptyStatement)
continue;
- if (s is Block)
- ((Block) s).unreachable = true;
-
if (!unreachable_shown && !(s is LabeledStatement)) {
Report.Warning (162, 2, s.loc, "Unreachable code detected");
unreachable_shown = true;
}
+
+ Block c_block = s as Block;
+ if (c_block != null)
+ c_block.unreachable = c_block.unreachable_shown = true;
}
//
//
if (!s.Resolve (ec)) {
+ ok = false;
if (ec.IsInProbingMode)
- return false;
+ break;
- ok = false;
statements [ix] = EmptyStatement.Value;
continue;
}
if (unreachable && !(s is LabeledStatement) && !(s is Block))
statements [ix] = EmptyStatement.Value;
- num_statements = ix + 1;
-
unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
if (unreachable && s is LabeledStatement)
throw new InternalErrorException ("should not happen");
}
Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
- ec.CurrentBranching, statement_count, num_statements);
-
- if (!ok)
- return false;
+ ec.CurrentBranching, statement_count);
while (ec.CurrentBranching is FlowBranchingLabeled)
ec.EndFlowBranching ();
- FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
+ bool flow_unreachable = ec.EndFlowBranching ();
ec.CurrentBlock = prev_block;
+ if (flow_unreachable)
+ flags |= Flags.HasRet;
+
// 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 == Toplevel && !Toplevel.IsThisAssigned (ec) && !vector.IsUnreachable)
+ if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
ok = false;
if ((labels != null) && (Report.WarningLevel >= 2)) {
foreach (LabeledStatement label in labels.Values)
if (!label.HasBeenReferenced)
- Report.Warning (164, 2, label.loc,
- "This label has not been referenced");
+ Report.Warning (164, 2, label.loc, "This label has not been referenced");
}
- Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
-
- if (vector.IsUnreachable)
- flags |= Flags.HasRet;
-
- if (ok && (errors == Report.Errors)) {
- UsageWarning (vector);
- }
+ if (ok && errors == Report.Errors)
+ UsageWarning ();
return ok;
}
protected override void DoEmit (EmitContext ec)
{
- for (int ix = 0; ix < num_statements; ix++){
+ for (int ix = 0; ix < statements.Count; ix++){
Statement s = (Statement) statements [ix];
s.Emit (ec);
}
public override void Emit (EmitContext ec)
{
Block prev_block = ec.CurrentBlock;
-
ec.CurrentBlock = this;
- bool emit_debug_info = (CodeGen.SymbolWriter != null);
- bool is_lexical_block = this == Explicit && Parent != null;
+ if (scope_initializers != null) {
+ SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
+
+ bool omit_debug_info = ec.OmitDebuggingInfo;
+ ec.OmitDebuggingInfo = true;
+ foreach (StatementExpression s in scope_initializers)
+ s.Emit (ec);
+ ec.OmitDebuggingInfo = omit_debug_info;
- if (emit_debug_info) {
- if (is_lexical_block)
- ec.BeginScope ();
+ SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
}
- ec.Mark (StartLocation, true);
- if (scope_init != null)
- scope_init.EmitStatement (ec);
+
+ ec.Mark (StartLocation);
DoEmit (ec);
- ec.Mark (EndLocation, true);
- if (emit_debug_info) {
- if (is_lexical_block)
- ec.EndScope ();
+ if (SymbolWriter.HasSymbolWriter)
+ EmitSymbolInfo (ec);
- if (variables != null) {
- foreach (DictionaryEntry de in variables) {
- string name = (string) de.Key;
- LocalInfo vi = (LocalInfo) de.Value;
+ ec.CurrentBlock = prev_block;
+ }
- vi.EmitSymbolInfo (ec, name);
- }
+ protected virtual void EmitSymbolInfo (EmitContext ec)
+ {
+ if (variables != null) {
+ foreach (LocalInfo vi in variables.Values) {
+ vi.EmitSymbolInfo (ec);
}
}
+ }
- ec.CurrentBlock = prev_block;
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ MutateVariables (storey);
+
+ foreach (Statement s in statements)
+ s.MutateHoistedGenericType (storey);
+ }
+
+ void MutateVariables (AnonymousMethodStorey storey)
+ {
+ if (variables != null) {
+ foreach (LocalInfo vi in variables.Values) {
+ vi.VariableType = storey.MutateType (vi.VariableType);
+ }
+ }
+
+ if (temporary_variables != null) {
+ foreach (LocalInfo vi in temporary_variables)
+ vi.VariableType = storey.MutateType (vi.VariableType);
+ }
}
public override string ToString ()
}
public class ExplicitBlock : Block {
+ HybridDictionary known_variables;
+ protected AnonymousMethodStorey am_storey;
+
public ExplicitBlock (Block parent, Location start, Location end)
: this (parent, (Flags) 0, start, end)
{
this.Explicit = this;
}
- Hashtable known_variables;
-
// <summary>
// Marks a variable with name @name as being used in this or a child block.
// If a variable name has been used in a child block, it's illegal to
internal void AddKnownVariable (string name, IKnownVariable info)
{
if (known_variables == null)
- known_variables = new Hashtable ();
+ known_variables = new HybridDictionary();
known_variables [name] = info;
Parent.Explicit.AddKnownVariable (name, info);
}
+ public AnonymousMethodStorey AnonymousMethodStorey {
+ get { return am_storey; }
+ }
+
+ //
+ // Creates anonymous method storey in current block
+ //
+ public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
+ {
+ //
+ // When referencing a variable in iterator storey from children anonymous method
+ //
+ if (Toplevel.am_storey is IteratorStorey) {
+ ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
+ return Toplevel.am_storey;
+ }
+
+ //
+ // An iterator has only 1 storey block
+ //
+ if (ec.CurrentIterator != null)
+ return ec.CurrentIterator.Storey;
+
+ if (am_storey == null) {
+ MemberBase mc = ec.ResolveContext as MemberBase;
+ GenericMethod gm = mc == null ? null : mc.GenericMethod;
+
+ //
+ // Create anonymous method storey for this block
+ //
+ am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
+ }
+
+ //
+ // Creates a link between this block and the anonymous method
+ //
+ // An anonymous method can reference variables from any outer block, but they are
+ // hoisted in their own ExplicitBlock. When more than one block is referenced we
+ // need to create another link between those variable storeys
+ //
+ ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
+ return am_storey;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (am_storey != null)
+ am_storey.EmitHoistedVariables (ec);
+
+ bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
+ if (emit_debug_info)
+ ec.BeginScope ();
+
+ base.Emit (ec);
+
+ if (emit_debug_info)
+ ec.EndScope ();
+ }
+
+ public override void EmitMeta (EmitContext ec)
+ {
+ //
+ // Creates anonymous method storey
+ //
+ if (am_storey != null) {
+ if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
+ am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
+ }
+
+ am_storey.DefineType ();
+ am_storey.ResolveType ();
+ am_storey.DefineMembers ();
+ am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
+ }
+
+ base.EmitMeta (ec);
+ }
+
internal IKnownVariable GetKnownVariable (string name)
{
return known_variables == null ? null : (IKnownVariable) known_variables [name];
}
+ public void PropagateStoreyReference (AnonymousMethodStorey s)
+ {
+ if (Parent != null && am_storey != s) {
+ if (am_storey != null)
+ am_storey.AddParentStoreyReference (s);
+
+ Parent.Explicit.PropagateStoreyReference (s);
+ }
+ }
+
+ public override bool Resolve (EmitContext ec)
+ {
+ bool ok = base.Resolve (ec);
+
+ //
+ // Discard an anonymous method storey when this block has no hoisted variables
+ //
+ if (am_storey != null && !am_storey.HasHoistedVariables) {
+ am_storey.Undo ();
+ am_storey = null;
+ }
+
+ return ok;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
ExplicitBlock target = (ExplicitBlock) t;
public Parameter Parameter {
get { return Block.Parameters [Index]; }
}
+
+ public Type ParameterType {
+ get { return Block.Parameters.Types [Index]; }
+ }
+
public Location Location {
get { return Parameter.Location; }
}
public class ToplevelBlock : ExplicitBlock {
GenericMethod generic;
FlowBranchingToplevel top_level_branching;
- AnonymousContainer anonymous_container;
- RootScopeInfo root_scope;
Parameters parameters;
ToplevelParameterInfo[] parameter_info;
+ LocalInfo this_variable;
- public bool HasVarargs {
- get { return (flags & Flags.HasVarargs) != 0; }
- set { flags |= Flags.HasVarargs; }
- }
-
- public bool IsIterator {
- get { return (flags & Flags.IsIterator) != 0; }
- }
+ public HoistedVariable HoistedThisVariable;
//
// The parameters for the block.
get { return parameters; }
}
- public bool CompleteContexts (EmitContext ec)
- {
- Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS", this, Parent, root_scope);
-
- if (root_scope != null)
- root_scope.LinkScopes ();
-
- if (Parent == null && root_scope != null) {
- Report.Debug (64, "TOPLEVEL COMPLETE CONTEXTS #1", this, root_scope);
-
- if (root_scope.DefineType () == null)
- return false;
- if (!root_scope.ResolveType ())
- return false;
- if (!root_scope.ResolveMembers ())
- return false;
- if (!root_scope.DefineMembers ())
- return false;
- }
-
- return true;
- }
-
public GenericMethod GenericMethod {
get { return generic; }
}
- public ToplevelBlock Container {
- get { return Parent == null ? null : Parent.Toplevel; }
+ public bool HasStoreyAccess {
+ set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
+ get { return (flags & Flags.HasStoreyAccess) != 0; }
}
- public AnonymousContainer AnonymousContainer {
- get { return anonymous_container; }
- set { anonymous_container = value; }
+ public ToplevelBlock Container {
+ get { return Parent == null ? null : Parent.Toplevel; }
}
public ToplevelBlock (Block parent, Parameters parameters, Location start) :
{
}
- public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
+ ToplevelBlock (Flags flags, Parameters parameters, Location start) :
this (null, flags, parameters, start)
{
}
if (parent != null)
parent.AddAnonymousChild (this);
- if (this.parameters.Count != 0)
+ if (!this.parameters.IsEmpty)
ProcessParameters ();
}
return true;
}
- public RootScopeInfo CreateRootScope (TypeContainer host)
+ public override Expression CreateExpressionTree (EmitContext ec)
{
- if (root_scope != null)
- return root_scope;
-
- if (Container == null)
- root_scope = new RootScopeInfo (
- this, host, generic, StartLocation);
-
- if (scope_info != null)
- throw new InternalErrorException ();
+ if (statements.Count == 1)
+ return ((Statement) statements [0]).CreateExpressionTree (ec);
- scope_info = root_scope;
- return root_scope;
+ return base.CreateExpressionTree (ec);
}
- public void CreateIteratorHost (RootScopeInfo root)
+ //
+ // Reformats this block to be top-level iterator block
+ //
+ public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
{
- Report.Debug (64, "CREATE ITERATOR HOST", this, root, Parent, root_scope);
+ // Create block with original statements
+ ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
+ IsIterator = true;
- if (Parent != null || root_scope != null)
- throw new InternalErrorException ();
+ // TODO: Change to iter_block.statements = statements;
+ foreach (Statement stmt in source.statements)
+ iter_block.AddStatement (stmt);
+ labels = source.labels;
+
+ AddStatement (new IteratorStatement (iterator, iter_block));
- scope_info = root_scope = root;
- }
+ source.statements = new ArrayList (1);
+ source.AddStatement (new Return (iterator, iterator.Location));
+ source.IsIterator = false;
- public RootScopeInfo RootScope {
- get {
- if (root_scope != null)
- return root_scope;
- else if (Container != null)
- return Container.RootScope;
- else
- return null;
- }
+ IteratorStorey iterator_storey = new IteratorStorey (iterator);
+ source.am_storey = iterator_storey;
+ return iterator_storey;
}
public FlowBranchingToplevel TopLevelBranching {
get { return top_level_branching; }
}
- //
- // This is used if anonymous methods are used inside an iterator
- // (see 2test-22.cs for an example).
- //
- // The AnonymousMethod is created while parsing - at a time when we don't
- // know yet that we're inside an iterator, so it's `Container' is initially
- // null. Later on, when resolving the iterator, we need to move the
- // anonymous method into that iterator.
- //
- public void ReParent (ToplevelBlock new_parent)
- {
- if ((flags & Flags.VariablesInitialized) != 0)
- throw new InternalErrorException ("block has already been resolved");
-
- Parent = new_parent;
- }
-
//
// Returns a `ParameterReference' for the given name, or null if there
// is no such parameter
return null;
}
- LocalInfo this_variable = null;
-
// <summary>
// Returns the "this" instance variable of this block.
// See AddThisVariable() for more information.
get { return this_variable; }
}
-
// <summary>
// This is used by non-static `struct' constructors which do not have an
// initializer - in this case, the constructor must initialize all of the
return this_variable;
}
+ public bool IsIterator {
+ get { return (flags & Flags.IsIterator) != 0; }
+ set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
+ }
+
public bool IsThisAssigned (EmitContext ec)
{
return this_variable == null || this_variable.IsThisAssigned (ec);
int offset = Parent == null ? 0 : Parent.AssignableSlots;
for (int i = 0; i < orig_count; ++i) {
- Parameter.Modifier mod = parameters.ParameterModifier (i);
+ Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
continue;
public override void EmitMeta (EmitContext ec)
{
+ parameters.ResolveVariable ();
+
+ // Avoid declaring an IL variable for this_variable since it is not accessed
+ // from the generated IL
+ if (this_variable != null)
+ Variables.Remove ("this");
base.EmitMeta (ec);
- parameters.ResolveVariable (this);
}
- public void MakeIterator (Iterator iterator)
+ protected override void EmitSymbolInfo (EmitContext ec)
{
- flags |= Flags.IsIterator;
-
- Block block = new ExplicitBlock (this, StartLocation, EndLocation);
- foreach (Statement stmt in statements)
- block.AddStatement (stmt);
- statements.Clear ();
- statements.Add (new MoveNextStatement (iterator, block));
- }
-
- protected class MoveNextStatement : Statement {
- Iterator iterator;
- Block block;
-
- public MoveNextStatement (Iterator iterator, Block block)
- {
- this.iterator = iterator;
- this.block = block;
- this.loc = iterator.Location;
- }
-
- public override bool Resolve (EmitContext ec)
- {
- return block.Resolve (ec);
- }
+ AnonymousExpression ae = ec.CurrentAnonymousMethod;
+ if ((ae != null) && (ae.Storey != null))
+ SymbolWriter.DefineScopeVariable (ae.Storey.ID);
- protected override void DoEmit (EmitContext ec)
- {
- iterator.EmitMoveNext (ec, block);
- }
+ base.EmitSymbolInfo (ec);
}
- public override string ToString ()
+ public override void Emit (EmitContext ec)
{
- return String.Format ("{0} ({1}:{2}{3}:{4})", GetType (), ID, StartLocation,
- root_scope, anonymous_container != null ?
- anonymous_container.Scope : null);
+ base.Emit (ec);
+ ec.Mark (EndLocation);
}
}
}
}
+ public Location Location {
+ get { return loc; }
+ }
+
public object Converted {
get {
return converted;
converted = NullStringCase;
return true;
}
-
- c = c.ImplicitConversionRequired (required_type, loc);
+
+ c = c.ImplicitConversionRequired (ec, required_type, loc);
if (c == null)
return false;
return true;
}
- public void Erorr_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
+ public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
{
string label;
if (converted == null)
label = "default";
else if (converted == NullStringCase)
label = "null";
- else if (TypeManager.IsEnumType (switch_type))
- label = TypeManager.CSharpEnumValue (switch_type, converted);
else
label = converted.ToString ();
Label null_target;
Expression new_expr;
bool is_constant;
+ bool has_null_case;
SwitchSection constant_section;
SwitchSection default_section;
+ ExpressionStatement string_dictionary;
+ FieldExpr switch_cache_field;
+ static int unique_counter;
+
#if GMCS_SOURCE
//
// Nullable Types support for GMCS.
//
Expression SwitchGoverningType (EmitContext ec, Expression expr)
{
- Type t = TypeManager.DropGenericTypeArguments (expr.Type);
+ Type t = expr.Type;
if (t == TypeManager.byte_type ||
t == TypeManager.sbyte_type ||
t == TypeManager.char_type ||
t == TypeManager.string_type ||
t == TypeManager.bool_type ||
- t.IsSubclassOf (TypeManager.enum_type))
+ TypeManager.IsEnumType (t))
return expr;
if (allowed_types == null){
TypeManager.int64_type,
TypeManager.uint64_type,
TypeManager.char_type,
- TypeManager.string_type,
- TypeManager.bool_type
+ TypeManager.string_type
};
}
continue;
if (converted != null){
- Report.ExtraInformation (
- loc,
- String.Format ("reason: more than one conversion to an integral type exist for type {0}",
- TypeManager.CSharpName (expr.Type)));
+ Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
return null;
}
foreach (SwitchLabel sl in ss.Labels){
if (sl.Label == null){
if (default_section != null){
- sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
+ sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
error = true;
}
default_section = ss;
}
object key = sl.Converted;
+ if (key == SwitchLabel.NullStringCase)
+ has_null_case = true;
+
try {
Elements.Add (key, sl);
} catch (ArgumentException) {
- sl.Erorr_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
+ sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
error = true;
}
}
/// <param name="ec"></param>
/// <param name="val"></param>
/// <returns></returns>
- void TableSwitchEmit (EmitContext ec, LocalBuilder val)
+ void TableSwitchEmit (EmitContext ec, Expression val)
{
int element_count = Elements.Count;
object [] element_keys = new object [element_count];
Array.Sort (element_keys);
// initialize the block list with one element per key
- ArrayList key_blocks = new ArrayList ();
+ ArrayList key_blocks = new ArrayList (element_count);
foreach (object key in element_keys)
key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
// okay now we can start...
ILGenerator ig = ec.ig;
Label lbl_end = ig.DefineLabel (); // at the end ;-)
- Label lbl_default = ig.DefineLabel ();
+ Label lbl_default = default_target;
Type type_keys = null;
if (element_keys.Length > 0)
Type compare_type;
if (TypeManager.IsEnumType (SwitchType))
- compare_type = TypeManager.EnumToUnderlying (SwitchType);
+ compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
else
compare_type = SwitchType;
for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
{
KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
- lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
+ lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
if (kb.Length <= 2)
{
- foreach (object key in kb.element_keys)
- {
- ig.Emit (OpCodes.Ldloc, val);
- EmitObjectInteger (ig, key);
+ foreach (object key in kb.element_keys) {
SwitchLabel sl = (SwitchLabel) Elements [key];
- ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
+ if (key is int && (int) key == 0) {
+ val.EmitBranchable (ec, sl.GetILLabel (ec), false);
+ } else {
+ val.Emit (ec);
+ EmitObjectInteger (ig, key);
+ ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
+ }
}
}
else
// TODO: optimize constant/I4 cases
// check block range (could be > 2^31)
- ig.Emit (OpCodes.Ldloc, val);
+ val.Emit (ec);
EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
ig.Emit (OpCodes.Blt, lbl_default);
- ig.Emit (OpCodes.Ldloc, val);
+ val.Emit (ec);
EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
ig.Emit (OpCodes.Bgt, lbl_default);
// normalize range
- ig.Emit (OpCodes.Ldloc, val);
+ val.Emit (ec);
if (kb.first != 0)
{
EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
else
{
// normalize range
- ig.Emit (OpCodes.Ldloc, val);
+ val.Emit (ec);
int first = (int) kb.first;
if (first > 0)
{
// make sure to mark other labels in the default section
// the last default just goes to the end
- ig.Emit (OpCodes.Br, lbl_default);
+ if (element_keys.Length > 0)
+ ig.Emit (OpCodes.Br, lbl_default);
// now emit the code for the sections
bool found_default = false;
- bool found_null = false;
- foreach (SwitchSection ss in Sections)
- {
- foreach (SwitchLabel sl in ss.Labels)
- if (sl.Converted == SwitchLabel.NullStringCase)
- found_null = true;
- }
- foreach (SwitchSection ss in Sections)
- {
- foreach (SwitchLabel sl in ss.Labels)
- {
- ig.MarkLabel (sl.GetILLabel (ec));
- ig.MarkLabel (sl.GetILLabelCode (ec));
- if (sl.Converted == SwitchLabel.NullStringCase)
+ foreach (SwitchSection ss in Sections) {
+ foreach (SwitchLabel sl in ss.Labels) {
+ if (sl.Converted == SwitchLabel.NullStringCase) {
ig.MarkLabel (null_target);
- else if (sl.Label == null) {
+ } else if (sl.Label == null) {
ig.MarkLabel (lbl_default);
found_default = true;
- if (!found_null)
+ if (!has_null_case)
ig.MarkLabel (null_target);
}
+ ig.MarkLabel (sl.GetILLabel (ec));
+ ig.MarkLabel (sl.GetILLabelCode (ec));
}
ss.Block.Emit (ec);
}
if (!found_default) {
ig.MarkLabel (lbl_default);
- if (HaveUnwrap && !found_null) {
+ if (!has_null_case) {
ig.MarkLabel (null_target);
}
}
ig.MarkLabel (lbl_end);
}
- //
- // This simple emit switch works, but does not take advantage of the
- // `switch' opcode.
- // TODO: remove non-string logic from here
- // TODO: binary search strings?
- //
- void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
- {
- ILGenerator ig = ec.ig;
- Label end_of_switch = ig.DefineLabel ();
- Label next_test = ig.DefineLabel ();
- bool first_test = true;
- bool pending_goto_end = false;
- bool null_marked = false;
- bool null_found;
- int section_count = Sections.Count;
-
- // TODO: implement switch optimization for string by using Hashtable
- //if (SwitchType == TypeManager.string_type && section_count > 7)
- // Console.WriteLine ("Switch optimization possible " + loc);
-
- ig.Emit (OpCodes.Ldloc, val);
-
- if (Elements.Contains (SwitchLabel.NullStringCase)){
- ig.Emit (OpCodes.Brfalse, null_target);
- } else
- ig.Emit (OpCodes.Brfalse, default_target);
-
- ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
- ig.Emit (OpCodes.Stloc, val);
-
- for (int section = 0; section < section_count; section++){
- SwitchSection ss = (SwitchSection) Sections [section];
-
- if (ss == default_section)
- continue;
-
- Label sec_begin = ig.DefineLabel ();
-
- ig.Emit (OpCodes.Nop);
-
- if (pending_goto_end)
- ig.Emit (OpCodes.Br, end_of_switch);
-
- int label_count = ss.Labels.Count;
- null_found = false;
- for (int label = 0; label < label_count; label++){
- SwitchLabel sl = (SwitchLabel) ss.Labels [label];
- ig.MarkLabel (sl.GetILLabel (ec));
-
- if (!first_test){
- ig.MarkLabel (next_test);
- next_test = ig.DefineLabel ();
- }
- //
- // If we are the default target
- //
- if (sl.Label != null){
- object lit = sl.Converted;
-
- if (lit == SwitchLabel.NullStringCase){
- null_found = true;
- if (label + 1 == label_count)
- ig.Emit (OpCodes.Br, next_test);
- continue;
- }
-
- ig.Emit (OpCodes.Ldloc, val);
- ig.Emit (OpCodes.Ldstr, (string)lit);
- 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);
- }
- }
- }
- if (null_found) {
- ig.MarkLabel (null_target);
- null_marked = true;
- }
- ig.MarkLabel (sec_begin);
- foreach (SwitchLabel sl in ss.Labels)
- ig.MarkLabel (sl.GetILLabelCode (ec));
-
- ss.Block.Emit (ec);
- pending_goto_end = !ss.Block.HasRet;
- first_test = false;
- }
- ig.MarkLabel (next_test);
- ig.MarkLabel (default_target);
- if (!null_marked)
- ig.MarkLabel (null_target);
- if (default_section != null)
- default_section.Block.Emit (ec);
- ig.MarkLabel (end_of_switch);
- }
SwitchSection FindSection (SwitchLabel label)
{
return null;
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ foreach (SwitchSection ss in Sections)
+ ss.Block.MutateHoistedGenericType (storey);
+ }
+
+ public static void Reset ()
+ {
+ unique_counter = 0;
+ }
+
public override bool Resolve (EmitContext ec)
{
Expr = Expr.Resolve (ec);
}
bool first = true;
+ bool ok = true;
foreach (SwitchSection ss in Sections){
if (!first)
ec.CurrentBranching.CreateSibling (
// one single section - mark all the others as
// unreachable.
ec.CurrentBranching.CurrentUsageVector.Goto ();
- if (!ss.Block.ResolveUnreachable (ec, true))
- return false;
+ if (!ss.Block.ResolveUnreachable (ec, true)) {
+ ok = false;
+ }
} else {
if (!ss.Block.Resolve (ec))
- return false;
+ ok = false;
}
}
Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
+ if (!ok)
+ return false;
+
+ if (SwitchType == TypeManager.string_type && !is_constant) {
+ // TODO: Optimize single case, and single+default case
+ ResolveStringSwitchMap (ec);
+ }
+
return true;
}
-
- protected override void DoEmit (EmitContext ec)
+
+ void ResolveStringSwitchMap (EmitContext ec)
{
- ILGenerator ig = ec.ig;
+ FullNamedExpression string_dictionary_type;
+#if GMCS_SOURCE
+ MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
+ new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
- default_target = ig.DefineLabel ();
- null_target = ig.DefineLabel ();
+ string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
+ new TypeArguments (loc,
+ new TypeExpression (TypeManager.string_type, loc),
+ new TypeExpression (TypeManager.int32_type, loc)), loc);
+#else
+ MemberAccess system_collections_generic = new MemberAccess (
+ new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
- // Store variable for comparission purposes
- LocalBuilder value;
- if (HaveUnwrap) {
- value = ig.DeclareLocal (SwitchType);
-#if GMCS_SOURCE
- unwrap.EmitCheck (ec);
- ig.Emit (OpCodes.Brfalse, null_target);
- new_expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, value);
+ string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
#endif
- } else if (!is_constant) {
- value = ig.DeclareLocal (SwitchType);
- new_expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, value);
- } else
- value = null;
+ Field field = new Field (ec.TypeContainer, string_dictionary_type,
+ Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
+ CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), null, loc);
+ if (!field.Define ())
+ return;
+ ec.TypeContainer.PartialContainer.AddField (field);
+
+ ArrayList init = new ArrayList ();
+ int counter = 0;
+ Elements.Clear ();
+ string value = null;
+ foreach (SwitchSection section in Sections) {
+ foreach (SwitchLabel sl in section.Labels) {
+ if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
+ value = null;
+ continue;
+ }
- //
- // Setup the codegen context
- //
- Label old_end = ec.LoopEnd;
- Switch old_switch = ec.Switch;
-
- ec.LoopEnd = ig.DefineLabel ();
- ec.Switch = this;
+ value = (string) sl.Converted;
+ ArrayList init_args = new ArrayList (2);
+ init_args.Add (new StringLiteral (value, sl.Location));
+ init_args.Add (new IntConstant (counter, loc));
+ init.Add (new CollectionElementInitializer (init_args, loc));
+ }
+
+ if (value == null)
+ continue;
+
+ Elements.Add (counter, section.Labels [0]);
+ ++counter;
+ }
+
+ ArrayList args = new ArrayList (1);
+ args.Add (new Argument (new IntConstant (Sections.Count, loc)));
+ Expression initializer = new NewInitialize (string_dictionary_type, args,
+ new CollectionOrObjectInitializers (init, loc), loc);
+
+ switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
+ string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
+ }
+
+ void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+ Label l_initialized = ig.DefineLabel ();
+
+ //
+ // Skip initialization when value is null
+ //
+ value.EmitBranchable (ec, null_target, false);
+
+ //
+ // Check if string dictionary is initialized and initialize
+ //
+ switch_cache_field.EmitBranchable (ec, l_initialized, true);
+ string_dictionary.EmitStatement (ec);
+ ig.MarkLabel (l_initialized);
+
+ LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
+
+#if GMCS_SOURCE
+ ArrayList get_value_args = new ArrayList (2);
+ get_value_args.Add (new Argument (value));
+ get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
+ Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
+ if (get_item == null)
+ return;
+
+ //
+ // A value was not found, go to default case
+ //
+ get_item.EmitBranchable (ec, default_target, false);
+#else
+ ArrayList get_value_args = new ArrayList (1);
+ get_value_args.Add (value);
+
+ Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
+ if (get_item == null)
+ return;
+
+ LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
+ get_item_object.EmitAssign (ec, get_item, true, false);
+ ec.ig.Emit (OpCodes.Brfalse, default_target);
+
+ ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
+ new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
+
+ get_item_int.EmitStatement (ec);
+ get_item_object.Release (ec);
+#endif
+ TableSwitchEmit (ec, string_switch_variable);
+ string_switch_variable.Release (ec);
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ default_target = ig.DefineLabel ();
+ null_target = ig.DefineLabel ();
+
+ // Store variable for comparission purposes
+ // TODO: Don't duplicate non-captured VariableReference
+ LocalTemporary value;
+ if (HaveUnwrap) {
+ value = new LocalTemporary (SwitchType);
+#if GMCS_SOURCE
+ unwrap.EmitCheck (ec);
+ ig.Emit (OpCodes.Brfalse, null_target);
+ new_expr.Emit (ec);
+ value.Store (ec);
+#endif
+ } else if (!is_constant) {
+ value = new LocalTemporary (SwitchType);
+ new_expr.Emit (ec);
+ value.Store (ec);
+ } else
+ value = null;
+
+ //
+ // Setup the codegen context
+ //
+ Label old_end = ec.LoopEnd;
+ Switch old_switch = ec.Switch;
+
+ ec.LoopEnd = ig.DefineLabel ();
+ ec.Switch = this;
// Emit Code.
if (is_constant) {
if (constant_section != null)
constant_section.Block.Emit (ec);
- } else if (SwitchType == TypeManager.string_type)
- SimpleSwitchEmit (ec, value);
- else
+ } else if (string_dictionary != null) {
+ DoEmitStringSwitch (value, ec);
+ } else {
TableSwitchEmit (ec, value);
+ }
+
+ if (value != null)
+ value.Release (ec);
// Restore context state.
ig.MarkLabel (ec.LoopEnd);
}
}
- public abstract class ExceptionStatement : Statement
+ // A place where execution can restart in an iterator
+ public abstract class ResumableStatement : Statement
{
- public abstract void EmitFinally (EmitContext ec);
+ bool prepared;
+ protected Label resume_point;
- protected bool emit_finally = true;
- ArrayList parent_vectors;
+ public Label PrepareForEmit (EmitContext ec)
+ {
+ if (!prepared) {
+ prepared = true;
+ resume_point = ec.ig.DefineLabel ();
+ }
+ return resume_point;
+ }
- protected void DoEmitFinally (EmitContext ec)
+ public virtual Label PrepareForDispose (EmitContext ec, Label end)
{
- if (emit_finally)
- ec.ig.BeginFinallyBlock ();
- else if (ec.InIterator)
- ec.CurrentIterator.MarkFinally (ec, parent_vectors);
- EmitFinally (ec);
+ return end;
}
+ public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
+ {
+ }
+ }
+
+ // Base class for statements that are implemented in terms of try...finally
+ public abstract class ExceptionStatement : ResumableStatement
+ {
+ bool code_follows;
- protected void ResolveFinally (FlowBranchingException branching)
+ protected abstract void EmitPreTryBody (EmitContext ec);
+ protected abstract void EmitTryBody (EmitContext ec);
+ protected abstract void EmitFinallyBody (EmitContext ec);
+
+ protected sealed override void DoEmit (EmitContext ec)
{
- emit_finally = branching.EmitFinally;
- if (!emit_finally)
- branching.Parent.StealFinallyClauses (ref parent_vectors);
+ ILGenerator ig = ec.ig;
+
+ EmitPreTryBody (ec);
+
+ if (resume_points != null) {
+ IntConstant.EmitInt (ig, (int) Iterator.State.Running);
+ ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
+ }
+
+ ig.BeginExceptionBlock ();
+
+ if (resume_points != null) {
+ ig.MarkLabel (resume_point);
+
+ // For normal control flow, we want to fall-through the Switch
+ // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
+ ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
+ IntConstant.EmitInt (ig, first_resume_pc);
+ ig.Emit (OpCodes.Sub);
+
+ Label [] labels = new Label [resume_points.Count];
+ for (int i = 0; i < resume_points.Count; ++i)
+ labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
+ ig.Emit (OpCodes.Switch, labels);
+ }
+
+ EmitTryBody (ec);
+
+ ig.BeginFinallyBlock ();
+
+ Label start_finally = ec.ig.DefineLabel ();
+ if (resume_points != null) {
+ ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
+ ig.Emit (OpCodes.Brfalse_S, start_finally);
+ ig.Emit (OpCodes.Endfinally);
+ }
+
+ ig.MarkLabel (start_finally);
+ EmitFinallyBody (ec);
+
+ ig.EndExceptionBlock ();
+ }
+
+ public void SomeCodeFollows ()
+ {
+ code_follows = true;
+ }
+
+ protected void ResolveReachability (EmitContext ec)
+ {
+ // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
+ // So, ensure there's some IL code after this statement.
+ if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
+ ec.NeedReturnLabel ();
+
+ }
+
+ ArrayList resume_points;
+ int first_resume_pc;
+ public void AddResumePoint (ResumableStatement stmt, int pc)
+ {
+ if (resume_points == null) {
+ resume_points = new ArrayList ();
+ first_resume_pc = pc;
+ }
+
+ if (pc != first_resume_pc + resume_points.Count)
+ throw new InternalErrorException ("missed an intervening AddResumePoint?");
+
+ resume_points.Add (stmt);
+ }
+
+ Label dispose_try_block;
+ bool prepared_for_dispose, emitted_dispose;
+ public override Label PrepareForDispose (EmitContext ec, Label end)
+ {
+ if (!prepared_for_dispose) {
+ prepared_for_dispose = true;
+ dispose_try_block = ec.ig.DefineLabel ();
+ }
+ return dispose_try_block;
+ }
+
+ public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
+ {
+ if (emitted_dispose)
+ return;
+
+ emitted_dispose = true;
+
+ ILGenerator ig = ec.ig;
+
+ Label end_of_try = ig.DefineLabel ();
+
+ // Ensure that the only way we can get into this code is through a dispatcher
+ if (have_dispatcher)
+ ig.Emit (OpCodes.Br, end);
+
+ ig.BeginExceptionBlock ();
+
+ ig.MarkLabel (dispose_try_block);
+
+ Label [] labels = null;
+ for (int i = 0; i < resume_points.Count; ++i) {
+ ResumableStatement s = (ResumableStatement) resume_points [i];
+ Label ret = s.PrepareForDispose (ec, end_of_try);
+ if (ret.Equals (end_of_try) && labels == null)
+ continue;
+ if (labels == null) {
+ labels = new Label [resume_points.Count];
+ for (int j = 0; j < i; ++j)
+ labels [j] = end_of_try;
+ }
+ labels [i] = ret;
+ }
+
+ if (labels != null) {
+ int j;
+ for (j = 1; j < labels.Length; ++j)
+ if (!labels [0].Equals (labels [j]))
+ break;
+ bool emit_dispatcher = j < labels.Length;
+
+ if (emit_dispatcher) {
+ //SymbolWriter.StartIteratorDispatcher (ec.ig);
+ ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
+ IntConstant.EmitInt (ig, first_resume_pc);
+ ig.Emit (OpCodes.Sub);
+ ig.Emit (OpCodes.Switch, labels);
+ //SymbolWriter.EndIteratorDispatcher (ec.ig);
+ }
+
+ foreach (ResumableStatement s in resume_points)
+ s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
+ }
+
+ ig.MarkLabel (end_of_try);
+
+ ig.BeginFinallyBlock ();
+
+ EmitFinallyBody (ec);
+
+ ig.EndExceptionBlock ();
}
}
return false;
}
- FlowBranchingException branching = ec.StartFlowBranching (this);
+ ec.StartFlowBranching (this);
bool ok = Statement.Resolve (ec);
-
- ResolveFinally (branching);
-
ec.EndFlowBranching ();
- // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
- // So, ensure there's some IL code after the finally block.
- ec.NeedReturnLabel ();
+ ResolveReachability (ec);
// Avoid creating libraries that reference the internal
// mcs NullType:
temp = new TemporaryVariable (t, loc);
temp.Resolve (ec);
+
+ if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
+ Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
+ TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
+ monitor_type, "Enter", loc, TypeManager.object_type);
+ TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
+ monitor_type, "Exit", loc, TypeManager.object_type);
+ }
return ok;
}
- protected override void DoEmit (EmitContext ec)
+ protected override void EmitPreTryBody (EmitContext ec)
{
ILGenerator ig = ec.ig;
- temp.Store (ec, expr);
+ temp.EmitAssign (ec, expr);
temp.Emit (ec);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+ }
- // try
- if (emit_finally)
- ig.BeginExceptionBlock ();
+ protected override void EmitTryBody (EmitContext ec)
+ {
Statement.Emit (ec);
-
- // finally
- DoEmitFinally (ec);
- if (emit_finally)
- ig.EndExceptionBlock ();
}
- public override void EmitFinally (EmitContext ec)
+ protected override void EmitFinallyBody (EmitContext ec)
{
temp.Emit (ec);
ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr.MutateHoistedGenericType (storey);
+ temp.MutateHoistedGenericType (storey);
+ Statement.MutateHoistedGenericType (storey);
+ }
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Block.Emit (ec);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ Block.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Unchecked target = (Unchecked) t;
Block.Emit (ec);
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ Block.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Checked target = (Checked) t;
using (ec.With (EmitContext.Flags.InUnsafe, true))
Block.Emit (ec);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ Block.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Unsafe target = (Unsafe) t;
// Store pointer in pinned location
//
converted.Emit (ec);
- vi.Variable.EmitAssign (ec);
+ vi.EmitAssign (ec);
}
public override void EmitExit (EmitContext ec)
{
ec.ig.Emit (OpCodes.Ldc_I4_0);
ec.ig.Emit (OpCodes.Conv_U);
- vi.Variable.EmitAssign (ec);
+ vi.EmitAssign (ec);
}
}
class StringEmitter : Emitter {
+ class StringPtr : Expression
+ {
+ LocalBuilder b;
+
+ public StringPtr (LocalBuilder b, Location l)
+ {
+ this.b = b;
+ eclass = ExprClass.Value;
+ type = TypeManager.char_ptr_type;
+ loc = l;
+ }
+
+ public override Expression CreateExpressionTree (EmitContext ec)
+ {
+ throw new NotSupportedException ("ET");
+ }
+
+ public override Expression DoResolve (EmitContext ec)
+ {
+ // This should never be invoked, we are born in fully
+ // initialized state.
+
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (TypeManager.int_get_offset_to_string_data == null) {
+ // TODO: Move to resolve !!
+ TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
+ TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
+ }
+
+ ILGenerator ig = ec.ig;
+
+ ig.Emit (OpCodes.Ldloc, b);
+ ig.Emit (OpCodes.Conv_I);
+ ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
+ ig.Emit (OpCodes.Add);
+ }
+ }
+
LocalBuilder pinned_string;
Location loc;
return;
converted.Emit (ec);
- vi.Variable.EmitAssign (ec);
+ vi.EmitAssign (ec);
}
public override void EmitExit (EmitContext ec)
return false;
}
- TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
- if (texpr == null)
+ TypeExpr texpr = type.ResolveAsContextualType (ec, false);
+ if (texpr == null) {
+ if (type is VarExpr)
+ Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
+
return false;
+ }
expr_type = texpr.Type;
Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
return false;
}
-
- //
- // Case 1: & object.
- //
- if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
- Expression child = ((Unary) e).Expr;
-
- if (child is ParameterReference || child is LocalVariableReference){
- Report.Error (
- 213, loc,
- "No need to use fixed statement for parameters or " +
- "local variable declarations (address is already " +
- "fixed)");
- return false;
- }
-
- ec.InFixedInitializer = true;
- e = e.Resolve (ec);
- ec.InFixedInitializer = false;
- if (e == null)
- return false;
-
- child = ((Unary) e).Expr;
-
- if (!TypeManager.VerifyUnManaged (child.Type, loc))
- return false;
-
- if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
- e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
- return false;
- }
-
- data [i] = new ExpressionEmitter (e, vi);
- i++;
-
- continue;
- }
ec.InFixedInitializer = true;
e = e.Resolve (ec);
// fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
//
converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
- new Binary (Binary.Operator.Equality, e, new NullConstant (loc)),
+ new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
- NullPointer.Null,
+ new NullPointer (loc),
converted);
converted = converted.Resolve (ec);
}
// Case 4: fixed buffer
- FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
- if (fixed_buffer_ptr != null) {
- data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
+ if (e is FixedBufferPtr) {
+ data [i++] = new ExpressionEmitter (e, vi);
continue;
}
//
- // For other cases, flag a `this is already fixed expression'
+ // Case 1: & object.
//
- 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;
+ Unary u = e as Unary;
+ if (u != null && u.Oper == Unary.Operator.AddressOf) {
+ IVariableReference vr = u.Expr as IVariableReference;
+ if (vr == null || !vr.IsFixedVariable) {
+ data [i] = new ExpressionEmitter (e, vi);
+ }
}
- Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
- return false;
+ if (data [i++] == null)
+ Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
+
+ e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
}
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
}
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ // Fixed statement cannot be used inside anonymous methods or lambdas
+ throw new NotSupportedException ();
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Fixed target = (Fixed) t;
}
}
- protected override void DoEmit(EmitContext ec)
+ protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
VarBlock.Emit (ec);
if (Name != null) {
- LocalInfo vi = Block.GetLocalInfo (Name);
- if (vi == null)
- throw new Exception ("Variable does not exist in this block");
-
- if (vi.Variable.NeedsTemporary) {
- LocalBuilder e = ig.DeclareLocal (vi.VariableType);
- ig.Emit (OpCodes.Stloc, e);
+ // TODO: Move to resolve
+ LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
+ lvr.Resolve (ec);
+
+ Expression source;
+ if (lvr.IsHoisted) {
+ LocalTemporary lt = new LocalTemporary (lvr.Type);
+ lt.Store (ec);
+ source = lt;
+ } else {
+ // Variable is at the top of the stack
+ source = EmptyExpression.Null;
+ }
- vi.Variable.EmitInstance (ec);
- ig.Emit (OpCodes.Ldloc, e);
- vi.Variable.EmitAssign (ec);
- } else
- vi.Variable.EmitAssign (ec);
+ lvr.EmitAssign (ec, source, false, false);
} else
ig.Emit (OpCodes.Pop);
type = te.Type;
- if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
+ if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
Error (155, "The type caught or thrown must be derived from System.Exception");
return false;
}
}
}
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ if (type != null)
+ type = storey.MutateType (type);
+ if (VarBlock != null)
+ VarBlock.MutateHoistedGenericType (storey);
+ Block.MutateHoistedGenericType (storey);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Catch target = (Catch) t;
}
}
- public class Try : ExceptionStatement {
- public Block Fini, Block;
+ public class TryFinally : ExceptionStatement {
+ Statement stmt;
+ Block fini;
+
+ public TryFinally (Statement stmt, Block fini, Location l)
+ {
+ this.stmt = stmt;
+ this.fini = fini;
+ loc = l;
+ }
+
+ public override bool Resolve (EmitContext ec)
+ {
+ bool ok = true;
+
+ ec.StartFlowBranching (this);
+
+ if (!stmt.Resolve (ec))
+ ok = false;
+
+ if (ok)
+ ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
+ using (ec.With (EmitContext.Flags.InFinally, true)) {
+ if (!fini.Resolve (ec))
+ ok = false;
+ }
+
+ ec.EndFlowBranching ();
+
+ ResolveReachability (ec);
+
+ return ok;
+ }
+
+ protected override void EmitPreTryBody (EmitContext ec)
+ {
+ }
+
+ protected override void EmitTryBody (EmitContext ec)
+ {
+ stmt.Emit (ec);
+ }
+
+ protected override void EmitFinallyBody (EmitContext ec)
+ {
+ fini.Emit (ec);
+ }
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ stmt.MutateHoistedGenericType (storey);
+ fini.MutateHoistedGenericType (storey);
+ }
+
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ TryFinally target = (TryFinally) t;
+
+ target.stmt = (Statement) stmt.Clone (clonectx);
+ if (fini != null)
+ target.fini = clonectx.LookupBlock (fini);
+ }
+ }
+
+ public class TryCatch : Statement {
+ public Block Block;
public ArrayList Specific;
public Catch General;
+ bool inside_try_finally, code_follows;
- bool need_exc_block;
-
- //
- // specific, general and fini might all be null.
- //
- public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
+ public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
{
- if (specific == null && general == null){
- Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
- }
-
this.Block = block;
- this.Specific = specific;
- this.General = general;
- this.Fini = fini;
+ this.Specific = catch_clauses;
+ this.General = null;
+ this.inside_try_finally = inside_try_finally;
+
+ for (int i = 0; i < catch_clauses.Count; ++i) {
+ Catch c = (Catch) catch_clauses [i];
+ if (c.IsGeneral) {
+ if (i != catch_clauses.Count - 1)
+ Report.Error (1017, c.loc, "Try statement already has an empty catch block");
+ this.General = c;
+ catch_clauses.RemoveAt (i);
+ i--;
+ }
+ }
+
loc = l;
}
public override bool Resolve (EmitContext ec)
{
bool ok = true;
-
- FlowBranchingException branching = ec.StartFlowBranching (this);
- Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
+ ec.StartFlowBranching (this);
if (!Block.Resolve (ec))
ok = false;
- FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
-
- Report.Debug (1, "START OF CATCH BLOCKS", vector);
-
Type[] prev_catches = new Type [Specific.Count];
int last_index = 0;
foreach (Catch c in Specific){
- ec.CurrentBranching.CreateSibling (
- c.Block, FlowBranching.SiblingType.Catch);
-
- Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
+ ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
if (c.Name != null) {
LocalInfo vi = c.Block.GetLocalInfo (c.Name);
}
if (!c.Resolve (ec))
- return false;
+ ok = false;
Type resolved_type = c.CatchType;
for (int ii = 0; ii < last_index; ++ii) {
- if (resolved_type == prev_catches [ii] || resolved_type.IsSubclassOf (prev_catches [ii])) {
+ if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
- return false;
+ ok = false;
}
}
prev_catches [last_index++] = resolved_type;
- need_exc_block = true;
}
- Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
-
- if (General != null){
+ if (General != null) {
if (CodeGen.Assembly.WrapNonExceptionThrows) {
foreach (Catch c in Specific){
if (c.CatchType == TypeManager.exception_type) {
}
}
- ec.CurrentBranching.CreateSibling (
- General.Block, FlowBranching.SiblingType.Catch);
-
- Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
+ ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
if (!General.Resolve (ec))
ok = false;
-
- need_exc_block = true;
}
- Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
-
- if (Fini != null) {
- if (ok)
- ec.CurrentBranching.CreateSibling (Fini, FlowBranching.SiblingType.Finally);
-
- Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
- using (ec.With (EmitContext.Flags.InFinally, true)) {
- if (!Fini.Resolve (ec))
- ok = false;
- }
-
- if (!ec.InIterator)
- need_exc_block = true;
- }
-
- if (ec.InIterator) {
- ResolveFinally (branching);
- need_exc_block |= emit_finally;
- } else
- emit_finally = Fini != null;
-
ec.EndFlowBranching ();
- // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
- // So, ensure there's some IL code after the finally block.
- ec.NeedReturnLabel ();
-
- FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
-
- Report.Debug (1, "END OF TRY", ec.CurrentBranching, vector, f_vector);
+ // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
+ // So, ensure there's some IL code after this statement
+ if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
+ ec.NeedReturnLabel ();
return ok;
}
+
+ public void SomeCodeFollows ()
+ {
+ code_follows = true;
+ }
protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
- if (need_exc_block)
+ if (!inside_try_finally)
ig.BeginExceptionBlock ();
+
Block.Emit (ec);
foreach (Catch c in Specific)
if (General != null)
General.Emit (ec);
- DoEmitFinally (ec);
- if (need_exc_block)
+ if (!inside_try_finally)
ig.EndExceptionBlock ();
}
- public override void EmitFinally (EmitContext ec)
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
{
- if (Fini != null)
- Fini.Emit (ec);
- }
+ Block.MutateHoistedGenericType (storey);
- public bool HasCatch
- {
- get {
- return General != null || Specific.Count > 0;
+ if (General != null)
+ General.MutateHoistedGenericType (storey);
+ if (Specific != null) {
+ foreach (Catch c in Specific)
+ c.MutateHoistedGenericType (storey);
}
}
protected override void CloneTo (CloneContext clonectx, Statement t)
{
- Try target = (Try) t;
+ TryCatch target = (TryCatch) t;
target.Block = clonectx.LookupBlock (Block);
- if (Fini != null)
- target.Fini = clonectx.LookupBlock (Fini);
if (General != null)
target.General = (Catch) General.Clone (clonectx);
if (Specific != null){
}
}
- public class Using : ExceptionStatement {
- object expression_or_block;
+ public class UsingTemporary : ExceptionStatement {
+ TemporaryVariable local_copy;
public Statement Statement;
- ArrayList var_list;
Expression expr;
- Type expr_type;
- Expression [] resolved_vars;
- Expression [] converted_vars;
- Expression [] assign;
- TemporaryVariable local_copy;
-
- public Using (object expression_or_block, Statement stmt, Location l)
+ Type expr_type;
+
+ public UsingTemporary (Expression expr, Statement stmt, Location l)
{
- this.expression_or_block = expression_or_block;
+ this.expr = expr;
Statement = stmt;
loc = l;
}
- //
- // Resolves for the case of using using a local variable declaration.
- //
- bool ResolveLocalVariableDecls (EmitContext ec)
+ public override bool Resolve (EmitContext ec)
{
- resolved_vars = new Expression[var_list.Count];
- assign = new Expression [var_list.Count];
- converted_vars = new Expression[var_list.Count];
+ expr = expr.Resolve (ec);
+ if (expr == null)
+ return false;
- for (int i = 0; i < assign.Length; ++i) {
- DictionaryEntry e = (DictionaryEntry) var_list [i];
- Expression var = (Expression) e.Key;
- Expression new_expr = (Expression) e.Value;
+ expr_type = expr.Type;
- Expression a = new Assign (var, new_expr, loc);
- a = a.Resolve (ec);
- if (a == null)
+ if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
+ if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
+ Using.Error_IsNotConvertibleToIDisposable (expr);
return false;
+ }
+ }
- resolved_vars [i] = var;
- assign [i] = a;
+ local_copy = new TemporaryVariable (expr_type, loc);
+ local_copy.Resolve (ec);
- if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
- converted_vars [i] = var;
- continue;
- }
+ ec.StartFlowBranching (this);
- a = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
- if (a == null) {
- Error_IsNotConvertibleToIDisposable (var);
- return false;
- }
+ bool ok = Statement.Resolve (ec);
- converted_vars [i] = a;
+ ec.EndFlowBranching ();
+
+ ResolveReachability (ec);
+
+ if (TypeManager.void_dispose_void == null) {
+ TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+ TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
}
- return true;
+ return ok;
}
- static void Error_IsNotConvertibleToIDisposable (Expression expr)
+ protected override void EmitPreTryBody (EmitContext ec)
{
- Report.SymbolRelatedToPreviousError (expr.Type);
- Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- expr.GetSignatureForError ());
+ local_copy.EmitAssign (ec, expr);
}
- bool ResolveExpression (EmitContext ec)
+ protected override void EmitTryBody (EmitContext ec)
{
- if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
- if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
- Error_IsNotConvertibleToIDisposable (expr);
- return false;
- }
- }
-
- local_copy = new TemporaryVariable (expr_type, loc);
- local_copy.Resolve (ec);
-
- return true;
+ Statement.Emit (ec);
}
-
- //
- // Emits the code for the case of using using a local variable declaration.
- //
- void EmitLocalVariableDecls (EmitContext ec)
+
+ protected override void EmitFinallyBody (EmitContext ec)
{
ILGenerator ig = ec.ig;
- int i = 0;
+ if (!expr_type.IsValueType) {
+ Label skip = ig.DefineLabel ();
+ local_copy.Emit (ec);
+ ig.Emit (OpCodes.Brfalse, skip);
+ local_copy.Emit (ec);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ ig.MarkLabel (skip);
+ return;
+ }
+
+ Expression ml = Expression.MemberLookup (
+ ec.ContainerType, TypeManager.idisposable_type, expr_type,
+ "Dispose", Location.Null);
+
+ if (!(ml is MethodGroupExpr)) {
+ local_copy.Emit (ec);
+ ig.Emit (OpCodes.Box, expr_type);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ return;
+ }
- for (i = 0; i < assign.Length; i++) {
- ExpressionStatement es = assign [i] as ExpressionStatement;
+ MethodInfo mi = null;
- if (es != null)
- es.EmitStatement (ec);
- else {
- assign [i].Emit (ec);
- ig.Emit (OpCodes.Pop);
+ foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
+ if (TypeManager.GetParameterData (mk).Count == 0) {
+ mi = mk;
+ break;
}
+ }
- if (emit_finally)
- ig.BeginExceptionBlock ();
+ if (mi == null) {
+ Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
+ return;
}
- Statement.Emit (ec);
- var_list.Reverse ();
+ local_copy.AddressOf (ec, AddressOp.Load);
+ ig.Emit (OpCodes.Call, mi);
+ }
- DoEmitFinally (ec);
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ expr_type = storey.MutateType (expr_type);
+ local_copy.MutateHoistedGenericType (storey);
+ Statement.MutateHoistedGenericType (storey);
}
- void EmitLocalVariableDeclFinally (EmitContext ec)
+ protected override void CloneTo (CloneContext clonectx, Statement t)
{
- ILGenerator ig = ec.ig;
+ UsingTemporary target = (UsingTemporary) t;
- int i = assign.Length;
- for (int ii = 0; ii < var_list.Count; ++ii){
- Expression var = resolved_vars [--i];
- Label skip = ig.DefineLabel ();
+ target.expr = expr.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
+ }
+ }
- if (emit_finally)
- ig.BeginFinallyBlock ();
-
- 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.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
+ public class Using : ExceptionStatement {
+ Statement stmt;
+ public Statement EmbeddedStatement {
+ get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
+ }
- 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;
+ Expression var;
+ Expression init;
- foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
- if (TypeManager.GetParameterData (mk).Count == 0) {
- mi = mk;
- break;
- }
- }
+ Expression converted_var;
+ ExpressionStatement assign;
- if (mi == null) {
- Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
- return;
- }
+ public Using (Expression var, Expression init, Statement stmt, Location l)
+ {
+ this.var = var;
+ this.init = init;
+ this.stmt = stmt;
+ loc = l;
+ }
- IMemoryLocation mloc = (IMemoryLocation) var;
+ bool ResolveVariable (EmitContext ec)
+ {
+ ExpressionStatement a = new SimpleAssign (var, init, loc);
+ a = a.ResolveStatement (ec);
+ if (a == null)
+ return false;
- mloc.AddressOf (ec, AddressOp.Load);
- ig.Emit (OpCodes.Call, mi);
- }
- }
+ assign = a;
- ig.MarkLabel (skip);
+ if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
+ converted_var = var;
+ return true;
+ }
- if (emit_finally) {
- ig.EndExceptionBlock ();
- if (i > 0)
- ig.BeginFinallyBlock ();
- }
+ Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
+ if (e == null) {
+ Error_IsNotConvertibleToIDisposable (var);
+ return false;
}
+
+ converted_var = e;
+
+ return true;
}
- void EmitExpression (EmitContext ec)
+ static public void Error_IsNotConvertibleToIDisposable (Expression expr)
{
- //
- // Make a copy of the expression and operate on that.
- //
- ILGenerator ig = ec.ig;
-
- local_copy.Store (ec, expr);
+ Report.SymbolRelatedToPreviousError (expr.Type);
+ Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+ expr.GetSignatureForError ());
+ }
- if (emit_finally)
- ig.BeginExceptionBlock ();
+ protected override void EmitPreTryBody (EmitContext ec)
+ {
+ assign.EmitStatement (ec);
+ }
- Statement.Emit (ec);
-
- DoEmitFinally (ec);
- if (emit_finally)
- ig.EndExceptionBlock ();
+ protected override void EmitTryBody (EmitContext ec)
+ {
+ stmt.Emit (ec);
}
- void EmitExpressionFinally (EmitContext ec)
+ protected override void EmitFinallyBody (EmitContext ec)
{
ILGenerator ig = ec.ig;
- if (!expr_type.IsValueType) {
+
+ if (!var.Type.IsValueType) {
Label skip = ig.DefineLabel ();
- local_copy.Emit (ec);
+ var.Emit (ec);
ig.Emit (OpCodes.Brfalse, skip);
- local_copy.Emit (ec);
+ converted_var.Emit (ec);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
ig.MarkLabel (skip);
} else {
- Expression ml = Expression.MemberLookup (
- ec.ContainerType, TypeManager.idisposable_type, expr_type,
- "Dispose", Location.Null);
+ Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
if (!(ml is MethodGroupExpr)) {
- local_copy.Emit (ec);
- ig.Emit (OpCodes.Box, expr_type);
+ var.Emit (ec);
+ ig.Emit (OpCodes.Box, var.Type);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
} else {
MethodInfo mi = null;
return;
}
- local_copy.AddressOf (ec, AddressOp.Load);
+ IMemoryLocation mloc = (IMemoryLocation) var;
+
+ mloc.AddressOf (ec, AddressOp.Load);
ig.Emit (OpCodes.Call, mi);
}
}
}
-
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ assign.MutateHoistedGenericType (storey);
+ var.MutateHoistedGenericType (storey);
+ stmt.MutateHoistedGenericType (storey);
+ }
+
public override bool Resolve (EmitContext ec)
{
- if (expression_or_block is DictionaryEntry){
- expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
- var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
+ if (!ResolveVariable (ec))
+ return false;
- if (!ResolveLocalVariableDecls (ec))
- return false;
+ ec.StartFlowBranching (this);
- } else if (expression_or_block is Expression){
- expr = (Expression) expression_or_block;
+ bool ok = stmt.Resolve (ec);
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
+ ec.EndFlowBranching ();
- expr_type = expr.Type;
+ ResolveReachability (ec);
- if (!ResolveExpression (ec))
- return false;
+ if (TypeManager.void_dispose_void == null) {
+ TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+ TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
}
- FlowBranchingException branching = ec.StartFlowBranching (this);
-
- bool ok = Statement.Resolve (ec);
-
- ResolveFinally (branching);
-
- ec.EndFlowBranching ();
-
- // System.Reflection.Emit automatically emits a 'leave' to the end of the finally block.
- // So, ensure there's some IL code after the finally block.
- ec.NeedReturnLabel ();
-
return ok;
}
-
- protected override void DoEmit (EmitContext ec)
- {
- if (expression_or_block is DictionaryEntry)
- EmitLocalVariableDecls (ec);
- else if (expression_or_block is Expression)
- EmitExpression (ec);
- }
-
- public override void EmitFinally (EmitContext ec)
- {
- if (expression_or_block is DictionaryEntry)
- EmitLocalVariableDeclFinally (ec);
- else if (expression_or_block is Expression)
- EmitExpressionFinally (ec);
- }
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Using target = (Using) t;
- if (expression_or_block is Expression)
- target.expression_or_block = ((Expression) expression_or_block).Clone (clonectx);
- else
- target.expression_or_block = ((Statement) expression_or_block).Clone (clonectx);
-
- target.Statement = Statement.Clone (clonectx);
+ target.var = var.Clone (clonectx);
+ target.init = init.Clone (clonectx);
+ target.stmt = stmt.Clone (clonectx);
}
}
Expression variable;
Expression expr;
Statement statement;
- ArrayForeach array;
- CollectionForeach collection;
public Foreach (Expression type, LocalVariableReference var, Expression expr,
Statement stmt, Location l)
if (expr == null)
return false;
- if (expr.Type == TypeManager.null_type) {
+ if (expr.IsNull) {
Report.Error (186, loc, "Use of null is not valid in this context");
return false;
}
return false;
}
- //
- // We need an instance variable. Not sure this is the best
- // way of doing this.
- //
- // FIXME: When we implement propertyaccess, will those turn
- // out to return values in ExprClass? I think they should.
- //
- if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
- expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
- collection.Error_Enumerator ();
- return false;
- }
-
if (expr.Type.IsArray) {
- array = new ArrayForeach (type, variable, expr, statement, loc);
- return array.Resolve (ec);
+ statement = new ArrayForeach (type, variable, expr, statement, loc);
+ } else {
+ statement = new CollectionForeach (type, variable, expr, statement, loc);
}
- collection = new CollectionForeach (type, variable, expr, statement, loc);
- return collection.Resolve (ec);
+ return statement.Resolve (ec);
}
protected override void DoEmit (EmitContext ec)
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();
- if (collection != null)
- collection.Emit (ec);
- else
- array.Emit (ec);
+ statement.Emit (ec);
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
protected class ArrayCounter : TemporaryVariable
{
+ StatementExpression increment;
+
public ArrayCounter (Location loc)
: base (TypeManager.int32_type, loc)
- { }
+ {
+ }
- public void Initialize (EmitContext ec)
+ public void ResolveIncrement (EmitContext ec)
{
- EmitThis (ec);
- ec.ig.Emit (OpCodes.Ldc_I4_0);
- EmitStore (ec);
+ increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
+ increment.Resolve (ec);
}
- public void Increment (EmitContext ec)
+ public void EmitIncrement (EmitContext ec)
{
- EmitThis (ec);
- Emit (ec);
- ec.ig.Emit (OpCodes.Ldc_I4_1);
- ec.ig.Emit (OpCodes.Add);
- EmitStore (ec);
+ increment.Emit (ec);
}
}
TemporaryVariable copy;
Expression access;
+ Expression[] length_exprs;
public ArrayForeach (Expression var_type, Expression var,
Expression expr, Statement stmt, Location l)
loc = l;
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotImplementedException ();
+ }
+
public override bool Resolve (EmitContext ec)
{
array_type = expr.Type;
counter = new ArrayCounter [rank];
lengths = new TemporaryVariable [rank];
+ length_exprs = new Expression [rank];
- ArrayList list = new ArrayList ();
+ ArrayList list = new ArrayList (rank);
for (int i = 0; i < rank; i++) {
counter [i] = new ArrayCounter (loc);
- counter [i].Resolve (ec);
+ counter [i].ResolveIncrement (ec);
lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
lengths [i].Resolve (ec);
+ if (rank == 1) {
+ length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
+ } else {
+ ArrayList args = new ArrayList (1);
+ args.Add (new Argument (new IntConstant (i, loc)));
+ length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
+ }
+
list.Add (counter [i]);
}
{
ILGenerator ig = ec.ig;
- copy.Store (ec, expr);
+ copy.EmitAssign (ec, expr);
Label[] test = new Label [rank];
Label[] loop = new Label [rank];
test [i] = ig.DefineLabel ();
loop [i] = ig.DefineLabel ();
- lengths [i].EmitThis (ec);
- ((ArrayAccess) access).EmitGetLength (ec, i);
- lengths [i].EmitStore (ec);
+ lengths [i].EmitAssign (ec, length_exprs [i]);
}
+ IntConstant zero = new IntConstant (0, loc);
for (int i = 0; i < rank; i++) {
- counter [i].Initialize (ec);
+ counter [i].EmitAssign (ec, zero);
ig.Emit (OpCodes.Br, test [i]);
ig.MarkLabel (loop [i]);
ig.MarkLabel (ec.LoopBegin);
for (int i = rank - 1; i >= 0; i--){
- counter [i].Increment (ec);
+ counter [i].EmitIncrement (ec);
ig.MarkLabel (test [i]);
counter [i].Emit (ec);
ig.MarkLabel (ec.LoopEnd);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ copy.MutateHoistedGenericType (storey);
+ conv.MutateHoistedGenericType (storey);
+ variable.MutateHoistedGenericType (storey);
+ statement.MutateHoistedGenericType (storey);
+
+ for (int i = 0; i < rank; i++) {
+ counter [i].MutateHoistedGenericType (storey);
+ lengths [i].MutateHoistedGenericType (storey);
+ }
+ }
}
- protected class CollectionForeach : ExceptionStatement
+ protected class CollectionForeach : Statement
{
Expression variable, expr;
Statement statement;
TemporaryVariable enumerator;
Expression init;
Statement loop;
+ Statement wrapper;
MethodGroupExpr get_enumerator;
PropertyExpr get_current;
MethodInfo move_next;
Expression var_type;
Type enumerator_type;
- bool is_disposable;
bool enumerator_found;
public CollectionForeach (Expression var_type, Expression var,
loc = l;
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotImplementedException ();
+ }
+
bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
{
Type return_type = mi.ReturnType;
// way I could do this without a goto
//
+ if (TypeManager.bool_movenext_void == null) {
+ TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
+ TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
+ }
+
+ if (TypeManager.ienumerator_getcurrent == null) {
+ TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
+ TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
+ }
+
#if GMCS_SOURCE
//
// Prefer a generic enumerator over a non-generic one.
}
enumerator_type = return_type;
- is_disposable = !enumerator_type.IsSealed ||
- TypeManager.ImplementsInterface (
- enumerator_type, TypeManager.idisposable_type);
return true;
}
return null;
}
- public void Error_Enumerator ()
+ void Error_Enumerator ()
{
if (enumerator_found) {
return;
public override bool Resolve (EmitContext ec)
{
enumerator_type = TypeManager.ienumerator_type;
- is_disposable = true;
if (!ProbeCollectionType (ec, expr.Type)) {
Error_Enumerator ();
return false;
}
+ bool is_disposable = !enumerator_type.IsSealed ||
+ TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
+
VarExpr ve = var_type as VarExpr;
if (ve != null) {
// Infer implicitly typed local variable from foreach enumerable type
loop = new While (move_next_expr, block, loc);
- bool ok = true;
+ wrapper = is_disposable ?
+ (Statement) new DisposableWrapper (this) :
+ (Statement) new NonDisposableWrapper (this);
+ return wrapper.Resolve (ec);
+ }
- FlowBranchingException branching = null;
- if (is_disposable)
- branching = ec.StartFlowBranching (this);
+ protected override void DoEmit (EmitContext ec)
+ {
+ wrapper.Emit (ec);
+ }
- if (!loop.Resolve (ec))
- ok = false;
+ class NonDisposableWrapper : Statement {
+ CollectionForeach parent;
- if (is_disposable) {
- ResolveFinally (branching);
- ec.EndFlowBranching ();
- } else
- emit_finally = true;
+ internal NonDisposableWrapper (CollectionForeach parent)
+ {
+ this.parent = parent;
+ }
- return ok;
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override bool Resolve (EmitContext ec)
+ {
+ return parent.ResolveLoop (ec);
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ parent.EmitLoopInit (ec);
+ parent.EmitLoopBody (ec);
+ }
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ throw new NotSupportedException ();
+ }
}
- protected override void DoEmit (EmitContext ec)
- {
- ILGenerator ig = ec.ig;
+ class DisposableWrapper : ExceptionStatement {
+ CollectionForeach parent;
- enumerator.Store (ec, init);
+ internal DisposableWrapper (CollectionForeach parent)
+ {
+ this.parent = parent;
+ }
- //
- // Protect the code in a try/finalize block, so that
- // if the beast implement IDisposable, we get rid of it
- //
- if (is_disposable && emit_finally)
- ig.BeginExceptionBlock ();
-
- loop.Emit (ec);
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotSupportedException ();
+ }
- //
- // Now the finally block
- //
- if (is_disposable) {
- DoEmitFinally (ec);
- if (emit_finally)
- ig.EndExceptionBlock ();
+ public override bool Resolve (EmitContext ec)
+ {
+ bool ok = true;
+
+ ec.StartFlowBranching (this);
+
+ if (!parent.ResolveLoop (ec))
+ ok = false;
+
+ ec.EndFlowBranching ();
+
+ ResolveReachability (ec);
+
+ if (TypeManager.void_dispose_void == null) {
+ TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
+ TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
+ }
+ return ok;
+ }
+
+ protected override void EmitPreTryBody (EmitContext ec)
+ {
+ parent.EmitLoopInit (ec);
+ }
+
+ protected override void EmitTryBody (EmitContext ec)
+ {
+ parent.EmitLoopBody (ec);
+ }
+
+ protected override void EmitFinallyBody (EmitContext ec)
+ {
+ parent.EmitFinallyBody (ec);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ throw new NotSupportedException ();
+ }
+ }
+
+ bool ResolveLoop (EmitContext ec)
+ {
+ return loop.Resolve (ec);
+ }
+
+ void EmitLoopInit (EmitContext ec)
+ {
+ enumerator.EmitAssign (ec, init);
}
+ void EmitLoopBody (EmitContext ec)
+ {
+ loop.Emit (ec);
+ }
- public override void EmitFinally (EmitContext ec)
+ void EmitFinallyBody (EmitContext ec)
{
ILGenerator ig = ec.ig;
if (enumerator_type.IsValueType) {
MethodInfo mi = FetchMethodDispose (enumerator_type);
if (mi != null) {
- enumerator.EmitLoadAddress (ec);
+ enumerator.AddressOf (ec, AddressOp.Load);
ig.Emit (OpCodes.Call, mi);
} else {
enumerator.Emit (ec);
ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
ig.Emit (OpCodes.Dup);
ig.Emit (OpCodes.Brtrue_S, call_dispose);
- ig.Emit (OpCodes.Pop);
- Label end_finally = ig.DefineLabel ();
- ig.Emit (OpCodes.Br, end_finally);
+ // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
+ // (Partition III, Section 3.35)
+ ig.Emit (OpCodes.Endfinally);
ig.MarkLabel (call_dispose);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
- ig.MarkLabel (end_finally);
}
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ enumerator_type = storey.MutateType (enumerator_type);
+ init.MutateHoistedGenericType (storey);
+ loop.MutateHoistedGenericType (storey);
+ }
}
protected class CollectionForeachStatement : Statement
this.loc = loc;
}
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ throw new NotImplementedException ();
+ }
+
public override bool Resolve (EmitContext ec)
{
current = current.Resolve (ec);
if (conv == null)
return false;
- assign = new Assign (variable, conv, loc);
+ assign = new SimpleAssign (variable, conv, loc);
if (assign.Resolve (ec) == null)
return false;
assign.EmitStatement (ec);
statement.Emit (ec);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ assign.MutateHoistedGenericType (storey);
+ statement.MutateHoistedGenericType (storey);
+ }
}
protected override void CloneTo (CloneContext clonectx, Statement t)
target.expr = expr.Clone (clonectx);
target.statement = statement.Clone (clonectx);
}
+
+ public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+ {
+ statement.MutateHoistedGenericType (storey);
+ }
}
}