Example #1
0
        /// <summary>
        /// Extracts the specified instruction:
        ///   The instruction is replaced with a load of a new temporary variable;
        ///   and the instruction is moved to a store to said variable at block-level.
        ///
        /// May return null if extraction is not possible.
        /// </summary>
        public static ILVariable Extract(ILInstruction instToExtract)
        {
            var function          = instToExtract.Ancestors.OfType <ILFunction>().First();
            ExtractionContext ctx = new ExtractionContext(function);

            ctx.FlagsBeingMoved = instToExtract.Flags;
            ILInstruction inst = instToExtract;

            while (inst != null)
            {
                if (inst.Parent is Block block && block.Kind == BlockKind.ControlFlow)
                {
                    // We've reached the target block, and extraction is possible all the way.
                    int insertIndex = inst.ChildIndex;
                    // Move instToExtract itself:
                    var v = function.RegisterVariable(VariableKind.StackSlot, instToExtract.ResultType);
                    instToExtract.ReplaceWith(new LdLoc(v));
                    block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract));
                    // Apply the other move actions:
                    foreach (var moveAction in ctx.MoveActions)
                    {
                        block.Instructions.Insert(insertIndex, moveAction());
                    }
                    return(v);
                }
                if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx))
                {
                    return(null);
                }
                inst = inst.Parent;
            }
            return(null);
        }
Example #2
0
 internal void RegisterMove(ILInstruction predecessor)
 {
     FlagsBeingMoved |= predecessor.Flags;
     MoveActions.Add(delegate {
         var v = Function.RegisterVariable(VariableKind.StackSlot, predecessor.ResultType);
         predecessor.ReplaceWith(new LdLoc(v));
         return(new StLoc(v, predecessor));
     });
 }
Example #3
0
 void HandleExit(ILInstruction inst)
 {
     if (currentExit == ExitNotYetDetermined && CanIntroduceAsExit(inst))
     {
         currentExit = inst;
         inst.ReplaceWith(new Leave(currentContainer)
         {
             ILRange = inst.ILRange
         });
     }
     else if (CompatibleExitInstruction(inst, currentExit))
     {
         inst.ReplaceWith(new Leave(currentContainer)
         {
             ILRange = inst.ILRange
         });
     }
 }
Example #4
0
 internal void RegisterMove(ILInstruction predecessor)
 {
     FlagsBeingMoved |= predecessor.Flags;
     MoveActions.Add(delegate {
         var type = context.TypeSystem.FindType(predecessor.ResultType.ToKnownTypeCode());
         var v    = Function.RegisterVariable(VariableKind.StackSlot, type);
         predecessor.ReplaceWith(new LdLoc(v));
         return(new StLoc(v, predecessor));
     });
 }
 /// <summary>
 /// Get index of deconstruction result or tuple element
 /// Returns -1 on failure.
 /// </summary>
 int FindIndex(ILInstruction inst, out Action <DeconstructInstruction> delayedActions)
 {
     delayedActions = null;
     if (inst.MatchLdLoc(out var v))
     {
         if (!deconstructionResultsLookup.TryGetValue(v, out int index))
         {
             return(-1);
         }
         return(index);
     }
     if (inst.MatchLdFld(out _, out _))
     {
         if (!TupleTransform.MatchTupleFieldAccess((LdFlda)((LdObj)inst).Target, out var tupleType, out var target, out int index))
         {
             return(-1);
         }
         // Item fields are one-based, we use zero-based indexing.
         index--;
         // normalize tuple type
         tupleType = TupleType.FromUnderlyingType(context.TypeSystem, tupleType);
         if (!target.MatchLdLoca(out v))
         {
             return(-1);
         }
         if (this.tupleVariable == null)
         {
             this.tupleVariable         = v;
             this.tupleType             = (TupleType)tupleType;
             this.deconstructionResults = new ILVariable[this.tupleType.Cardinality];
         }
         if (this.tupleType.Cardinality < 2)
         {
             return(-1);
         }
         if (v != tupleVariable || !this.tupleType.Equals(tupleType))
         {
             return(-1);
         }
         if (this.deconstructionResults[index] == null)
         {
             var freshVar = new ILVariable(VariableKind.StackSlot, this.tupleType.ElementTypes[index])
             {
                 Name = "E_" + index
             };
             delayedActions += _ => context.Function.Variables.Add(freshVar);
             this.deconstructionResults[index] = freshVar;
         }
         delayedActions += _ => {
             inst.ReplaceWith(new LdLoc(this.deconstructionResults[index]));
         };
         return(index);
     }
     return(-1);
 }
