public FieldAnnotation(FieldDefinition field, DynamicallyAccessedMemberKinds annotation) => (Field, Annotation) = (field, annotation);
private TypeAnnotations BuildTypeAnnotations(TypeDefinition type) { var annotatedFields = new ArrayBuilder <FieldAnnotation> (); // First go over all fields with an explicit annotation if (type.HasFields) { foreach (FieldDefinition field in type.Fields) { if (!IsTypeInterestingForDataflow(field.FieldType)) { continue; } DynamicallyAccessedMemberKinds annotation = _source.GetFieldAnnotation(field); if (annotation == 0) { continue; } annotatedFields.Add(new FieldAnnotation(field, annotation)); } } var annotatedMethods = new ArrayBuilder <MethodAnnotations> (); // Next go over all methods with an explicit annotation if (type.HasMethods) { foreach (MethodDefinition method in type.Methods) { DynamicallyAccessedMemberKinds [] paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. int offset; if (method.HasImplicitThis()) { offset = 1; if (IsTypeInterestingForDataflow(method.DeclaringType)) { DynamicallyAccessedMemberKinds ta = _source.GetThisParameterAnnotation(method); if (ta != 0) { paramAnnotations = new DynamicallyAccessedMemberKinds [method.Parameters.Count + offset]; paramAnnotations [0] = ta; } } } else { offset = 0; } for (int i = 0; i < method.Parameters.Count; i++) { if (!IsTypeInterestingForDataflow(method.Parameters [i].ParameterType)) { continue; } DynamicallyAccessedMemberKinds pa = _source.GetParameterAnnotation(method, i); if (pa == 0) { continue; } if (paramAnnotations == null) { paramAnnotations = new DynamicallyAccessedMemberKinds [method.Parameters.Count + offset]; } paramAnnotations [i + offset] = pa; } DynamicallyAccessedMemberKinds returnAnnotation = IsTypeInterestingForDataflow(method.ReturnType) ? _source.GetReturnParameterAnnotation(method) : 0; if (returnAnnotation != 0 || paramAnnotations != null) { annotatedMethods.Add(new MethodAnnotations(method, paramAnnotations, returnAnnotation)); } } } // 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; // } // if (type.HasProperties) { foreach (PropertyDefinition property in type.Properties) { if (!IsTypeInterestingForDataflow(property.PropertyType)) { continue; } DynamicallyAccessedMemberKinds annotation = _source.GetPropertyAnnotation(property); if (annotation == 0) { continue; } FieldDefinition backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDefinition setMethod = property.SetMethod; if (setMethod != null) { // TODO: Handle abstract properties - no way to propagate the annotation to the field if (!setMethod.HasBody || !ScanMethodBodyForFieldAccess(setMethod.Body, write: true, out backingFieldFromSetter)) { // TODO: warn we couldn't find a unique backing field } if (annotatedMethods.Any(a => a.Method == setMethod)) { // TODO: warn: duplicate annotation. not propagating. } else { int offset = setMethod.HasImplicitThis() ? 1 : 0; if (setMethod.Parameters.Count > 0) { DynamicallyAccessedMemberKinds [] paramAnnotations = new DynamicallyAccessedMemberKinds [setMethod.Parameters.Count + offset]; paramAnnotations [offset] = annotation; annotatedMethods.Add(new MethodAnnotations(setMethod, paramAnnotations, 0)); } } } FieldDefinition backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDefinition getMethod = property.GetMethod; if (getMethod != null) { // TODO: Handle abstract properties - no way to propagate the annotation to the field if (!getMethod.HasBody || !ScanMethodBodyForFieldAccess(getMethod.Body, write: false, out backingFieldFromGetter)) { // TODO: warn we couldn't find a unique backing field } if (annotatedMethods.Any(a => a.Method == getMethod)) { // TODO: warn: duplicate annotation. not propagating. } else { annotatedMethods.Add(new MethodAnnotations(getMethod, null, annotation)); } } FieldDefinition backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { // TODO: warn we couldn't find a unique backing field backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; } if (backingField != null) { if (annotatedFields.Any(a => a.Field == backingField)) { // TODO: warn about duplicate annotations } else { annotatedFields.Add(new FieldAnnotation(backingField, annotation)); } } } } return(new TypeAnnotations(annotatedMethods.ToArray(), annotatedFields.ToArray())); }
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberKinds memberKinds) { MemberKinds = memberKinds; }
public MethodAnnotations(MethodDefinition method, DynamicallyAccessedMemberKinds [] paramAnnotations, DynamicallyAccessedMemberKinds returnParamAnnotations) => (Method, ParameterAnnotations, ReturnParameterAnnotation) = (method, paramAnnotations, returnParamAnnotations);
public MethodReturnValue(DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds) { Kind = ValueNodeKind.MethodReturn; DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds; }
public MethodParameterValue(int parameterIndex, DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds) { Kind = ValueNodeKind.MethodParameter; ParameterIndex = parameterIndex; DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds; }
public LoadFieldValue(FieldDefinition fieldToLoad, DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds) { Kind = ValueNodeKind.LoadField; Field = fieldToLoad; DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds; }
private void Initialize(LinkContext context, string jsonFile) { // Need "using" because JsonDocument won't close this as part of Dispose(). using FileStream jsonFileStream = File.OpenRead(jsonFile); // We only support UTF-8 using JsonDocument jsonDoc = JsonDocument.Parse(jsonFileStream, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip }); // TODO: need to also check the document is structurally sound. foreach (var assemblyElement in jsonDoc.RootElement.EnumerateObject()) { var assembly = context.Resolve(new AssemblyNameReference(assemblyElement.Name, new Version())); if (assembly == null) { context.LogMessage($"Assembly {assemblyElement.Name} couldn't be resolved"); continue; } foreach (var ns in assemblyElement.Value.EnumerateObject()) { string namespaceName = ns.Name; foreach (var typeElement in ns.Value.EnumerateObject()) { string typeName = typeElement.Name; var type = assembly.MainModule.GetType(namespaceName, typeName); if (type == null) { context.LogMessage($"Type {namespaceName}.{typeName} couldn't be resolved"); continue; } foreach (var member in typeElement.Value.EnumerateObject()) { string memberName = member.Name; // Technically, '(' is a valid character in both method and field names, // but the existing PreserveDependencyAttribute parser has a limitation in supporting // that anyway, so we will use '(' to distinguish methods from fields/properties. if (memberName.Contains("(")) { // This is a method // Parser uses same format as PreserveDependencyAttribute string[] signature = null; memberName = memberName.Replace(" ", ""); var sign_start = memberName.IndexOf('('); var sign_end = memberName.LastIndexOf(')'); if (sign_start > 0 && sign_end > sign_start) { var parameters = memberName.Substring(sign_start + 1, sign_end - sign_start - 1); signature = string.IsNullOrEmpty(parameters) ? Array.Empty <string> () : parameters.Split(','); memberName = memberName.Substring(0, sign_start); } MethodDefinition method = null; foreach (var candidate in type.Methods) { if (candidate.Name != memberName) { continue; } if (signature != null) { if (candidate.Parameters.Count != signature.Length) { continue; } bool sigMatch = true; for (int i = 0; i < candidate.Parameters.Count; i++) { if (candidate.Parameters[i].ParameterType.FullName != signature[i].ToCecilName()) { sigMatch = false; break; } } if (!sigMatch) { continue; } } if (method != null) { context.LogMessage($"Multiple matches for method {memberName}"); } method = candidate; } if (method == null) { context.LogMessage($"No match for {memberName}"); continue; } DynamicallyAccessedMemberKinds returnAnnotation = 0; var parameterAnnotations = new ArrayBuilder <(string ParamName, DynamicallyAccessedMemberKinds Annotation)> (); foreach (var parameter in member.Value.EnumerateObject()) { if (parameter.Name == "return") { returnAnnotation = ParseKinds(parameter.Value); } else { DynamicallyAccessedMemberKinds paramAnnotation = ParseKinds(parameter.Value); if (paramAnnotation != 0) { parameterAnnotations.Add((parameter.Name, paramAnnotation)); } } } if (returnAnnotation != 0 || parameterAnnotations.Count > 0) { _methods[method] = new AnnotatedMethod(returnAnnotation, parameterAnnotations.ToArray()); } } else { // This is a field or property FieldDefinition field = null; foreach (var candidate in type.Fields) { if (candidate.Name != memberName) { continue; } // IL allows overloaded fields, but not worth adding messages for that... field = candidate; break; } if (field != null) { DynamicallyAccessedMemberKinds fieldAnnotation = ParseKinds(member.Value); if (fieldAnnotation != 0) { _fields[field] = fieldAnnotation; } continue; } PropertyDefinition property = null; foreach (var candidate in type.Properties) { if (candidate.Name != memberName) { continue; } // IL allows overloaded properties, but not worth adding messages for that... property = candidate; break; } if (property != null) { DynamicallyAccessedMemberKinds propertyAnnotation = ParseKinds(member.Value); if (propertyAnnotation != 0) { _properties[property] = propertyAnnotation; } } if (field == null && property == null) { context.LogMessage($"No match for field or property {memberName}"); } } } } } } }
public AnnotatedMethod(DynamicallyAccessedMemberKinds returnAnnotation, (string ParamName, DynamicallyAccessedMemberKinds Annotation)[] paramAnnotations)