//
// Author:
// Miguel de Icaza (miguel@ximian.com)
-// Martin Baulig (martin@gnome.org)
+// Martin Baulig (martin@ximian.com)
//
// (C) 2001, 2002, 2003 Ximian, Inc.
+// (C) 2003, 2004 Novell, Inc.
//
using System;
// in unreachable code, for instance.
//
+ if (warn && (RootContext.WarningLevel >= 2))
+ Report.Warning (162, loc, "Unreachable code detected");
+
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
bool ok = Resolve (ec);
ec.KillFlowBranching ();
- if (!ok)
- return false;
-
- if (warn)
- Report.Warning (162, loc, "Unreachable code detected");
- return true;
+ return ok;
}
protected void CheckObsolete (Type type)
public override bool Resolve (EmitContext ec)
{
+ bool ok = true;
+
Report.Debug (1, "START IF BLOCK", loc);
expr = Expression.ResolveBoolean (ec, expr, loc);
if (expr == null){
- return false;
+ ok = false;
+ goto skip;
+ }
+
+ Assign ass = expr as Assign;
+ if (ass != null && ass.Source is Constant) {
+ Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
}
//
return true;
}
-
+ skip:
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
- bool ok = TrueStatement.Resolve (ec);
+ ok &= TrueStatement.Resolve (ec);
is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
ec.CurrentBranching.CreateSibling ();
- if ((FalseStatement != null) && !FalseStatement.Resolve (ec))
- ok = false;
+ if (FalseStatement != null)
+ ok &= FalseStatement.Resolve (ec);
ec.EndFlowBranching ();
public override bool Resolve (EmitContext ec)
{
- expr = expr.ResolveStatement (ec);
+ if (expr != null)
+ expr = expr.ResolveStatement (ec);
return expr != null;
}
{
if (ec.ReturnType == null){
if (Expr != null){
+ if (ec.CurrentAnonymousMethod != null){
+ Report.Error (1662, loc, String.Format (
+ "Anonymous method could not be converted to delegate " +
+ "since the return value does not match the delegate value"));
+ }
Error (127, "Return with a value not allowed here");
return false;
}
return false;
}
+ if (ec.InIterator) {
+ 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");
+ return false;
+ }
+
Expr = Expr.Resolve (ec);
if (Expr == null)
return false;
if (ec.CurrentBranching.InTryOrCatch (true)) {
ec.CurrentBranching.AddFinallyVector (vector);
in_exc = true;
- } else if (ec.CurrentBranching.InFinally (true)) {
+ } else if (ec.InFinally) {
Error (157, "Control can not leave the body of the finally block");
return false;
} else
vector.CheckOutParameters (ec.CurrentBranching);
+ if (in_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Return ();
return true;
}
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (in_exc) {
- ec.NeedReturnLabel ();
+ if (in_exc)
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
- } else {
+ else
ec.ig.Emit (OpCodes.Ret);
- }
}
}
public class Goto : Statement {
string target;
- Block block;
LabeledStatement label;
public override bool Resolve (EmitContext ec)
label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
ec.CurrentBranching.CurrentUsageVector.Goto ();
+ label.AddReference ();
return true;
}
- public Goto (Block parent_block, string label, Location l)
+ public Goto (string label, Location l)
{
- block = parent_block;
loc = l;
target = label;
}
bool defined;
bool referenced;
Label label;
+ ILGenerator ig;
FlowBranching.UsageVector vectors;
{
if (defined)
return label;
+ ig = ec.ig;
label = ec.ig.DefineLabel ();
defined = true;
{
ec.CurrentBranching.Label (vectors);
- referenced = true;
-
return true;
}
protected override void DoEmit (EmitContext ec)
{
+ if (ig != null && ig != ec.ig) {
+ Report.Error (1632, "Control cannot leave body of anonymous method");
+ return;
+ }
LabelTarget (ec);
ec.ig.MarkLabel (label);
}
+
+ public void AddReference ()
+ {
+ referenced = true;
+ }
}
/// </summary>
public class GotoCase : Statement {
Expression expr;
- Label label;
+ SwitchLabel sl;
public GotoCase (Expression e, Location l)
{
if (val == null)
return false;
- SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
+ sl = (SwitchLabel) ec.Switch.Elements [val];
if (sl == null){
Report.Error (
return false;
}
- label = sl.ILLabelCode;
-
ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (OpCodes.Br, label);
+ ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
}
}
public override bool Resolve (EmitContext ec)
{
- bool in_catch = ec.CurrentBranching.InCatch ();
ec.CurrentBranching.CurrentUsageVector.Throw ();
if (expr != null){
ExprClass eclass = expr.eclass;
if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
- eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
- expr.Error_UnexpectedKind ("value, variable, property or indexer access ");
+ eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
+ expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
return false;
}
Type t = expr.Type;
if ((t != TypeManager.exception_type) &&
- !t.IsSubclassOf (TypeManager.exception_type) &&
- !(expr is NullLiteral)) {
+ !t.IsSubclassOf (TypeManager.exception_type) &&
+ !(expr is NullLiteral)) {
Error (155,
- "The type caught or thrown must be derived " +
- "from System.Exception");
+ "The type caught or thrown must be derived " +
+ "from System.Exception");
return false;
}
- } else if (!in_catch) {
- Error (156,
- "A throw statement with no argument is only " +
- "allowed in a catch clause");
+ return true;
+ }
+
+ if (!ec.InCatch) {
+ Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
return false;
}
+ if (ec.InFinally) {
+ Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
+ return false;
+ }
return true;
}
if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
Error (139, "No enclosing loop or switch to continue to");
return false;
- } else if (ec.CurrentBranching.InFinally (false)) {
+ } else if (ec.InFinally) {
Error (157, "Control can not leave the body of the finally block");
return false;
} else if (ec.CurrentBranching.InTryOrCatch (false))
crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
+ if (!crossing_exc)
+ ec.NeedReturnLabel ();
+
ec.CurrentBranching.CurrentUsageVector.Break ();
return true;
}
if (crossing_exc)
ig.Emit (OpCodes.Leave, ec.LoopEnd);
else {
- ec.NeedReturnLabel ();
ig.Emit (OpCodes.Br, ec.LoopEnd);
}
}
if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
Error (139, "No enclosing loop to continue to");
return false;
- } else if (ec.CurrentBranching.InFinally (false)) {
+ } else if (ec.InFinally) {
Error (157, "Control can not leave the body of the finally block");
return false;
} else if (ec.CurrentBranching.InTryOrCatch (false))
}
}
+ //
+ // The information about a user-perceived local variable
+ //
public class LocalInfo {
public Expression Type;
//
// Most of the time a variable will be stored in a LocalBuilder
//
- // But sometimes, it will be stored in a field. The context of the field will
+ // But sometimes, it will be stored in a field (variables that have been
+ // hoisted by iterators or by anonymous methods). The context of the field will
// be stored in the EmitContext
//
//
enum Flags : byte {
Used = 1,
ReadOnly = 2,
- Pinned = 4
+ Pinned = 4,
+ IsThis = 8,
+ Captured = 16,
+ AddressTaken = 32
}
Flags flags;
public bool Resolve (EmitContext ec)
{
- if (VariableType == null)
- VariableType = ec.DeclSpace.ResolveType (Type, false, Location);
+ if (VariableType == null) {
+ TypeExpr texpr = Type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
+ return false;
+
+ VariableType = texpr.ResolveType (ec);
+ }
if (VariableType == TypeManager.void_type) {
Report.Error (1547, Location,
return false;
}
- if (VariableType == null)
+ if (VariableType.IsAbstract && VariableType.IsSealed) {
+ Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
return false;
-
+ }
// TODO: breaks the build
// if (VariableType.IsPointer && !ec.InUnsafe)
// Expression.UnsafeError (Location);
}
}
+ public bool IsCaptured {
+ get {
+ return (flags & Flags.Captured) != 0;
+ }
+
+ set {
+ flags |= Flags.Captured;
+ }
+ }
+
+ public bool AddressTaken {
+ get {
+ return (flags & Flags.AddressTaken) != 0;
+ }
+
+ set {
+ flags |= Flags.AddressTaken;
+ }
+ }
+
public override string ToString ()
{
return String.Format ("LocalInfo ({0},{1},{2},{3})",
return (flags & Flags.Used) != 0;
}
set {
- flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
+ flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
}
}
return (flags & Flags.ReadOnly) != 0;
}
set {
- flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
+ flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
}
}
flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
}
}
+
+ public bool IsThis {
+ get {
+ return (flags & Flags.IsThis) != 0;
+ }
+ set {
+ flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
+ }
+ }
}
/// <summary>
/// they contain extra information that is not necessary on normal blocks.
/// </remarks>
public class Block : Statement {
- public readonly Block Parent;
+ public Block Parent;
public readonly Location StartLocation;
- public Location EndLocation = Location.Null;
+ public Location EndLocation = Location.Null;
+
+ public readonly ToplevelBlock Toplevel;
[Flags]
- public enum Flags : byte {
+ public enum Flags {
Implicit = 1,
Unchecked = 2,
BlockUsed = 4,
VariablesInitialized = 8,
HasRet = 16,
IsDestructor = 32,
- HasVarargs = 64
+ HasVarargs = 64,
+ IsToplevel = 128,
+ Unsafe = 256
}
Flags flags;
}
}
+ public bool Unsafe {
+ get {
+ return (flags & Flags.Unsafe) != 0;
+ }
+ set {
+ flags |= Flags.Unsafe;
+ }
+ }
+
public bool HasVarargs {
get {
if (Parent != null)
//
// Keeps track of constants
Hashtable constants;
-
+
//
// If this is a switch section, the enclosing switch block.
//
Block switch_block;
- static int id;
+ protected static int id;
int this_id;
: this (parent, flags, Location.Null, Location.Null)
{ }
- public Block (Block parent, Flags flags, Parameters parameters)
- : this (parent, flags, parameters, Location.Null, Location.Null)
- { }
-
public Block (Block parent, Location start, Location end)
: this (parent, (Flags) 0, start, end)
{ }
- public Block (Block parent, Parameters parameters, Location start, Location end)
- : this (parent, (Flags) 0, parameters, start, end)
- { }
-
public Block (Block parent, Flags flags, Location start, Location end)
- : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
- { }
-
- public Block (Block parent, Flags flags, Parameters parameters,
- Location start, Location end)
{
if (parent != null)
parent.AddChild (this);
this.Parent = parent;
this.flags = flags;
- this.parameters = parameters;
this.StartLocation = start;
this.EndLocation = end;
this.loc = start;
this_id = id++;
statements = new ArrayList ();
+ if ((flags & Flags.IsToplevel) != 0)
+ Toplevel = (ToplevelBlock) this;
+ else
+ Toplevel = parent.Toplevel;
+
if (parent != null && Implicit) {
- if (parent.child_variable_names == null)
- parent.child_variable_names = new Hashtable();
+ if (parent.known_variables == null)
+ parent.known_variables = new Hashtable ();
// share with parent
- child_variable_names = parent.child_variable_names;
+ known_variables = parent.known_variables;
}
}
// </summary>
public LocalInfo ThisVariable {
get {
- if (this_variable != null)
- return this_variable;
- else if (Parent != null)
- return Parent.ThisVariable;
- else
- return null;
+ for (Block b = this; b != null; b = b.Parent) {
+ if (b.this_variable != null)
+ return b.this_variable;
+ }
+
+ return null;
}
}
- Hashtable child_variable_names;
+ Hashtable known_variables;
// <summary>
- // Marks a variable with name @name as being used in a child block.
+ // 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
// declare a variable with the same name in the current block.
// </summary>
- public void AddChildVariableName (string name)
+ void AddKnownVariable (string name, LocalInfo info)
{
- if (child_variable_names == null)
- child_variable_names = new Hashtable ();
+ if (known_variables == null)
+ known_variables = new Hashtable ();
- if (!child_variable_names.Contains (name))
- child_variable_names.Add (name, true);
+ known_variables [name] = info;
}
- // <summary>
- // Checks whether a variable name has already been used in a child block.
- // </summary>
- public bool IsVariableNameUsedInChildBlock (string name)
+ LocalInfo GetKnownVariableInfo (string name)
+ {
+ if (known_variables == null)
+ return null;
+ return (LocalInfo) known_variables [name];
+ }
+
+ public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
{
- if (child_variable_names == null)
+ LocalInfo kvi = GetKnownVariableInfo (name);
+ if (kvi == null || kvi.Block == this)
+ return true;
+
+ if (known_variables != kvi.Block.known_variables) {
+ Report.SymbolRelatedToPreviousError (kvi.Location, name);
+ Report.Error (135, loc, "'{0}' has a different meaning in a child block", name);
return false;
+ }
+
+ //
+ // this block and kvi.Block are the same textual block.
+ // However, different variables are extant.
+ //
+ // Check if the variable is in scope in both blocks. We use
+ // an indirect check that depends on AddVariable doing its
+ // part in maintaining the invariant-meaning-in-block property.
+ //
+ if (e is LocalVariableReference || (e is Constant && GetLocalInfo (name) != null))
+ return true;
- return child_variable_names.Contains (name);
+ Report.SymbolRelatedToPreviousError (kvi.Location, name);
+ Report.Error (136, loc, "'{0}' has a different meaning later in the block", name);
+ return false;
}
// <summary>
this_variable = new LocalInfo (tc, this, l);
this_variable.Used = true;
+ this_variable.IsThis = true;
variables.Add ("this", this_variable);
return this_variable;
}
- public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
+ public LocalInfo AddVariable (Expression type, string name, Location l)
{
if (variables == null)
variables = new Hashtable ();
LocalInfo vi = GetLocalInfo (name);
if (vi != null) {
- if (vi.Block != this)
- Report.Error (136, l, "A local variable named `" + name + "' " +
- "cannot be declared in this scope since it would " +
- "give a different meaning to `" + name + "', which " +
- "is already used in a `parent or current' scope to " +
- "denote something else");
+ Report.SymbolRelatedToPreviousError (vi.Location, name);
+ if (known_variables == vi.Block.known_variables)
+ Report.Error (128, l,
+ "A local variable '{0}' is already declared in this scope", name);
else
- Report.Error (128, l, "A local variable `" + name + "' is already " +
- "defined in this scope");
+ Report.Error (136, l,
+ "'{0}' hides the declaration of local variable '{0}' in a parent scope", name);
return null;
}
- if (IsVariableNameUsedInChildBlock (name)) {
- Report.Error (136, l, "A local variable named `" + name + "' " +
- "cannot be declared in this scope since it would " +
- "give a different meaning to `" + name + "', which " +
- "is already used in a `child' scope to denote something " +
- "else");
+ vi = GetKnownVariableInfo (name);
+ if (vi != null) {
+ Report.SymbolRelatedToPreviousError (vi.Location, name);
+ Report.Error (136, l,
+ "A child block already has a declaration of local variable '{0}':" +
+ " allowing this declaration would violate 'invariant meaning in a block'",
+ name);
return null;
}
- if (pars != null) {
- int idx;
- Parameter p = pars.GetParameterByName (name, out idx);
- if (p != null) {
- Report.Error (136, l, "A local variable named `" + name + "' " +
- "cannot be declared in this scope since it would " +
- "give a different meaning to `" + name + "', which " +
- "is already used in a `parent or current' scope to " +
- "denote something else");
- return null;
- }
+ int idx;
+ Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx);
+ if (p != null) {
+ Report.SymbolRelatedToPreviousError (Toplevel.Parameters.Location, name);
+ Report.Error (136, l, "'{0}' hides a method parameter", name);
+ return null;
}
vi = new LocalInfo (type, name, this, l);
variables.Add (name, vi);
- // Mark 'name' as "used by a child block" in every surrounding block
- Block cur = this;
- while (cur != null && cur.Implicit)
- cur = cur.Parent;
- if (cur != null)
- for (Block par = cur.Parent; par != null; par = par.Parent)
- par.AddChildVariableName (name);
+ for (Block b = this; b != null; b = b.Parent)
+ b.AddKnownVariable (name, vi);
if ((flags & Flags.VariablesInitialized) != 0)
throw new Exception ();
return vi;
}
- public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
+ public bool AddConstant (Expression type, string name, Expression value, Location l)
{
- if (AddVariable (type, name, pars, l) == null)
+ if (AddVariable (type, name, l) == null)
return false;
if (constants == null)
return e != null;
}
-
- Parameters parameters = null;
- public Parameters Parameters {
- get {
- Block b = this;
- while (b.Parent != null)
- b = b.Parent;
- return b.parameters;
+
+ //
+ // Returns a `ParameterReference' for the given name, or null if there
+ // is no such parameter
+ //
+ public ParameterReference GetParameterReference (string name, Location loc)
+ {
+ Parameter par;
+ int idx;
+
+ for (Block b = this; b != null; b = b.Toplevel.Parent) {
+ Parameters pars = b.Toplevel.Parameters;
+ par = pars.GetParameterByName (name, out idx);
+ if (par != null)
+ return new ParameterReference (pars, this, idx, name, loc);
}
+ return null;
+ }
+
+ //
+ // Whether the parameter named `name' is local to this block,
+ // or false, if the parameter belongs to an encompassing block.
+ //
+ public bool IsLocalParameter (string name)
+ {
+ return Toplevel.Parameters.GetParameterByName (name) != null;
}
+
+ //
+ // Whether the `name' is a parameter reference
+ //
+ public bool IsParameterReference (string name)
+ {
+ Parameter par;
+ int idx;
+ for (Block b = this; b != null; b = b.Toplevel.Parent) {
+ par = b.Toplevel.Parameters.GetParameterByName (name, out idx);
+ if (par != null)
+ return true;
+ }
+ return false;
+ }
+
/// <returns>
/// A list of labels that were not used within this block
/// </returns>
public VariableMap ParameterMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return param_map;
}
public VariableMap LocalMap {
get {
if ((flags & Flags.VariablesInitialized) == 0)
- throw new Exception ();
+ throw new Exception ("Variables have not been initialized yet");
return local_map;
}
}
- public bool LiftVariable (LocalInfo local_info)
- {
- return false;
- }
-
/// <summary>
/// Emits the variable declarations and labels.
/// </summary>
/// tc: is our typecontainer (to resolve type references)
/// ig: is the code generator:
/// </remarks>
- public void EmitMeta (EmitContext ec, InternalParameters ip)
+ public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
{
- ILGenerator ig = ec.ig;
+ bool old_unsafe = ec.InUnsafe;
+
+ // If some parent block was unsafe, we remain unsafe even if this block
+ // isn't explicitly marked as such.
+ ec.InUnsafe |= Unsafe;
//
// Compute the VariableMap's.
bool old_check_state = ec.ConstantCheckState;
ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
- bool remap_locals = ec.RemapToProxy;
-
+
//
// Process this block variables
//
continue;
}
+#if false
if (remap_locals)
vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
- else {
+ else if (vi.Pinned)
//
// This is needed to compile on both .NET 1.x and .NET 2.x
// the later introduced `DeclareLocal (Type t, bool pinned)'
//
- if (vi.Pinned)
- vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
- else
- vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
- }
+ vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
+ else if (!vi.IsThis)
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+#endif
if (constants == null)
continue;
//
if (children != null){
foreach (Block b in children)
- b.EmitMeta (ec, ip);
+ b.ResolveMeta (toplevel, ec, ip);
+ }
+ ec.InUnsafe = old_unsafe;
+ }
+
+ //
+ // Emits the local variable declarations for a block
+ //
+ public void EmitMeta (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (variables != null){
+ bool have_captured_vars = ec.HaveCapturedVariables ();
+ bool remap_locals = ec.RemapToProxy;
+
+ foreach (DictionaryEntry de in variables){
+ LocalInfo vi = (LocalInfo) de.Value;
+
+ if (have_captured_vars && ec.IsCaptured (vi))
+ continue;
+
+ if (remap_locals){
+ vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType);
+ } else {
+ if (vi.Pinned)
+ //
+ // This is needed to compile on both .NET 1.x and .NET 2.x
+ // the later introduced `DeclareLocal (Type t, bool pinned)'
+ //
+ vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
+ else if (!vi.IsThis)
+ vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
+ }
+ }
+ }
+
+ if (children != null){
+ foreach (Block b in children)
+ b.EmitMeta (ec);
}
}
{
string name;
- if (variables != null){
+ if ((variables != null) && (RootContext.WarningLevel >= 3)) {
foreach (DictionaryEntry de in variables){
LocalInfo vi = (LocalInfo) de.Value;
name = (string) de.Key;
if (vector.IsAssigned (vi.VariableInfo)){
- Report.Warning (
- 219, vi.Location, "The variable `" + name +
- "' is assigned but its value is never used");
+ Report.Warning (219, vi.Location, "The variable '{0}' is assigned but its value is never used", name);
} else {
- Report.Warning (
- 168, vi.Location, "The variable `" +
- name +
- "' is declared but never used");
- }
+ Report.Warning (168, vi.Location, "The variable '{0}' is declared but never used", name);
+ }
}
}
}
+ bool unreachable_shown;
+
public override bool Resolve (EmitContext ec)
{
Block prev_block = ec.CurrentBlock;
Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
- bool unreachable = false, warning_shown = false;
+ //
+ // This flag is used to notate nested statements as unreachable from the beginning of this block.
+ // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
+ // from the beginning of the function. The outer Resolve() that detected the unreachability is
+ // responsible for handling the situation.
+ //
+ bool unreachable = false;
int statement_count = statements.Count;
for (int ix = 0; ix < statement_count; ix++){
if (s == EmptyStatement.Value)
s.loc = EndLocation;
- if (!s.ResolveUnreachable (ec, !warning_shown))
+ if (!s.ResolveUnreachable (ec, !unreachable_shown))
ok = false;
if (s != EmptyStatement.Value)
- warning_shown = true;
+ unreachable_shown = true;
else
s.loc = Location.Null;
- statements [ix] = EmptyStatement.Value;
- continue;
+ if (ok && !(s is Block)) {
+ statements [ix] = EmptyStatement.Value;
+ continue;
+ }
}
if (s.Resolve (ec) == false) {
return ok;
}
+
+ public override bool ResolveUnreachable (EmitContext ec, bool warn)
+ {
+ unreachable_shown = true;
+
+ if (warn && (RootContext.WarningLevel >= 2))
+ Report.Warning (162, loc, "Unreachable code detected");
+
+ if (Implicit)
+ return Resolve (ec);
+
+ ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
+ bool ok = Resolve (ec);
+ ec.KillFlowBranching ();
+
+ return ok;
+ }
protected override void DoEmit (EmitContext ec)
{
// Check whether we are the last statement in a
// top-level block.
- if ((Parent == null) && (ix+1 == num_statements))
+ if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block))
ec.IsLastStatement = true;
else
ec.IsLastStatement = false;
if (emit_debug_info) {
if (is_lexical_block)
- ec.ig.BeginScope ();
+ ec.BeginScope ();
if (variables != null) {
foreach (DictionaryEntry de in variables) {
if (vi.LocalBuilder == null)
continue;
- vi.LocalBuilder.SetLocalSymInfo (name);
+ ec.DefineLocalVariable (name, vi.LocalBuilder);
}
}
}
ec.Mark (EndLocation, true);
if (emit_debug_info && is_lexical_block)
- ec.ig.EndScope ();
+ ec.EndScope ();
ec.CurrentBlock = prev_block;
}
+
+ //
+ // Returns true if we ar ea child of `b'.
+ //
+ public bool IsChildOf (Block b)
+ {
+ Block current = this;
+
+ do {
+ if (current.Parent == b)
+ return true;
+ current = current.Parent;
+ } while (current != null);
+ return false;
+ }
}
//
+ // A toplevel block contains extra information, the split is done
+ // only to separate information that would otherwise bloat the more
+ // lightweight Block.
+ //
+ // In particular, this was introduced when the support for Anonymous
+ // Methods was implemented.
//
public class ToplevelBlock : Block {
+ //
+ // Pointer to the host of this anonymous method, or null
+ // if we are the topmost block
+ //
+ public ToplevelBlock Container;
+ CaptureContext capture_context;
+
+ Hashtable capture_contexts;
+
+ //
+ // The parameters for the block.
+ //
+ public readonly Parameters Parameters;
+
+ public void RegisterCaptureContext (CaptureContext cc)
+ {
+ if (capture_contexts == null)
+ capture_contexts = new Hashtable ();
+ capture_contexts [cc] = cc;
+ }
+
+ public void CompleteContexts ()
+ {
+ if (capture_contexts == null)
+ return;
+
+ foreach (CaptureContext cc in capture_contexts.Keys){
+ cc.AdjustScopes ();
+ }
+ }
+
+ public CaptureContext ToplevelBlockCaptureContext {
+ get {
+ return capture_context;
+ }
+ }
+
+ //
+ // Parent is only used by anonymous blocks to link back to their
+ // parents
+ //
+ public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
+ this (container, (Flags) 0, parameters, start)
+ {
+ }
+
public ToplevelBlock (Parameters parameters, Location start) :
- base (null, parameters, start, Location.Null)
+ this (null, (Flags) 0, parameters, start)
{
}
public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
- base (null, flags, parameters, start, Location.Null)
+ this (null, flags, parameters, start)
+ {
+ }
+
+ public ToplevelBlock (ToplevelBlock container, Flags flags, Parameters parameters, Location start) :
+ base (null, flags | Flags.IsToplevel, start, Location.Null)
+ {
+ Parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
+ Container = container;
+ }
+
+ public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
+ {
+ }
+
+ public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
{
+ if (capture_context == null)
+ capture_context = new CaptureContext (this, loc, host);
+ }
+
+ public CaptureContext CaptureContext {
+ get {
+ return capture_context;
+ }
}
}
Expression label;
object converted;
public Location loc;
- public Label ILLabel;
- public Label ILLabelCode;
+
+ Label il_label;
+ bool il_label_set;
+ Label il_label_code;
+ bool il_label_code_set;
//
// if expr == null, then it is the default case.
}
}
+ public Label GetILLabel (EmitContext ec)
+ {
+ if (!il_label_set){
+ il_label = ec.ig.DefineLabel ();
+ il_label_set = true;
+ }
+ return il_label;
+ }
+
+ public Label GetILLabelCode (EmitContext ec)
+ {
+ if (!il_label_code_set){
+ il_label_code = ec.ig.DefineLabel ();
+ il_label_code_set = true;
+ }
+ return il_label_code;
+ }
+
//
// Resolves the expression, reduces it to a literal if possible
// and then converts it to the requested type.
//
public bool ResolveAndReduce (EmitContext ec, Type required_type)
{
- ILLabel = ec.ig.DefineLabel ();
- ILLabelCode = ec.ig.DefineLabel ();
-
if (label == null)
return true;
if (e is StringConstant || e is NullLiteral){
if (required_type == TypeManager.string_type){
converted = e;
- ILLabel = ec.ig.DefineLabel ();
return true;
}
}
bool got_default;
Label default_target;
Expression new_expr;
+ bool is_constant;
+ SwitchSection constant_section;
//
// The types allowed to be implicitly cast from
if (allowed_types == null){
allowed_types = new Type [] {
+ TypeManager.int32_type,
+ TypeManager.uint32_type,
TypeManager.sbyte_type,
TypeManager.byte_type,
TypeManager.short_type,
TypeManager.ushort_type,
- TypeManager.int32_type,
- TypeManager.uint32_type,
TypeManager.int64_type,
TypeManager.uint64_type,
TypeManager.char_type,
if (e == null)
continue;
+ //
+ // Ignore over-worked ImplicitUserConversions that do
+ // an implicit conversion in addition to the user conversion.
+ //
+ if (e is UserCast){
+ UserCast ue = e as UserCast;
+
+ if (ue.Source != Expr)
+ e = null;
+ }
+
if (converted != null){
- Report.Error (-12, loc, "More than one conversion to an integral " +
- " type exists for type `" +
- TypeManager.CSharpName (Expr.Type)+"'");
+ Report.ExtraInformation (
+ loc,
+ String.Format ("reason: more than one conversion to an integral type exist for type {0}",
+ TypeManager.CSharpName (Expr.Type)));
return null;
- } else
+ } else {
converted = e;
+ }
}
return converted;
}
- void error152 (string n)
- {
- Report.Error (
- 152, "The label `" + n + ":' " +
- "is already present on this switch statement");
+ static string Error152 {
+ get {
+ return "The label '{0}:' already occurs in this switch statement";
+ }
}
//
if (sl.Label == null){
if (got_default){
- error152 ("default");
+ Report.Error (152, sl.loc, Error152, "default");
error = true;
}
got_default = true;
SwitchType + " " + compare_type);
}
- if (lname != null){
- error152 ("case + " + lname);
+ if (lname != null) {
+ Report.Error (152, sl.loc, Error152, "case " + lname);
error = true;
}
}
ig.Emit (OpCodes.Ldloc, val);
EmitObjectInteger (ig, key);
SwitchLabel sl = (SwitchLabel) Elements [key];
- ig.Emit (OpCodes.Beq, sl.ILLabel);
+ ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
}
}
else
if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
{
SwitchLabel sl = (SwitchLabel) Elements [key];
- rgLabels [iJump] = sl.ILLabel;
+ rgLabels [iJump] = sl.GetILLabel (ec);
iKey++;
}
else
{
foreach (SwitchLabel sl in ss.Labels)
{
- ig.MarkLabel (sl.ILLabel);
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabel (ec));
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (sl.Label == null)
{
ig.MarkLabel (lblDefault);
null_found = false;
for (int label = 0; label < label_count; label++){
SwitchLabel sl = (SwitchLabel) ss.Labels [label];
- ig.MarkLabel (sl.ILLabel);
+ ig.MarkLabel (sl.GetILLabel (ec));
if (!first_test){
ig.MarkLabel (next_test);
ig.MarkLabel (null_target);
ig.MarkLabel (sec_begin);
foreach (SwitchLabel sl in ss.Labels)
- ig.MarkLabel (sl.ILLabelCode);
+ ig.MarkLabel (sl.GetILLabelCode (ec));
if (mark_default)
ig.MarkLabel (default_target);
ig.MarkLabel (end_of_switch);
}
+ SwitchSection FindSection (SwitchLabel label)
+ {
+ foreach (SwitchSection ss in Sections){
+ foreach (SwitchLabel sl in ss.Labels){
+ if (label == sl)
+ return ss;
+ }
+ }
+
+ return null;
+ }
+
public override bool Resolve (EmitContext ec)
{
Expr = Expr.Resolve (ec);
Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
+ is_constant = new_expr is Constant;
+ if (is_constant) {
+ object key = ((Constant) new_expr).GetValue ();
+ SwitchLabel label = (SwitchLabel) Elements [key];
+
+ constant_section = FindSection (label);
+ }
+
bool first = true;
foreach (SwitchSection ss in Sections){
if (!first)
else
first = false;
- if (ss.Block.Resolve (ec) != true)
- return false;
+ if (is_constant && (ss != constant_section)) {
+ // If we're a constant switch, we're only emitting
+ // one single section - mark all the others as
+ // unreachable.
+ ec.CurrentBranching.CurrentUsageVector.Goto ();
+ if (!ss.Block.ResolveUnreachable (ec, true))
+ return false;
+ } else {
+ if (!ss.Block.Resolve (ec))
+ return false;
+ }
}
-
if (!got_default)
ec.CurrentBranching.CreateSibling (
null, FlowBranching.SiblingType.SwitchSection);
protected override void DoEmit (EmitContext ec)
{
- // Store variable for comparission purposes
- LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
- new_expr.Emit (ec);
- ec.ig.Emit (OpCodes.Stloc, value);
-
ILGenerator ig = ec.ig;
+ // Store variable for comparission purposes
+ LocalBuilder value;
+ if (!is_constant) {
+ value = ig.DeclareLocal (SwitchType);
+ new_expr.Emit (ec);
+ ig.Emit (OpCodes.Stloc, value);
+ } else
+ value = null;
+
default_target = ig.DefineLabel ();
//
ec.Switch = this;
// Emit Code.
- if (SwitchType == TypeManager.string_type)
+ if (is_constant) {
+ if (constant_section != null)
+ constant_section.Block.Emit (ec);
+ } else if (SwitchType == TypeManager.string_type)
SimpleSwitchEmit (ec, value);
else
TableSwitchEmit (ec, value);
}
}
- public class Lock : Statement {
+ public abstract class ExceptionStatement : Statement
+ {
+ public abstract void EmitFinally (EmitContext ec);
+
+ protected bool emit_finally = true;
+ ArrayList parent_vectors;
+
+ protected void DoEmitFinally (EmitContext ec)
+ {
+ if (emit_finally)
+ ec.ig.BeginFinallyBlock ();
+ else
+ ec.CurrentIterator.MarkFinally (ec, parent_vectors);
+ EmitFinally (ec);
+ }
+
+ protected void ResolveFinally (FlowBranchingException branching)
+ {
+ emit_finally = branching.EmitFinally;
+ if (!emit_finally)
+ branching.Parent.StealFinallyClauses (ref parent_vectors);
+ }
+ }
+
+ public class Lock : ExceptionStatement {
Expression expr;
Statement Statement;
+ LocalBuilder temp;
public Lock (Expression expr, Statement stmt, Location l)
{
return false;
}
- ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
+ FlowBranchingException branching = ec.StartFlowBranching (this);
bool ok = Statement.Resolve (ec);
if (!ok) {
ec.KillFlowBranching ();
return false;
}
+ ResolveFinally (branching);
+
FlowBranching.Reachability reachability = ec.EndFlowBranching ();
if (reachability.Returns != FlowBranching.FlowReturns.Always) {
// Unfortunately, System.Reflection.Emit automatically emits
Type type = expr.Type;
ILGenerator ig = ec.ig;
- LocalBuilder temp = ig.DeclareLocal (type);
+ temp = ig.DeclareLocal (type);
expr.Emit (ec);
ig.Emit (OpCodes.Dup);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
// try
- ig.BeginExceptionBlock ();
- Label finish = ig.DefineLabel ();
+ if (emit_finally)
+ ig.BeginExceptionBlock ();
Statement.Emit (ec);
- // ig.Emit (OpCodes.Leave, finish);
-
- ig.MarkLabel (finish);
// finally
- ig.BeginFinallyBlock ();
+ DoEmitFinally (ec);
+ if (emit_finally)
+ ig.EndExceptionBlock ();
+ }
+
+ public override void EmitFinally (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
ig.Emit (OpCodes.Ldloc, temp);
ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
- ig.EndExceptionBlock ();
}
}
public Unsafe (Block b)
{
Block = b;
+ Block.Unsafe = true;
}
public override bool Resolve (EmitContext ec)
ArrayList declarators;
Statement statement;
Type expr_type;
- FixedData[] data;
+ Emitter[] data;
bool has_ret;
- struct FixedData {
- public bool is_object;
- public LocalInfo vi;
- public Expression expr;
- public Expression converted;
- }
+ abstract class Emitter
+ {
+ protected LocalInfo vi;
+ protected Expression converted;
+
+ protected Emitter (Expression expr, LocalInfo li)
+ {
+ converted = expr;
+ vi = li;
+ }
+
+ public abstract void Emit (EmitContext ec);
+ public abstract void EmitExit (ILGenerator ig);
+ }
+
+ class ExpressionEmitter: Emitter {
+ public ExpressionEmitter (Expression converted, LocalInfo li) :
+ base (converted, li)
+ {
+ }
+
+ public override void Emit (EmitContext ec) {
+ //
+ // Store pointer in pinned location
+ //
+ converted.Emit (ec);
+ ec.ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
+ }
+
+ public override void EmitExit (ILGenerator ig)
+ {
+ ig.Emit (OpCodes.Ldc_I4_0);
+ ig.Emit (OpCodes.Conv_U);
+ ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
+ }
+ }
+
+ class StringEmitter: Emitter {
+ LocalBuilder pinned_string;
+ Location loc;
+
+ public StringEmitter (Expression expr, LocalInfo li, Location loc):
+ base (expr, li)
+ {
+ this.loc = loc;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+ pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
+
+ converted.Emit (ec);
+ ig.Emit (OpCodes.Stloc, pinned_string);
+
+ Expression sptr = new StringPtr (pinned_string, loc);
+ converted = Convert.ImplicitConversionRequired (
+ ec, sptr, vi.VariableType, loc);
+
+ if (converted == null)
+ return;
+
+ converted.Emit (ec);
+ ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
+ }
+
+ public override void EmitExit(ILGenerator ig)
+ {
+ ig.Emit (OpCodes.Ldnull);
+ ig.Emit (OpCodes.Stloc, pinned_string);
+ }
+ }
public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
{
return false;
}
- expr_type = ec.DeclSpace.ResolveType (type, false, loc);
- if (expr_type == null)
+ TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
return false;
+ expr_type = texpr.ResolveType (ec);
+
CheckObsolete (expr_type);
if (ec.RemapToProxy){
return false;
}
- data = new FixedData [declarators.Count];
+ data = new Emitter [declarators.Count];
if (!expr_type.IsPointer){
Report.Error (209, loc, "Variables in a fixed statement must be pointers");
LocalInfo vi = (LocalInfo) p.First;
Expression e = (Expression) p.Second;
- vi.VariableInfo = null;
+ vi.VariableInfo.SetAssigned (ec);
vi.ReadOnly = true;
//
if (!TypeManager.VerifyUnManaged (child.Type, loc))
return false;
- data [i].is_object = true;
- data [i].expr = e;
- data [i].converted = null;
- data [i].vi = vi;
+ data [i] = new ExpressionEmitter (e, vi);
i++;
continue;
// and T* is implicitly convertible to the
// pointer type given in the fixed statement.
//
- ArrayPtr array_ptr = new ArrayPtr (e, loc);
+ ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
Expression converted = Convert.ImplicitConversionRequired (
ec, array_ptr, vi.VariableType, loc);
if (converted == null)
return false;
- data [i].is_object = false;
- data [i].expr = e;
- data [i].converted = converted;
- data [i].vi = vi;
+ data [i] = new ExpressionEmitter (converted, vi);
i++;
continue;
// Case 3: string
//
if (e.Type == TypeManager.string_type){
- data [i].is_object = false;
- data [i].expr = e;
- data [i].converted = null;
- data [i].vi = vi;
+ data [i] = new StringEmitter (e, vi, loc);
i++;
continue;
}
+ // Case 4: fixed buffer
+ FieldExpr fe = e as FieldExpr;
+ if (fe != null) {
+ IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
+ if (ff != null) {
+ Expression fixed_buffer_ptr = new FixedBufferPtr (fe, ff.ElementType, loc);
+
+ Expression converted = Convert.ImplicitConversionRequired (
+ ec, fixed_buffer_ptr, vi.VariableType, loc);
+ if (converted == null)
+ return false;
+
+ data [i] = new ExpressionEmitter (converted, vi);
+ i++;
+
+ continue;
+ }
+ }
+
//
// For other cases, flag a `this is already fixed expression'
//
protected override void DoEmit (EmitContext ec)
{
- ILGenerator ig = ec.ig;
-
- LocalBuilder [] clear_list = new LocalBuilder [data.Length];
-
for (int i = 0; i < data.Length; i++) {
- LocalInfo vi = data [i].vi;
-
- //
- // Case 1: & object.
- //
- if (data [i].is_object) {
- //
- // Store pointer in pinned location
- //
- data [i].expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
- clear_list [i] = vi.LocalBuilder;
- continue;
- }
-
- //
- // Case 2: Array
- //
- if (data [i].expr.Type.IsArray){
- //
- // Store pointer in pinned location
- //
- data [i].converted.Emit (ec);
-
- ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
- clear_list [i] = vi.LocalBuilder;
- continue;
- }
-
- //
- // Case 3: string
- //
- if (data [i].expr.Type == TypeManager.string_type){
- LocalBuilder pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
- clear_list [i] = pinned_string;
-
- data [i].expr.Emit (ec);
- ig.Emit (OpCodes.Stloc, pinned_string);
-
- Expression sptr = new StringPtr (pinned_string, loc);
- Expression converted = Convert.ImplicitConversionRequired (
- ec, sptr, vi.VariableType, loc);
-
- if (converted == null)
- continue;
-
- converted.Emit (ec);
- ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
- }
+ data [i].Emit (ec);
}
statement.Emit (ec);
if (has_ret)
return;
+ ILGenerator ig = ec.ig;
+
//
// Clear the pinned variable
//
for (int i = 0; i < data.Length; i++) {
- if (data [i].is_object || data [i].expr.Type.IsArray) {
- ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Conv_U);
- ig.Emit (OpCodes.Stloc, clear_list [i]);
- } else if (data [i].expr.Type == TypeManager.string_type){
- ig.Emit (OpCodes.Ldnull);
- ig.Emit (OpCodes.Stloc, clear_list [i]);
- }
+ data [i].EmitExit (ig);
}
}
}
public override bool Resolve (EmitContext ec)
{
- if (type_expr != null) {
- type = ec.DeclSpace.ResolveType (type_expr, false, loc);
- if (type == null)
- return false;
+ bool was_catch = ec.InCatch;
+ ec.InCatch = true;
+ try {
+ if (type_expr != null) {
+ TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
+ if (te == null)
+ return false;
- CheckObsolete (type);
+ type = te.ResolveType (ec);
- if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
- Error (155, "The type caught or thrown must be derived from System.Exception");
- return false;
- }
- } else
- type = null;
+ CheckObsolete (type);
- return Block.Resolve (ec);
+ if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
+ Error (155, "The type caught or thrown must be derived from System.Exception");
+ return false;
+ }
+ } else
+ type = null;
+
+ return Block.Resolve (ec);
+ }
+ finally {
+ ec.InCatch = was_catch;
+ }
}
}
- public class Try : Statement {
+ public class Try : ExceptionStatement {
public readonly Block Fini, Block;
public readonly ArrayList Specific;
public readonly Catch General;
+
+ bool need_exc_block;
//
// specific, general and fini might all be null.
{
bool ok = true;
- ec.StartFlowBranching (FlowBranching.BranchingType.Exception, Block.StartLocation);
+ FlowBranchingException branching = ec.StartFlowBranching (this);
Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
Type resolvedType = c.CatchType;
for (int ii = 0; ii < last_index; ++ii) {
- if (resolvedType.IsSubclassOf (prevCatches [ii])) {
- Report.Error_T (160, c.loc, prevCatches [ii].FullName);
+ if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
+ Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName);
return false;
}
}
prevCatches [last_index++] = resolvedType;
+ need_exc_block = true;
}
Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
if (!General.Resolve (ec))
ok = false;
+
+ need_exc_block = true;
}
Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
Fini, FlowBranching.SiblingType.Finally);
Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
-
+ bool was_finally = ec.InFinally;
+ ec.InFinally = true;
if (!Fini.Resolve (ec))
ok = false;
+ ec.InFinally = was_finally;
}
+ ResolveFinally (branching);
+ need_exc_block |= emit_finally;
+
FlowBranching.Reachability reachability = ec.EndFlowBranching ();
FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
if (reachability.Returns != FlowBranching.FlowReturns.Always) {
- // Unfortunately, System.Reflection.Emit automatically emits a leave
- // to the end of the finally block. This is a problem if `returns'
- // is true since we may jump to a point after the end of the method.
+ // Unfortunately, System.Reflection.Emit automatically emits
+ // a leave to the end of the finally block. This is a problem
+ // if `returns' is true since we may jump to a point after the
+ // end of the method.
// As a workaround, emit an explicit ret here.
ec.NeedReturnLabel ();
}
protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
- Label finish = ig.DefineLabel ();;
- ig.BeginExceptionBlock ();
+ if (need_exc_block)
+ ig.BeginExceptionBlock ();
Block.Emit (ec);
- //
- // System.Reflection.Emit provides this automatically:
- // ig.Emit (OpCodes.Leave, finish);
-
foreach (Catch c in Specific){
LocalInfo vi;
General.Block.Emit (ec);
}
- ig.MarkLabel (finish);
+ DoEmitFinally (ec);
+ if (need_exc_block)
+ ig.EndExceptionBlock ();
+ }
+
+ public override void EmitFinally (EmitContext ec)
+ {
if (Fini != null){
- ig.BeginFinallyBlock ();
Fini.Emit (ec);
}
-
- ig.EndExceptionBlock ();
+ }
+
+ public bool HasCatch
+ {
+ get {
+ return General != null || Specific.Count > 0;
+ }
}
}
- public class Using : Statement {
+ public class Using : ExceptionStatement {
object expression_or_block;
Statement Statement;
ArrayList var_list;
Expression expr;
Type expr_type;
Expression conv;
+ Expression [] resolved_vars;
Expression [] converted_vars;
ExpressionStatement [] assign;
+ LocalBuilder local_copy;
public Using (object expression_or_block, Statement stmt, Location l)
{
//
bool ResolveLocalVariableDecls (EmitContext ec)
{
- bool need_conv = false;
- expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
int i = 0;
- if (expr_type == null)
+ TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
return false;
+ expr_type = texpr.ResolveType (ec);
+
//
// The type must be an IDisposable or an implicit conversion
// must exist.
//
converted_vars = new Expression [var_list.Count];
+ resolved_vars = new Expression [var_list.Count];
assign = new ExpressionStatement [var_list.Count];
- if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
- foreach (DictionaryEntry e in var_list){
- Expression var = (Expression) e.Key;
- var = var.ResolveLValue (ec, new EmptyExpression ());
- if (var == null)
- return false;
-
- converted_vars [i] = Convert.ImplicitConversionRequired (
- ec, var, TypeManager.idisposable_type, loc);
+ bool need_conv = !TypeManager.ImplementsInterface (
+ expr_type, TypeManager.idisposable_type);
- if (converted_vars [i] == null)
- return false;
+ foreach (DictionaryEntry e in var_list){
+ Expression var = (Expression) e.Key;
+
+ var = var.ResolveLValue (ec, new EmptyExpression ());
+ if (var == null)
+ return false;
+
+ resolved_vars [i] = var;
+
+ if (!need_conv) {
i++;
+ continue;
}
- need_conv = true;
+
+ converted_vars [i] = Convert.ImplicitConversionRequired (
+ ec, var, TypeManager.idisposable_type, loc);
+
+ if (converted_vars [i] == null)
+ return false;
+
+ i++;
}
i = 0;
foreach (DictionaryEntry e in var_list){
- LocalVariableReference var = (LocalVariableReference) e.Key;
+ Expression var = resolved_vars [i];
Expression new_expr = (Expression) e.Value;
Expression a;
bool ResolveExpression (EmitContext ec)
{
if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
- conv = Convert.ImplicitConversionRequired (
- ec, expr, TypeManager.idisposable_type, loc);
-
- if (conv == null)
+ if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
+ Report.Error (1674, loc, "'{0}': type used in a using statement must be implicitly convertible to 'System.IDisposable'",
+ TypeManager.CSharpName (expr_type));
return false;
+ }
}
return true;
//
// Emits the code for the case of using using a local variable declaration.
//
- bool EmitLocalVariableDecls (EmitContext ec)
+ void EmitLocalVariableDecls (EmitContext ec)
{
ILGenerator ig = ec.ig;
int i = 0;
for (i = 0; i < assign.Length; i++) {
assign [i].EmitStatement (ec);
-
- ig.BeginExceptionBlock ();
+
+ if (emit_finally)
+ ig.BeginExceptionBlock ();
}
Statement.Emit (ec);
-
var_list.Reverse ();
- foreach (DictionaryEntry e in var_list){
- LocalVariableReference var = (LocalVariableReference) e.Key;
+
+ DoEmitFinally (ec);
+ }
+
+ void EmitLocalVariableDeclFinally (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ int i = assign.Length;
+ for (int ii = 0; ii < var_list.Count; ++ii){
+ Expression var = resolved_vars [--i];
Label skip = ig.DefineLabel ();
- i--;
-
- ig.BeginFinallyBlock ();
if (!var.Type.IsValueType) {
var.Emit (ec);
MethodInfo mi = null;
foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
- if (mk.GetParameters().Length == 0) {
+ if (TypeManager.GetArgumentTypes (mk).Length == 0) {
mi = mk;
break;
}
if (mi == null) {
Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
- return false;
+ return;
}
- var.AddressOf (ec, AddressOp.Load);
+ IMemoryLocation mloc = (IMemoryLocation) var;
+
+ mloc.AddressOf (ec, AddressOp.Load);
ig.Emit (OpCodes.Call, mi);
}
}
ig.MarkLabel (skip);
- ig.EndExceptionBlock ();
- }
- return false;
+ if (emit_finally) {
+ ig.EndExceptionBlock ();
+ if (i > 0)
+ ig.BeginFinallyBlock ();
+ }
+ }
}
- bool EmitExpression (EmitContext ec)
+ void EmitExpression (EmitContext ec)
{
//
// Make a copy of the expression and operate on that.
//
ILGenerator ig = ec.ig;
- LocalBuilder local_copy = ig.DeclareLocal (expr_type);
+ local_copy = ig.DeclareLocal (expr_type);
if (conv != null)
conv.Emit (ec);
else
expr.Emit (ec);
ig.Emit (OpCodes.Stloc, local_copy);
- ig.BeginExceptionBlock ();
+ if (emit_finally)
+ ig.BeginExceptionBlock ();
+
Statement.Emit (ec);
- Label skip = ig.DefineLabel ();
- ig.BeginFinallyBlock ();
- ig.Emit (OpCodes.Ldloc, local_copy);
- ig.Emit (OpCodes.Brfalse, skip);
- ig.Emit (OpCodes.Ldloc, local_copy);
- ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
- ig.MarkLabel (skip);
- ig.EndExceptionBlock ();
+ DoEmitFinally (ec);
+ if (emit_finally)
+ ig.EndExceptionBlock ();
+ }
- return false;
+ void EmitExpressionFinally (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+ if (!local_copy.LocalType.IsValueType) {
+ Label skip = ig.DefineLabel ();
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Brfalse, skip);
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ ig.MarkLabel (skip);
+ } else {
+ Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, local_copy.LocalType, "Dispose", Mono.CSharp.Location.Null);
+
+ if (!(ml is MethodGroupExpr)) {
+ ig.Emit (OpCodes.Ldloc, local_copy);
+ ig.Emit (OpCodes.Box, local_copy.LocalType);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ } else {
+ MethodInfo mi = null;
+
+ foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
+ if (TypeManager.GetArgumentTypes (mk).Length == 0) {
+ mi = mk;
+ break;
+ }
+ }
+
+ if (mi == null) {
+ Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
+ return;
+ }
+
+ ig.Emit (OpCodes.Ldloca, local_copy);
+ ig.Emit (OpCodes.Call, mi);
+ }
+ }
}
public override bool Resolve (EmitContext ec)
return false;
}
- ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
+ FlowBranchingException branching = ec.StartFlowBranching (this);
bool ok = Statement.Resolve (ec);
ec.KillFlowBranching ();
return false;
}
-
+
+ ResolveFinally (branching);
FlowBranching.Reachability reachability = ec.EndFlowBranching ();
if (reachability.Returns != FlowBranching.FlowReturns.Always) {
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);
+ }
}
/// <summary>
/// Implementation of the foreach C# statement
/// </summary>
- public class Foreach : Statement {
+ public class Foreach : ExceptionStatement {
Expression type;
Expression variable;
Expression expr;
Expression empty, conv;
Type array_type, element_type;
Type var_type;
+ VariableStorage enumerator;
public Foreach (Expression type, LocalVariableReference var, Expression expr,
Statement stmt, Location l)
if (expr == null)
return false;
- var_type = ec.DeclSpace.ResolveType (type, false, loc);
- if (var_type == null)
+ if (expr is NullLiteral) {
+ Report.Error (186, expr.Location, "Use of null is not valid in this context");
return false;
+ }
+
+ TypeExpr texpr = type.ResolveAsTypeTerminal (ec, false);
+ if (texpr == null)
+ return false;
+
+ var_type = texpr.ResolveType (ec);
//
// We need an instance variable. Not sure this is the best
if (hm == null){
error1579 (expr.Type);
return false;
- }
+ }
+ // When ProbeCollection reported error
+ if (hm.move_next == null)
+ return false;
+
array_type = expr.Type;
element_type = hm.element_type;
ok = false;
bool disposable = (hm != null) && hm.is_disposable;
+ FlowBranchingException branching = null;
if (disposable)
- ec.StartFlowBranching (FlowBranching.BranchingType.Exception, loc);
+ branching = ec.StartFlowBranching (this);
if (!statement.Resolve (ec))
ok = false;
- if (disposable)
+ if (disposable) {
+ ResolveFinally (branching);
ec.EndFlowBranching ();
+ } else
+ emit_finally = true;
ec.EndFlowBranching ();
return null;
}
+ //
+ // Retrieves a `public void Dispose ()' method from the Type `t'
+ //
+ static MethodInfo FetchMethodDispose (Type t)
+ {
+ MemberList dispose_list;
+
+ dispose_list = TypeContainer.FindMembers (
+ t, MemberTypes.Method,
+ BindingFlags.Public | BindingFlags.Instance,
+ Type.FilterName, "Dispose");
+ if (dispose_list.Count == 0)
+ return null;
+
+ foreach (MemberInfo m in dispose_list){
+ MethodInfo mi = (MethodInfo) m;
+ Type [] args;
+
+ args = TypeManager.GetArgumentTypes (mi);
+ if (args != null && args.Length == 0){
+ if (mi.ReturnType == TypeManager.void_type)
+ return mi;
+ }
+ }
+ return null;
+ }
+
//
// This struct records the helper methods used by the Foreach construct
//
public Type element_type;
public Type enumerator_type;
public bool is_disposable;
+ public readonly Location Location;
- public ForeachHelperMethods (EmitContext ec)
+ public ForeachHelperMethods (EmitContext ec, Location loc)
{
this.ec = ec;
+ this.Location = loc;
this.element_type = TypeManager.object_type;
this.enumerator_type = TypeManager.ienumerator_type;
this.is_disposable = true;
return false;
}
ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
- EmitContext ec = hm.ec;
- //
- // Check whether GetEnumerator is accessible to us
- //
- MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
-
- Type declaring = mi.DeclaringType;
- if (prot == MethodAttributes.Private){
- if (declaring != ec.ContainerType)
- return false;
- } else if (prot == MethodAttributes.FamANDAssem){
- // If from a different assembly, false
- if (!(mi is MethodBuilder))
- return false;
- //
- // Are we being invoked from the same class, or from a derived method?
- //
- if (ec.ContainerType != declaring){
- if (!ec.ContainerType.IsSubclassOf (declaring))
- return false;
- }
- } else if (prot == MethodAttributes.FamORAssem){
- if (!(mi is MethodBuilder ||
- ec.ContainerType == declaring ||
- ec.ContainerType.IsSubclassOf (declaring)))
- return false;
- } if (prot == MethodAttributes.Family){
- if (!(ec.ContainerType == declaring ||
- ec.ContainerType.IsSubclassOf (declaring)))
- return false;
- }
+ // Check whether GetEnumerator is public
+ if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
+ return false;
- if ((mi.ReturnType == TypeManager.ienumerator_type) && (declaring == TypeManager.string_type))
+ if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
//
// Apply the same optimization as MS: skip the GetEnumerator
// returning an IEnumerator, and use the one returning a
} else {
+ if (return_type.IsPointer || return_type.IsArray) {
+ Report.SymbolRelatedToPreviousError (mi);
+ Type t = return_type.GetElementType ();
+ Report.SymbolRelatedToPreviousError (t);
+ Report.Error (202, hm.Location, "foreach requires that the return type '{0}' of '{1}' must have a suitable public MoveNext method and public Current property",
+ TypeManager.CSharpName (return_type), TypeManager.GetFullNameSignature (m));
+ hm.get_enumerator = mi;
+ return false;
+ }
+
//
// Ok, so they dont return an IEnumerable, we will have to
// find if they support the GetEnumerator pattern.
//
ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
{
- ForeachHelperMethods hm = new ForeachHelperMethods (ec);
+ ForeachHelperMethods hm = new ForeachHelperMethods (ec, loc);
for (Type tt = t; tt != null && tt != TypeManager.object_type;){
if (TryType (tt, hm))
bool EmitCollectionForeach (EmitContext ec)
{
ILGenerator ig = ec.ig;
- VariableStorage enumerator;
enumerator = new VariableStorage (ec, hm.enumerator_type);
- enumerator.EmitThis ();
+ enumerator.EmitThis (ig);
//
// Instantiate the enumerator
//
expr.Emit (ec);
ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
}
- enumerator.EmitStore ();
+ enumerator.EmitStore (ig);
//
// Protect the code in a try/finalize block, so that
// if the beast implement IDisposable, we get rid of it
//
- if (hm.is_disposable)
+ if (hm.is_disposable && emit_finally)
ig.BeginExceptionBlock ();
Label end_try = ig.DefineLabel ();
ig.MarkLabel (ec.LoopBegin);
- enumerator.EmitCall (hm.move_next);
+ enumerator.EmitCall (ig, hm.move_next);
ig.Emit (OpCodes.Brfalse, end_try);
+
if (ec.InIterator)
- ec.EmitThis ();
+ ig.Emit (OpCodes.Ldarg_0);
- enumerator.EmitCall (hm.get_current);
+ enumerator.EmitCall (ig, hm.get_current);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
ig.Emit (OpCodes.Br, ec.LoopBegin);
ig.MarkLabel (end_try);
- // The runtime provides this for us.
- // ig.Emit (OpCodes.Leave, end);
-
//
// Now the finally block
//
if (hm.is_disposable) {
+ DoEmitFinally (ec);
+ if (emit_finally)
+ ig.EndExceptionBlock ();
+ }
+
+ ig.MarkLabel (ec.LoopEnd);
+ return false;
+ }
+
+ public override void EmitFinally (EmitContext ec)
+ {
+ ILGenerator ig = ec.ig;
+
+ if (hm.enumerator_type.IsValueType) {
+ enumerator.EmitThis (ig);
+
+ MethodInfo mi = FetchMethodDispose (hm.enumerator_type);
+ if (mi != null) {
+ enumerator.EmitLoadAddress (ig);
+ ig.Emit (OpCodes.Call, mi);
+ } else {
+ enumerator.EmitLoad (ig);
+ ig.Emit (OpCodes.Box, hm.enumerator_type);
+ ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
+ }
+ } else {
Label call_dispose = ig.DefineLabel ();
- ig.BeginFinallyBlock ();
-
- enumerator.EmitThis ();
- enumerator.EmitLoad ();
+
+ enumerator.EmitThis (ig);
+ enumerator.EmitLoad (ig);
ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
ig.Emit (OpCodes.Dup);
ig.Emit (OpCodes.Brtrue_S, call_dispose);
ig.Emit (OpCodes.Pop);
- ig.Emit (OpCodes.Endfinally);
-
+
+ Label end_finally = ig.DefineLabel ();
+ ig.Emit (OpCodes.Br, end_finally);
+
ig.MarkLabel (call_dispose);
ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
-
-
- // The runtime generates this anyways.
- // ig.Emit (OpCodes.Endfinally);
+ ig.MarkLabel (end_finally);
- ig.EndExceptionBlock ();
+ if (emit_finally)
+ ig.Emit (OpCodes.Endfinally);
}
-
- ig.MarkLabel (ec.LoopEnd);
- return false;
}
//
//
// Make our copy of the array
//
- copy.EmitThis ();
+ copy.EmitThis (ig);
expr.Emit (ec);
- copy.EmitStore ();
+ copy.EmitStore (ig);
if (rank == 1){
VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
Label loop, test;
- counter.EmitThis ();
+ counter.EmitThis (ig);
ig.Emit (OpCodes.Ldc_I4_0);
- counter.EmitStore ();
+ counter.EmitStore (ig);
test = ig.DefineLabel ();
ig.Emit (OpCodes.Br, test);
ig.MarkLabel (loop);
if (ec.InIterator)
- ec.EmitThis ();
+ ig.Emit (OpCodes.Ldarg_0);
- copy.EmitThis ();
- copy.EmitLoad ();
- counter.EmitThis ();
- counter.EmitLoad ();
+ copy.EmitThis (ig);
+ copy.EmitLoad (ig);
+ counter.EmitThis (ig);
+ counter.EmitLoad (ig);
//
// Load the value, we load the value using the underlying type,
ArrayAccess.EmitLoadOpcode (ig, element_type);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
- counter.EmitThis ();
- counter.EmitThis ();
- counter.EmitLoad ();
+ counter.EmitThis (ig);
+ counter.EmitThis (ig);
+ counter.EmitLoad (ig);
ig.Emit (OpCodes.Ldc_I4_1);
ig.Emit (OpCodes.Add);
- counter.EmitStore ();
+ counter.EmitStore (ig);
ig.MarkLabel (test);
- counter.EmitThis ();
- counter.EmitLoad ();
- copy.EmitThis ();
- copy.EmitLoad ();
+ counter.EmitThis (ig);
+ counter.EmitLoad (ig);
+ copy.EmitThis (ig);
+ copy.EmitLoad (ig);
ig.Emit (OpCodes.Ldlen);
ig.Emit (OpCodes.Conv_I4);
ig.Emit (OpCodes.Blt, loop);
}
for (dim = 0; dim < rank; dim++){
- dim_len [dim].EmitThis ();
- copy.EmitThis ();
- copy.EmitLoad ();
+ dim_len [dim].EmitThis (ig);
+ copy.EmitThis (ig);
+ copy.EmitLoad (ig);
IntLiteral.EmitInt (ig, dim);
ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
- dim_len [dim].EmitStore ();
+ dim_len [dim].EmitStore (ig);
}
for (dim = 0; dim < rank; dim++){
- dim_count [dim].EmitThis ();
+ dim_count [dim].EmitThis (ig);
ig.Emit (OpCodes.Ldc_I4_0);
- dim_count [dim].EmitStore ();
+ dim_count [dim].EmitStore (ig);
ig.Emit (OpCodes.Br, test [dim]);
ig.MarkLabel (loop [dim]);
}
if (ec.InIterator)
- ec.EmitThis ();
- copy.EmitThis ();
- copy.EmitLoad ();
+ ig.Emit (OpCodes.Ldarg_0);
+
+ copy.EmitThis (ig);
+ copy.EmitLoad (ig);
for (dim = 0; dim < rank; dim++){
- dim_count [dim].EmitThis ();
- dim_count [dim].EmitLoad ();
+ dim_count [dim].EmitThis (ig);
+ dim_count [dim].EmitLoad (ig);
}
//
ig.Emit (OpCodes.Call, get);
if (ec.InIterator){
conv.Emit (ec);
- ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
+ ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
} else
((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
statement.Emit (ec);
ig.MarkLabel (ec.LoopBegin);
for (dim = rank - 1; dim >= 0; dim--){
- dim_count [dim].EmitThis ();
- dim_count [dim].EmitThis ();
- dim_count [dim].EmitLoad ();
+ dim_count [dim].EmitThis (ig);
+ dim_count [dim].EmitThis (ig);
+ dim_count [dim].EmitLoad (ig);
ig.Emit (OpCodes.Ldc_I4_1);
ig.Emit (OpCodes.Add);
- dim_count [dim].EmitStore ();
+ dim_count [dim].EmitStore (ig);
ig.MarkLabel (test [dim]);
- dim_count [dim].EmitThis ();
- dim_count [dim].EmitLoad ();
- dim_len [dim].EmitThis ();
- dim_len [dim].EmitLoad ();
+ dim_count [dim].EmitThis (ig);
+ dim_count [dim].EmitLoad (ig);
+ dim_len [dim].EmitThis (ig);
+ dim_len [dim].EmitLoad (ig);
ig.Emit (OpCodes.Blt, loop [dim]);
}
}
protected override void DoEmit (EmitContext ec)
{
ILGenerator ig = ec.ig;
-
+
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
ec.LoopBegin = ig.DefineLabel ();
ec.LoopEnd = ig.DefineLabel ();