/// <summary>
        /// Matches Roslyn C# switch on nullable.
        /// </summary>
        bool MatchRoslynSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
        {
            newSwitch = null;
            // match first block:
            // if (logic.not(call get_HasValue(target))) br nullCaseBlock
            // br switchBlock
            if (!instructions[i].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
            {
                return(false);
            }
            if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction target) || !SemanticHelper.IsPure(target.Flags))
            {
                return(false);
            }
            // match second block: switchBlock
            // note: I have seen cases where switchVar is inlined into the switch.
            // stloc switchVar(call GetValueOrDefault(ldloca tmp))
            // switch (ldloc switchVar) {
            //  case [0..1): br caseBlock1
            // ... more cases ...
            //  case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
            // }
            if (switchBlock.IncomingEdgeCount != 1)
            {
                return(false);
            }
            SwitchInstruction switchInst;

            switch (switchBlock.Instructions.Count)
            {
            case 2: {
                // this is the normal case described by the pattern above
                if (!switchBlock.Instructions[0].MatchStLoc(out var switchVar, out var getValueOrDefault))
                {
                    return(false);
                }
                if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1)
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                if (!(switchBlock.Instructions[1] is SwitchInstruction si))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            case 1: {
                // this is the special case where `call GetValueOrDefault(ldloca tmp)` is inlined into the switch.
                if (!(switchBlock.Instructions[0] is SwitchInstruction si))
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchGetValueOrDefault(si.Value, out ILInstruction target2) && target2.Match(target).Success))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            default: {
                return(false);
            }
            }
            ILInstruction switchValue;

            if (target.MatchLdLoca(out var v))
            {
                switchValue = new LdLoc(v).WithILRange(target);
            }
            else
            {
                switchValue = new LdObj(target, ((CallInstruction)getHasValue).Method.DeclaringType);
            }
            newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue);
            return(true);
        }
 /// <summary>
 /// Matches legacy C# switch on nullable.
 /// </summary>
 bool MatchSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
 {
     newSwitch = null;
     // match first block:
     // stloc tmp(ldloca switchValueVar)
     // stloc switchVariable(call GetValueOrDefault(ldloc tmp))
     // if (logic.not(call get_HasValue(ldloc tmp))) br nullCaseBlock
     // br switchBlock
     if (i < 2)
     {
         return(false);
     }
     if (!instructions[i - 2].MatchStLoc(out var tmp, out var ldloca) ||
         !instructions[i - 1].MatchStLoc(out var switchVariable, out var getValueOrDefault) ||
         !instructions[i].MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     if (!tmp.IsSingleDefinition || tmp.LoadCount != 2)
     {
         return(false);
     }
     if (!switchVariable.IsSingleDefinition || switchVariable.LoadCount != 1)
     {
         return(false);
     }
     if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
     {
         return(false);
     }
     if (!ldloca.MatchLdLoca(out var switchValueVar))
     {
         return(false);
     }
     if (!condition.MatchLogicNot(out var getHasValue))
     {
         return(false);
     }
     if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction getValueOrDefaultArg))
     {
         return(false);
     }
     if (!NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction getHasValueArg))
     {
         return(false);
     }
     if (!(getHasValueArg.MatchLdLoc(tmp) && getValueOrDefaultArg.MatchLdLoc(tmp)))
     {
         return(false);
     }
     // match second block: switchBlock
     // switch (ldloc switchVariable) {
     //  case [0..1): br caseBlock1
     //  ... more cases ...
     //  case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
     // }
     if (switchBlock.Instructions.Count != 1 || switchBlock.IncomingEdgeCount != 1)
     {
         return(false);
     }
     if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst))
     {
         return(false);
     }
     newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, new LdLoc(switchValueVar));
     return(true);
 }
Example #3
0
        bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck)
        {
            numObjVarLoadsInCheck = 2;
            CallVirt callVirt;

            if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT))
            {
                if (checkInst.MatchIfInstruction(out var condition, out var disposeInst))
                {
                    if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar))
                    {
                        return(false);
                    }
                    if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
                    {
                        return(false);
                    }
                    callVirt = disposeBlock.Instructions[0] as CallVirt;
                }
                else if (checkInst.MatchNullableRewrap(out disposeInst))
                {
                    callVirt = disposeInst as CallVirt;
                }
                else
                {
                    return(false);
                }
                if (callVirt == null)
                {
                    return(false);
                }
                if (callVirt.Method.FullName != "System.IDisposable.Dispose")
                {
                    return(false);
                }
                if (callVirt.Method.Parameters.Count > 0)
                {
                    return(false);
                }
                if (callVirt.Arguments.Count != 1)
                {
                    return(false);
                }
                var firstArg = callVirt.Arguments.FirstOrDefault();
                if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable)))
                {
                    if (!firstArg.MatchAddressOf(out var innerArg2))
                    {
                        return(false);
                    }
                    return(NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) ||
                           (innerArg2 is NullableUnwrap unwrap &&
                            unwrap.Argument.MatchLdLoc(objVar)));
                }
                else
                {
                    if (!(innerArg1.MatchBox(out firstArg, out var boxType) && boxType.IsKnownType(KnownTypeCode.NullableOfT) &&
                          NullableType.GetUnderlyingType(boxType).Equals(NullableType.GetUnderlyingType(objVar.Type))))
                    {
                        return(false);
                    }
                    return(firstArg.MatchLdLoc(objVar));
                }
            }