Example #1
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     try {
         if (this.context != null)
         {
             throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
         }
         this.context = context;
         var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
         // Traverse nested functions in post-order:
         // Inner functions are transformed before outer functions
         foreach (var f in function.Descendants.OfType <ILFunction>())
         {
             foreach (var v in f.Variables.ToArray())
             {
                 if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f))
                 {
                     continue;
                 }
                 if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst))
                 {
                     if (!CanRemoveAllReferencesTo(context, v))
                     {
                         continue;
                     }
                     if (inst is StObj || inst is StLoc)
                     {
                         instructionsToRemove.Add(inst);
                     }
                     AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
                     continue;
                 }
                 if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext))
                 {
                     AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
                     continue;
                 }
                 AnalyzeUseSites(v);
             }
         }
         VisitILFunction(function);
         if (instructionsToRemove.Count > 0)
         {
             context.Step($"Remove instructions", function);
             foreach (var store in instructionsToRemove)
             {
                 if (store.Parent is Block containingBlock)
                 {
                     containingBlock.Instructions.Remove(store);
                 }
             }
         }
         foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions))
         {
             RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context);
         }
     } finally {
         instructionsToRemove.Clear();
         displayClasses.Clear();
         fieldAssignmentsWithVariableValue.Clear();
         this.context = null;
     }
 }
Example #2
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
        /// </summary>
        public static bool InlineOneIfPossible(Block block, int pos, InliningOptions options, ILTransformContext context)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            StLoc stloc = block.Instructions[pos] as StLoc;

            if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal)
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            // ensure the variable is accessed only a single time
            if (v.StoreCount != 1)
            {
                return(false);
            }
            if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
            {
                return(false);
            }
            // TODO: inlining of small integer types might be semantically incorrect,
            // but we can't avoid it this easily without breaking lots of tests.
            //if (v.Type.IsSmallIntegerType())
            //	return false; // stloc might perform implicit truncation
            return(InlineOne(stloc, options, context));
        }
Example #3
0
        public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context)
        {
            int?ctorCallStart = null;

            return(InlineAllInBlock(function, block, context, ref ctorCallStart));
        }
Example #4
0
        void IILTransform.Run(ILFunction function, ILTransformContext context)
        {
            var callsToFix = new List <Call>();

            foreach (var call in function.Descendants.OfType <Call>())
            {
                if (!(call.Method.IsOperator && (call.Method.Name == "op_Increment" || call.Method.Name == "op_Decrement")))
                {
                    continue;
                }
                if (call.Arguments.Count != 1)
                {
                    continue;
                }
                if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal))
                {
                    // For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)".
                    // We can handle these calls in ReplaceMethodCallsWithOperators.
                    continue;
                }
                callsToFix.Add(call);
            }
            foreach (var call in callsToFix)
            {
                // A user-defined increment/decrement that was not handled by TransformAssignment.
                // This can happen because the variable-being-incremented was optimized out by Roslyn,
                // e.g.
                //   public void Issue1552Pre(UserType a, UserType b)
                //   {
                //      UserType num = a + b;
                //      Console.WriteLine(++num);
                //   }
                // can end up being compiled to:
                //      Console.WriteLine(UserType.op_Increment(a + b));
                if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot)
                {
                    var store = (StLoc)call.Parent;
                    var block = (Block)store.Parent;
                    context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call);
                    // stloc V(call op_Increment(...))
                    // ->
                    // stloc V(...)
                    // compound.assign op_Increment(V)
                    call.ReplaceWith(call.Arguments[0]);
                    block.Instructions.Insert(store.ChildIndex + 1,
                                              new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue,
                                                                            new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call));
                }
                else
                {
                    context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call);
                    var newVariable = call.Arguments[0].Extract(context);
                    if (newVariable == null)
                    {
                        Debug.Fail("Failed to extract argument of remaining increment/decrement");
                        continue;
                    }
                    newVariable.Type = call.GetParameter(0).Type;
                    Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable));
                    call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue,
                                                                   new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call));
                }
            }
        }