Example #6
0
 void HandleExit(ILInstruction inst)
 {
     if (currentExit == ExitNotYetDetermined && CanIntroduceAsExit(inst))
     {
         potentialExits.Add(inst);
     }
     else if (CompatibleExitInstruction(inst, currentExit))
     {
         inst.ReplaceWith(new Leave(currentContainer).WithILRange(inst));
     }
 }
Example #7
0
 public void HandleExit(ILInstruction inst)
 {
     if (this.CurrentExit == ExitNotYetDetermined && this.Container.LeaveCount == 0)
     {
         this.PotentialExits !.Add(inst);
     }
     else if (CompatibleExitInstruction(inst, this.CurrentExit))
     {
         inst.ReplaceWith(new Leave(this.Container).WithILRange(inst));
     }
 }
Example #8
0
        /// <summary>
        /// Extracts the specified instruction:
        ///   The instruction is replaced with a load of a new temporary variable;
        ///   and the instruction is moved to a store to said variable at block-level.
        ///
        /// May return null if extraction is not possible.
        /// </summary>
        public static ILVariable Extract(ILInstruction instToExtract, ILTransformContext context)
        {
            var function          = instToExtract.Ancestors.OfType <ILFunction>().First();
            ExtractionContext ctx = new ExtractionContext(function, context);

            ctx.FlagsBeingMoved = instToExtract.Flags;
            ILInstruction inst = instToExtract;

            while (inst != null)
            {
                if (inst.Parent is IfInstruction ifInst && inst.SlotInfo != IfInstruction.ConditionSlot)
                {
                    // this context doesn't support extraction, but maybe we can create a block here?
                    if (ifInst.ResultType == StackType.Void)
                    {
                        Block newBlock = new Block();
                        inst.ReplaceWith(newBlock);
                        newBlock.Instructions.Add(inst);
                    }
                }
                if (inst.Parent is Block block && block.Kind == BlockKind.ControlFlow)
                {
                    // We've reached the target block, and extraction is possible all the way.
                    int insertIndex = inst.ChildIndex;
                    var type        = context.TypeSystem.FindType(instToExtract.ResultType);
                    // Move instToExtract itself:
                    var v = function.RegisterVariable(VariableKind.StackSlot, type);
                    instToExtract.ReplaceWith(new LdLoc(v));
                    block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract));
                    // Apply the other move actions:
                    foreach (var moveAction in ctx.MoveActions)
                    {
                        block.Instructions.Insert(insertIndex, moveAction());
                    }
                    return(v);
                }
                if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx))
                {
                    return(null);
                }
                inst = inst.Parent;
            }
            return(null);
        }
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            // Check if the using resource matches the GetEnumerator pattern.
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            // The using body must be a BlockContainer.
            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            // The using-variable is the enumerator.
            var enumeratorVar = inst.Variable;
            // If there's another BlockContainer nested in this container and it only has one child block, unwrap it.
            // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            // Detect whether we're dealing with a while loop with multiple embedded statements.
            var loop = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            // The loop condition must be a call to enumerator.MoveNext()
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            // Check enumerator variable references.
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar)
            {
                return(null);
            }
            // Detect which foreach-variable transformation is necessary/possible.
            var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(),
                                                                out var singleGetter, out var foreachVariable);

            if (transformation == RequiredGetCurrentTransformation.NoForeach)
            {
                return(null);
            }
            // The existing foreach variable, if found, can only be used in the loop container.
            if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            // Extract in-expression
            var collectionExpr = m.Get <Expression>("collection").Single();

            // Special case: foreach (var item in this) is decompiled as foreach (var item in base)
            // but a base reference is not valid in this context.
            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            // Handle explicit casts:
            // This is the case if an explicit type different from the collection-item-type was used.
            // For example: foreach (ClassA item in nonGenericEnumerable)
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            // Handle the required foreach-variable transformation:
            switch (transformation)
            {
            case RequiredGetCurrentTransformation.UseExistingVariable:
                foreachVariable.Type = type;
                foreachVariable.Kind = VariableKind.ForeachLocal;
                foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), foreachVariable);
                break;

            case RequiredGetCurrentTransformation.UninlineAndUseExistingVariable:
                // Unwrap stloc chain.
                var nestedStores = new Stack <ILVariable>();
                var currentInst  = instToReplace;                        // instToReplace is the innermost value of the stloc chain.
                while (currentInst.Parent is StLoc stloc)
                {
                    // Exclude nested stores to foreachVariable
                    // we'll insert one store at the beginning of the block.
                    if (stloc.Variable != foreachVariable && stloc.Parent is StLoc)
                    {
                        nestedStores.Push(stloc.Variable);
                    }
                    currentInst = stloc;
                }
                // Rebuild the nested store instructions:
                ILInstruction reorderedStores = new LdLoc(foreachVariable);
                while (nestedStores.Count > 0)
                {
                    reorderedStores = new StLoc(nestedStores.Pop(), reorderedStores);
                }
                currentInst.ReplaceWith(reorderedStores);
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                // Adjust variable type, kind and name.
                goto case RequiredGetCurrentTransformation.UseExistingVariable;

            case RequiredGetCurrentTransformation.IntroduceNewVariable:
                foreachVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(foreachVariable));
                body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
                break;
            }
            // Convert the modified body to C# AST:
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement.
            foreachBody.Statements.First().Detach();
            // Construct the foreach loop.
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && foreachVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type),
                VariableName      = foreachVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
            foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
            // If there was an optional return statement, return it as well.
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }
Example #10
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 #11
0
        Statement TransformToForeach(UsingInstruction inst, out Expression resource)
        {
            resource = exprBuilder.Translate(inst.ResourceExpression);
            var m = getEnumeratorPattern.Match(resource);

            if (!(inst.Body is BlockContainer container) || !m.Success)
            {
                return(null);
            }
            var enumeratorVar = inst.Variable;
            var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop);
            var loop          = DetectedLoop.DetectLoop(loopContainer);

            if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
            {
                return(null);
            }
            var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
            var m2        = moveNextConditionPattern.Match(condition.Expression);

            if (!m2.Success)
            {
                return(null);
            }
            var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable();

            if (enumeratorVar2 != enumeratorVar || !BodyHasSingleGetCurrent(body, enumeratorVar, condition.ILInstructions.Single(),
                                                                            out var singleGetter, out var needsUninlining, out var itemVariable))
            {
                return(null);
            }
            if (itemVariable != null && !(itemVariable.CaptureScope == null || itemVariable.CaptureScope == loopContainer))
            {
                return(null);
            }
            var collectionExpr = m.Get <Expression>("collection").Single();

            if (collectionExpr is BaseReferenceExpression)
            {
                collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr);
            }
            var           type          = singleGetter.Method.ReturnType;
            ILInstruction instToReplace = singleGetter;

            switch (instToReplace.Parent)
            {
            case CastClass cc:
                type          = cc.Type;
                instToReplace = cc;
                break;

            case UnboxAny ua:
                type          = ua.Type;
                instToReplace = ua;
                break;
            }
            if (needsUninlining)
            {
                itemVariable = currentFunction.RegisterVariable(
                    VariableKind.ForeachLocal, type,
                    AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>())
                    );
                instToReplace.ReplaceWith(new LdLoc(itemVariable));
                body.Instructions.Insert(0, new StLoc(itemVariable, instToReplace));
            }
            else
            {
                if (itemVariable.StoreCount != 1)
                {
                    return(null);
                }
                itemVariable.Type = type;
                itemVariable.Kind = VariableKind.ForeachLocal;
                itemVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), itemVariable);
            }
            var            whileLoop   = (WhileStatement)ConvertAsBlock(container).First();
            BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();

            foreachBody.Statements.First().Detach();
            var foreachStmt = new ForeachStatement {
                VariableType      = settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(itemVariable.Type),
                VariableName      = itemVariable.Name,
                InExpression      = collectionExpr.Detach(),
                EmbeddedStatement = foreachBody
            };

            foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type));
            if (optionalReturnAfterLoop != null)
            {
                return(new BlockStatement {
                    Statements =
                    {
                        foreachStmt,
                        optionalReturnAfterLoop.AcceptVisitor(this)
                    }
                });
            }
            return(foreachStmt);
        }
