public override void VisitForeachStatement(ForeachStatement foreachStatement) { var ferr = (ForEachResolveResult)_resolver.Resolve(foreachStatement); var iterator = (LocalResolveResult)_resolver.Resolve(foreachStatement.VariableNameToken); var getEnumeratorMethod = (ferr.GetEnumeratorCall is InvocationResolveResult ? ((InvocationResolveResult)ferr.GetEnumeratorCall).Member as IMethod : null); var systemArray = _compilation.FindType(KnownTypeCode.Array); var inExpression = ResolveWithConversion(foreachStatement.InExpression); if (Equals(inExpression.Type, systemArray) || inExpression.Type.DirectBaseTypes.Contains(systemArray) || (getEnumeratorMethod != null && _metadataImporter.GetMethodSemantics(getEnumeratorMethod).EnumerateAsArray)) { var arrayResult = CompileExpression(foreachStatement.InExpression, CompileExpressionFlags.ReturnValueIsImportant); _result.AddRange(arrayResult.AdditionalStatements); var array = arrayResult.Expression; if (IsJsExpressionComplexEnoughToGetATemporaryVariable.Analyze(array)) { var tmpArray = CreateTemporaryVariable(ferr.CollectionType, foreachStatement.GetRegion()); _result.Add(JsStatement.Var(_variables[tmpArray].Name, array)); array = JsExpression.Identifier(_variables[tmpArray].Name); } var length = systemArray.GetProperties().SingleOrDefault(p => p.Name == "Length"); if (length == null) { _errorReporter.InternalError("Property Array.Length not found."); return; } var lengthSem = _metadataImporter.GetPropertySemantics(length); if (lengthSem.Type != PropertyScriptSemantics.ImplType.Field) { _errorReporter.InternalError("Property Array.Length is not implemented as a field."); return; } var index = CreateTemporaryVariable(_compilation.FindType(KnownTypeCode.Int32), foreachStatement.GetRegion()); var jsIndex = JsExpression.Identifier(_variables[index].Name); JsExpression iteratorValue = MaybeCloneValueType(JsExpression.Index(array, jsIndex), null, ferr.ElementType); if (_variables[iterator.Variable].UseByRefSemantics) iteratorValue = JsExpression.ObjectLiteral(new JsObjectLiteralProperty("$", iteratorValue)); var body = new[] { JsStatement.Var(_variables[iterator.Variable].Name, iteratorValue) } .Concat(CreateInnerCompiler().Compile(foreachStatement.EmbeddedStatement).Statements); _result.Add(JsStatement.For(JsStatement.Var(_variables[index].Name, JsExpression.Number(0)), JsExpression.Lesser(jsIndex, JsExpression.Member(array, lengthSem.FieldName)), JsExpression.PostfixPlusPlus(jsIndex), JsStatement.Block(body))); } else { var getEnumeratorCall = _expressionCompiler.Compile(ferr.GetEnumeratorCall, true); _result.AddRange(getEnumeratorCall.AdditionalStatements); var enumerator = CreateTemporaryVariable(ferr.EnumeratorType, foreachStatement.GetRegion()); _result.Add(JsStatement.Var(_variables[enumerator].Name, getEnumeratorCall.Expression)); var moveNextInvocation = _expressionCompiler.Compile(new CSharpInvocationResolveResult(new LocalResolveResult(enumerator), ferr.MoveNextMethod, new ResolveResult[0]), true); if (moveNextInvocation.AdditionalStatements.Count > 0) _errorReporter.InternalError("MoveNext() invocation is not allowed to require additional statements."); var getCurrent = _expressionCompiler.Compile(new MemberResolveResult(new LocalResolveResult(enumerator), ferr.CurrentProperty), true); JsExpression getCurrentValue = MaybeCloneValueType(getCurrent.Expression, null, ferr.ElementType); if (_variables[iterator.Variable].UseByRefSemantics) getCurrentValue = JsExpression.ObjectLiteral(new JsObjectLiteralProperty("$", getCurrentValue)); var preBody = getCurrent.AdditionalStatements.Concat(new[] { JsStatement.Var(_variables[iterator.Variable].Name, getCurrentValue) }).ToList(); var body = CreateInnerCompiler().Compile(foreachStatement.EmbeddedStatement); body = JsStatement.Block(preBody.Concat(body.Statements)); JsStatement disposer; var systemIDisposable = _compilation.FindType(KnownTypeCode.IDisposable); var disposeMethod = systemIDisposable.GetMethods().Single(m => m.Name == "Dispose"); var conversions = CSharpConversions.Get(_compilation); var disposableConversion = conversions.ImplicitConversion(enumerator.Type, systemIDisposable); if (disposableConversion.IsValid) { // If the enumerator is implicitly convertible to IDisposable, we should dispose it. var compileResult = _expressionCompiler.Compile(new CSharpInvocationResolveResult(new ConversionResolveResult(systemIDisposable, new LocalResolveResult(enumerator), disposableConversion), disposeMethod, new ResolveResult[0]), false); if (compileResult.AdditionalStatements.Count != 0) _errorReporter.InternalError("Call to IDisposable.Dispose must not return additional statements."); disposer = compileResult.Expression; } else if (enumerator.Type.GetDefinition().IsSealed) { // If the enumerator is sealed and not implicitly convertible to IDisposable, we need not dispose it. disposer = null; } else { // We don't know whether the enumerator is convertible to IDisposable, so we need to conditionally dispose it. var test = _expressionCompiler.Compile(new TypeIsResolveResult(new LocalResolveResult(enumerator), systemIDisposable, _compilation.FindType(KnownTypeCode.Boolean)), true); if (test.AdditionalStatements.Count > 0) _errorReporter.InternalError("\"is\" test must not return additional statements."); var innerStatements = _expressionCompiler.Compile(new CSharpInvocationResolveResult(new ConversionResolveResult(systemIDisposable, new LocalResolveResult(enumerator), conversions.ExplicitConversion(enumerator.Type, systemIDisposable)), disposeMethod, new ResolveResult[0]), false); disposer = JsStatement.If(test.Expression, JsStatement.Block(innerStatements.AdditionalStatements.Concat(new JsStatement[] { innerStatements.Expression })), null); } JsStatement stmt = JsStatement.While(moveNextInvocation.Expression, body); if (disposer != null) stmt = JsStatement.Try(stmt, null, disposer); _result.Add(stmt); } }