Example #5
0
        internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass)
        {
            var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType <ILFunction>().Last().Method);

            return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass));
        }
        private bool PatternMatchRefTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph?cfg)
        {
            if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst))
            {
                return(false);
            }
            int pos = block.Instructions.Count - 3;

            if (condition.MatchLdLoc(out var conditionVar))
            {
                // stloc conditionVar(comp.o(ldloc s == ldnull))
                // if (logic.not(ldloc conditionVar)) br trueBlock
                if (pos < 0)
                {
                    return(false);
                }
                if (!(conditionVar.IsSingleDefinition && conditionVar.LoadCount == 1 &&
                      conditionVar.Kind == VariableKind.StackSlot))
                {
                    return(false);
                }
                if (!block.Instructions[pos].MatchStLoc(conditionVar, out condition))
                {
                    return(false);
                }
                pos--;
            }
            if (condition.MatchCompEqualsNull(out var loadInNullCheck))
            {
                ExtensionMethods.Swap(ref trueInst, ref falseInst);
            }
            else if (condition.MatchCompNotEqualsNull(out loadInNullCheck))
            {
                // do nothing
            }
            else
            {
                return(false);
            }
            if (!loadInNullCheck.MatchLdLoc(out var s))
            {
                return(false);
            }
            if (!s.IsSingleDefinition)
            {
                return(false);
            }
            if (s.Kind is not(VariableKind.Local or VariableKind.StackSlot))
            {
                return(false);
            }
            if (pos < 0)
            {
                return(false);
            }
            // stloc V(isinst T(testedOperand))
            ILInstruction storeToV = block.Instructions[pos];

            if (!storeToV.MatchStLoc(out var v, out var value))
            {
                return(false);
            }
            if (value.MatchLdLoc(s))
            {
                // stloc v(ldloc s)
                pos--;
                if (pos < 0 || !block.Instructions[pos].MatchStLoc(s, out value))
                {
                    return(false);
                }
                if (v.Kind is not(VariableKind.Local or VariableKind.StackSlot))
                {
                    return(false);
                }
                if (s.LoadCount != 2)
                {
                    return(false);
                }
            }
            else
            {
                if (v != s)
                {
                    return(false);
                }
            }
            IType?unboxType;

            if (value is UnboxAny unboxAny)
            {
                // stloc S(unbox.any T(isinst T(testedOperand)))
                unboxType = unboxAny.Type;
                value     = unboxAny.Argument;
            }
            else
            {
                unboxType = null;
            }
            if (value is not IsInst {
                Argument : var testedOperand, Type : var type
            })
Example #7
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, options);

            if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument)
            {
                var loadInst = r.LoadInst;
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, r, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                if (r.Type == FindResultType.NamedArgument)
                {
                    NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context);
                    // Now that the argument is evaluated early, we can inline as usual
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    Debug.Assert(((LdLoca)loadInst).Variable == v);
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            return(false);
        }
Example #8
0
 public void Run(ILFunction function, ILTransformContext context)
 {
     this.context = context;
     Default(function);
 }