Example #12
0
        /// <summary>
        /// Extracts the specified instruction:
        ///   The instruction is replaced with a load of a new temporary variable;
        ///   and the instruction is moved to a store to said variable at block-level.
        ///
        /// May return null if extraction is not possible.
        /// </summary>
        public static ILVariable Extract(ILInstruction instToExtract, ILTransformContext context)
        {
            var function          = instToExtract.Ancestors.OfType <ILFunction>().First();
            ExtractionContext ctx = new ExtractionContext(function, context);

            ctx.FlagsBeingMoved = instToExtract.Flags;
            ILInstruction inst = instToExtract;

            while (inst != null)
            {
                if (inst.Parent is IfInstruction ifInst && inst.SlotInfo != IfInstruction.ConditionSlot)
                {
                    // this context doesn't support extraction, but maybe we can create a block here?
                    if (ifInst.ResultType == StackType.Void)
                    {
                        Block newBlock = new Block();

                        inst.ReplaceWith(newBlock);
                        newBlock.Instructions.Add(inst);
                    }
                }
                if (inst.Parent is Block {
                    Kind : BlockKind.ControlFlow
                } block)
                {
                    // We've reached a target block, and extraction is possible all the way.
                    // Check if the parent BlockContainer allows extraction:
                    if (block.Parent is BlockContainer container)
                    {
                        switch (container.Kind)
                        {
                        case ContainerKind.Normal :
                        case ContainerKind.Loop   :
                            // extraction is always possible
                            break;

                        case ContainerKind.Switch:
                            // extraction is possible, unless in the entry-point (i.e., the switch head)
                            if (block == container.EntryPoint && inst.ChildIndex == 0)
                            {
                                // try to extract to the container's parent block, if it's a valid location
                                inst = container;
                                continue;
                            }
                            break;

                        case ContainerKind.While:
                            // extraction is possible, unless in the entry-point (i.e., the condition block)
                            if (block == container.EntryPoint)
                            {
                                return(null);
                            }
                            break;

                        case ContainerKind.DoWhile:
                            // extraction is possible, unless in the last block (i.e., the condition block)
                            if (block == container.Blocks.Last())
                            {
                                return(null);
                            }
                            break;

                        case ContainerKind.For:
                            // extraction is possible, unless in the first or last block
                            // (i.e., the condition block or increment block)
                            if (block == container.EntryPoint ||
                                block == container.Blocks.Last())
                            {
                                return(null);
                            }
                            break;
                        }
                    }
                    int insertIndex = inst.ChildIndex;
                    var type        = context.TypeSystem.FindType(instToExtract.ResultType);
                    // Move instToExtract itself:
                    var v = function.RegisterVariable(VariableKind.StackSlot, type);
                    instToExtract.ReplaceWith(new LdLoc(v));
                    block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract));
                    // Apply the other move actions:
                    foreach (var moveAction in ctx.MoveActions)
                    {
                        block.Instructions.Insert(insertIndex, moveAction());
                    }
                    return(v);
                }
                if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx))
                {
                    return(null);
                }
                inst = inst.Parent;
            }
            return(null);
        }