+
+ 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);
+ }
+ }