Ejemplo n.º 1
0
        int AssignStateRanges(List <ILNode> body, int bodyLength, bool forDispose)
        {
            if (bodyLength == 0)
            {
                return(0);
            }
            for (int i = 0; i < bodyLength; i++)
            {
                StateRange nodeRange = ranges[body[i]];
                nodeRange.Simplify();

                ILLabel label = body[i] as ILLabel;
                if (label != null)
                {
                    ranges[body[i + 1]].UnionWith(nodeRange);
                    continue;
                }

                ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
                if (tryFinally != null)
                {
                    if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    ranges[tryFinally.TryBlock].UnionWith(nodeRange);
                    AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
                    continue;
                }

                ILExpression expr = body[i] as ILExpression;
                if (expr == null)
                {
                    throw new YieldAnalysisFailedException();
                }
                switch (expr.Code)
                {
                case ILCode.Switch:
                {
                    SymbolicValue val = Eval(expr.Arguments[0]);
                    if (val.Type != SymbolicValueType.State)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    ILLabel[] targetLabels = (ILLabel[])expr.Operand;
                    for (int j = 0; j < targetLabels.Length; j++)
                    {
                        int state = j - val.Constant;
                        ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
                    }
                    StateRange nextRange = ranges[body[i + 1]];
                    nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
                    nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
                    break;
                }

                case ILCode.Br:
                case ILCode.Leave:
                    ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
                    break;

                case ILCode.Brtrue:
                {
                    SymbolicValue val = Eval(expr.Arguments[0]);
                    if (val.Type == SymbolicValueType.StateEquals)
                    {
                        ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
                        StateRange nextRange = ranges[body[i + 1]];
                        nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
                        nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
                    }
                    else if (val.Type == SymbolicValueType.StateInEquals)
                    {
                        ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
                        StateRange targetRange = ranges[(ILLabel)expr.Operand];
                        targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
                        targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    break;
                }

                case ILCode.Nop:
                    ranges[body[i + 1]].UnionWith(nodeRange);
                    break;

                case ILCode.Ret:
                    break;

                case ILCode.Stloc:
                {
                    SymbolicValue val = Eval(expr.Arguments[0]);
                    if (val.Type == SymbolicValueType.State && val.Constant == 0 && rangeAnalysisStateVariable == null)
                    {
                        rangeAnalysisStateVariable = (ILVariable)expr.Operand;
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    goto case ILCode.Nop;
                }

                case ILCode.Call:
                    // in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
                    if (forDispose)
                    {
                        MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
                        if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
                        {
                            throw new YieldAnalysisFailedException();
                        }
                        finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
                    }
                    else
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    break;

                default:
                    if (forDispose)
                    {
                        throw new YieldAnalysisFailedException();
                    }
                    else
                    {
                        return(i);
                    }
                }
            }
            return(bodyLength);
        }
Ejemplo n.º 2
0
        public bool SimplifyTernaryOperator(List <ILNode> body, ILBasicBlock head, int pos)
        {
            Debug.Assert(body.Contains(head));

            ILExpression condExpr;
            ILLabel      trueLabel;
            ILLabel      falseLabel;
            ILVariable   trueLocVar = null;
            ILExpression trueExpr;
            ILLabel      trueFall;
            ILVariable   falseLocVar = null;
            ILExpression falseExpr;
            ILLabel      falseFall;
            object       unused;

            if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
                labelGlobalRefCount[trueLabel] == 1 &&
                labelGlobalRefCount[falseLabel] == 1 &&
                ((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
                  labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
                  trueLocVar == falseLocVar && trueFall == falseFall) ||
                 (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) &&
                  labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) &&
                body.Contains(labelToBasicBlock[trueLabel]) &&
                body.Contains(labelToBasicBlock[falseLabel])
                )
            {
                bool         isStloc          = trueLocVar != null;
                ILCode       opCode           = isStloc ? ILCode.Stloc : ILCode.Ret;
                TypeSig      retType          = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
                bool         retTypeIsBoolean = retType.GetElementType() == ElementType.Boolean;
                int          leftBoolVal;
                int          rightBoolVal;
                ILExpression newExpr;
                // a ? true:false  is equivalent to  a
                // a ? false:true  is equivalent to  !a
                // a ? true : b    is equivalent to  a || b
                // a ? b : true    is equivalent to  !a || b
                // a ? b : false   is equivalent to  a && b
                // a ? false : b   is equivalent to  !a && b
                if (retTypeIsBoolean &&
                    trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
                    falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
                    ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
                    )
                {
                    // It can be expressed as trivilal expression
                    if (leftBoolVal != 0)
                    {
                        newExpr = condExpr;
                    }
                    else
                    {
                        newExpr = new ILExpression(ILCode.LogicNot, null, condExpr)
                        {
                            InferredType = corLib.Boolean
                        };
                    }
                }
                else if ((retTypeIsBoolean || falseExpr.InferredType.GetElementType() == ElementType.Boolean) && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && (leftBoolVal == 0 || leftBoolVal == 1))
                {
                    // It can be expressed as logical expression
                    if (leftBoolVal != 0)
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
                    }
                    else
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
                    }
                }
                else if ((retTypeIsBoolean || trueExpr.InferredType.GetElementType() == ElementType.Boolean) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && (rightBoolVal == 0 || rightBoolVal == 1))
                {
                    // It can be expressed as logical expression
                    if (rightBoolVal != 0)
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
                    }
                    else
                    {
                        newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
                    }
                }
                else
                {
                    // Ternary operator tends to create long complicated return statements
                    if (opCode == ILCode.Ret)
                    {
                        return(false);
                    }

                    // Only simplify generated variables
                    if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated)
                    {
                        return(false);
                    }

                    // Create ternary expression
                    newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
                }

                var tail         = head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
                var newExprNodes = newExpr.GetSelfAndChildrenRecursive <ILNode>().ToArray();
                foreach (var node in labelToBasicBlock[trueLabel].GetSelfAndChildrenRecursive <ILNode>().Except(newExprNodes))
                {
                    newExpr.ILRanges.AddRange(node.AllILRanges);
                }
                foreach (var node in labelToBasicBlock[falseLabel].GetSelfAndChildrenRecursive <ILNode>().Except(newExprNodes))
                {
                    newExpr.ILRanges.AddRange(node.AllILRanges);
                }
                newExpr.ILRanges.AddRange(tail[0].ILRanges);
                newExpr.ILRanges.AddRange(tail[1].GetSelfAndChildrenRecursiveILRanges());

                head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr));
                if (isStloc)
                {
                    head.Body.Add(new ILExpression(ILCode.Br, trueFall));
                }

                // Remove the old basic blocks
                body.RemoveOrThrow(labelToBasicBlock[trueLabel]);
                body.RemoveOrThrow(labelToBasicBlock[falseLabel]);

                return(true);
            }
            return(false);
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Initializes the state range logic:
 /// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
 /// </summary>
 void InitStateRanges(ILNode entryPoint)
 {
     ranges                     = new DefaultDictionary <ILNode, StateRange>(n => new StateRange());
     ranges[entryPoint]         = new StateRange(int.MinValue, int.MaxValue);
     rangeAnalysisStateVariable = null;
 }