Example #9
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            this.context = context;
            foreach (var inst in function.Descendants.OfType <CallInstruction>())
            {
                MethodDefinition methodDef = context.TypeSystem.GetCecil(inst.Method) as MethodDefinition;
                if (methodDef != null && methodDef.Body != null)
                {
                    if (inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
                    {
                        // partially copied from CSharpDecompiler
                        var specializingTypeSystem = this.context.TypeSystem.GetSpecializingTypeSystem(inst.Method.Substitution);
                        var ilReader = new ILReader(specializingTypeSystem);
                        System.Threading.CancellationToken cancellationToken = new System.Threading.CancellationToken();
                        var proxyFunction    = ilReader.ReadIL(methodDef.Body, cancellationToken);
                        var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, this.context.Settings)
                        {
                            CancellationToken = cancellationToken,
                            DecompileRun      = context.DecompileRun
                        };
                        foreach (var transform in CSharp.CSharpDecompiler.GetILTransforms())
                        {
                            if (transform.GetType() != typeof(ProxyCallReplacer))                               // don't call itself on itself
                            {
                                cancellationToken.ThrowIfCancellationRequested();
                                transform.Run(proxyFunction, transformContext);
                            }
                        }

                        if (!(proxyFunction.Body is BlockContainer blockContainer))
                        {
                            return;
                        }
                        if (blockContainer.Blocks.Count != 1)
                        {
                            return;
                        }
                        var  block = blockContainer.Blocks[0];
                        Call call  = null;
                        if (block.Instructions.Count == 1)
                        {
                            // leave IL_0000 (call Test(ldloc this, ldloc A_1))
                            if (!block.Instructions[0].MatchLeave(blockContainer, out ILInstruction returnValue))
                            {
                                return;
                            }
                            call = returnValue as Call;
                        }
                        else if (block.Instructions.Count == 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 ILInstruction returnValue))
                            {
                                return;
                            }
                            if (!returnValue.MatchNop())
                            {
                                return;
                            }
                        }
                        if (call == null)
                        {
                            return;
                        }
                        if (call.Method.IsConstructor)
                        {
                            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;
                            }
                        }

                        Call newInst = (Call)call.Clone();

                        newInst.Arguments.ReplaceList(inst.Arguments);
                        inst.ReplaceWith(newInst);
                    }
                }
            }
        }
        internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst)
        {
            var decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(context.Function.Method));

            return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition));
        }
Example #11
0
 public BlockTransformContext(ILTransformContext context) : base(context)
 {
 }
Example #12
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, bool aggressive, ILTransformContext context)
        {
            ILInstruction loadInst;

            if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            return(false);
        }
Example #13
0
        /// <summary>
        /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible.
        /// </summary>
        public static bool InlineOneIfPossible(Block block, int pos, bool aggressive, ILTransformContext context)
        {
            context.CancellationToken.ThrowIfCancellationRequested();
            StLoc stloc = block.Instructions[pos] as StLoc;

            if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal)
            {
                return(false);
            }
            ILVariable v = stloc.Variable;

            // ensure the variable is accessed only a single time
            if (v.StoreCount != 1)
            {
                return(false);
            }
            if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1)
            {
                return(false);
            }
            return(InlineOne(stloc, aggressive, context));
        }
Example #14
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);
        }
Example #15
0
 public NullPropagationTransform(ILTransformContext context)
 {
     this.context = context;
 }
Example #16
0
        static void DoPropagate(ILVariable v, ILInstruction copiedExpr, Block block, ref int i, ILTransformContext context)
        {
            context.Step($"Copy propagate {v.Name}", copiedExpr);
            // un-inline the arguments of the ldArg instruction
            ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count];
            for (int j = 0; j < uninlinedArgs.Length; j++)
            {
                var arg  = copiedExpr.Children[j];
                var type = context.TypeSystem.FindType(arg.ResultType);
                uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType)
                {
                    Name             = "C_" + arg.StartILOffset,
                    HasGeneratedName = true,
                };
                block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
            }
            v.Function.Variables.AddRange(uninlinedArgs);
            // perform copy propagation:
            foreach (var expr in v.LoadInstructions.ToArray())
            {
                var clone = copiedExpr.Clone();
                for (int j = 0; j < uninlinedArgs.Length; j++)
                {
                    clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j]));
                }
                expr.ReplaceWith(clone);
            }
            block.Instructions.RemoveAt(i);
            int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context);

            i -= c + 1;
        }
