示例#1
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms.
        /// </remarks>
        internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context)
        {
            // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
            // ==> compound.op.new(callvirt get_Property(ldloc S_1), value)
            var setterValue   = setterCall.Arguments.LastOrDefault();
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
                // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value))
                setterValue = storeInSetter.Value;
            }
            setterValue = UnwrapSmallIntegerConv(setterValue, out var conv);
            if (!(setterValue is BinaryNumericInstruction binary))
            {
                return(false);
            }
            var getterCall = binary.Left as CallInstruction;

            if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
            {
                return(false);
            }
            IType targetType = getterCall.Method.ReturnType;

            if (!ValidateCompoundAssign(binary, conv, targetType))
            {
                return(false);
            }
            if (storeInSetter != null && storeInSetter.Variable.Type.IsSmallIntegerType())
            {
                // 'stloc v' implicitly truncates.
                // Ensure that type of 'v' must match type of the property:
                if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize())
                {
                    return(false);
                }
                if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign())
                {
                    return(false);
                }
            }
            context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
            ILInstruction newInst = new CompoundAssignmentInstruction(
                binary, getterCall, binary.Right,
                getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue);

            if (storeInSetter != null)
            {
                storeInSetter.Value = newInst;
                newInst             = storeInSetter;
                context.RequestRerun();                 // moving stloc to top-level might trigger inlining
            }
            setterCall.ReplaceWith(newInst);
            return(true);
        }
示例#2
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms.
        /// </remarks>
        internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context)
        {
            // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
            // ==> compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value)
            var setterValue   = setterCall.Arguments.LastOrDefault();
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
                // ==> stloc v(compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value))
                setterValue = storeInSetter.Value;
            }
            if (!(setterValue is BinaryNumericInstruction binary))
            {
                return(false);
            }
            var getterCall = binary.Left as CallInstruction;

            if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
            {
                return(false);
            }
            if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
            {
                return(false);
            }
            context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
            ILInstruction newInst = new CompoundAssignmentInstruction(
                binary, getterCall, binary.Right,
                getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue);

            if (storeInSetter != null)
            {
                storeInSetter.Value = newInst;
                newInst             = storeInSetter;
                context.RequestRerun();                 // moving stloc to top-level might trigger inlining
            }
            setterCall.ReplaceWith(newInst);
            return(true);
        }
示例#3
0
        void Run(CallInstruction inst, ILTransformContext context)
        {
            if (inst.Method.IsStatic)
            {
                return;
            }
            if (inst.Method.MetadataToken.IsNil || inst.Method.MetadataToken.Kind != HandleKind.MethodDefinition)
            {
                return;
            }
            var handle = (MethodDefinitionHandle)inst.Method.MetadataToken;

            if (!IsDefinedInCurrentOrOuterClass(inst.Method, context.Function.Method.DeclaringTypeDefinition))
            {
                return;
            }
            if (!inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
            {
                return;
            }
            var metadata = context.PEFile.Metadata;
            MethodDefinition methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)inst.Method.MetadataToken);

            if (!methodDef.HasBody())
            {
                return;
            }
            var genericContext = DelegateConstruction.GenericContextFromTypeArguments(inst.Method.Substitution);

            if (genericContext == null)
            {
                return;
            }
            // partially copied from CSharpDecompiler
            var ilReader         = context.CreateILReader();
            var body             = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
            var proxyFunction    = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken);
            var transformContext = new ILTransformContext(context, proxyFunction);

            proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext);
            if (!(proxyFunction.Body is BlockContainer blockContainer))
            {
                return;
            }
            if (blockContainer.Blocks.Count != 1)
            {
                return;
            }
            var           block = blockContainer.Blocks[0];
            Call          call;
            ILInstruction returnValue;

            switch (block.Instructions.Count)
            {
            case 1:
                // leave IL_0000 (call Test(ldloc this, ldloc A_1))
                if (!block.Instructions[0].MatchLeave(blockContainer, out returnValue))
                {
                    return;
                }
                call = returnValue as Call;
                break;

            case 2:
                // call Test(ldloc this, ldloc A_1)
                // leave IL_0000(nop)
                call = block.Instructions[0] as Call;
                if (!block.Instructions[1].MatchLeave(blockContainer, out returnValue))
                {
                    return;
                }
                if (!returnValue.MatchNop())
                {
                    return;
                }
                break;

            default:
                return;
            }
            if (call == null || call.Method.IsConstructor)
            {
                return;
            }
            if (call.Method.IsStatic || call.Method.Parameters.Count != inst.Method.Parameters.Count)
            {
                return;
            }
            // check if original arguments are only correct ldloc calls
            for (int i = 0; i < call.Arguments.Count; i++)
            {
                var originalArg = call.Arguments[i];
                if (!originalArg.MatchLdLoc(out ILVariable var) ||
                    var.Kind != VariableKind.Parameter ||
                    var.Index != i - 1)
                {
                    return;
                }
            }
            context.Step("Replace proxy: " + inst.Method.Name + " with " + call.Method.Name, inst);
            Call newInst = (Call)call.Clone();

            newInst.Arguments.ReplaceList(inst.Arguments);
            inst.ReplaceWith(newInst);
        }