Ejemplo n.º 4
0
            public readonly ILVariable LoadFrom;                 // Variable used for storage of the value

            public StackSlot(ByteCode[] definitions, ILVariable loadFrom)
            {
                this.Definitions = definitions;
                this.LoadFrom    = loadFrom;
            }
Ejemplo n.º 5
0
        void AnalyzeMoveNext()
        {
            MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
            ILBlock          ilMethod       = CreateILAst(moveNextMethod);

            if (ilMethod.Body.Count == 0)
            {
                throw new SymbolicAnalysisFailedException();
            }
            ILExpression lastReturnArg;

            if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // There are two possibilities:
            if (lastReturnArg.Code == ILCode.Ldloc)
            {
                // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
                returnVariable = (ILVariable)lastReturnArg.Operand;
                returnLabel    = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
                if (returnLabel == null)
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }
            else
            {
                // b) the compiler directly returns constants
                returnVariable = null;
                returnLabel    = null;
                // In this case, the last return must return false.
                if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
                {
                    throw new SymbolicAnalysisFailedException();
                }
            }

            ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
            List <ILNode>   body;
            int             bodyLength;

            if (tryFaultBlock != null)
            {
                // there are try-finally blocks
                if (returnVariable == null)                 // in this case, we must use a return variable
                {
                    throw new SymbolicAnalysisFailedException();
                }
                // must be a try-fault block:
                if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
                {
                    throw new SymbolicAnalysisFailedException();
                }

                ILBlock faultBlock = tryFaultBlock.FaultBlock;
                // Ensure the fault block contains the call to Dispose().
                if (faultBlock.Body.Count != 2)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                MethodReference disposeMethodRef;
                ILExpression    disposeArg;
                if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (!faultBlock.Body[1].Match(ILCode.Endfinally))
                {
                    throw new SymbolicAnalysisFailedException();
                }

                body       = tryFaultBlock.TryBlock.Body;
                bodyLength = body.Count;
            }
            else
            {
                // no try-finally blocks
                body = ilMethod.Body;
                if (returnVariable == null)
                {
                    bodyLength = body.Count - 1;                     // all except for the return statement
                }
                else
                {
                    bodyLength = body.Count - 2;                     // all except for the return label and statement
                }
            }

            // Now verify that the last instruction in the body is 'ret(false)'
            if (returnVariable != null)
            {
                // If we don't have a return variable, we already verified that above.
                // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'

                // Maybe might be a jump to the return label after the stloc:
                ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
                {
                    bodyLength--;
                }
                ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
                {
                    throw new SymbolicAnalysisFailedException();
                }
                if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
                {
                    throw new SymbolicAnalysisFailedException();
                }

                bodyLength--;                 // don't conside the stloc instruction to be part of the body
            }
            // The last element in the body usually is a label pointing to the 'ret(false)'
            returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
            // Note: in Roslyn-compiled code, returnFalseLabel may be null.

            var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
            int pos           = rangeAnalysis.AssignStateRanges(body, bodyLength);

            rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength);

            var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength);

            ConvertBody(body, pos, bodyLength, labels);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
        /// </summary>
        /// <param name="next">The next top-level expression</param>
        /// <param name="parent">The direct parent of the load within 'next'</param>
        /// <param name="pos">Index of the load within 'parent'</param>
        /// <param name="v">The variable being inlined.</param>
        /// <param name="inlinedExpression">The expression being inlined</param>
        bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
        {
            if (pos == 0 && v.Type != null && DnlibExtensions.IsValueType(v.Type))
            {
                // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
                // that the method is operating on a copy.
                // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
                switch (inlinedExpression.Code)
                {
                case ILCode.Ldloc:
                case ILCode.Stloc:
                case ILCode.CompoundAssignment:
                case ILCode.Ldelem:
                case ILCode.Ldelem_I:
                case ILCode.Ldelem_I1:
                case ILCode.Ldelem_I2:
                case ILCode.Ldelem_I4:
                case ILCode.Ldelem_I8:
                case ILCode.Ldelem_R4:
                case ILCode.Ldelem_R8:
                case ILCode.Ldelem_Ref:
                case ILCode.Ldelem_U1:
                case ILCode.Ldelem_U2:
                case ILCode.Ldelem_U4:
                case ILCode.Ldobj:
                case ILCode.Ldind_Ref:
                    return(false);

                case ILCode.Ldfld:
                case ILCode.Stfld:
                case ILCode.Ldsfld:
                case ILCode.Stsfld:
                    // allow inlining field access only if it's a readonly field
                    FieldDef f = ((IField)inlinedExpression.Operand).Resolve();
                    if (!(f != null && f.IsInitOnly))
                    {
                        return(false);
                    }
                    break;

                case ILCode.Call:
                case ILCode.CallGetter:
                    // inlining runs both before and after IntroducePropertyAccessInstructions,
                    // so we have to handle both 'call' and 'callgetter'
                    IMethod mr = (IMethod)inlinedExpression.Operand;
                    // ensure that it's not an multi-dimensional array getter
                    TypeSig ts;
                    if (mr.DeclaringType is TypeSpec && (ts = ((TypeSpec)mr.DeclaringType).TypeSig.RemovePinnedAndModifiers()) != null && ts.IsSingleOrMultiDimensionalArray)
                    {
                        return(false);
                    }
                    goto case ILCode.Callvirt;

                case ILCode.Callvirt:
                case ILCode.CallvirtGetter:
                    // don't inline foreach loop variables:
                    mr = (IMethod)inlinedExpression.Operand;
                    if (mr.Name == "get_Current" && mr.MethodSig != null && mr.MethodSig.HasThis)
                    {
                        return(false);
                    }
                    break;

                case ILCode.Castclass:
                case ILCode.Unbox_Any:
                    // These are valid, but might occur as part of a foreach loop variable.
                    ILExpression arg = inlinedExpression.Arguments[0];
                    if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt)
                    {
                        mr = (IMethod)arg.Operand;
                        if (mr.Name == "get_Current" && mr.MethodSig != null && mr.MethodSig.HasThis)
                        {
                            return(false);                                    // looks like a foreach loop variable, so don't inline it
                        }
                    }
                    break;
                }

                // inline the compiler-generated variable that are used when accessing a member on a value type:
                switch (parent.Code)
                {
                case ILCode.Call:
                case ILCode.CallGetter:
                case ILCode.CallSetter:
                case ILCode.Callvirt:
                case ILCode.CallvirtGetter:
                case ILCode.CallvirtSetter:
                    IMethod mr = parent.Operand as IMethod;
                    return(mr == null || mr.MethodSig == null ? false : mr.MethodSig.HasThis);

                case ILCode.Stfld:
                case ILCode.Ldfld:
                case ILCode.Ldflda:
                case ILCode.Await:
                    return(true);
                }
            }
            return(false);
        }
