/// <summary> /// Returns true if a ValueNode graph contains a cycle /// </summary> /// <param name="node">Node to evaluate</param> /// <param name="seenNodes">Set of nodes previously seen on the current arc. Callers may pass a non-empty set /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk /// and should not be used by the caller after returning</param> /// <param name="allNodesSeen">Optional. The set of all nodes encountered during a walk after DetectCycle returns</param> /// <returns></returns> public static bool DetectCycle(this ValueNode node, HashSet <ValueNode> seenNodes, HashSet <ValueNode> allNodesSeen) { if (node == null) { return(false); } if (seenNodes.Contains(node)) { return(true); } seenNodes.Add(node); if (allNodesSeen != null) { allNodesSeen.Add(node); } bool foundCycle = false; switch (node.Kind) { // // Leaf nodes // case ValueNodeKind.Unknown: case ValueNodeKind.Null: case ValueNodeKind.SystemType: case ValueNodeKind.RuntimeTypeHandle: case ValueNodeKind.KnownString: case ValueNodeKind.AnnotatedString: case ValueNodeKind.ConstInt: case ValueNodeKind.MethodParameter: case ValueNodeKind.MethodReturn: case ValueNodeKind.SystemTypeForGenericParameter: case ValueNodeKind.RuntimeTypeHandleForGenericParameter: case ValueNodeKind.LoadField: break; // // Nodes with children // case ValueNodeKind.MergePoint: foreach (ValueNode val in ((MergePointValue)node).Values) { if (val.DetectCycle(seenNodes, allNodesSeen)) { foundCycle = true; } } break; case ValueNodeKind.GetTypeFromString: GetTypeFromStringValue gtfsv = (GetTypeFromStringValue)node; foundCycle = gtfsv.AssemblyIdentity.DetectCycle(seenNodes, allNodesSeen); foundCycle |= gtfsv.NameString.DetectCycle(seenNodes, allNodesSeen); break; case ValueNodeKind.Array: ArrayValue av = (ArrayValue)node; foundCycle = av.Size.DetectCycle(seenNodes, allNodesSeen); break; default: throw new Exception(String.Format("Unknown node kind: {0}", node.Kind)); } seenNodes.Remove(node); return(foundCycle); }
public GetTypeFromStringValue(TypeResolver resolver, ValueNode assemblyIdentity, ValueNode nameString) { _resolver = resolver; Kind = ValueNodeKind.GetTypeFromString; AssemblyIdentity = assemblyIdentity; NameString = nameString; }
/// <summary> /// Constructs an array value of the given size /// </summary> public ArrayValue(ValueNode size) { Kind = ValueNodeKind.Array; Size = size ?? UnknownValue.Instance; }
protected virtual void HandleStoreParameter(MethodDefinition method, int index, Instruction operation, ValueNode valueToStore) { }
protected virtual void HandleStoreField(MethodDefinition method, FieldDefinition field, Instruction operation, ValueNode valueToStore) { }
public StackSlot(ValueNode value, bool isByRef = false) { Value = value; IsByRef = isByRef; }
public void Scan(MethodBody methodBody) { MethodDefinition thisMethod = methodBody.Method; Dictionary <VariableDefinition, ValueBasicBlockPair> locals = new Dictionary <VariableDefinition, ValueBasicBlockPair> (methodBody.Variables.Count); Dictionary <int, Stack <StackSlot> > knownStacks = new Dictionary <int, Stack <StackSlot> > (); Stack <StackSlot> currentStack = new Stack <StackSlot> (methodBody.MaxStackSize); ScanExceptionInformation(knownStacks, methodBody); BasicBlockIterator blockIterator = new BasicBlockIterator(methodBody); MethodReturnValue = null; foreach (Instruction operation in methodBody.Instructions) { int curBasicBlock = blockIterator.MoveNext(operation); if (knownStacks.ContainsKey(operation.Offset)) { if (currentStack == null) { // The stack copy constructor reverses the stack currentStack = new Stack <StackSlot> (knownStacks[operation.Offset].Reverse()); } else { currentStack = MergeStack(currentStack, knownStacks[operation.Offset]); } } if (currentStack == null) { currentStack = new Stack <StackSlot> (methodBody.MaxStackSize); } switch (operation.OpCode.Code) { case Code.Add: case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.And: case Code.Div: case Code.Div_Un: case Code.Mul: case Code.Mul_Ovf: case Code.Mul_Ovf_Un: case Code.Or: case Code.Rem: case Code.Rem_Un: case Code.Sub: case Code.Sub_Ovf: case Code.Sub_Ovf_Un: case Code.Xor: case Code.Cgt: case Code.Cgt_Un: case Code.Clt: case Code.Clt_Un: case Code.Shl: case Code.Shr: case Code.Shr_Un: case Code.Ceq: PopUnknown(currentStack, 2, methodBody, operation.Offset); PushUnknown(currentStack); break; case Code.Dup: currentStack.Push(currentStack.Peek()); break; case Code.Ldnull: currentStack.Push(new StackSlot(NullValue.Instance)); break; case Code.Ldc_I4_0: case Code.Ldc_I4_1: case Code.Ldc_I4_2: case Code.Ldc_I4_3: case Code.Ldc_I4_4: case Code.Ldc_I4_5: case Code.Ldc_I4_6: case Code.Ldc_I4_7: case Code.Ldc_I4_8: { int value = operation.OpCode.Code - Code.Ldc_I4_0; ConstIntValue civ = new ConstIntValue(value); StackSlot slot = new StackSlot(civ); currentStack.Push(slot); } break; case Code.Ldc_I4_M1: { ConstIntValue civ = new ConstIntValue(-1); StackSlot slot = new StackSlot(civ); currentStack.Push(slot); } break; case Code.Ldc_I4: { int value = (int)operation.Operand; ConstIntValue civ = new ConstIntValue(value); StackSlot slot = new StackSlot(civ); currentStack.Push(slot); } break; case Code.Ldc_I4_S: { int value = (sbyte)operation.Operand; ConstIntValue civ = new ConstIntValue(value); StackSlot slot = new StackSlot(civ); currentStack.Push(slot); } break; case Code.Arglist: case Code.Ldftn: case Code.Sizeof: case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: PushUnknown(currentStack); break; case Code.Ldarg: case Code.Ldarg_0: case Code.Ldarg_1: case Code.Ldarg_2: case Code.Ldarg_3: case Code.Ldarg_S: case Code.Ldarga: case Code.Ldarga_S: ScanLdarg(operation, currentStack, thisMethod, methodBody); break; case Code.Ldloc: case Code.Ldloc_0: case Code.Ldloc_1: case Code.Ldloc_2: case Code.Ldloc_3: case Code.Ldloc_S: case Code.Ldloca: case Code.Ldloca_S: ScanLdloc(operation, currentStack, methodBody, locals); break; case Code.Ldstr: { StackSlot slot = new StackSlot(new KnownStringValue((string)operation.Operand)); currentStack.Push(slot); } break; case Code.Ldtoken: ScanLdtoken(operation, currentStack); break; case Code.Ldind_I: case Code.Ldind_I1: case Code.Ldind_I2: case Code.Ldind_I4: case Code.Ldind_I8: case Code.Ldind_R4: case Code.Ldind_R8: case Code.Ldind_U1: case Code.Ldind_U2: case Code.Ldind_U4: case Code.Ldlen: case Code.Ldvirtftn: case Code.Localloc: case Code.Refanytype: case Code.Refanyval: case Code.Conv_I1: case Code.Conv_I2: case Code.Conv_I4: case Code.Conv_Ovf_I1: case Code.Conv_Ovf_I1_Un: case Code.Conv_Ovf_I2: case Code.Conv_Ovf_I2_Un: case Code.Conv_Ovf_I4: case Code.Conv_Ovf_I4_Un: case Code.Conv_Ovf_U: case Code.Conv_Ovf_U_Un: case Code.Conv_Ovf_U1: case Code.Conv_Ovf_U1_Un: case Code.Conv_Ovf_U2: case Code.Conv_Ovf_U2_Un: case Code.Conv_Ovf_U4: case Code.Conv_Ovf_U4_Un: case Code.Conv_U1: case Code.Conv_U2: case Code.Conv_U4: case Code.Conv_I8: case Code.Conv_Ovf_I8: case Code.Conv_Ovf_I8_Un: case Code.Conv_Ovf_U8: case Code.Conv_Ovf_U8_Un: case Code.Conv_U8: case Code.Conv_I: case Code.Conv_Ovf_I: case Code.Conv_Ovf_I_Un: case Code.Conv_U: case Code.Conv_R_Un: case Code.Conv_R4: case Code.Conv_R8: case Code.Ldind_Ref: case Code.Ldobj: case Code.Mkrefany: case Code.Unbox: case Code.Unbox_Any: case Code.Box: case Code.Neg: case Code.Not: PopUnknown(currentStack, 1, methodBody, operation.Offset); PushUnknown(currentStack); break; case Code.Isinst: case Code.Castclass: // We can consider a NOP because the value doesn't change. // It might change to NULL, but for the purposes of dataflow analysis // it doesn't hurt much. break; case Code.Ldfld: case Code.Ldsfld: case Code.Ldflda: case Code.Ldsflda: ScanLdfld(operation, currentStack, thisMethod, methodBody); break; case Code.Newarr: { StackSlot count = PopUnknown(currentStack, 1, methodBody, operation.Offset); currentStack.Push(new StackSlot(new ArrayValue(count.Value, (TypeReference)operation.Operand))); } break; case Code.Stelem_I: case Code.Stelem_I1: case Code.Stelem_I2: case Code.Stelem_I4: case Code.Stelem_I8: case Code.Stelem_R4: case Code.Stelem_R8: case Code.Stelem_Any: case Code.Stelem_Ref: ScanStelem(operation, currentStack, methodBody, curBasicBlock); break; case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: case Code.Ldelem_I4: case Code.Ldelem_I8: case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_U1: case Code.Ldelem_U2: case Code.Ldelem_U4: case Code.Ldelem_Any: case Code.Ldelem_Ref: case Code.Ldelema: ScanLdelem(operation, currentStack, methodBody, curBasicBlock); break; case Code.Cpblk: case Code.Initblk: PopUnknown(currentStack, 3, methodBody, operation.Offset); break; case Code.Stfld: case Code.Stsfld: ScanStfld(operation, currentStack, thisMethod, methodBody); break; case Code.Cpobj: PopUnknown(currentStack, 2, methodBody, operation.Offset); break; case Code.Stind_I: case Code.Stind_I1: case Code.Stind_I2: case Code.Stind_I4: case Code.Stind_I8: case Code.Stind_R4: case Code.Stind_R8: case Code.Stind_Ref: case Code.Stobj: ScanIndirectStore(operation, currentStack, methodBody); break; case Code.Initobj: case Code.Pop: PopUnknown(currentStack, 1, methodBody, operation.Offset); break; case Code.Starg: case Code.Starg_S: ScanStarg(operation, currentStack, thisMethod, methodBody); break; case Code.Stloc: case Code.Stloc_S: case Code.Stloc_0: case Code.Stloc_1: case Code.Stloc_2: case Code.Stloc_3: ScanStloc(operation, currentStack, methodBody, locals, curBasicBlock); break; case Code.Constrained: case Code.No: case Code.Readonly: case Code.Tail: case Code.Unaligned: case Code.Volatile: break; case Code.Brfalse: case Code.Brfalse_S: case Code.Brtrue: case Code.Brtrue_S: PopUnknown(currentStack, 1, methodBody, operation.Offset); NewKnownStack(knownStacks, ((Instruction)operation.Operand).Offset, currentStack); break; case Code.Calli: { var signature = (CallSite)operation.Operand; if (signature.HasThis && !signature.ExplicitThis) { PopUnknown(currentStack, 1, methodBody, operation.Offset); } // Pop arguments PopUnknown(currentStack, signature.Parameters.Count, methodBody, operation.Offset); // Pop function pointer PopUnknown(currentStack, 1, methodBody, operation.Offset); // Push return value if (signature.ReturnType.MetadataType != MetadataType.Void) { PushUnknown(currentStack); } } break; case Code.Call: case Code.Callvirt: case Code.Newobj: HandleCall(methodBody, operation, currentStack, curBasicBlock); break; case Code.Jmp: // Not generated by mainstream compilers break; case Code.Br: case Code.Br_S: NewKnownStack(knownStacks, ((Instruction)operation.Operand).Offset, currentStack); ClearStack(ref currentStack); break; case Code.Leave: case Code.Leave_S: ClearStack(ref currentStack); NewKnownStack(knownStacks, ((Instruction)operation.Operand).Offset, new Stack <StackSlot> (methodBody.MaxStackSize)); break; case Code.Endfilter: case Code.Endfinally: case Code.Rethrow: case Code.Throw: ClearStack(ref currentStack); break; case Code.Ret: { bool hasReturnValue = methodBody.Method.ReturnType.MetadataType != MetadataType.Void; if (currentStack.Count != (hasReturnValue ? 1 : 0)) { WarnAboutInvalidILInMethod(methodBody, operation.Offset); } if (hasReturnValue) { StackSlot retValue = PopUnknown(currentStack, 1, methodBody, operation.Offset); MethodReturnValue = MergePointValue.MergeValues(MethodReturnValue, retValue.Value); } ClearStack(ref currentStack); break; } case Code.Switch: { PopUnknown(currentStack, 1, methodBody, operation.Offset); Instruction[] targets = (Instruction[])operation.Operand; foreach (Instruction target in targets) { NewKnownStack(knownStacks, target.Offset, currentStack); } break; } case Code.Beq: case Code.Beq_S: case Code.Bne_Un: case Code.Bne_Un_S: case Code.Bge: case Code.Bge_S: case Code.Bge_Un: case Code.Bge_Un_S: case Code.Bgt: case Code.Bgt_S: case Code.Bgt_Un: case Code.Bgt_Un_S: case Code.Ble: case Code.Ble_S: case Code.Ble_Un: case Code.Ble_Un_S: case Code.Blt: case Code.Blt_S: case Code.Blt_Un: case Code.Blt_Un_S: PopUnknown(currentStack, 2, methodBody, operation.Offset); NewKnownStack(knownStacks, ((Instruction)operation.Operand).Offset, currentStack); break; } } }
public abstract bool HandleCall( MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out ValueNode methodReturnValue);