private ValueNodeList PopCallArguments( Stack <StackSlot> currentStack, MethodReference methodCalled, MethodBody containingMethodBody, bool isNewObj, int ilOffset, out ValueNode newObjValue) { newObjValue = null; int countToPop = 0; if (!isNewObj && methodCalled.HasThis && !methodCalled.ExplicitThis) { countToPop++; } countToPop += methodCalled.Parameters.Count; ValueNodeList methodParams = new ValueNodeList(countToPop); for (int iParam = 0; iParam < countToPop; ++iParam) { StackSlot slot = PopUnknown(currentStack, 1, containingMethodBody, ilOffset); methodParams.Add(slot.Value); } if (isNewObj) { newObjValue = UnknownValue.Instance; methodParams.Add(newObjValue); } methodParams.Reverse(); return(methodParams); }
private static void ScanLdtoken(Instruction operation, Stack <StackSlot> currentStack) { if (operation.Operand is GenericParameter genericParameter) { StackSlot slot = new StackSlot(new RuntimeTypeHandleForGenericParameterValue(genericParameter)); currentStack.Push(slot); return; } if (operation.Operand is TypeReference typeReference) { var resolvedReference = typeReference.ResolveToMainTypeDefinition(); if (resolvedReference != null) { StackSlot slot = new StackSlot(new RuntimeTypeHandleValue(resolvedReference)); currentStack.Push(slot); return; } } else if (operation.Operand is MethodReference methodReference) { var resolvedMethod = methodReference.Resolve(); if (resolvedMethod != null) { StackSlot slot = new StackSlot(new RuntimeMethodHandleValue(resolvedMethod)); currentStack.Push(slot); return; } } PushUnknown(currentStack); }
private void ScanLdfld( Instruction operation, Stack <StackSlot> currentStack, MethodDefinition thisMethod, MethodBody methodBody) { Code code = operation.OpCode.Code; if (code == Code.Ldfld || code == Code.Ldflda) { PopUnknown(currentStack, 1, methodBody, operation.Offset); } bool isByRef = code == Code.Ldflda || code == Code.Ldsflda; FieldDefinition field = (operation.Operand as FieldReference)?.Resolve(); if (field != null) { StackSlot slot = new StackSlot(GetFieldValue(thisMethod, field), isByRef); currentStack.Push(slot); return; } PushUnknown(currentStack); }
private void ScanLdarg(Instruction operation, Stack <StackSlot> currentStack, MethodDefinition thisMethod, MethodBody methodBody) { Code code = operation.OpCode.Code; bool isByRef; // Thank you Cecil, Operand being a ParameterDefinition instead of an integer, // (except for Ldarg_0 - Ldarg_3, where it's null) makes all of this really convenient... // NOT. int paramNum; if (code >= Code.Ldarg_0 && code <= Code.Ldarg_3) { paramNum = code - Code.Ldarg_0; if (thisMethod.HasImplicitThis()) { if (paramNum == 0) { isByRef = thisMethod.DeclaringType.IsValueType; } else { isByRef = thisMethod.Parameters[paramNum - 1].ParameterType.IsByRefOrPointer(); } } else { isByRef = thisMethod.Parameters[paramNum].ParameterType.IsByRefOrPointer(); } } else { var paramDefinition = (ParameterDefinition)operation.Operand; if (thisMethod.HasImplicitThis()) { if (paramDefinition == methodBody.ThisParameter) { paramNum = 0; } else { paramNum = paramDefinition.Index + 1; } } else { paramNum = paramDefinition.Index; } isByRef = paramDefinition.ParameterType.IsByRefOrPointer(); } isByRef |= code == Code.Ldarga || code == Code.Ldarga_S; StackSlot slot = new StackSlot(GetMethodParameterValue(thisMethod, paramNum), isByRef); currentStack.Push(slot); }
private void ScanStelem( Instruction operation, Stack <StackSlot> currentStack, MethodBody methodBody, int curBasicBlock) { StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, operation.Offset); StackSlot indexToStoreAt = PopUnknown(currentStack, 1, methodBody, operation.Offset); StackSlot arrayToStoreIn = PopUnknown(currentStack, 1, methodBody, operation.Offset); int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt(); foreach (var array in arrayToStoreIn.Value.UniqueValues()) { if (array is ArrayValue arrValue) { if (indexToStoreAtInt == null) { MarkArrayValuesAsUnknown(arrValue, curBasicBlock); } else { // When we know the index, we can record the value at that index. StoreMethodLocalValue(arrValue.IndexValues, valueToStore.Value, indexToStoreAtInt.Value, curBasicBlock, MaxTrackedArrayValues); } } } }
private void ScanLdtoken( Instruction operation, Stack <StackSlot> currentStack, MethodDefinition thisMethod, MethodBody methodBody) { if (operation.Operand is GenericParameter genericParameter) { StackSlot slot = new StackSlot(new RuntimeTypeHandleForGenericParameterValue(genericParameter)); currentStack.Push(slot); return; } if (operation.Operand is TypeReference typeReference) { var resolvedReference = typeReference.Resolve(); if (resolvedReference != null) { StackSlot slot = new StackSlot(new RuntimeTypeHandleValue(resolvedReference)); currentStack.Push(slot); return; } } PushUnknown(currentStack); }
private void ScanStloc( Instruction operation, Stack <StackSlot> currentStack, MethodBody methodBody, Dictionary <VariableDefinition, ValueBasicBlockPair> locals, int curBasicBlock) { StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, operation.Offset); VariableDefinition localDef = GetLocalDef(operation, methodBody.Variables); if (localDef == null) { WarnAboutInvalidILInMethod(methodBody, operation.Offset); return; } StoreMethodLocalValue(locals, valueToStore.Value, localDef, curBasicBlock); }
private static StackSlot MergeStackElement(StackSlot a, StackSlot b) { StackSlot mergedSlot; if (b.Value == null) { mergedSlot = a; } else if (a.Value == null) { mergedSlot = b; } else { mergedSlot = new StackSlot(MergePointValue.MergeValues(a.Value, b.Value)); } return(mergedSlot); }
private void ScanLdelem( Instruction operation, Stack <StackSlot> currentStack, MethodBody methodBody, int curBasicBlock) { StackSlot indexToLoadFrom = PopUnknown(currentStack, 1, methodBody, operation.Offset); StackSlot arrayToLoadFrom = PopUnknown(currentStack, 1, methodBody, operation.Offset); if (arrayToLoadFrom.Value is not ArrayValue arr) { PushUnknown(currentStack); return; } bool isByRef = operation.OpCode.Code == Code.Ldelema; int?index = indexToLoadFrom.Value.AsConstInt(); if (index == null) { PushUnknown(currentStack); if (isByRef) { MarkArrayValuesAsUnknown(arr, curBasicBlock); } return; } ValueBasicBlockPair arrayIndexValue; arr.IndexValues.TryGetValue(index.Value, out arrayIndexValue); if (arrayIndexValue.Value != null) { ValueNode valueToPush = arrayIndexValue.Value; currentStack.Push(new StackSlot(valueToPush, isByRef)); } else { currentStack.Push(new StackSlot(null, isByRef)); } }
private void ScanStfld( Instruction operation, Stack <StackSlot> currentStack, MethodDefinition thisMethod, MethodBody methodBody) { StackSlot valueToStoreSlot = PopUnknown(currentStack, 1, methodBody, operation.Offset); if (operation.OpCode.Code == Code.Stfld) { PopUnknown(currentStack, 1, methodBody, operation.Offset); } FieldDefinition field = (operation.Operand as FieldReference)?.Resolve(); if (field != null) { HandleStoreField(thisMethod, field, operation, valueToStoreSlot.Value); } }
private void ScanIndirectStore( Instruction operation, Stack <StackSlot> currentStack, MethodBody methodBody) { StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, operation.Offset); StackSlot destination = PopUnknown(currentStack, 1, methodBody, operation.Offset); foreach (var uniqueDestination in destination.Value.UniqueValues()) { if (uniqueDestination.Kind == ValueNodeKind.LoadField) { HandleStoreField(methodBody.Method, ((LoadFieldValue)uniqueDestination).Field, operation, valueToStore.Value); } else if (uniqueDestination.Kind == ValueNodeKind.MethodParameter) { HandleStoreParameter(methodBody.Method, ((MethodParameterValue)uniqueDestination).ParameterIndex, operation, valueToStore.Value); } } }
private StackSlot PopUnknown(Stack <StackSlot> stack, int count, MethodBody method, int ilOffset) { if (count < 1) { throw new InvalidOperationException(); } StackSlot topOfStack = default; CheckForInvalidStack(stack, count, method, ilOffset); for (int i = 0; i < count; ++i) { StackSlot slot = stack.Pop(); if (i == 0) { topOfStack = slot; } } return(topOfStack); }
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; } } }
private static StackSlot MergeStackElement(StackSlot a, StackSlot b) { return(new StackSlot(MultiValueLattice.Meet(a.Value, b.Value))); }