Ejemplo n.º 7
0
        List <ByteCode> StackAnalysis(MethodDef methodDef)
        {
            Dictionary <Instruction, ByteCode> instrToByteCode = new Dictionary <Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            List <ByteCode>    body     = new List <ByteCode>(methodDef.Body.Instructions.Count);
            List <Instruction> prefixes = null;
            var instructions            = methodDef.Body.Instructions;

            for (int i = 0; i < instructions.Count; i++)
            {
                var inst = instructions[i];
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                    {
                        prefixes = new List <Instruction>(1);
                    }
                    prefixes.Add(inst);
                    continue;
                }
                ILCode code    = ilCodeTranslation[inst.OpCode.Code];
                object operand = inst.Operand;
                ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef);
                var      next     = i + 1 < instructions.Count ? instructions[i + 1] : null;
                ByteCode byteCode = new ByteCode()
                {
                    Offset    = inst.Offset,
                    EndOffset = next != null ? next.Offset : (uint)methodDef.Body.GetCodeSize(),
                    Code      = code,
                    Operand   = operand,
                    PopCount  = inst.GetPopDelta(methodDef),
                    PushCount = inst.GetPushDelta(methodDef)
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset   = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes          = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            Stack <ByteCode> agenda = new Stack <ByteCode>();

            int varCount = methodDef.Body.Variables.Count;

            var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => eh.HandlerStart == null ? null : instrToByteCode[eh.HandlerStart]));

            exceptionHandlerStarts.Remove(null);

            // HACK: Some MS reference assemblies contain just a RET instruction. If the method
            // returns a value, the code below will eventually throw an exception in
            // StackSlot.ModifyStack().
            if (body.Count == 1 && body[0].Code == ILCode.Ret)
            {
                body[0].PopCount = 0;
            }

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (ExceptionHandler ex in methodDef.Body.ExceptionHandlers)
                {
                    if (ex.HandlerStart == null)
                    {
                        continue;
                    }
                    ByteCode handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore     = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex]         = ldexception;
                        handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter && ex.FilterStart != null)
                    {
                        ByteCode filterStart = instrToByteCode[ex.FilterStart];
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        ldfilters[ex]               = ldexception;
                        filterStart.StackBefore     = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
                        filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore     = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                ByteCode byteCode = agenda.Pop();

                // Calculate new stack
                StackSlot[] newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);

                // Calculate new variable state
                VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition && byteCode.Operand is Local)
                {
                    newVariableState[((Local)byteCode.Operand).Index] = new VariableSlot(new [] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == ILCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUknownState(varCount);
                }

                // Find all successors
                List <ByteCode> branchTargets = new List <ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    if (exceptionHandlerStarts.Contains(byteCode.Next))
                    {
                        // Do not fall though down to exception handler
                        // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
                    }
                    else
                    {
                        branchTargets.Add(byteCode.Next);
                    }
                }
                if (byteCode.Operand is IList <Instruction> )
                {
                    foreach (Instruction inst in (IList <Instruction>)byteCode.Operand)
                    {
                        ByteCode target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new ILLabel()
                            {
                                Name = target.Name
                            };
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new ILLabel()
                        {
                            Name = target.Name
                        };
                    }
                }

                // Apply the state to successors
                foreach (ByteCode branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore     = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore     = StackSlot.ModifyStack(newStack, 0, 0, null);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Length != newStack.Length)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Length; i++)
                        {
                            ByteCode[] oldDefs = branchTarget.StackBefore[i].Definitions;
                            ByteCode[] newDefs = oldDefs.Union(newStack[i].Definitions);
                            if (newDefs.Length > oldDefs.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            VariableSlot oldSlot = branchTarget.VariablesBefore[i];
                            VariableSlot newSlot = newVariableState[i];
                            if (!oldSlot.UnknownDefinition)
                            {
                                if (newSlot.UnknownDefinition)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldDefs = oldSlot.Definitions;
                                    ByteCode[] newDefs = oldDefs.Union(newSlot.Definitions);
                                    if (newDefs.Length > oldDefs.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentionally invalid)
            // I believe it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Generate temporary variables to replace stack
            foreach (ByteCode byteCode in body)
            {
                int argIdx   = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true
                    };
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List <ILVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (ByteCode byteCode in body)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        ILVariable tmpVar = new ILVariable()
                        {
                            Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true
                        };
                        byteCode.StoreTo = new List <ILVariable>()
                        {
                            tmpVar
                        };
                        foreach (ByteCode bc in body)
                        {
                            for (int i = 0; i < bc.StackBefore.Length; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (ByteCode byteCode in body)
            {
                if (byteCode.Operand is IList <Instruction> )
                {
                    List <ILLabel> newOperand = new List <ILLabel>();
                    foreach (Instruction target in (IList <Instruction>)byteCode.Operand)
                    {
                        newOperand.Add(instrToByteCode[target].Label);
                    }
                    byteCode.Operand = newOperand.ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return(body);
        }
Ejemplo n.º 8
0
 public StackSlot(ByteCode pushedBy)
 {
     this.PushedBy = new[] { pushedBy };
     this.LoadFrom = null;
 }
Ejemplo n.º 9
0
        List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            List <ILNode> ast = new List <ILNode>();

            while (ehs.Any())
            {
                ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();

                // Find the first and widest scope
                int tryStart = ehs.Min(eh => eh.TryStart.Offset);
                int tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList();

                // Cut all instructions up to the try block
                {
                    int tryStartIdx;
                    for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++)
                    {
                        ;
                    }
                    ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
                }

                // Cut the try block
                {
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    int tryEndIdx;
                    for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++)
                    {
                        ;
                    }
                    tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>();
                foreach (ExceptionHandler eh in handlers)
                {
                    int startIndex;
                    for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++)
                    {
                        ;
                    }
                    int endInclusiveIndex;
                    if (eh.HandlerEnd == null)
                    {
                        endInclusiveIndex = body.Count - 1;
                    }
                    // Note that the end(exclusive) instruction may not necessarly be in our body
                    else
                    {
                        for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++)
                        {
                            ;
                        }
                    }
                    int count = 1 + endInclusiveIndex - startIndex;
                    HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)));
                    ehs.ExceptWith(nestedEHs);
                    List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock()
                        {
                            ExceptionType = eh.CatchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        ByteCode ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo.Count == 0)
                        {
                            throw new Exception("Exception should be consumed by something");
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            ILExpression first = catchBlock.Body[0] as ILExpression;
                            if (first != null &&
                                first.Code == ILCode.Pop &&
                                first.Arguments[0].Code == ILCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                catchBlock.ExceptionVariable = null;
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            ILVariable exTemp = new ILVariable()
                            {
                                Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true
                            };
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (ILVariable storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
Ejemplo n.º 10
0
        List <ByteCode> StackAnalysis(MethodDefinition methodDef)
        {
            Dictionary <Instruction, ByteCode> instrToByteCode = new Dictionary <Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            List <ByteCode>    body     = new List <ByteCode>(methodDef.Body.Instructions.Count);
            List <Instruction> prefixes = null;

            foreach (Instruction inst in methodDef.Body.Instructions)
            {
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                    {
                        prefixes = new List <Instruction>(1);
                    }
                    prefixes.Add(inst);
                    continue;
                }
                ILCode code    = (ILCode)inst.OpCode.Code;
                object operand = inst.Operand;
                ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
                ByteCode byteCode = new ByteCode()
                {
                    Offset    = inst.Offset,
                    EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    Code      = code,
                    Operand   = operand,
                    PopCount  = inst.GetPopDelta(),
                    PushCount = inst.GetPushDelta()
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset   = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes          = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            Stack <ByteCode> agenda = new Stack <ByteCode>();

            int varCount = methodDef.Body.Variables.Count;

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (ExceptionHandler ex in methodDef.Body.ExceptionHandlers)
                {
                    ByteCode handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore     = new List <StackSlot>();
                    handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex] = ldexception;
                        handlerStart.StackBefore.Add(new StackSlot(ldexception));
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        ByteCode filterStart = instrToByteCode[ex.FilterStart];
                        filterStart.StackBefore     = new List <StackSlot>();
                        filterStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
                        ByteCode ldexception = new ByteCode()
                        {
                            Code      = ILCode.Ldexception,
                            Operand   = ex.CatchType,
                            PopCount  = 0,
                            PushCount = 1
                        };
                        // TODO: ldexceptions[ex] = ldexception;
                        filterStart.StackBefore.Add(new StackSlot(ldexception));
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore     = new List <StackSlot>();
            body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                ByteCode byteCode = agenda.Pop();

                // Calculate new stack
                List <StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount);
                for (int i = 0; i < byteCode.PushCount; i++)
                {
                    newStack.Add(new StackSlot(byteCode));
                }

                // Calculate new variable state
                VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.Code == ILCode.Stloc)
                {
                    int varIndex = ((VariableReference)byteCode.Operand).Index;
                    newVariableState[varIndex] = new VariableSlot(byteCode);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == ILCode.Leave)
                {
                    newVariableState = VariableSlot.MakeFullState(varCount);
                }

                // Find all successors
                List <ByteCode> branchTargets = new List <ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    branchTargets.Add(byteCode.Next);
                }
                if (byteCode.Operand is Instruction[])
                {
                    foreach (Instruction inst in (Instruction[])byteCode.Operand)
                    {
                        ByteCode target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new ILLabel()
                            {
                                Name = target.Name
                            };
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new ILLabel()
                        {
                            Name = target.Name
                        };
                    }
                }

                // Apply the state to successors
                foreach (ByteCode branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore     = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore     = StackSlot.CloneStack(newStack, 0);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Count != newStack.Count)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Count; i++)
                        {
                            ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy;
                            ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy);
                            if (newPushedBy.Length > oldPushedBy.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            VariableSlot oldSlot = branchTarget.VariablesBefore[i];
                            VariableSlot newSlot = newVariableState[i];
                            // All can not be unioned further
                            if (!oldSlot.StoredByAll)
                            {
                                if (newSlot.StoredByAll)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldStoredBy = oldSlot.StoredBy;
                                    ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy);
                                    if (newStoredBy.Length > oldStoredBy.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compiler generates unreachable code - it can be usually just ignored
            var reachableBody   = body.Where(b => b.StackBefore != null);
            var unreachableBody = body.Where(b => b.StackBefore == null);

            // Genertate temporary variables to replace stack
            // Unrachable code does not need temporary variables - the values are never pushed on the stack for consuption
            foreach (ByteCode byteCode in reachableBody)
            {
                int argIdx   = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
                for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++)
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true
                    };
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].PushedBy)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List <ILVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            // Unrachable code will not have any StoreTo
            foreach (ByteCode byteCode in reachableBody)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Where(s => s.LoadFrom == locVar).Single()).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        ILVariable tmpVar = new ILVariable()
                        {
                            Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true
                        };
                        byteCode.StoreTo = new List <ILVariable>()
                        {
                            tmpVar
                        };
                        foreach (ByteCode bc in reachableBody)
                        {
                            for (int i = 0; i < bc.StackBefore.Count; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].PushedBy, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (ByteCode byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    List <ILLabel> newOperand = new List <ILLabel>();
                    foreach (Instruction target in (Instruction[])byteCode.Operand)
                    {
                        newOperand.Add(instrToByteCode[target].Label);
                    }
                    byteCode.Operand = newOperand.ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return(body);
        }
Ejemplo n.º 11
0
            public readonly ILVariable LoadFrom;              // Where can we get the value from in AST

            public StackSlot(ByteCode[] pushedBy, ILVariable loadFrom)
            {
                this.PushedBy = pushedBy;
                this.LoadFrom = loadFrom;
            }
        public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr)
        {
            ILVariable v;

            return(node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar);
        }
        public static bool MatchLdloca(this ILNode node, ILVariable expectedVar)
        {
            ILVariable v;

            return(node.Match(ILCode.Ldloca, out v) && v == expectedVar);
        }
Ejemplo n.º 14
0
        void HandleAwait(List <ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID)
        {
            // Handle the instructions prior to the exit out of the method to detect what is being awaited.
            // (analyses the last instructions in newBody and removes the analyzed instructions from newBody)

            if (doFinallyBodies != null)
            {
                // stloc(<>t__doFinallyBodies, ldc.i4(0))
                ILExpression dfbInitExpr;
                if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                int val;
                if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0))
                {
                    throw new SymbolicAnalysisFailedException();
                }
                newBody.RemoveAt(newBody.Count - 1);                 // remove doFinallyBodies assignment
            }

            // call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this))
            ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression;

            newBody.RemoveAt(newBody.Count - 1);             // remove AwaitUnsafeOnCompleted call
            if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call)
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name != "AwaitUnsafeOnCompleted")
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (callAwaitUnsafeOnCompleted.Arguments.Count != 3)
            {
                throw new SymbolicAnalysisFailedException();
            }
            if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001))
            FieldReference awaiterFieldRef;
            ILExpression   loadThis, loadAwaiterVar;

            if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar))
            {
                throw new SymbolicAnalysisFailedException();
            }
            newBody.RemoveAt(newBody.Count - 1);             // remove awaiter field assignment
            awaiterField = awaiterFieldRef.ResolveWithinSameModule();
            if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar)))
            {
                throw new SymbolicAnalysisFailedException();
            }

            // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
            if (!MatchStateAssignment(newBody.LastOrDefault(), out targetStateID))
            {
                throw new SymbolicAnalysisFailedException();
            }
            newBody.RemoveAt(newBody.Count - 1);             // remove awaiter field assignment
        }