Example #17
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.Dynamic)
            {
                return;
            }

            this.context = context;

            Dictionary <IField, CallSiteInfo> callsites          = new Dictionary <IField, CallSiteInfo>();
            HashSet <BlockContainer>          modifiedContainers = new HashSet <BlockContainer>();

            foreach (var block in function.Descendants.OfType <Block>())
            {
                if (block.Instructions.Count < 2)
                {
                    continue;
                }
                // Check if, we deal with a callsite cache field null check:
                // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c
                // br IL_002b
                if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst))
                {
                    continue;
                }
                if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit))
                {
                    continue;
                }
                if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches))
                {
                    continue;
                }
                if (!ifInst.TrueInst.MatchBranch(out var trueBlock))
                {
                    continue;
                }
                Block callSiteInitBlock, targetBlockAfterInit;
                if (invertBranches)
                {
                    callSiteInitBlock    = branchAfterInit.TargetBlock;
                    targetBlockAfterInit = trueBlock;
                }
                else
                {
                    callSiteInitBlock    = trueBlock;
                    targetBlockAfterInit = branchAfterInit.TargetBlock;
                }
                if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit))
                {
                    continue;
                }
                if (targetBlockAfterInit != blockAfterInit)
                {
                    continue;
                }
                callSiteInfo.DelegateType          = callSiteDelegate;
                callSiteInfo.ConditionalJumpToInit = ifInst;
                callSiteInfo.Inverted        = invertBranches;
                callSiteInfo.BranchAfterInit = branchAfterInit;
                callsites.Add(callSiteCacheField, callSiteInfo);
            }

            var storesToRemove = new List <StLoc>();

            foreach (var invokeCall in function.Descendants.OfType <CallVirt>())
            {
                if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0)
                {
                    continue;
                }
                var firstArgument = invokeCall.Arguments[0];
                if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition)
                {
                    firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value;
                }
                if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField))
                {
                    continue;
                }
                if (!cacheFieldLoad.MatchLdsFld(out var cacheField))
                {
                    continue;
                }
                if (!callsites.TryGetValue(cacheField, out var callsite))
                {
                    continue;
                }
                context.Stepper.Step("Transform callsite for " + callsite.MemberName);
                var           deadArguments = new List <ILInstruction>();
                ILInstruction replacement   = MakeDynamicInstruction(callsite, invokeCall, deadArguments);
                if (replacement == null)
                {
                    continue;
                }
                invokeCall.ReplaceWith(replacement);
                Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block);
                var block = ((Block)callsite.ConditionalJumpToInit.Parent);
                if (callsite.Inverted)
                {
                    block.Instructions.Remove(callsite.ConditionalJumpToInit);
                    callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst);
                }
                else
                {
                    block.Instructions.Remove(callsite.ConditionalJumpToInit);
                }
                foreach (var arg in deadArguments)
                {
                    if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0)
                    {
                        StLoc stLoc = (StLoc)temporary.StoreInstructions[0];
                        if (stLoc.Parent is Block storeParentBlock)
                        {
                            var value = stLoc.Value;
                            if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField))
                            {
                                storesToRemove.Add(stLoc);
                            }
                            if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy))
                            {
                                storesToRemove.Add(stLoc);
                            }
                        }
                    }
                }
                modifiedContainers.Add((BlockContainer)block.Parent);
            }

            foreach (var inst in storesToRemove)
            {
                Block parentBlock = (Block)inst.Parent;
                parentBlock.Instructions.RemoveAt(inst.ChildIndex);
            }

            foreach (var container in modifiedContainers)
            {
                container.SortBlocks(deleteUnreachableBlocks: true);
            }
        }
