Exemplo n.º 1
0
 public FieldAnnotation(FieldDefinition field, DynamicallyAccessedMemberKinds annotation)
 => (Field, Annotation) = (field, annotation);
Exemplo n.º 2
0
        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;
 }
Exemplo n.º 4
0
 public MethodAnnotations(MethodDefinition method, DynamicallyAccessedMemberKinds [] paramAnnotations, DynamicallyAccessedMemberKinds returnParamAnnotations)
 => (Method, ParameterAnnotations, ReturnParameterAnnotation) = (method, paramAnnotations, returnParamAnnotations);
Exemplo n.º 5
0
 public MethodReturnValue(DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds)
 {
     Kind = ValueNodeKind.MethodReturn;
     DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds;
 }
Exemplo n.º 6
0
 public MethodParameterValue(int parameterIndex, DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds)
 {
     Kind           = ValueNodeKind.MethodParameter;
     ParameterIndex = parameterIndex;
     DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds;
 }
Exemplo n.º 7
0
 public LoadFieldValue(FieldDefinition fieldToLoad, DynamicallyAccessedMemberKinds dynamicallyAccessedMemberKinds)
 {
     Kind  = ValueNodeKind.LoadField;
     Field = fieldToLoad;
     DynamicallyAccessedMemberKinds = dynamicallyAccessedMemberKinds;
 }
Exemplo n.º 8
0
        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}");
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 9
0
 public AnnotatedMethod(DynamicallyAccessedMemberKinds returnAnnotation,
                        (string ParamName, DynamicallyAccessedMemberKinds Annotation)[] paramAnnotations)