Ejemplo n.º 15
0
        SymbolicValue Eval(ILExpression expr)
        {
            SymbolicValue left, right;

            switch (expr.Code)
            {
            case ILCode.Sub:
                left  = Eval(expr.Arguments[0]);
                right = Eval(expr.Arguments[1]);
                if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
                {
                    throw new YieldAnalysisFailedException();
                }
                if (right.Type != SymbolicValueType.IntegerConstant)
                {
                    throw new YieldAnalysisFailedException();
                }
                return(new SymbolicValue(left.Type, unchecked (left.Constant - right.Constant)));

            case ILCode.Ldfld:
                if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
                {
                    throw new YieldAnalysisFailedException();
                }
                if (GetFieldDefinition(expr.Operand as FieldReference) != stateField)
                {
                    throw new YieldAnalysisFailedException();
                }
                return(new SymbolicValue(SymbolicValueType.State));

            case ILCode.Ldloc:
                ILVariable loadedVariable = (ILVariable)expr.Operand;
                if (loadedVariable == rangeAnalysisStateVariable)
                {
                    return(new SymbolicValue(SymbolicValueType.State));
                }
                else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0)
                {
                    return(new SymbolicValue(SymbolicValueType.This));
                }
                else
                {
                    throw new YieldAnalysisFailedException();
                }

            case ILCode.Ldc_I4:
                return(new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand));

            case ILCode.Ceq:
                left  = Eval(expr.Arguments[0]);
                right = Eval(expr.Arguments[1]);
                if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
                {
                    throw new YieldAnalysisFailedException();
                }
                // bool: (state + left.Constant == right.Constant)
                // bool: (state == right.Constant - left.Constant)
                return(new SymbolicValue(SymbolicValueType.StateEquals, unchecked (right.Constant - left.Constant)));

            case ILCode.LogicNot:
                SymbolicValue val = Eval(expr.Arguments[0]);
                if (val.Type == SymbolicValueType.StateEquals)
                {
                    return(new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant));
                }
                else if (val.Type == SymbolicValueType.StateInEquals)
                {
                    return(new SymbolicValue(SymbolicValueType.StateEquals, val.Constant));
                }
                else
                {
                    throw new YieldAnalysisFailedException();
                }

            default:
                throw new YieldAnalysisFailedException();
            }
        }
