Esempio n. 1
0
        /// <summary>
        /// stloc v(value)
        /// if (logic.not(call get_HasValue(ldloca v))) throw(...)
        /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ...
        /// =>
        /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
        /// </summary>
        bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
        {
            if (pos + 2 >= block.Instructions.Count)
            {
                return(false);
            }
            if (!(block.Instructions[pos] is StLoc stloc))
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
            {
                return(false);
            }
            if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (!(Block.Unwrap(trueInst) is Throw throwInst))
            {
                return(false);
            }
            if (!condition.MatchLogicNot(out var arg))
            {
                return(false);
            }
            if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v)))
            {
                return(false);
            }
            var throwInstParent         = throwInst.Parent;
            var throwInstChildIndex     = throwInst.ChildIndex;
            var nullCoalescingWithThrow = new NullCoalescingInstruction(
                NullCoalescingKind.NullableWithValueFallback,
                stloc.Value,
                throwInst);
            var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType();

            nullCoalescingWithThrow.UnderlyingResultType = resultType;
            var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None);

            if (result.Type == ILInlining.FindResultType.Found &&
                NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v))
            {
                context.Step("NullCoalescingTransform (value types + throw expression)", stloc);
                throwInst.resultType = resultType;
                result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow);
                block.Instructions.RemoveRange(pos, 2);                 // remove store(s) and if instruction
                return(true);
            }
            else
            {
                // reset the primary position (see remarks on ILInstruction.Parent)
                stloc.Value = stloc.Value;
                var children = throwInstParent.Children;
                children[throwInstChildIndex] = throwInst;
                return(false);
            }
        }
 /// <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);
 }
        /// <summary>
        /// Matches Roslyn C# switch on nullable.
        /// </summary>
        bool MatchRoslynSwitchOnNullable(InstructionCollection <ILInstruction> instructions, int i, out SwitchInstruction newSwitch)
        {
            newSwitch = null;
            // match first block:
            // stloc tmp(ldloc switchValueVar)
            // if (logic.not(call get_HasValue(ldloca tmp))) br nullCaseBlock
            // br switchBlock
            if (i < 1)
            {
                return(false);
            }
            if (!instructions[i - 1].MatchStLoc(out var tmp, out var switchValue) ||
                !instructions[i].MatchIfInstruction(out var condition, out var trueInst))
            {
                return(false);
            }
            if (tmp.StoreCount != 1 || tmp.AddressCount != 2 || tmp.LoadCount != 0)
            {
                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 ILVariable target1) || target1 != tmp)
            {
                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, tmp))
                {
                    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, tmp))
                {
                    return(false);
                }
                switchInst = si;
                break;
            }

            default: {
                return(false);
            }
            }
            newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue);
            return(true);
        }
        /// <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);
        }
Esempio n. 5
0
        bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull)
        {
            var entryPoint = container.EntryPoint;

            if (entryPoint.Instructions.Count < 2 || entryPoint.Instructions.Count > 3 || entryPoint.IncomingEdgeCount != 1)
            {
                return(false);
            }
            int  leaveIndex  = entryPoint.Instructions.Count == 2 ? 1 : 2;
            int  checkIndex  = entryPoint.Instructions.Count == 2 ? 0 : 1;
            int  castIndex   = entryPoint.Instructions.Count == 3 ? 0 : -1;
            bool isReference = objVar.Type.IsReferenceType != false;

            if (castIndex > -1)
            {
                if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst))
                {
                    return(false);
                }
                if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(KnownTypeCode.IDisposable))
                {
                    return(false);
                }
                if (tempVar.StoreCount != 1 || tempVar.LoadCount != 2)
                {
                    return(false);
                }
                objVar      = tempVar;
                isReference = true;
            }
            if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop())
            {
                return(false);
            }
            CallVirt callVirt;

            if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT))
            {
                if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst))
                {
                    return(false);
                }
                if (!(NullableLiftingTransform.MatchHasValueCall(condition, out var v) && v == objVar))
                {
                    return(false);
                }
                if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1)
                {
                    return(false);
                }
                if (!(disposeBlock.Instructions[0] is CallVirt cv))
                {
                    return(false);
                }
                callVirt = cv;
                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 = cv.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));
                }
                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));
                }
            }
Esempio n. 6
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));
                }
            }