public override void InterproceduralScan(MethodBody methodBody)
        {
            base.InterproceduralScan(methodBody);

            var reflectionMarker = new ReflectionMarker(_context, _markStep, enabled: true);

            TrimAnalysisPatterns.MarkAndProduceDiagnostics(reflectionMarker, _markStep);
        }
示例#2
0
 public ReflectionMethodBodyScanner(LinkContext context, MarkStep parent, MessageOrigin origin)
     : base(context)
 {
     _markStep         = parent;
     _origin           = origin;
     _annotations      = context.Annotations.FlowAnnotations;
     _reflectionMarker = new ReflectionMarker(context, parent);
 }
 public ReflectionMethodBodyScanner(LinkContext context, MarkStep parent, MessageOrigin origin)
     : base(context)
 {
     _markStep            = parent;
     _origin              = origin;
     _annotations         = context.Annotations.FlowAnnotations;
     _reflectionMarker    = new ReflectionMarker(context, parent, enabled: false);
     TrimAnalysisPatterns = new TrimAnalysisPatternStore(MultiValueLattice, context);
 }
        public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, MarkStep markStep, LinkContext context)
        {
            bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(Origin.Provider);
            var  diagnosticContext  = new DiagnosticContext(Origin, diagnosticsEnabled, context);

            ReflectionMethodBodyScanner.HandleCall(Operation, CalledMethod, Instance, Arguments,
                                                   diagnosticContext,
                                                   reflectionMarker,
                                                   context,
                                                   markStep,
                                                   out MultiValue _);
        }
        public static bool HandleCall(
            Instruction operation,
            MethodReference calledMethod,
            MultiValue instanceValue,
            ImmutableArray <MultiValue> argumentValues,
            DiagnosticContext diagnosticContext,
            ReflectionMarker reflectionMarker,
            LinkContext context,
            MarkStep markStep,
            out MultiValue methodReturnValue)
        {
            var origin = diagnosticContext.Origin;
            var calledMethodDefinition = context.TryResolve(calledMethod);

            Debug.Assert(calledMethodDefinition != null);
            var callingMethodDefinition = origin.Provider as MethodDefinition;

            Debug.Assert(callingMethodDefinition != null);

            bool requiresDataFlowAnalysis   = context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis(calledMethodDefinition);
            var  annotatedMethodReturnValue = context.Annotations.FlowAnnotations.GetMethodReturnValue(calledMethodDefinition);

            Debug.Assert(requiresDataFlowAnalysis || annotatedMethodReturnValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.None);

            MultiValue?maybeMethodReturnValue = null;

            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 fieldOrPropertyIntrinsic when fieldOrPropertyIntrinsic == IntrinsicId.Expression_Field || fieldOrPropertyIntrinsic == 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: {
                return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, argumentValues, out methodReturnValue, out _));
            }

            case IntrinsicId.None: {
                if (calledMethodDefinition.IsPInvokeImpl)
                {
                    // Is the PInvoke dangerous?
                    bool comDangerousMethod = IsComInterop(calledMethodDefinition.MethodReturnType, calledMethodDefinition.ReturnType, context);
                    foreach (ParameterDefinition pd in calledMethodDefinition.Parameters)
                    {
                        comDangerousMethod |= IsComInterop(pd, pd.ParameterType, context);
                    }

                    if (comDangerousMethod)
                    {
                        diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethodDefinition.GetDisplayName());
                    }
                }
                if (context.Annotations.DoesMethodRequireUnreferencedCode(calledMethodDefinition, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCode))
                {
                    MarkStep.ReportRequiresUnreferencedCode(calledMethodDefinition.GetDisplayName(), requiresUnreferencedCode, diagnosticContext);
                }

                return(handleCallAction.Invoke(calledMethodDefinition, instanceValue, argumentValues, out methodReturnValue, out _));
            }

            case IntrinsicId.TypeDelegator_Ctor: {
                // This is an identity function for analysis purposes
                if (operation.OpCode == OpCodes.Newobj)
                {
                    AddReturnValue(argumentValues[0]);
                }
            }
            break;

            case IntrinsicId.Array_Empty: {
                AddReturnValue(ArrayValue.Create(0, ((GenericInstanceMethod)calledMethod).GenericArguments[0]));
            }
            break;

            case IntrinsicId.Enum_GetValues:
            case IntrinsicId.Marshal_SizeOf:
            case IntrinsicId.Marshal_OffsetOf:
            case IntrinsicId.Marshal_PtrToStructure:
            case IntrinsicId.Marshal_DestroyStructure:
            case IntrinsicId.Marshal_GetDelegateForFunctionPointer:
                // These intrinsics are not interesting for trimmer (they are interesting for AOT and that's why they are recognized)
                break;

            //
            // System.Object
            //
            // GetType()
            //
            case IntrinsicId.Object_GetType: {
                foreach (var valueNode in instanceValue)
                {
                    // 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(context.Annotations.FlowAnnotations.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(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(context.Annotations.FlowAnnotations.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 intrinsic");
            }

            // 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 :
                                                           annotatedMethodReturnValue);

            // Validate that the return value has the correct annotations as per the method return value annotations
            if (annotatedMethodReturnValue.DynamicallyAccessedMemberTypes != 0)
            {
                foreach (var uniqueValue in methodReturnValue)
                {
                    if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes)
                    {
                        if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(annotatedMethodReturnValue.DynamicallyAccessedMemberTypes))
                        {
                            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 fulfill 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);
            }
        }