Example #18
0
        ILFunction TransformDelegateConstruction(
            ILInstruction value, IMethod targetMethod,
            ILInstruction target, IType delegateType)
        {
            if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
            {
                return(null);
            }
            if (targetMethod.MetadataToken.IsNil)
            {
                return(null);
            }
            if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context))
            {
                return(null);
            }
            if (!ValidateDelegateTarget(target))
            {
                return(null);
            }
            var handle = (MethodDefinitionHandle)targetMethod.MetadataToken;

            if (activeMethods.Contains(handle))
            {
                this.context.Function.Warnings.Add(" Found self-referencing delegate construction. Abort transformation to avoid stack overflow.");
                return(null);
            }
            var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);

            if (!methodDefinition.HasBody())
            {
                return(null);
            }
            var genericContext = GenericContextFromTypeArguments(targetMethod.Substitution);

            if (genericContext == null)
            {
                return(null);
            }
            var ilReader = context.CreateILReader();
            var body     = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
            var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken);

            function.DelegateType = delegateType;
            // Embed the lambda into the parent function's ILAst, so that "Show steps" can show
            // how the lambda body is being transformed.
            value.ReplaceWith(function);
            function.CheckInvariant(ILPhase.Normal);

            var contextPrefix = targetMethod.Name;

            foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter))
            {
                v.Name = contextPrefix + v.Name;
            }

            var nestedContext = new ILTransformContext(context, function);

            function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext);
            nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function);
            function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(VariableKindExtensions.IsThis)));
            // handle nested lambdas
            nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function);
            ((IILTransform)this).Run(function, nestedContext);
            nestedContext.StepEndGroup();
            function.AddILRange(target);
            function.AddILRange(value);
            if (value is Call call)
            {
                function.AddILRange(call.Arguments[1]);
            }
            return(function);
        }
Example #19
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            this.context          = context;
            currentFieldNames     = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray();
            reservedVariableNames = new Dictionary <string, int>();
            loopCounters          = CollectLoopCounters(function);
            foreach (var f in function.Descendants.OfType <ILFunction>())
            {
                if (f.Method != null)
                {
                    if (IsSetOrEventAccessor(f.Method) && f.Method.Parameters.Count > 0)
                    {
                        for (int i = 0; i < f.Method.Parameters.Count - 1; i++)
                        {
                            AddExistingName(reservedVariableNames, f.Method.Parameters[i].Name);
                        }
                        var lastParameter = f.Method.Parameters.Last();
                        switch (f.Method.AccessorOwner)
                        {
                        case IProperty prop:
                            if (f.Method.AccessorKind == MethodSemanticsAttributes.Setter)
                            {
                                if (prop.Parameters.Any(p => p.Name == "value"))
                                {
                                    f.Warnings.Add("Parameter named \"value\" already present in property signature!");
                                    break;
                                }
                                var variableForLastParameter = f.Variables.FirstOrDefault(v => v.Function == f &&
                                                                                          v.Kind == VariableKind.Parameter &&
                                                                                          v.Index == f.Method.Parameters.Count - 1);
                                if (variableForLastParameter == null)
                                {
                                    AddExistingName(reservedVariableNames, lastParameter.Name);
                                }
                                else
                                {
                                    if (variableForLastParameter.Name != "value")
                                    {
                                        variableForLastParameter.Name = "value";
                                    }
                                    AddExistingName(reservedVariableNames, variableForLastParameter.Name);
                                }
                            }
                            break;

                        case IEvent ev:
                            if (f.Method.AccessorKind != MethodSemanticsAttributes.Raiser)
                            {
                                var variableForLastParameter = f.Variables.FirstOrDefault(v => v.Function == f &&
                                                                                          v.Kind == VariableKind.Parameter &&
                                                                                          v.Index == f.Method.Parameters.Count - 1);
                                if (variableForLastParameter == null)
                                {
                                    AddExistingName(reservedVariableNames, lastParameter.Name);
                                }
                                else
                                {
                                    if (variableForLastParameter.Name != "value")
                                    {
                                        variableForLastParameter.Name = "value";
                                    }
                                    AddExistingName(reservedVariableNames, variableForLastParameter.Name);
                                }
                            }
                            break;

                        default:
                            AddExistingName(reservedVariableNames, lastParameter.Name);
                            break;
                        }
                    }
                    else
                    {
                        foreach (var p in f.Method.Parameters)
                        {
                            AddExistingName(reservedVariableNames, p.Name);
                        }
                    }
                }
                else
                {
                    foreach (var p in f.Variables.Where(v => v.Kind == VariableKind.Parameter))
                    {
                        AddExistingName(reservedVariableNames, p.Name);
                    }
                }
            }
            foreach (ILFunction f in function.Descendants.OfType <ILFunction>().Reverse())
            {
                PerformAssignment(f);
            }
        }
