Example #1
0
        internal static bool IsCatchWhenBlock(Block block)
        {
            var container = BlockContainer.FindClosestContainer(block);

            return(container?.Parent is TryCatchHandler handler &&
                   handler.Filter == container);
        }
Example #2
0
        void IILTransform.Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AnonymousMethods)
            {
                return;
            }
            this.context = context;
            this.decompilationContext = new SimpleTypeResolveContext(function.Method);
            var orphanedVariableInits    = new List <ILInstruction>();
            var targetsToReplace         = new List <IInstructionWithVariableOperand>();
            var translatedDisplayClasses = new HashSet <ITypeDefinition>();
            var cancellationToken        = context.CancellationToken;

            foreach (var inst in function.Descendants)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (inst is NewObj call)
                {
                    context.StepStartGroup($"TransformDelegateConstruction {call.StartILOffset}", call);
                    ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
                    if (f != null && target is IInstructionWithVariableOperand instWithVar)
                    {
                        if (instWithVar.Variable.Kind == VariableKind.Local)
                        {
                            instWithVar.Variable.Kind = VariableKind.DisplayClassLocal;
                        }
                        targetsToReplace.Add(instWithVar);
                    }
                    context.StepEndGroup();
                }
                if (inst.MatchStLoc(out ILVariable targetVariable, out ILInstruction value))
                {
                    var newObj = value as NewObj;
                    // TODO : it is probably not a good idea to remove *all* display-classes
                    // is there a way to minimize the false-positives?
                    if (newObj != null && IsInSimpleDisplayClass(newObj.Method))
                    {
                        targetVariable.CaptureScope = BlockContainer.FindClosestContainer(inst);
                        targetsToReplace.Add((IInstructionWithVariableOperand)inst);
                        translatedDisplayClasses.Add(newObj.Method.DeclaringTypeDefinition);
                    }
                }
            }
            foreach (var target in targetsToReplace.OrderByDescending(t => ((ILInstruction)t).StartILOffset))
            {
                context.Step($"TransformDisplayClassUsages {target.Variable}", (ILInstruction)target);
                function.AcceptVisitor(new TransformDisplayClassUsages(function, target, target.Variable.CaptureScope, orphanedVariableInits, translatedDisplayClasses));
            }
            context.Step($"Remove orphanedVariableInits", function);
            foreach (var store in orphanedVariableInits)
            {
                if (store.Parent is Block containingBlock)
                {
                    containingBlock.Instructions.Remove(store);
                }
            }
        }
Example #3
0
        private void DetermineCaptureAndDeclarationScopes(Dictionary <MethodDefinitionHandle, LocalFunctionInfo> .ValueCollection localFunctions)
        {
            foreach (var info in localFunctions)
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                if (info.Definition == null)
                {
                    context.Function.Warnings.Add($"Could not decode local function '{info.Method}'");
                    continue;
                }

                context.StepStartGroup($"Determine and move to declaration scope of " + info.Definition.Name, info.Definition);
                try
                {
                    var localFunction = info.Definition;

                    foreach (var useSite in info.UseSites)
                    {
                        DetermineCaptureAndDeclarationScope(info, useSite);

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

                    if (localFunction.DeclarationScope == null)
                    {
                        localFunction.DeclarationScope = (BlockContainer)context.Function.Body;
                    }

                    ILFunction declaringFunction = GetDeclaringFunction(localFunction);
                    if (declaringFunction != context.Function)
                    {
                        context.Step($"Move {localFunction.Name} from {context.Function.Name} to {declaringFunction.Name}", localFunction);
                        context.Function.LocalFunctions.Remove(localFunction);
                        declaringFunction.LocalFunctions.Add(localFunction);
                    }

                    if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters)
                    {
                        Debug.Assert(false);
                        context.Function.Warnings.Add($"Could not decode local function '{info.Method}'");
                        if (declaringFunction != context.Function)
                        {
                            declaringFunction.LocalFunctions.Remove(localFunction);
                        }
                    }
                }
                finally
                {
                    context.StepEndGroup(keepIfEmpty: true);
                }
            }
        }
Example #4
0
        void IILTransform.Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AnonymousMethods)
            {
                return;
            }
            var prevContext = this.context;
            var prevDecompilationContext = this.decompilationContext;

            try
            {
                activeMethods.Push((MethodDefinitionHandle)function.Method.MetadataToken);
                this.context = context;
                this.decompilationContext = new SimpleTypeResolveContext(function.Method);
                var cancellationToken = context.CancellationToken;
                foreach (var inst in function.Descendants)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    if (!MatchDelegateConstruction(inst, out var targetMethod, out var target,
                                                   out var delegateType, allowTransformed: false))
                    {
                        continue;
                    }
                    context.StepStartGroup($"TransformDelegateConstruction {inst.StartILOffset}", inst);
                    ILFunction f = TransformDelegateConstruction(inst, targetMethod, target, delegateType);
                    if (f != null && target is IInstructionWithVariableOperand instWithVar)
                    {
                        var v = instWithVar.Variable;
                        if (v.Kind == VariableKind.Local)
                        {
                            v.Kind = VariableKind.DisplayClassLocal;
                        }
                        if (v.IsSingleDefinition &&
                            v.StoreInstructions.SingleOrDefault() is StLoc store &&
                            store.Value is NewObj)
                        {
                            v.CaptureScope = BlockContainer.FindClosestContainer(store);
                        }
                    }
                    context.StepEndGroup();
                }
            }
            finally
            {
                this.context = prevContext;
                this.decompilationContext = prevDecompilationContext;
                activeMethods.Pop();
            }
        }