Ejemplo n.º 16
0
        List <ILNode> ConvertToAst(List <ByteCode> body)
        {
            List <ILNode> ast = new List <ILNode>();

            // Convert stack-based IL code to ILAst tree
            foreach (ByteCode byteCode in body)
            {
                ILRange ilRange = new ILRange(byteCode.Offset, byteCode.EndOffset);

                if (byteCode.StackBefore == null)
                {
                    // Unreachable code
                    continue;
                }

                ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
                expr.ILRanges.Add(ilRange);
                if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0)
                {
                    ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length];
                    for (int i = 0; i < prefixes.Length; i++)
                    {
                        prefixes[i] = new ILExpressionPrefix(ilCodeTranslation[byteCode.Prefixes[i].OpCode.Code], byteCode.Prefixes[i].Operand);
                    }
                    expr.Prefixes = prefixes;
                }

                // Label for this instruction
                if (byteCode.Label != null)
                {
                    ast.Add(byteCode.Label);
                }

                // Reference arguments using temporary variables
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    StackSlot slot = byteCode.StackBefore[i];
                    expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom));
                }

                // Store the result to temporary variable(s) if needed
                if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0)
                {
                    ast.Add(expr);
                }
                else if (byteCode.StoreTo.Count == 1)
                {
                    ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr));
                }
                else
                {
                    ILVariable tmpVar = new ILVariable()
                    {
                        Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true
                    };
                    ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
                    foreach (ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse())
                    {
                        ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
                    }
                }
            }

            return(ast);
        }
