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 void HandleCall( MethodBody callingMethodBody, Instruction operation, Stack <StackSlot> currentStack, int curBasicBlock) { MethodReference calledMethod = (MethodReference)operation.Operand; bool isNewObj = operation.OpCode.Code == Code.Newobj; ValueNode newObjValue; ValueNodeList methodParams = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj, operation.Offset, out newObjValue); ValueNode methodReturnValue; bool handledFunction = HandleCall( callingMethodBody, calledMethod, operation, methodParams, out methodReturnValue); // Handle the return value or newobj result if (!handledFunction) { if (isNewObj) { if (newObjValue == null) { PushUnknown(currentStack); } else { methodReturnValue = newObjValue; } } else { if (calledMethod.ReturnType.MetadataType != MetadataType.Void) { methodReturnValue = UnknownValue.Instance; } } } if (methodReturnValue != null) { currentStack.Push(new StackSlot(methodReturnValue, calledMethod.ReturnType.IsByRefOrPointer())); } foreach (var param in methodParams) { if (param is ArrayValue arr) { MarkArrayValuesAsUnknown(arr, curBasicBlock); } } }
public override bool Equals(object other) { ValueNodeList otherList = other as ValueNodeList; if (otherList == null) { return(false); } if (otherList.Count != Count) { return(false); } for (int i = 0; i < Count; i++) { if (!otherList[i].Equals(this[i])) { return(false); } } return(true); }
public abstract bool HandleCall( MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out ValueNode methodReturnValue);
public override bool HandleCall(MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out MultiValue methodReturnValue) { methodReturnValue = new (); var reflectionProcessed = _markStep.ProcessReflectionDependency(callingMethodBody, operation); if (reflectionProcessed) { return(false); } Debug.Assert(callingMethodBody.Method == _origin.Provider); var calledMethodDefinition = _context.TryResolve(calledMethod); if (calledMethodDefinition == null) { return(false); } _origin = _origin.WithInstructionOffset(operation.Offset); MultiValue instanceValue; ImmutableArray <MultiValue> arguments; if (calledMethodDefinition.HasImplicitThis()) { instanceValue = methodParams[0]; arguments = methodParams.Skip(1).ToImmutableArray(); } else { instanceValue = MultiValueLattice.Top; arguments = methodParams.ToImmutableArray(); } TrimAnalysisPatterns.Add(new TrimAnalysisMethodCallPattern( operation, calledMethod, instanceValue, arguments, _origin )); var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: false, _context); return(HandleCall( operation, calledMethod, instanceValue, arguments, diagnosticContext, _reflectionMarker, _context, _markStep, out methodReturnValue)); }
public override bool HandleCall(MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out MultiValue methodReturnValue) { methodReturnValue = new (); MultiValue?maybeMethodReturnValue = null; var reflectionProcessed = _markStep.ProcessReflectionDependency(callingMethodBody, operation); if (reflectionProcessed) { return(false); } var callingMethodDefinition = callingMethodBody.Method; var calledMethodDefinition = _context.TryResolve(calledMethod); if (calledMethodDefinition == null) { return(false); } bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis(calledMethodDefinition); DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation(calledMethodDefinition) : 0; _origin = _origin.WithInstructionOffset(operation.Offset); bool diagnosticsEnabled = ShouldEnableReflectionPatternReporting(_origin.Provider); var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled, _context); var handleCallAction = new HandleCallAction(_context, _reflectionMarker, diagnosticContext, callingMethodDefinition); switch (Intrinsics.GetIntrinsicIdForMethod(calledMethodDefinition)) { case IntrinsicId.IntrospectionExtensions_GetTypeInfo: case IntrinsicId.TypeInfo_AsType: case IntrinsicId.Type_get_UnderlyingSystemType: case IntrinsicId.Type_GetTypeFromHandle: case IntrinsicId.Type_get_TypeHandle: case IntrinsicId.Type_GetInterface: case IntrinsicId.Type_get_AssemblyQualifiedName: case IntrinsicId.RuntimeHelpers_RunClassConstructor: case var callType when(callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) && calledMethod.DeclaringType.IsTypeOf(WellKnownType.System_Type) && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" && calledMethod.HasThis: case var fieldPropertyOrEvent when(fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) && calledMethod.DeclaringType.IsTypeOf(WellKnownType.System_Type) && calledMethod.Parameters[0].ParameterType.IsTypeOf(WellKnownType.System_String) && calledMethod.HasThis: case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: case IntrinsicId.Type_GetMember: case IntrinsicId.Type_GetMethod: case IntrinsicId.Type_GetNestedType: case IntrinsicId.Nullable_GetUnderlyingType: case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType(1, "System.Reflection.MethodInfo"): case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: case IntrinsicId.Type_get_BaseType: case IntrinsicId.Type_GetConstructor: case IntrinsicId.MethodBase_GetMethodFromHandle: case IntrinsicId.MethodBase_get_MethodHandle: case IntrinsicId.Type_MakeGenericType: case IntrinsicId.MethodInfo_MakeGenericMethod: case IntrinsicId.Expression_Call: case IntrinsicId.Expression_New: case IntrinsicId.Type_GetType: case IntrinsicId.Activator_CreateInstance_Type: case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: case IntrinsicId.Activator_CreateInstanceFrom: case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: case IntrinsicId.Assembly_CreateInstance: { var instanceValue = MultiValueLattice.Top; IReadOnlyList <MultiValue> parameterValues = methodParams; if (calledMethodDefinition.HasImplicitThis()) { instanceValue = methodParams[0]; parameterValues = parameterValues.Skip(1).ToImmutableList(); } return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _)); } case IntrinsicId.None: { if (calledMethodDefinition.IsPInvokeImpl) { // Is the PInvoke dangerous? bool comDangerousMethod = IsComInterop(calledMethodDefinition.MethodReturnType, calledMethodDefinition.ReturnType); foreach (ParameterDefinition pd in calledMethodDefinition.Parameters) { comDangerousMethod |= IsComInterop(pd, pd.ParameterType); } if (comDangerousMethod) { diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethodDefinition.GetDisplayName()); } } _markStep.CheckAndReportRequiresUnreferencedCode(calledMethodDefinition, _origin); var instanceValue = MultiValueLattice.Top; IReadOnlyList <MultiValue> parameterValues = methodParams; if (calledMethodDefinition.HasImplicitThis()) { instanceValue = methodParams[0]; parameterValues = parameterValues.Skip(1).ToImmutableList(); } return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _)); } case IntrinsicId.TypeDelegator_Ctor: { // This is an identity function for analysis purposes if (operation.OpCode == OpCodes.Newobj) { AddReturnValue(methodParams[1]); } } break; case IntrinsicId.Array_Empty: { AddReturnValue(ArrayValue.Create(0, ((GenericInstanceMethod)calledMethod).GenericArguments[0])); } break; // // System.Object // // GetType() // case IntrinsicId.Object_GetType: { foreach (var valueNode in methodParams[0]) { // Note that valueNode can be statically typed in IL as some generic argument type. // For example: // void Method<T>(T instance) { instance.GetType().... } // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. // But it could be that T is annotated with for example PublicMethods: // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } // In this case it's in theory possible to handle it, by treating the T basically as a base class // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking // has to happen on the callsite, which doesn't know that GetType() will be used...). // For now we're intentionally ignoring this case - it will produce a warning. // The counter example is: // Method<Base>(new Derived); // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which // currently it won't do. TypeDefinition?staticType = (valueNode as IValueWithStaticType)?.StaticType; if (staticType is null) { // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations AddReturnValue(_annotations.GetMethodReturnValue(calledMethodDefinition)); } else if (staticType.IsSealed || staticType.IsTypeOf("System", "Delegate")) { // We can treat this one the same as if it was a typeof() expression // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods // on delegates anyway so reflection on something this approximation would miss is actually safe. // We ignore the fact that the type can be annotated (see below for handling of annotated types) // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge // of the type. So for example even if the type is annotated with PublicMethods // but the code calls GetProperties on it - it will work - mark properties, don't mark methods // since we ignored the fact that it's annotated. // This can be seen a little bit as a violation of the annotation, but we already have similar cases // where a parameter is annotated and if something in the method sets a specific known type to it // we will also make it just work, even if the annotation doesn't match the usage. AddReturnValue(new SystemTypeValue(staticType)); } else { // Make sure the type is marked (this will mark it as used via reflection, which is sort of true) // This should already be true for most cases (method params, fields, ...), but just in case _reflectionMarker.MarkType(_origin, staticType); var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy .ApplyDynamicallyAccessedMembersToTypeHierarchy(_reflectionMarker, staticType); // Return a value which is "unknown type" with annotation. For now we'll use the return value node // for the method, which means we're loosing the information about which staticType this // started with. For now we don't need it, but we can add it later on. AddReturnValue(_annotations.GetMethodReturnValue(calledMethodDefinition, annotation)); } } } break; // Note about Activator.CreateInstance<T> // There are 2 interesting cases: // - The generic argument for T is either specific type or annotated - in that case generic instantiation will handle this // since from .NET 6+ the T is annotated with PublicParameterlessConstructor annotation, so the linker would apply this as for any other method. // - The generic argument for T is unannotated type - the generic instantiantion handling has a special case for handling PublicParameterlessConstructor requirement // in such that if the generic argument type has the "new" constraint it will not warn (as it is effectively the same thing semantically). // For all other cases, the linker would have already produced a warning. default: throw new NotImplementedException("Unhandled instrinsic"); } // If we get here, we handled this as an intrinsic. As a convenience, if the code above // didn't set the return value (and the method has a return value), we will set it to be an // unknown value with the return type of the method. bool returnsVoid = calledMethod.ReturnsVoid(); methodReturnValue = maybeMethodReturnValue ?? (returnsVoid ? MultiValueLattice.Top : _annotations.GetMethodReturnValue(calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes)); // Validate that the return value has the correct annotations as per the method return value annotations if (returnValueDynamicallyAccessedMemberTypes != 0) { foreach (var uniqueValue in methodReturnValue) { if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes) { if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(returnValueDynamicallyAccessedMemberTypes)) { throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); } } else if (uniqueValue is SystemTypeValue) { // SystemTypeValue can fullfill any requirement, so it's always valid // The requirements will be applied at the point where it's consumed (passed as a method parameter, set as field value, returned from the method) } else { throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); } } } return(true); void AddReturnValue(MultiValue value) { maybeMethodReturnValue = (maybeMethodReturnValue is null) ? value : MultiValueLattice.Meet((MultiValue)maybeMethodReturnValue, value); } }