Example #5
0
        bool ShouldSwapIfTargets(ILInstruction inst1, ILInstruction inst2)
        {
            Block block1 = null, block2 = null;

            if (inst1.MatchBranch(out block1) && inst2.MatchBranch(out block2))
            {
                // prefer arranging stuff in IL order
                return(block1.ILRange.Start > block2.ILRange.Start);
            }
            BlockContainer container1, container2;

            if (inst1.MatchLeave(out container1) && container1.Parent is TryInstruction)
            {
                // 'leave tryBlock' is considered to have a later target than
                // any branch within the container, and also a later target
                // than a return instruction.
                // This is necessary to avoid "goto" statements in the
                // ExceptionHandling.ConditionalReturnInThrow test.
                if (!inst2.MatchLeave(out container2))
                {
                    container2 = block2?.Parent as BlockContainer;
                }
                return(container2 == null || container2.IsDescendantOf(container1));
            }
            if (inst1.MatchBranch(out block1) && inst2.MatchLeave(out container2) &&
                block1.IncomingEdgeCount > 1)
            {
                // if (..) goto x; leave c;
                // Unless x can be inlined, it's better to swap the order if the 'leave'
                // has a chance to turn into a 'break;' or 'return;'
                if (container2.Parent is ILFunction)
                {
                    return(true);                    // return
                }
                if (container2.EntryPoint.IncomingEdgeCount > 1)
                {
                    // break
                    return(BlockContainer.FindClosestContainer(inst2) == container2);
                }
            }
            return(false);
        }
Example #6
0
        void IILTransform.Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AnonymousMethods)
            {
                return;
            }
            this.context = context;
            this.decompilationContext = new SimpleTypeResolveContext(function.Method);
            var cancellationToken = context.CancellationToken;

            foreach (var inst in function.Descendants)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (inst is NewObj call)
                {
                    context.StepStartGroup($"TransformDelegateConstruction {call.StartILOffset}", call);
                    ILFunction f = TransformDelegateConstruction(call, out ILInstruction target);
                    if (f != null && target is IInstructionWithVariableOperand instWithVar)
                    {
                        if (instWithVar.Variable.Kind == VariableKind.Local)
                        {
                            instWithVar.Variable.Kind = VariableKind.DisplayClassLocal;
                        }
                        var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition();
                        if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store)
                        {
                            if (store.Value is NewObj newObj)
                            {
                                instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store);
                            }
                        }
                    }
                    context.StepEndGroup();
                }
            }
        }
        /// <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;
            }
            // Disable the transform if we are decompiling a display-class or local function method:
            // This happens if a local function or display class is selected in the ILSpy tree view.
            if (IsLocalFunctionMethod(function.Method, context) || IsLocalFunctionDisplayClass(function.Method.ParentModule.PEFile, (TypeDefinitionHandle)function.Method.DeclaringTypeDefinition.MetadataToken, context))
            {
                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(_, info) in localFunctions)
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (info.Definition == null)
                {
                    function.Warnings.Add($"Could not decode local function '{info.Method}'");
                    continue;
                }

                context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
                try {
                    var localFunction = info.Definition;
                    if (!localFunction.Method.IsStatic)
                    {
                        var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
                        var target  = info.UseSites.Where(us => us.Arguments[0].MatchLdLoc(out _)).FirstOrDefault()?.Arguments[0];
                        if (target == null)
                        {
                            target = info.UseSites[0].Arguments[0];
                            if (target.MatchLdFld(out var target1, out var field) && thisVar.Type.Equals(field.Type) && field.Type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition()))
                            {
                                var variable = function.Descendants.OfType <ILFunction>().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault();
                                if (variable != null)
                                {
                                    target = new LdLoc(variable);
                                    HandleArgument(localFunction, 1, 0, target);
                                }
                            }
                        }
                        context.Step($"Replace 'this' with {target}", localFunction);
                        localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
                    }

                    foreach (var useSite in info.UseSites)
                    {
                        DetermineCaptureAndDeclarationScope(localFunction, useSite);

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

                    if (localFunction.DeclarationScope == null)
                    {
                        localFunction.DeclarationScope = (BlockContainer)function.Body;
                    }

                    ILFunction declaringFunction = GetDeclaringFunction(localFunction);
                    if (declaringFunction != function)
                    {
                        function.LocalFunctions.Remove(localFunction);
                        declaringFunction.LocalFunctions.Add(localFunction);
                    }

                    if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters)
                    {
                        Debug.Assert(false);
                        function.Warnings.Add($"Could not decode local function '{info.Method}'");
                        if (declaringFunction != function)
                        {
                            declaringFunction.LocalFunctions.Remove(localFunction);
                        }
                        continue;
                    }

                    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
                        {
                            TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
                        }
                    }
                } finally {
                    context.StepEndGroup();
                }
            }
        }