Example #20
0
 public void Run(Block block, BlockTransformContext context)
 {
     this.context = context;
     Default(block);
 }
 public void Run(ILFunction function, ILTransformContext context)
 {
     throw new NotImplementedException();
 }
Example #22
0
        ILFunction TransformDelegateConstruction(NewObj value, out ILInstruction target)
        {
            target = null;
            if (!IsDelegateConstruction(value))
            {
                return(null);
            }
            var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;

            if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
            {
                return(null);
            }
            if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken))
            {
                return(null);
            }
            target = value.Arguments[0];
            if (targetMethod.MetadataToken.IsNil)
            {
                return(null);
            }
            var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);

            if (!methodDefinition.HasBody())
            {
                return(null);
            }
            var genericContext = GenericContextFromTypeArguments(targetMethod.Substitution);

            if (genericContext == null)
            {
                return(null);
            }
            var ilReader = context.CreateILReader();
            var body     = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
            var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken);

            function.DelegateType = value.Method.DeclaringType;
            function.CheckInvariant(ILPhase.Normal);
            // Embed the lambda into the parent function's ILAst, so that "Show steps" can show
            // how the lambda body is being transformed.
            value.ReplaceWith(function);

            var contextPrefix = targetMethod.Name;

            foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter))
            {
                v.Name = contextPrefix + v.Name;
            }

            var nestedContext = new ILTransformContext(context, function);

            function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext);
            nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function);
            function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
            // handle nested lambdas
            nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function);
            ((IILTransform) new DelegateConstruction()).Run(function, nestedContext);
            nestedContext.StepEndGroup();
            function.AddILRange(target);
            function.AddILRange(value);
            function.AddILRange(value.Arguments[1]);
            return(function);
        }
Example #23
0
        internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst)
        {
            var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType <ILFunction>().Last().Method);

            return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition));
        }
Example #24
0
        /// <summary>
        /// The transform works like this:
        ///
        /// <para>
        /// local functions can either be used in method calls, i.e., call and callvirt instructions,
        /// or can be used as part of the "delegate construction" pattern, i.e., <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
        /// </para>
        /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions.
        /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function.
        /// Then all use-sites of the local-function are transformed to a call to the <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
        /// In a next step we handle all nested local functions.
        /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope.
        /// </summary>
        public void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.LocalFunctions)
            {
                return;
            }
            this.context        = context;
            this.resolveContext = new SimpleTypeResolveContext(function.Method);
            var localFunctions    = new Dictionary <MethodDefinitionHandle, LocalFunctionInfo>();
            var cancellationToken = context.CancellationToken;

            // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas.
            FindUseSites(function, context, localFunctions);
            foreach (var(method, info) in localFunctions)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var firstUseSite = info.UseSites[0];
                context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
                try {
                    var localFunction = info.Definition;
                    if (!localFunction.Method.IsStatic)
                    {
                        var target = firstUseSite.Arguments[0];
                        context.Step($"Replace 'this' with {target}", localFunction);
                        var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
                        localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
                    }

                    foreach (var useSite in info.UseSites)
                    {
                        context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite);
                        if (useSite.OpCode == OpCode.NewObj)
                        {
                            TransformToLocalFunctionReference(localFunction, useSite);
                        }
                        else
                        {
                            DetermineCaptureAndDeclarationScope(localFunction, useSite);
                            TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
                        }

                        if (function.Method.IsConstructor && localFunction.DeclarationScope == null)
                        {
                            localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite);
                        }
                    }

                    if (localFunction.DeclarationScope == null)
                    {
                        localFunction.DeclarationScope = (BlockContainer)function.Body;
                    }
                    else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction)
                    {
                        function.LocalFunctions.Remove(localFunction);
                        declaringFunction.LocalFunctions.Add(localFunction);
                    }
                } finally {
                    context.StepEndGroup();
                }
            }
        }
Example #25
0
 /// <summary>
 /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
 /// </summary>
 public static bool InlineIfPossible(Block block, int pos, ILTransformContext context)
 {
     return(InlineOneIfPossible(block, pos, InliningOptions.Aggressive, context));
 }
