bool BodyHasSingleGetCurrent(Block body, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out bool needsUninlining, out ILVariable existingVariable)
        {
            singleGetter     = null;
            needsUninlining  = false;
            existingVariable = null;
            var loads = (enumerator.LoadInstructions.OfType <ILInstruction>().Concat(enumerator.AddressInstructions.OfType <ILInstruction>())).Where(ld => !ld.IsDescendantOf(moveNextUsage)).ToArray();

            if (loads.Length == 1 && ParentIsCurrentGetter(loads[0]))
            {
                singleGetter = (CallInstruction)loads[0].Parent;
                ILInstruction inst = singleGetter;
                while (inst.Parent is UnboxAny || inst.Parent is CastClass)
                {
                    inst = inst.Parent;
                }
                needsUninlining = !inst.Parent.MatchStLoc(out existingVariable);
            }
            return(singleGetter != null && singleGetter.IsDescendantOf(body.Instructions[0]) && ILInlining.CanUninline(singleGetter, body.Instructions[0]));
        }
Beispiel #2
0
        /// <summary>
        /// Determines whether <paramref name="enumerator"/> is only used once inside <paramref name="loopBody"/> for accessing the Current property.
        /// </summary>
        /// <param name="usingContainer">The using body container. This is only used for variable usage checks.</param>
        /// <param name="loopBody">The loop body. The first statement of this block is analyzed.</param>
        /// <param name="enumerator">The current enumerator.</param>
        /// <param name="moveNextUsage">The call MoveNext(ldloc enumerator) pattern.</param>
        /// <param name="singleGetter">Returns the call instruction invoking Current's getter.</param>
        /// <param name="foreachVariable">Returns the the foreach variable, if a suitable was found. This variable is only assigned once and its assignment is the first statement in <paramref name="loopBody"/>.</param>
        /// <returns><see cref="RequiredGetCurrentTransformation"/> for details.</returns>
        RequiredGetCurrentTransformation DetectGetCurrentTransformation(BlockContainer usingContainer, Block loopBody, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out ILVariable foreachVariable)
        {
            singleGetter    = null;
            foreachVariable = null;
            var loads = (enumerator.LoadInstructions.OfType <ILInstruction>().Concat(enumerator.AddressInstructions.OfType <ILInstruction>())).Where(ld => !ld.IsDescendantOf(moveNextUsage)).ToArray();

            // enumerator is used in multiple locations or not in conjunction with get_Current
            // => no foreach
            if (loads.Length != 1 || !ParentIsCurrentGetter(loads[0]))
            {
                return(RequiredGetCurrentTransformation.NoForeach);
            }
            singleGetter = (CallInstruction)loads[0].Parent;
            // singleGetter is not part of the first instruction in body or cannot be uninlined
            // => no foreach
            if (!(singleGetter.IsDescendantOf(loopBody.Instructions[0]) && ILInlining.CanUninline(singleGetter, loopBody.Instructions[0])))
            {
                return(RequiredGetCurrentTransformation.NoForeach);
            }
            ILInstruction inst = singleGetter;

            // in some cases, i.e. foreach variable with explicit type different from the collection-item-type,
            // the result of call get_Current is casted.
            while (inst.Parent is UnboxAny || inst.Parent is CastClass)
            {
                inst = inst.Parent;
            }
            // One variable was found.
            if (inst.Parent is StLoc stloc)
            {
                // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once.
                if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer))
                {
                    foreachVariable = stloc.Variable;
                    return(RequiredGetCurrentTransformation.UseExistingVariable);
                }
            }
            // No suitable variable was found: we need a new one.
            return(RequiredGetCurrentTransformation.IntroduceNewVariable);
        }