protected override TypeAnnotations CreateValueFromKey(TypeDesc key) { // We scan the entire type at this point; the reason for doing that is properties. // // We allow annotating properties, but those annotations need to flow onto individual get/set methods // and backing fields. Without scanning all properties, we can't answer questions about fields/methods. // And if we're going over all properties, we might as well go over everything else to keep things simple. Debug.Assert(key.IsTypeDefinition); if (key is not EcmaType ecmaType) { return(new TypeAnnotations(key, DynamicallyAccessedMemberTypes.None, null, null, null)); } MetadataReader reader = ecmaType.MetadataReader; // class, interface, struct can have annotations TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); DynamicallyAccessedMemberTypes typeAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, typeDef.GetCustomAttributes()); try { // Also inherit annotation from bases TypeDesc baseType = key.BaseType; while (baseType != null) { TypeDefinition baseTypeDef = reader.GetTypeDefinition(((EcmaType)baseType.GetTypeDefinition()).Handle); typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, baseTypeDef.GetCustomAttributes()); baseType = baseType.BaseType; } // And inherit them from interfaces foreach (DefType runtimeInterface in key.RuntimeInterfaces) { TypeDefinition interfaceTypeDef = reader.GetTypeDefinition(((EcmaType)runtimeInterface.GetTypeDefinition()).Handle); typeAnnotation |= GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, interfaceTypeDef.GetCustomAttributes()); } } catch (TypeSystemException) { // If the class hierarchy is not walkable, just stop collecting the annotations. } var annotatedFields = new ArrayBuilder <FieldAnnotation>(); // First go over all fields with an explicit annotation foreach (EcmaField field in ecmaType.GetFields()) { FieldDefinition fieldDef = reader.GetFieldDefinition(field.Handle); DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, fieldDef.GetCustomAttributes()); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(field.FieldType)) { // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it _logger.LogWarning(field, DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings, field.GetDisplayName()); continue; } annotatedFields.Add(new FieldAnnotation(field, annotation)); } var annotatedMethods = new ArrayBuilder <MethodAnnotations>(); // Next go over all methods with an explicit annotation foreach (EcmaMethod method in ecmaType.GetMethods()) { DynamicallyAccessedMemberTypes[]? paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, reader.GetMethodDefinition(method.Handle).GetCustomAttributes()); MethodSignature signature; try { signature = method.Signature; } catch (TypeSystemException) { // If we cannot resolve things in the signature, just move along. continue; } int offset; if (!signature.IsStatic) { offset = 1; } else { offset = 0; } // If there's an annotation on the method itself and it's one of the special types (System.Type for example) // treat that annotation as annotating the "this" parameter. if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { if (IsTypeInterestingForDataflow(method.OwningType) && !signature.IsStatic) { paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; paramAnnotations[0] = methodMemberTypes; } else { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); } } MethodDefinition methodDef = reader.GetMethodDefinition(method.Handle); ParameterHandleCollection parameterHandles = methodDef.GetParameters(); DynamicallyAccessedMemberTypes returnAnnotation = DynamicallyAccessedMemberTypes.None; foreach (ParameterHandle parameterHandle in parameterHandles) { Parameter parameter = reader.GetParameter(parameterHandle); if (parameter.SequenceNumber == 0) { // this is the return parameter returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow(signature.ReturnType)) { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings, method.GetDisplayName()); } } else { DynamicallyAccessedMemberTypes pa = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, parameter.GetCustomAttributes()); if (pa == DynamicallyAccessedMemberTypes.None) { continue; } if (!IsTypeInterestingForDataflow(signature[parameter.SequenceNumber - 1])) { _logger.LogWarning(method, DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings, DiagnosticUtilities.GetParameterNameForErrorMessage(method, parameter.SequenceNumber - 1), method.GetDisplayName()); continue; } if (paramAnnotations == null) { paramAnnotations = new DynamicallyAccessedMemberTypes[signature.Length + offset]; } paramAnnotations[parameter.SequenceNumber - 1 + offset] = pa; } } DynamicallyAccessedMemberTypes[]? genericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in method.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); if (annotation != DynamicallyAccessedMemberTypes.None) { if (genericParameterAnnotations == null) { genericParameterAnnotations = new DynamicallyAccessedMemberTypes[method.Instantiation.Length]; } genericParameterAnnotations[genericParameter.Index] = annotation; } } if (returnAnnotation != DynamicallyAccessedMemberTypes.None || paramAnnotations != null || genericParameterAnnotations != null) { annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation, genericParameterAnnotations)); } } // Next up are properties. Annotations on properties are kind of meta because we need to // map them to annotations on methods/fields. They're syntactic sugar - what they do is expressible // by placing attribute on the accessor/backing field. For complex properties, that's what people // will need to do anyway. Like so: // // [field: Attribute] // Type MyProperty // { // [return: Attribute] // get; // [value: Attribute] // set; // } foreach (PropertyDefinitionHandle propertyHandle in reader.GetTypeDefinition(ecmaType.Handle).GetProperties()) { DynamicallyAccessedMemberTypes annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute( reader, reader.GetPropertyDefinition(propertyHandle).GetCustomAttributes()); if (annotation == DynamicallyAccessedMemberTypes.None) { continue; } PropertyPseudoDesc property = new PropertyPseudoDesc(ecmaType, propertyHandle); if (!IsTypeInterestingForDataflow(property.Signature.ReturnType)) { _logger.LogWarning(property, DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings, property.GetDisplayName()); continue; } FieldDesc?backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDesc setMethod = property.SetMethod; if (setMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. MethodIL methodBody = _ilProvider.GetMethodIL(setMethod); if (methodBody != null) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(methodBody, write: true, out backingFieldFromSetter); } if (annotatedMethods.Any(a => a.Method == setMethod)) { _logger.LogWarning(setMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName(), setMethod.GetDisplayName()); } else { int offset = setMethod.Signature.IsStatic ? 0 : 1; if (setMethod.Signature.Length > 0) { DynamicallyAccessedMemberTypes[] paramAnnotations = new DynamicallyAccessedMemberTypes[setMethod.Signature.Length + offset]; paramAnnotations[paramAnnotations.Length - 1] = annotation; annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, DynamicallyAccessedMemberTypes.None, null)); } } } FieldDesc?backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDesc getMethod = property.GetMethod; if (getMethod != null) { // Abstract property backing field propagation doesn't make sense, and any derived property will be validated // to have the exact same annotations on getter/setter, and thus if it has a detectable backing field that will be validated as well. MethodIL methodBody = _ilProvider.GetMethodIL(getMethod); if (methodBody != null) { // Look for the compiler generated backing field. If it doesn't work out simply move on. In such case we would still // propagate the annotation to the setter/getter and later on when analyzing the setter/getter we will warn // that the field (which ever it is) must be annotated as well. ScanMethodBodyForFieldAccess(methodBody, write: false, out backingFieldFromGetter); } if (annotatedMethods.Any(a => a.Method == getMethod)) { _logger.LogWarning(getMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName(), getMethod.GetDisplayName()); } else { annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation, null)); } } FieldDesc?backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { _logger.LogWarning(property, DiagnosticId.DynamicallyAccessedMembersCouldNotFindBackingField, property.GetDisplayName()); backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; } if (backingField != null) { if (annotatedFields.Any(a => a.Field == backingField)) { _logger.LogWarning(backingField, DiagnosticId.DynamicallyAccessedMembersOnPropertyConflictsWithBackingField, property.GetDisplayName(), backingField.GetDisplayName()); } else { annotatedFields.Add(new FieldAnnotation(backingField, annotation)); } } } DynamicallyAccessedMemberTypes[]? typeGenericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in ecmaType.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); var annotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, genericParameterDef.GetCustomAttributes()); if (annotation != DynamicallyAccessedMemberTypes.None) { if (typeGenericParameterAnnotations == null) { typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[ecmaType.Instantiation.Length]; } typeGenericParameterAnnotations[genericParameter.Index] = annotation; } } return(new TypeAnnotations(ecmaType, typeAnnotation, annotatedMethods.ToArray(), annotatedFields.ToArray(), typeGenericParameterAnnotations)); }