X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fstatement.cs;h=69977e353094c7c7e8d904933c6246fbcb28a5fb;hb=60e8d68027b115d6b4c1df3e179bb0cd7b5ee171;hp=eba5e52557c59ae0b2fc6a482d832ebb0bfc88c1;hpb=a75673c6089b9b39a4bc5f776e850718daeb56e4;p=mono.git diff --git a/mcs/gmcs/statement.cs b/mcs/gmcs/statement.cs index eba5e52557c..69977e35309 100644 --- a/mcs/gmcs/statement.cs +++ b/mcs/gmcs/statement.cs @@ -175,8 +175,8 @@ namespace Mono.CSharp { if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) - return false; - } + return false; + } return true; } @@ -355,8 +355,8 @@ namespace Mono.CSharp { if (!Statement.Resolve (ec)) ok = false; - ec.CurrentBranching.Infinite = infinite; - ec.EndFlowBranching (); + ec.CurrentBranching.Infinite = infinite; + ec.EndFlowBranching (); return ok; } @@ -567,8 +567,15 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + AnonymousContainer am = ec.CurrentAnonymousMethod; + if ((am != null) && am.IsIterator && 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; + } + if (ec.ReturnType == null){ - if (Expr != null){ + if (Expr != null){ if (ec.CurrentAnonymousMethod != null){ Report.Error (1662, loc, String.Format ( "Anonymous method could not be converted to delegate " + @@ -585,12 +592,6 @@ namespace Mono.CSharp { 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; @@ -603,11 +604,6 @@ namespace Mono.CSharp { } } - if (ec.InIterator){ - Error (-206, "Return statement not allowed inside iterators"); - return false; - } - FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector; if (ec.CurrentBranching.InTryOrCatch (true)) { @@ -861,10 +857,10 @@ namespace Mono.CSharp { } Type t = expr.Type; - + if ((t != TypeManager.exception_type) && - !t.IsSubclassOf (TypeManager.exception_type) && - !(expr is NullLiteral)) { + !TypeManager.IsSubclassOf (t, TypeManager.exception_type) && + !(expr is NullLiteral)) { Error (155, "The type caught or thrown must be derived " + "from System.Exception"); @@ -917,7 +913,7 @@ namespace Mono.CSharp { } else if (ec.CurrentBranching.InTryOrCatch (false)) ec.CurrentBranching.AddFinallyVector ( ec.CurrentBranching.CurrentUsageVector); - else if (ec.CurrentBranching.InLoop ()) + else if (ec.CurrentBranching.InLoop () || ec.CurrentBranching.InSwitch ()) ec.CurrentBranching.AddBreakVector ( ec.CurrentBranching.CurrentUsageVector); @@ -1009,11 +1005,19 @@ namespace Mono.CSharp { Pinned = 4, IsThis = 8, Captured = 16, - AddressTaken = 32 + AddressTaken = 32, + CompilerGenerated = 64 } - Flags flags; + public enum ReadOnlyContext: byte { + Using, + Foreach, + Fixed + } + Flags flags; + ReadOnlyContext ro_context; + public LocalInfo (Expression type, string name, Block block, Location l) { Type = type; @@ -1107,6 +1111,16 @@ namespace Mono.CSharp { } } + public bool CompilerGenerated { + get { + return (flags & Flags.CompilerGenerated) != 0; + } + + set { + flags |= Flags.CompilerGenerated; + } + } + public override string ToString () { return String.Format ("LocalInfo ({0},{1},{2},{3})", @@ -1126,9 +1140,28 @@ namespace Mono.CSharp { get { return (flags & Flags.ReadOnly) != 0; } - set { - flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly)); + } + + public void SetReadOnlyContext (ReadOnlyContext context) + { + flags |= Flags.ReadOnly; + ro_context = context; + } + + public string GetReadOnlyContext () + { + if (!ReadOnly) + throw new InternalErrorException ("Variable is not readonly"); + + switch (ro_context) { + case ReadOnlyContext.Fixed: + return "fixed variable"; + case ReadOnlyContext.Foreach: + return "foreach iteration variable"; + case ReadOnlyContext.Using: + return "using variable"; } + throw new NotImplementedException (); } // @@ -1239,7 +1272,7 @@ namespace Mono.CSharp { // statements. // ArrayList children; - + // // Labels. (label, block) pairs. // @@ -1253,6 +1286,11 @@ namespace Mono.CSharp { // // Keeps track of constants Hashtable constants; + + // + // Temporary variables. + // + ArrayList temporary_variables; // // If this is a switch section, the enclosing switch block. @@ -1548,7 +1586,7 @@ namespace Mono.CSharp { int idx; Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx); if (p != null) { - Report.SymbolRelatedToPreviousError (Toplevel.Parameters.Location, name); + Report.SymbolRelatedToPreviousError (p.Location, name); Report.Error (136, l, "'{0}' hides a method parameter", name); return null; } @@ -1579,6 +1617,22 @@ namespace Mono.CSharp { return true; } + static int next_temp_id = 0; + + public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc) + { + if (temporary_variables == null) + temporary_variables = new ArrayList (); + + int id = ++next_temp_id; + string name = "$s_" + id.ToString (); + + LocalInfo li = new LocalInfo (te, name, this, loc); + li.CompilerGenerated = true; + temporary_variables.Add (li); + return li; + } + public Hashtable Variables { get { return variables; @@ -1631,49 +1685,6 @@ namespace Mono.CSharp { return e != null; } - // - // 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; - } - /// /// A list of labels that were not used within this block /// @@ -1804,19 +1815,6 @@ namespace Mono.CSharp { continue; } -#if false - if (remap_locals) - vi.FieldBuilder = ec.MapVariable (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); -#endif - if (constants == null) continue; @@ -1826,14 +1824,11 @@ namespace Mono.CSharp { ec.CurrentBlock = this; Expression e = cv.Resolve (ec); - if (e == null) - continue; Constant ce = e as Constant; if (ce == null){ Report.Error (133, vi.Location, - "The expression being assigned to `" + - name + "' must be constant (" + e + ")"); + "The expression being assigned to '{0}' must be constant", name); continue; } @@ -1868,7 +1863,6 @@ namespace Mono.CSharp { if (variables != null){ bool have_captured_vars = ec.HaveCapturedVariables (); - bool remap_locals = ec.RemapToProxy; foreach (DictionaryEntry de in variables){ LocalInfo vi = (LocalInfo) de.Value; @@ -1876,18 +1870,32 @@ namespace Mono.CSharp { 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 (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 (temporary_variables != null) { + AnonymousContainer am = ec.CurrentAnonymousMethod; + TypeBuilder scope = null; + if ((am != null) && am.IsIterator) { + scope = am.Scope.ScopeTypeBuilder; + if (scope == null) + throw new InternalErrorException (); + } + foreach (LocalInfo vi in temporary_variables) { + if (scope != null) { + if (vi.FieldBuilder == null) + vi.FieldBuilder = scope.DefineField ( + vi.Name, vi.VariableType, FieldAttributes.Assembly); + } else + vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); } } @@ -1920,6 +1928,7 @@ namespace Mono.CSharp { } bool unreachable_shown; + bool unreachable; public override bool Resolve (EmitContext ec) { @@ -1939,38 +1948,31 @@ namespace Mono.CSharp { // 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++){ Statement s = (Statement) statements [ix]; - - if (unreachable && !(s is LabeledStatement)) { - if (s == EmptyStatement.Value) - s.loc = EndLocation; - if (!s.ResolveUnreachable (ec, !unreachable_shown)) - ok = false; + if (unreachable) { + if (s is Block) + ((Block) s).unreachable = true; - if (s != EmptyStatement.Value) + if (!unreachable_shown && (RootContext.WarningLevel >= 2)) { + Report.Warning ( + 162, loc, "Unreachable code detected"); unreachable_shown = true; - else - s.loc = Location.Null; - - if (ok && !(s is Block)) { - statements [ix] = EmptyStatement.Value; - continue; } } - if (s.Resolve (ec) == false) { - ok = false; + if (!s.Resolve (ec)) { + ok = false; statements [ix] = EmptyStatement.Value; continue; } - num_statements = ix + 1; + if (unreachable && !(s is LabeledStatement) && !(s is Block)) + statements [ix] = EmptyStatement.Value; + num_statements = ix + 1; if (s is LabeledStatement) unreachable = false; else @@ -1980,7 +1982,6 @@ namespace Mono.CSharp { Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching, statement_count, num_statements); - FlowBranching.UsageVector vector = ec.DoEndFlowBranching (); ec.CurrentBlock = prev_block; @@ -2017,13 +2018,11 @@ namespace Mono.CSharp { public override bool ResolveUnreachable (EmitContext ec, bool warn) { unreachable_shown = true; + unreachable = 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 (); @@ -2118,11 +2117,12 @@ namespace Mono.CSharp { // Pointer to the host of this anonymous method, or null // if we are the topmost block // - public ToplevelBlock Container; + ToplevelBlock container; CaptureContext capture_context; FlowBranching top_level_branching; Hashtable capture_contexts; + ArrayList children; // // The parameters for the block. @@ -2145,13 +2145,27 @@ namespace Mono.CSharp { cc.AdjustScopes (); } } - + public CaptureContext ToplevelBlockCaptureContext { get { return capture_context; } } - + + public ToplevelBlock Container { + get { + return container; + } + } + + protected void AddChild (ToplevelBlock block) + { + if (children == null) + children = new ArrayList (); + + children.Add (block); + } + // // Parent is only used by anonymous blocks to link back to their // parents @@ -2175,14 +2189,17 @@ namespace Mono.CSharp { base (null, flags | Flags.IsToplevel, start, Location.Null) { Parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters; - Container = container; + this.container = container; + + if (container != null) + container.AddChild (this); } public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc) { } - public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host) + public void SetHaveAnonymousMethods (Location loc, AnonymousContainer host) { if (capture_context == null) capture_context = new CaptureContext (this, loc, host); @@ -2200,6 +2217,65 @@ namespace Mono.CSharp { } } + // + // 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, AnonymousContainer new_host) + { + foreach (ToplevelBlock block in children) { + if (block.CaptureContext == null) + continue; + + block.container = new_parent; + block.CaptureContext.ReParent (new_parent, new_host); + } + } + + // + // 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 (ToplevelBlock t = this; t != null; t = t.Container) { + Parameters pars = t.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 Parameters.GetParameterByName (name) != null; + } + + // + // Whether the `name' is a parameter reference + // + public bool IsParameterReference (string name) + { + for (ToplevelBlock t = this; t != null; t = t.Container) { + if (t.IsLocalParameter (name)) + return true; + } + return false; + } + public bool ResolveMeta (EmitContext ec, InternalParameters ip) { int errors = Report.Errors; @@ -3100,7 +3176,7 @@ namespace Mono.CSharp { { if (emit_finally) ec.ig.BeginFinallyBlock (); - else + else if (ec.InIterator) ec.CurrentIterator.MarkFinally (ec, parent_vectors); EmitFinally (ec); } @@ -3401,11 +3477,6 @@ namespace Mono.CSharp { CheckObsolete (expr_type); - if (ec.RemapToProxy){ - Report.Error (-210, loc, "Fixed statement not allowed in iterators"); - return false; - } - data = new Emitter [declarators.Count]; if (!expr_type.IsPointer){ @@ -3419,7 +3490,7 @@ namespace Mono.CSharp { Expression e = (Expression) p.Second; vi.VariableInfo.SetAssigned (ec); - vi.ReadOnly = true; + vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed); // // The rules for the possible declarators are pretty wise, @@ -3737,10 +3808,16 @@ namespace Mono.CSharp { if (!Fini.Resolve (ec)) ok = false; ec.InFinally = was_finally; + + if (!ec.InIterator) + need_exc_block = true; } - ResolveFinally (branching); - need_exc_block |= emit_finally; + if (ec.InIterator) { + ResolveFinally (branching); + need_exc_block |= emit_finally; + } else + emit_finally = Fini != null; FlowBranching.Reachability reachability = ec.EndFlowBranching (); @@ -3798,9 +3875,8 @@ namespace Mono.CSharp { public override void EmitFinally (EmitContext ec) { - if (Fini != null){ + if (Fini != null) Fini.Emit (ec); - } } public bool HasCatch @@ -4112,17 +4188,13 @@ namespace Mono.CSharp { /// /// Implementation of the foreach C# statement /// - public class Foreach : ExceptionStatement { + public class Foreach : Statement { Expression type; Expression variable; Expression expr; Statement statement; - ForeachHelperMethods hm; - Expression empty, conv; - Type array_type, element_type; - Type var_type; - VariableStorage enumerator; ArrayForeach array; + CollectionForeach collection; public Foreach (Expression type, LocalVariableReference var, Expression expr, Statement stmt, Location l) @@ -4148,8 +4220,8 @@ namespace Mono.CSharp { TypeExpr texpr = type.ResolveAsTypeTerminal (ec); if (texpr == null) return false; - - var_type = texpr.Type; + + Type var_type = texpr.Type; // // We need an instance variable. Not sure this is the best @@ -4160,541 +4232,120 @@ namespace Mono.CSharp { // if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value || expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){ - error1579 (expr.Type); + CollectionForeach.error1579 (expr.Type, loc); return false; } if (expr.Type.IsArray) { - array_type = expr.Type; - element_type = TypeManager.GetElementType (array_type); - - empty = new EmptyExpression (element_type); - - array = new ArrayForeach (type, variable, expr, statement, loc); + array = new ArrayForeach (var_type, variable, expr, statement, loc); return array.Resolve (ec); } else { - hm = ProbeCollectionType (ec, expr.Type); - if (hm == null){ - error1579 (expr.Type); - return false; - } - - array_type = expr.Type; - element_type = hm.element_type; - - empty = new EmptyExpression (hm.element_type); + collection = new CollectionForeach ( + var_type, variable, expr, statement, loc); + return collection.Resolve (ec); } + } - bool ok = true; + 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 (); - ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); - ec.CurrentBranching.CreateSibling (); + if (collection != null) + collection.Emit (ec); + else + array.Emit (ec); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } - // - // - // FIXME: maybe we can apply the same trick we do in the - // array handling to avoid creating empty and conv in some cases. - // - // Although it is not as important in this case, as the type - // will not likely be object (what the enumerator will return). - // - conv = Convert.ExplicitConversion (ec, empty, var_type, loc); - if (conv == null) - ok = false; + protected class TemporaryVariable : Expression, IMemoryLocation + { + LocalInfo li; - variable = variable.ResolveLValue (ec, empty); - if (variable == null) - ok = false; + public TemporaryVariable (Type type, Location loc) + { + this.type = type; + this.loc = loc; + eclass = ExprClass.Value; + } - bool disposable = (hm != null) && hm.is_disposable; - FlowBranchingException branching = null; - if (disposable) - branching = ec.StartFlowBranching (this); + public override Expression DoResolve (EmitContext ec) + { + if (li != null) + return this; - if (!statement.Resolve (ec)) - ok = false; + TypeExpr te = new TypeExpression (type, loc); + li = ec.CurrentBlock.AddTemporaryVariable (te, loc); + if (!li.Resolve (ec)) + return null; - if (disposable) { - ResolveFinally (branching); - ec.EndFlowBranching (); - } else - emit_finally = true; + AnonymousContainer am = ec.CurrentAnonymousMethod; + if ((am != null) && am.IsIterator) + ec.CaptureVariable (li); - ec.EndFlowBranching (); + return this; + } - return ok; - } - - // - // Retrieves a `public bool MoveNext ()' method from the Type `t' - // - static MethodInfo FetchMethodMoveNext (Type t) - { - MemberList move_next_list; - - move_next_list = TypeContainer.FindMembers ( - t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, - Type.FilterName, "MoveNext"); - if (move_next_list.Count == 0) - return null; + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; - foreach (MemberInfo m in move_next_list){ - MethodInfo mi = (MethodInfo) m; - Type [] args; - - args = TypeManager.GetArgumentTypes (mi); - if (args != null && args.Length == 0){ - if (TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) - return mi; + if (li.FieldBuilder != null) { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, li.FieldBuilder); + } else { + ig.Emit (OpCodes.Ldloc, li.LocalBuilder); } } - return null; - } - - // - // Retrieves a `public T get_Current ()' method from the Type `t' - // - static MethodInfo FetchMethodGetCurrent (Type t) - { - MemberList get_current_list; - - get_current_list = TypeContainer.FindMembers ( - t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, - Type.FilterName, "get_Current"); - if (get_current_list.Count == 0) - return null; - - foreach (MemberInfo m in get_current_list){ - MethodInfo mi = (MethodInfo) m; - Type [] args; - - args = TypeManager.GetArgumentTypes (mi); - if (args != null && args.Length == 0) - return mi; - } - 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 - // - class ForeachHelperMethods { - public EmitContext ec; - public MethodInfo get_enumerator; - public MethodInfo move_next; - public MethodInfo get_current; - public Type element_type; - public Type enumerator_type; - public bool is_disposable; - - public ForeachHelperMethods (EmitContext ec) - { - this.ec = ec; - this.element_type = TypeManager.object_type; - this.enumerator_type = TypeManager.ienumerator_type; - this.is_disposable = true; - } - } - - static bool GetEnumeratorFilter (MemberInfo m, object criteria) - { - if (m == null) - return false; - - if (!(m is MethodInfo)) - return false; - - if (m.Name != "GetEnumerator") - return false; - - MethodInfo mi = (MethodInfo) m; - Type [] args = TypeManager.GetArgumentTypes (mi); - if (args != null){ - if (args.Length != 0) - return false; - } - ForeachHelperMethods hm = (ForeachHelperMethods) criteria; - - // Check whether GetEnumerator is public - if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) - return false; - - 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 - // CharEnumerator instead. This allows us to avoid the - // try-finally block and the boxing. - // - return false; - - // - // Ok, we can access it, now make sure that we can do something - // with this `GetEnumerator' - // - - Type return_type = mi.ReturnType; - if (mi.ReturnType == TypeManager.ienumerator_type || - TypeManager.ienumerator_type.IsAssignableFrom (return_type) || - (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { - - // - // If it is not an interface, lets try to find the methods ourselves. - // For example, if we have: - // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} - // We can avoid the iface call. This is a runtime perf boost. - // even bigger if we have a ValueType, because we avoid the cost - // of boxing. - // - // We have to make sure that both methods exist for us to take - // this path. If one of the methods does not exist, we will just - // use the interface. Sadly, this complex if statement is the only - // way I could do this without a goto - // - - if (return_type.IsInterface || - (hm.move_next = FetchMethodMoveNext (return_type)) == null || - (hm.get_current = FetchMethodGetCurrent (return_type)) == null) { - - hm.move_next = TypeManager.bool_movenext_void; - hm.get_current = TypeManager.object_getcurrent_void; - return true; - } - - } else { - - // - // Ok, so they dont return an IEnumerable, we will have to - // find if they support the GetEnumerator pattern. - // - - hm.move_next = FetchMethodMoveNext (return_type); - if (hm.move_next == null) - return false; - - hm.get_current = FetchMethodGetCurrent (return_type); - if (hm.get_current == null) - return false; - } - - hm.element_type = hm.get_current.ReturnType; - hm.enumerator_type = return_type; - hm.is_disposable = !hm.enumerator_type.IsSealed || - TypeManager.ImplementsInterface ( - hm.enumerator_type, TypeManager.idisposable_type); - - return true; - } - - /// - /// This filter is used to find the GetEnumerator method - /// on which IEnumerator operates - /// - static MemberFilter FilterEnumerator; - - static Foreach () - { - FilterEnumerator = new MemberFilter (GetEnumeratorFilter); - } - - void error1579 (Type t) - { - Report.Error (1579, loc, - "foreach statement cannot operate on variables of type `" + - t.FullName + "' because that class does not provide a " + - " GetEnumerator method or it is inaccessible"); - } - - static bool TryType (Type t, ForeachHelperMethods hm) - { - MemberList mi; - - mi = TypeContainer.FindMembers (t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.Instance | BindingFlags.DeclaredOnly, - FilterEnumerator, hm); - - if (mi.Count == 0) - return false; - - hm.get_enumerator = (MethodInfo) mi [0]; - return true; - } - - // - // Looks for a usable GetEnumerator in the Type, and if found returns - // the three methods that participate: GetEnumerator, MoveNext and get_Current - // - ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t) - { - ForeachHelperMethods hm = new ForeachHelperMethods (ec); - - for (Type tt = t; tt != null && tt != TypeManager.object_type;){ - if (TryType (tt, hm)) - return hm; - tt = tt.BaseType; - } - - // - // Now try to find the method in the interfaces - // - while (t != null){ - Type [] ifaces = t.GetInterfaces (); - - foreach (Type i in ifaces){ - if (TryType (i, hm)) - return hm; - } - - // - // Since TypeBuilder.GetInterfaces only returns the interface - // types for this type, we have to keep looping, but once - // we hit a non-TypeBuilder (ie, a Type), then we know we are - // done, because it returns all the types - // - if ((t is TypeBuilder)) - t = t.BaseType; - else - break; - } - - return null; - } - - // - // FIXME: possible optimization. - // We might be able to avoid creating `empty' if the type is the sam - // - bool EmitCollectionForeach (EmitContext ec) - { - ILGenerator ig = ec.ig; - - enumerator = new VariableStorage (ec, hm.enumerator_type); - enumerator.EmitThis (ig); - // - // Instantiate the enumerator - // - if (expr.Type.IsValueType){ - IMemoryLocation ml = expr as IMemoryLocation; - // Load the address of the value type. - if (ml == null) { - // This happens if, for example, you have a property - // returning a struct which is IEnumerable - LocalBuilder t = ec.GetTemporaryLocal (expr.Type); - expr.Emit(ec); - ig.Emit (OpCodes.Stloc, t); - ig.Emit (OpCodes.Ldloca, t); - ec.FreeTemporaryLocal (t, expr.Type); - } else { - ml.AddressOf (ec, AddressOp.Load); - } - - // Emit the call. - if (hm.get_enumerator.DeclaringType.IsValueType) { - // the method is declared on the value type - ig.Emit (OpCodes.Call, hm.get_enumerator); - } else { - // it is an interface method, so we must box - ig.Emit (OpCodes.Box, expr.Type); - ig.Emit (OpCodes.Callvirt, hm.get_enumerator); - } - } else { - expr.Emit (ec); - ig.Emit (OpCodes.Callvirt, hm.get_enumerator); - } - 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 && emit_finally) - ig.BeginExceptionBlock (); - - Label end_try = ig.DefineLabel (); - - ig.MarkLabel (ec.LoopBegin); - - enumerator.EmitCall (ig, hm.move_next); - - ig.Emit (OpCodes.Brfalse, end_try); - - if (ec.InIterator) - ig.Emit (OpCodes.Ldarg_0); - - enumerator.EmitCall (ig, hm.get_current); - - if (ec.InIterator){ - conv.Emit (ec); - ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder); - } else - ((IAssignMethod)variable).EmitAssign (ec, conv, false, false); - - statement.Emit (ec); - 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 (); - - 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); - - Label end_finally = ig.DefineLabel (); - ig.Emit (OpCodes.Br, end_finally); - ig.MarkLabel (call_dispose); - ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); - ig.MarkLabel (end_finally); - - if (emit_finally) - ig.Emit (OpCodes.Endfinally); - } - } - - 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 (); - - if (hm != null) - EmitCollectionForeach (ec); - else - array.Emit (ec); - - ec.LoopBegin = old_begin; - ec.LoopEnd = old_end; - } - - protected class TemporaryVariable : Expression - { - FieldBuilder fb; - LocalBuilder local; - - public TemporaryVariable (Type type, Location loc) - { - this.type = type; - this.loc = loc; - eclass = ExprClass.Value; - } - - static int count; - - public override Expression DoResolve (EmitContext ec) - { - if (ec.InIterator) { - count++; - fb = ec.CurrentIterator.MapVariable ( - "s_", count.ToString (), type); - } else - local = ec.ig.DeclareLocal (type); - - return this; - } - - public override void Emit (EmitContext ec) + public void EmitLoadAddress (EmitContext ec) { ILGenerator ig = ec.ig; - if (fb != null) { + if (li.FieldBuilder != null) { ig.Emit (OpCodes.Ldarg_0); - ig.Emit (OpCodes.Ldfld, fb); + ig.Emit (OpCodes.Ldflda, li.FieldBuilder); } else { - ig.Emit (OpCodes.Ldloc, local); + ig.Emit (OpCodes.Ldloca, li.LocalBuilder); } } public void Store (EmitContext ec, Expression right_side) { - if (fb != null) + if (li.FieldBuilder != null) ec.ig.Emit (OpCodes.Ldarg_0); + right_side.Emit (ec); - if (fb == null) - ec.ig.Emit (OpCodes.Stloc, local); - else - ec.ig.Emit (OpCodes.Stfld, fb); + if (li.FieldBuilder != null) { + ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder); + } else { + ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder); + } } - public void EmitThis (ILGenerator ig) + public void EmitThis (EmitContext ec) { - if (fb != null) - ig.Emit (OpCodes.Ldarg_0); + if (li.FieldBuilder != null) { + ec.ig.Emit (OpCodes.Ldarg_0); + } } public void EmitStore (ILGenerator ig) { - if (fb == null) - ig.Emit (OpCodes.Stloc, local); + if (li.FieldBuilder != null) + ig.Emit (OpCodes.Stfld, li.FieldBuilder); else - ig.Emit (OpCodes.Stfld, fb); + ig.Emit (OpCodes.Stloc, li.LocalBuilder); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + EmitLoadAddress (ec); } } @@ -4706,14 +4357,14 @@ namespace Mono.CSharp { public void Initialize (EmitContext ec) { - EmitThis (ec.ig); + EmitThis (ec); ec.ig.Emit (OpCodes.Ldc_I4_0); EmitStore (ec.ig); } public void Increment (EmitContext ec) { - EmitThis (ec.ig); + EmitThis (ec); Emit (ec); ec.ig.Emit (OpCodes.Ldc_I4_1); ec.ig.Emit (OpCodes.Add); @@ -4723,9 +4374,9 @@ namespace Mono.CSharp { protected class ArrayForeach : Statement { - Expression type, variable, expr, conv; + Expression variable, expr, conv; Statement statement; - Type array_type, element_type; + Type array_type; Type var_type; TemporaryVariable[] lengths; ArrayCounter[] counter; @@ -4734,10 +4385,10 @@ namespace Mono.CSharp { TemporaryVariable copy; Expression access; - public ArrayForeach (Expression type, Expression var, + public ArrayForeach (Type var_type, Expression var, Expression expr, Statement stmt, Location l) { - this.type = type; + this.var_type = var_type; this.variable = var; this.expr = expr; statement = stmt; @@ -4745,15 +4396,8 @@ namespace Mono.CSharp { } public override bool Resolve (EmitContext ec) - { - TypeExpr texpr = type.ResolveAsTypeTerminal (ec); - if (texpr == null) - return false; - - var_type = texpr.Type; - + { array_type = expr.Type; - element_type = TypeManager.GetElementType (array_type); rank = array_type.GetArrayRank (); copy = new TemporaryVariable (array_type, loc); @@ -4811,7 +4455,7 @@ namespace Mono.CSharp { test [i] = ig.DefineLabel (); loop [i] = ig.DefineLabel (); - lengths [i].EmitThis (ig); + lengths [i].EmitThis (ec); ((ArrayAccess) access).EmitGetLength (ec, i); lengths [i].EmitStore (ig); } @@ -4841,5 +4485,413 @@ namespace Mono.CSharp { ig.MarkLabel (ec.LoopEnd); } } + + protected class CollectionForeach : ExceptionStatement + { + Expression variable, expr; + Statement statement; + + TemporaryVariable enumerator; + Expression init; + Statement loop; + + MethodGroupExpr get_enumerator; + PropertyExpr get_current; + MethodInfo move_next; + Type var_type, enumerator_type; + bool is_disposable; + + public CollectionForeach (Type var_type, Expression var, + Expression expr, Statement stmt, Location l) + { + this.var_type = var_type; + this.variable = var; + this.expr = expr; + statement = stmt; + loc = l; + } + + bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi) + { + Type [] args = TypeManager.GetArgumentTypes (mi); + if (args != null){ + if (args.Length != 0) + return false; + } + + if (TypeManager.IsOverride (mi)) + return false; + + // Check whether GetEnumerator is public + if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) + return false; + + 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 + // CharEnumerator instead. This allows us to avoid the + // try-finally block and the boxing. + // + return false; + + // + // Ok, we can access it, now make sure that we can do something + // with this `GetEnumerator' + // + + Type return_type = mi.ReturnType; + if (mi.ReturnType == TypeManager.ienumerator_type || + TypeManager.ienumerator_type.IsAssignableFrom (return_type) || + (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { + // + // If it is not an interface, lets try to find the methods ourselves. + // For example, if we have: + // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} + // We can avoid the iface call. This is a runtime perf boost. + // even bigger if we have a ValueType, because we avoid the cost + // of boxing. + // + // We have to make sure that both methods exist for us to take + // this path. If one of the methods does not exist, we will just + // use the interface. Sadly, this complex if statement is the only + // way I could do this without a goto + // + + if (return_type.IsInterface || + !FetchMoveNext (ec, return_type) || + !FetchGetCurrent (ec, return_type)) { + move_next = TypeManager.bool_movenext_void; + get_current = new PropertyExpr ( + ec, TypeManager.ienumerator_getcurrent, loc); + return true; + } + } else { + // + // Ok, so they dont return an IEnumerable, we will have to + // find if they support the GetEnumerator pattern. + // + + if (!FetchMoveNext (ec, return_type)) + return false; + + if (!FetchGetCurrent (ec, return_type)) + return false; + } + + enumerator_type = return_type; + is_disposable = !enumerator_type.IsSealed || + TypeManager.ImplementsInterface ( + enumerator_type, TypeManager.idisposable_type); + + return true; + } + + // + // Retrieves a `public bool MoveNext ()' method from the Type `t' + // + bool FetchMoveNext (EmitContext ec, Type t) + { + MemberList move_next_list; + + move_next_list = TypeContainer.FindMembers ( + t, MemberTypes.Method, + BindingFlags.Public | BindingFlags.Instance, + Type.FilterName, "MoveNext"); + if (move_next_list.Count == 0) + return false; + + foreach (MemberInfo m in move_next_list){ + MethodInfo mi = (MethodInfo) m; + Type [] args; + + args = TypeManager.GetArgumentTypes (mi); + if ((args != null) && (args.Length == 0) && + TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) { + move_next = mi; + return true; + } + } + + return false; + } + + // + // Retrieves a `public T get_Current ()' method from the Type `t' + // + bool FetchGetCurrent (EmitContext ec, Type t) + { + PropertyExpr pe = Expression.MemberLookup ( + ec, t, "Current", MemberTypes.Property, + Expression.AllBindingFlags, loc) as PropertyExpr; + if (pe == null) + return false; + + get_current = pe; + return true; + } + + // + // 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; + } + + static public void error1579 (Type t, Location loc) + { + Report.Error (1579, loc, "foreach statement cannot operate on " + + "variables of type `{0}' because that class does " + + "not provide a GetEnumerator method or it is " + + "inaccessible", t.FullName); + } + + bool TryType (EmitContext ec, Type t) + { + MethodGroupExpr mg = Expression.MemberLookup ( + ec, t, "GetEnumerator", MemberTypes.Method, + Expression.AllBindingFlags, loc) as MethodGroupExpr; + if (mg == null) + return false; + + foreach (MethodBase mb in mg.Methods) { + if (!GetEnumeratorFilter (ec, (MethodInfo) mb)) + continue; + + MethodInfo[] mi = new MethodInfo[] { (MethodInfo) mb }; + get_enumerator = new MethodGroupExpr (mi, loc); + + if (t != expr.Type) { + expr = Convert.ExplicitConversion ( + ec, expr, t, loc); + if (expr == null) + throw new InternalErrorException (); + } + + get_enumerator.InstanceExpression = expr; + get_enumerator.IsBase = t != expr.Type; + + return true; + } + + return false; + } + + bool ProbeCollectionType (EmitContext ec, Type t) + { + for (Type tt = t; tt != null && tt != TypeManager.object_type;){ + if (TryType (ec, tt)) + return true; + tt = tt.BaseType; + } + + // + // Now try to find the method in the interfaces + // + while (t != null){ + Type [] ifaces = t.GetInterfaces (); + + foreach (Type i in ifaces){ + if (TryType (ec, i)) + return true; + } + + // + // Since TypeBuilder.GetInterfaces only returns the interface + // types for this type, we have to keep looping, but once + // we hit a non-TypeBuilder (ie, a Type), then we know we are + // done, because it returns all the types + // + if ((t is TypeBuilder)) + t = t.BaseType; + else + break; + } + + return false; + } + + public override bool Resolve (EmitContext ec) + { + enumerator_type = TypeManager.ienumerator_type; + is_disposable = true; + + if (!ProbeCollectionType (ec, expr.Type)) { + error1579 (expr.Type, loc); + return false; + } + + enumerator = new TemporaryVariable (enumerator_type, loc); + enumerator.Resolve (ec); + + init = new Invocation (get_enumerator, new ArrayList (), loc); + init = init.Resolve (ec); + if (init == null) + return false; + + Expression move_next_expr; + { + MemberInfo[] mi = new MemberInfo[] { move_next }; + MethodGroupExpr mg = new MethodGroupExpr (mi, loc); + mg.InstanceExpression = enumerator; + + move_next_expr = new Invocation (mg, new ArrayList (), loc); + } + + get_current.InstanceExpression = enumerator; + + Statement block = new CollectionForeachStatement ( + var_type, variable, get_current, statement, loc); + + loop = new While (move_next_expr, block, loc); + + bool ok = true; + + ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); + ec.CurrentBranching.CreateSibling (); + + FlowBranchingException branching = null; + if (is_disposable) + branching = ec.StartFlowBranching (this); + + if (!loop.Resolve (ec)) + ok = false; + + if (is_disposable) { + ResolveFinally (branching); + ec.EndFlowBranching (); + } else + emit_finally = true; + + ec.EndFlowBranching (); + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + enumerator.Store (ec, init); + + // + // 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); + + // + // Now the finally block + // + if (is_disposable) { + DoEmitFinally (ec); + if (emit_finally) + ig.EndExceptionBlock (); + } + } + + + public override void EmitFinally (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (enumerator_type.IsValueType) { + enumerator.Emit (ec); + + MethodInfo mi = FetchMethodDispose (enumerator_type); + if (mi != null) { + enumerator.EmitLoadAddress (ec); + ig.Emit (OpCodes.Call, mi); + } else { + enumerator.Emit (ec); + ig.Emit (OpCodes.Box, enumerator_type); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + } + } else { + Label call_dispose = ig.DefineLabel (); + + 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); + + ig.MarkLabel (call_dispose); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (end_finally); + } + } + } + + protected class CollectionForeachStatement : Statement + { + Type type; + Expression variable, current, conv; + Statement statement; + Assign assign; + + public CollectionForeachStatement (Type type, Expression variable, + Expression current, Statement statement, + Location loc) + { + this.type = type; + this.variable = variable; + this.current = current; + this.statement = statement; + this.loc = loc; + } + + public override bool Resolve (EmitContext ec) + { + current = current.Resolve (ec); + if (current == null) + return false; + + conv = Convert.ExplicitConversion (ec, current, type, loc); + if (conv == null) + return false; + + assign = new Assign (variable, conv, loc); + if (assign.Resolve (ec) == null) + return false; + + if (!statement.Resolve (ec)) + return false; + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + assign.EmitStatement (ec); + statement.Emit (ec); + } + } } }