Beispiel #1
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);
                }
            }
        }
Beispiel #2
0
 public void Run(Block block, int pos, StatementTransformContext context)
 {
     this.context = context;
     context.StepStartGroup($"ExpressionTransforms ({block.Label}:{pos})", block.Instructions[pos]);
     block.Instructions[pos].AcceptVisitor(this);
     context.StepEndGroup(keepIfEmpty: true);
 }
        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);
                }
            }
        }
        /// <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 " + useSite.StartILOffset, useSite);
                        if (useSite.OpCode == OpCode.NewObj)
                        {
                            TransformToLocalFunctionReference(localFunction, useSite);
                        }
                        else
                        {
                            DetermineCaptureAndDeclarationScope(localFunction, useSite);
                            TransformToLocalFunctionInvocation(localFunction.ReducedMethod, 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();
                }
            }
        }
 protected internal override void VisitILFunction(ILFunction function)
 {
     context.StepStartGroup("Visit " + function.Name);
     try {
         this.currentFunctions.Push(function);
         base.VisitILFunction(function);
     } finally {
         this.currentFunctions.Pop();
         context.StepEndGroup(keepIfEmpty: true);
     }
 }
Beispiel #6
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();
            }
        }
Beispiel #7
0
        /// <summary>
        /// catch E_189 : 0200007C System.Exception when (BlockContainer {
        ///     Block IL_0079 (incoming: 1) {
        ///         stloc S_30(ldloc E_189)
        ///         br IL_0085
        ///     }
        ///
        ///     Block IL_0085 (incoming: 1) {
        ///         stloc I_1(ldloc S_30)
        /// where S_30 and I_1 are single definition
        /// =>
        /// copy-propagate E_189 to replace all uses of S_30 and I_1
        /// </summary>
        static void PropagateExceptionVariable(ILTransformContext context, TryCatchHandler handler)
        {
            var exceptionVariable = handler.Variable;

            if (!exceptionVariable.IsSingleDefinition)
            {
                return;
            }
            context.StepStartGroup(nameof(PropagateExceptionVariable));
            int i = 0;

            while (i < exceptionVariable.LoadInstructions.Count)
            {
                var load = exceptionVariable.LoadInstructions[i];

                if (!load.IsDescendantOf(handler))
                {
                    i++;
                    continue;
                }

                // We are only interested in store "statements" copying the exception variable
                // without modifying it.
                var statement = LocalFunctionDecompiler.GetStatement(load);
                if (!(statement is StLoc stloc))
                {
                    i++;
                    continue;
                }
                // simple copy case:
                // stloc b(ldloc a)
                if (stloc.Value == load)
                {
                    PropagateExceptionInstance(stloc);
                }
                // if the type of the cast-class instruction matches the exceptionType,
                // this cast can be removed without losing any side-effects.
                // Note: this would also hold true iff exceptionType were an exception derived
                // from cc.Type, however, we are more restrictive to match the pattern exactly.
                // stloc b(castclass exceptionType(ldloc a))
                else if (stloc.Value is CastClass cc && cc.Type.Equals(exceptionVariable.Type) && cc.Argument == load)
                {
                    stloc.Value = load;
                    PropagateExceptionInstance(stloc);
                }
Beispiel #8
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();
                }
            }
        }
        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);
        }
Beispiel #11
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);
        }