Example #8
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 #9
0
 private BlockContainer FindBlockContainer(Block block)
 {
     return(BlockContainer.FindClosestContainer(block) ?? throw new NotSupportedException());
 }
Example #10
0
        public static void Run(ILFunction function, ILTransformContext context)
        {
            if (!context.Settings.AwaitInCatchFinally)
            {
                return;
            }
            HashSet <BlockContainer> changedContainers = new HashSet <BlockContainer>();

            // analyze all try-catch statements in the function
            foreach (var tryCatch in function.Descendants.OfType <TryCatch>().ToArray())
            {
                if (!(tryCatch.Parent?.Parent is BlockContainer container))
                {
                    continue;
                }
                // await in finally uses a single catch block with catch-type object
                if (tryCatch.Handlers.Count != 1 || !(tryCatch.Handlers[0].Body is BlockContainer catchBlockContainer) || !tryCatch.Handlers[0].Variable.Type.IsKnownType(KnownTypeCode.Object))
                {
                    continue;
                }
                // and consists of an assignment to a temporary that is used outside the catch block
                // and a jump to the finally block
                var block = catchBlockContainer.EntryPoint;
                if (block.Instructions.Count < 2 || !block.Instructions[0].MatchStLoc(out var globalCopyVar, out var value) || !value.MatchLdLoc(tryCatch.Handlers[0].Variable))
                {
                    continue;
                }
                if (block.Instructions.Count == 3)
                {
                    if (!block.Instructions[1].MatchStLoc(out var globalCopyVarTemp, out value) || !value.MatchLdLoc(globalCopyVar))
                    {
                        continue;
                    }
                    globalCopyVar = globalCopyVarTemp;
                }
                if (!block.Instructions[block.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally))
                {
                    continue;
                }
                // globalCopyVar should only be used once, at the end of the finally-block
                if (globalCopyVar.LoadCount != 1 || globalCopyVar.StoreCount > 2)
                {
                    continue;
                }
                var tempStore = globalCopyVar.LoadInstructions[0].Parent as StLoc;
                if (tempStore == null || !MatchExceptionCaptureBlock(tempStore, out var exitOfFinally, out var afterFinally, out var blocksToRemove))
                {
                    continue;
                }
                if (!MatchAfterFinallyBlock(ref afterFinally, blocksToRemove, out bool removeFirstInstructionInAfterFinally))
                {
                    continue;
                }
                var cfg = new ControlFlowGraph(container, context.CancellationToken);
                var exitOfFinallyNode       = cfg.GetNode(exitOfFinally);
                var entryPointOfFinallyNode = cfg.GetNode(entryPointOfFinally);

                var additionalBlocksInFinally = new HashSet <Block>();
                var invalidExits = new List <ControlFlowNode>();

                TraverseDominatorTree(entryPointOfFinallyNode);

                void TraverseDominatorTree(ControlFlowNode node)
                {
                    if (entryPointOfFinallyNode != node)
                    {
                        if (entryPointOfFinallyNode.Dominates(node))
                        {
                            additionalBlocksInFinally.Add((Block)node.UserData);
                        }
                        else
                        {
                            invalidExits.Add(node);
                        }
                    }

                    if (node == exitOfFinallyNode)
                    {
                        return;
                    }

                    foreach (var child in node.DominatorTreeChildren)
                    {
                        TraverseDominatorTree(child);
                    }
                }

                if (invalidExits.Any())
                {
                    continue;
                }

                context.Step("Inline finally block with await", tryCatch.Handlers[0]);

                foreach (var blockToRemove in blocksToRemove)
                {
                    blockToRemove.Remove();
                }
                var finallyContainer = new BlockContainer();
                entryPointOfFinally.Remove();
                if (removeFirstInstructionInAfterFinally)
                {
                    afterFinally.Instructions.RemoveAt(0);
                }
                changedContainers.Add(container);
                var outer = BlockContainer.FindClosestContainer(container.Parent);
                if (outer != null)
                {
                    changedContainers.Add(outer);
                }
                finallyContainer.Blocks.Add(entryPointOfFinally);
                finallyContainer.AddILRange(entryPointOfFinally);
                exitOfFinally.Instructions.RemoveRange(tempStore.ChildIndex, 3);
                exitOfFinally.Instructions.Add(new Leave(finallyContainer));
                foreach (var branchToFinally in container.Descendants.OfType <Branch>())
                {
                    if (branchToFinally.TargetBlock == entryPointOfFinally)
                    {
                        branchToFinally.ReplaceWith(new Branch(afterFinally));
                    }
                }
                foreach (var newBlock in additionalBlocksInFinally)
                {
                    newBlock.Remove();
                    finallyContainer.Blocks.Add(newBlock);
                    finallyContainer.AddILRange(newBlock);
                }
                tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock));
            }

            // clean up all modified containers
            foreach (var container in changedContainers)
            {
                container.SortBlocks(deleteUnreachableBlocks: true);
            }
        }