Example #26
0
        public void Run(ILFunction function, ILTransformContext context)
        {
            ResetHasInitialValueFlag(function, context);
            // Remove dead stores to variables that are never read from.
            // If the stored value has some side-effect, the value is unwrapped.
            // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler.
            // In yield return + async, the C# compiler tends to store null/default(T) to variables
            // when the variable goes out of scope.
            if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores)
            {
                var variableQueue = new Queue <ILVariable>(function.Variables);
                while (variableQueue.Count > 0)
                {
                    var v = variableQueue.Dequeue();
                    if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot)
                    {
                        continue;
                    }
                    // Skip variables that are captured in a mcs yield state-machine
                    // loads of these will only be visible after DelegateConstruction step.
                    if (function.StateMachineCompiledWithMono && v.StateMachineField != null)
                    {
                        continue;
                    }
                    if (v.LoadCount != 0 || v.AddressCount != 0)
                    {
                        continue;
                    }
                    foreach (var stloc in v.StoreInstructions.OfType <StLoc>().ToArray())
                    {
                        if (stloc.Parent is Block block)
                        {
                            if (SemanticHelper.IsPure(stloc.Value.Flags))
                            {
                                block.Instructions.Remove(stloc);
                            }
                            else
                            {
                                stloc.ReplaceWith(stloc.Value);
                            }
                            if (stloc.Value is LdLoc ldloc)
                            {
                                variableQueue.Enqueue(ldloc.Variable);
                            }
                        }
                    }
                }
            }

            // Try to infer IType of stack slots that are of StackType.Ref:
            foreach (var v in function.Variables)
            {
                if (v.Kind == VariableKind.StackSlot && v.StackType == StackType.Ref && v.AddressCount == 0)
                {
                    IType newType = null;
                    // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example.
                    foreach (var stloc in v.StoreInstructions.OfType <StLoc>())
                    {
                        var inferredType = stloc.Value.InferType(context.TypeSystem);
                        // cancel, if types of values do not match exactly
                        if (newType != null && !newType.Equals(inferredType))
                        {
                            newType = SpecialType.UnknownType;
                            break;
                        }
                        newType = inferredType;
                    }
                    // Only overwrite existing type, if a "better" type was found.
                    if (newType != null && newType != SpecialType.UnknownType)
                    {
                        v.Type = newType;
                    }
                }
            }
        }
Example #27
0
        /// <summary>
        /// Inlines 'expr' into 'next', if possible.
        ///
        /// Note that this method does not check whether 'v' has only one use;
        /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'.
        /// </summary>
        static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context)
        {
            var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst);

            if (r == FindResult.Found)
            {
                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression))
                    {
                        return(false);
                    }
                }
                else
                {
                    Debug.Assert(loadInst.OpCode == OpCode.LdLoc);
                    bool aggressive = (options & InliningOptions.Aggressive) != 0;
                    if (!aggressive && v.Kind != VariableKind.StackSlot &&
                        !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v))
                    {
                        return(false);
                    }
                }

                context.Step($"Inline variable '{v.Name}'", inlinedExpression);
                // Assign the ranges of the ldloc instruction:
                inlinedExpression.AddILRange(loadInst.ILRange);

                if (loadInst.OpCode == OpCode.LdLoca)
                {
                    // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof'
                    // to preserve the semantics of the compiler-generated temporary
                    loadInst.ReplaceWith(new AddressOf(inlinedExpression));
                }
                else
                {
                    loadInst.ReplaceWith(inlinedExpression);
                }
                return(true);
            }
            else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0)
            {
                return(NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst,
                                                       options, context));
            }
            return(false);
        }
 void ClearState()
 {
     displayClasses.Clear();
     this.decompilationContext = null;
     this.context = null;
 }
Example #29
0
 public NullableLiftingTransform(ILTransformContext context)
 {
     this.context      = context;
     this.nullableVars = null;
 }
 internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer)
 {
     return(IsClosure(context, variable, instructionsToRemove: null, out closureType, out initializer));
 }