private void GenerateArrayMemberBlock(MemberToken memberToken, IMethodBlock memberAccess, Method.BlockCollection block) { var elementTypeToken = memberToken.TypeToken.GetElementTypeToken(); var arraySize = GetCardinality(memberToken); var arraySizeExpression = Expr.Constant(arraySize); // Give the generator a chance to create the array var arrayInitializer = CreateArrayInitializer(memberToken, memberAccess); var defaultArrayInitializer = arrayInitializer == default(IMethodBlock); if (defaultArrayInitializer) // Otherwise just assign new T[N]; { arrayInitializer = elementTypeToken.NewArrayBounds(arraySizeExpression).ToMethodBlock(); } if (arrayInitializer != null) { block.Children.Add(new Method.Assignment(memberAccess, arrayInitializer)); // If the generator provided a specialized initializer, exit now. if (!defaultArrayInitializer) { return; } } var loopUnrollingNeeded = OnLoopGenerationStart(memberToken) != UnrollingMode.Never; if (!loopUnrollingNeeded) // Fast path to reduce allocations { arraySize = 1; } var elementAccesses = ArrayPool <Method.DelegatedArrayAccess> .Shared.Rent(arraySize); var loopBodyBlocks = ArrayPool <Method.BlockCollection> .Shared.Rent(arraySize); for (var i = 0; i < arraySize; ++i) { elementAccesses[i] = new Method.DelegatedArrayAccess(memberAccess); loopBodyBlocks[i] = new Method.BlockCollection(); // Classes need a public default ctor if (elementTypeToken.IsClass && elementTypeToken != typeof(string)) { var newElementBlock = elementTypeToken.NewExpression().ToMethodBlock(); loopBodyBlocks[i].Children.Add(new Method.Assignment(elementAccesses[i], newElementBlock)); } GenerateValueMemberBlock(elementTypeToken, elementAccesses[i], loopBodyBlocks[i]); // If the implementation decides we never need to unroll this loop, save ourselves the trouble var unrollingMode = OnLoopGenerationIteration(i, memberToken); if (unrollingMode == UnrollingMode.Never) { break; } // If unrolling state was decided to be unknown, we determine if we needed to unroll // This is technically legacy code if (unrollingMode == UnrollingMode.Unknown) { if (i > 0 && !loopUnrollingNeeded) { loopUnrollingNeeded = !loopBodyBlocks[i].Equals(loopBodyBlocks[0]); } } } OnLoopGenerationEnd(memberToken); // If all the invocation bodies are equal, we can just loop. Otherwise, we need to unroll it. if (loopUnrollingNeeded) { for (var i = 0; i < arraySize; ++i) { elementAccesses[i].Index = Expr.Constant(i).ToMethodBlock(); foreach (var variable in loopBodyBlocks[i].Variables) { block.Variables.Add(variable); } block.Children.AddRange(loopBodyBlocks[i].Children); } } else { var iterationCounter = _iteratorProvider.Rent(); elementAccesses[0].Index = iterationCounter; block.Variables.Add(iterationCounter); var arraySizeBlock = arraySizeExpression.ToMethodBlock(); block.Children.Add(new Method.Assignment(iterationCounter, Expr.Constant(0).ToMethodBlock())); block.Children.Add(new Method.Loop(iterationCounter, arraySizeBlock, loopBodyBlocks[0])); _iteratorProvider.Return(iterationCounter); } ArrayPool <Method.DelegatedArrayAccess> .Shared.Return(elementAccesses); ArrayPool <Method.BlockCollection> .Shared.Return(loopBodyBlocks); }