Ejemplo n.º 17
0
        void AnalyzeMoveNext()
        {
            MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
            ILBlock          ilMethod       = CreateILAst(moveNextMethod);

            if (ilMethod.Body.Count == 0)
            {
                throw new YieldAnalysisFailedException();
            }
            ILExpression lastReturnArg;

            if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
            {
                throw new YieldAnalysisFailedException();
            }

            // There are two possibilities:
            if (lastReturnArg.Code == ILCode.Ldloc)
            {
                // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
                returnVariable = (ILVariable)lastReturnArg.Operand;
                returnLabel    = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
                if (returnLabel == null)
                {
                    throw new YieldAnalysisFailedException();
                }
            }
            else
            {
                // b) the compiler directly returns constants
                returnVariable = null;
                returnLabel    = null;
                // In this case, the last return must return false.
                if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
                {
                    throw new YieldAnalysisFailedException();
                }
            }

            ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
            List <ILNode>   body;
            int             bodyLength;

            if (tryFaultBlock != null)
            {
                // there are try-finally blocks
                if (returnVariable == null)                 // in this case, we must use a return variable
                {
                    throw new YieldAnalysisFailedException();
                }
                // must be a try-fault block:
                if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
                {
                    throw new YieldAnalysisFailedException();
                }

                ILBlock faultBlock = tryFaultBlock.FaultBlock;
                // Ensure the fault block contains the call to Dispose().
                if (faultBlock.Body.Count != 2)
                {
                    throw new YieldAnalysisFailedException();
                }
                MethodReference disposeMethodRef;
                ILExpression    disposeArg;
                if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
                {
                    throw new YieldAnalysisFailedException();
                }
                if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
                {
                    throw new YieldAnalysisFailedException();
                }
                if (!faultBlock.Body[1].Match(ILCode.Endfinally))
                {
                    throw new YieldAnalysisFailedException();
                }

                body       = tryFaultBlock.TryBlock.Body;
                bodyLength = body.Count;
            }
            else
            {
                // no try-finally blocks
                body = ilMethod.Body;
                if (returnVariable == null)
                {
                    bodyLength = body.Count - 1;                     // all except for the return statement
                }
                else
                {
                    bodyLength = body.Count - 2;                     // all except for the return label and statement
                }
            }

            // Now verify that the last instruction in the body is 'ret(false)'
            if (returnVariable != null)
            {
                // If we don't have a return variable, we already verified that above.
                // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'

                // Maybe might be a jump to the return label after the stloc:
                ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
                {
                    bodyLength--;
                }
                ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
                if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
                {
                    throw new YieldAnalysisFailedException();
                }
                if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
                {
                    throw new YieldAnalysisFailedException();
                }

                bodyLength--;                 // don't conside the stloc instruction to be part of the body
            }
            // verify that the last element in the body is a label pointing to the 'ret(false)'
            returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
            if (returnFalseLabel == null)
            {
                throw new YieldAnalysisFailedException();
            }

            InitStateRanges(body[0]);
            int pos = AssignStateRanges(body, bodyLength, forDispose: false);

            if (pos > 0 && body[pos - 1] is ILLabel)
            {
                pos--;
            }
            else
            {
                // ensure that the first element at body[pos] is a label:
                ILLabel newLabel = new ILLabel();
                newLabel.Name    = "YieldReturnEntryPoint";
                ranges[newLabel] = ranges[body[pos]];                 // give the label the range of the instruction at body[pos]

                body.Insert(pos, newLabel);
                bodyLength++;
            }

            List <KeyValuePair <ILLabel, StateRange> > labels = new List <KeyValuePair <ILLabel, StateRange> >();

            for (int i = pos; i < bodyLength; i++)
            {
                ILLabel label = body[i] as ILLabel;
                if (label != null)
                {
                    labels.Add(new KeyValuePair <ILLabel, StateRange>(label, ranges[label]));
                }
            }

            ConvertBody(body, pos, bodyLength, labels);
        }
        static bool AdjustInitializerStack(List <ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType)
        {
            // Argument is of the form 'getter(getter(...(v)))'
            // Unpack it into a list of getters:
            List <ILExpression> getters = new List <ILExpression>();

            while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld)
            {
                getters.Add(argument);
                if (argument.Arguments.Count != 1)
                {
                    return(false);
                }
                argument = argument.Arguments[0];
            }
            // Ensure that the final argument is 'v'
            if (isValueType)
            {
                ILVariable loadedVar;
                if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v))
                {
                    return(false);
                }
            }
            else
            {
                if (!argument.MatchLdloc(v))
                {
                    return(false);
                }
            }
            // Now compare the getters with those that are currently active on the initializer stack:
            int i;

            for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++)
            {
                ILExpression g1 = initializerStack[i].Arguments[0];           // getter stored in initializer
                ILExpression g2 = getters[getters.Count - i];                 // matching getter from argument
                if (g1.Operand != g2.Operand)
                {
                    // operands differ, so we abort the comparison
                    break;
                }
            }
            // Remove all initializers from the stack that were not matched with one from the argument:
            initializerStack.RemoveRange(i, initializerStack.Count - i);
            // Now create new initializers for the remaining arguments:
            for (; i <= getters.Count; i++)
            {
                ILExpression    g  = getters[getters.Count - i];
                MemberReference mr = (MemberReference)g.Operand;
                TypeReference   returnType;
                if (mr is FieldReference)
                {
                    returnType = TypeAnalysis.GetFieldType((FieldReference)mr);
                }
                else
                {
                    returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr);
                }

                ILExpression nestedInitializer = new ILExpression(
                    IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject,
                    null, g);
                // add new initializer to its parent:
                ILExpression parentInitializer = initializerStack[initializerStack.Count - 1];
                if (parentInitializer.Code == ILCode.InitCollection)
                {
                    // can't add children to collection initializer
                    if (parentInitializer.Arguments.Count == 1)
                    {
                        // convert empty collection initializer to object initializer
                        parentInitializer.Code = ILCode.InitObject;
                    }
                    else
                    {
                        return(false);
                    }
                }
                parentInitializer.Arguments.Add(nestedInitializer);
                initializerStack.Add(nestedInitializer);
            }
            ILExpression lastInitializer = initializerStack[initializerStack.Count - 1];

            if (isCollection)
            {
                return(lastInitializer.Code == ILCode.InitCollection);
            }
            else
            {
                if (lastInitializer.Code == ILCode.InitCollection)
                {
                    if (lastInitializer.Arguments.Count == 1)
                    {
                        // convert empty collection initializer to object initializer
                        lastInitializer.Code = ILCode.InitObject;
                        return(true);
                    }
                    else
                    {
                        return(false);
                    }
                }
                else
                {
                    return(true);
                }
            }
        }