// declaredOnly will cause this to retrieve interfaces recursively required by the type, but doesn't necessarily // include interfaces required by any base types. public static IEnumerable <InterfaceImplementation> GetAllInterfaceImplementations(this TypeDefinition thisType, LinkContext context, bool declaredOnly) { TypeDefinition?type = thisType; while (type != null) { foreach (var i in type.Interfaces) { yield return(i); TypeDefinition?interfaceType = context.TryResolve(i.InterfaceType); if (interfaceType != null) { // declaredOnly here doesn't matter since interfaces don't have base types foreach (var innerInterface in interfaceType.GetAllInterfaceImplementations(context, declaredOnly: true)) { yield return(innerInterface); } } } if (declaredOnly) { yield break; } type = context.TryResolve(type.BaseType); } }
public static IEnumerable <FieldDefinition> GetFieldsOnTypeHierarchy(this TypeDefinition thisType, LinkContext context, Func <FieldDefinition, bool>?filter, BindingFlags?bindingFlags = BindingFlags.Default) { TypeDefinition?type = thisType; bool onBaseType = false; while (type != null) { foreach (var field in type.Fields) { // Ignore private fields on a base type - those are completely ignored by reflection // (anything private on the base type is not visible via the derived type) if (onBaseType && field.IsPrivate) { continue; } // Note that compiler generated fields backing some properties and events will get through here. // This is intentional as reflection treats these as fields as well. if (filter != null && !filter(field)) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !field.IsStatic) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && field.IsStatic) { continue; } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !field.IsPublic) { continue; } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && field.IsPublic) { continue; } yield return(field); } if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) { yield break; } type = context.TryResolve(type.BaseType); onBaseType = true; } }
public static IEnumerable <MethodDefinition> GetMethodsOnTypeHierarchy(this TypeDefinition type, LinkContext context, Func <MethodDefinition, bool> filter, BindingFlags?bindingFlags = null) { bool onBaseType = false; while (type != null) { foreach (var method in type.Methods) { // Ignore constructors as those are not considered methods from a reflection's point of view if (method.IsConstructor) { continue; } // Ignore private methods on a base type - those are completely ignored by reflection // (anything private on the base type is not visible via the derived type) if (onBaseType && method.IsPrivate) { continue; } // Note that special methods like property getter/setter, event adder/remover will still get through and will be marked. // This is intentional as reflection treats these as methods as well. if (filter != null && !filter(method)) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static && !method.IsStatic) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance && method.IsStatic) { continue; } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public && !method.IsPublic) { continue; } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic && method.IsPublic) { continue; } yield return(method); } type = context.TryResolve(type.BaseType); onBaseType = true; } }
TypeReference ResolveTypeName(AssemblyDefinition assembly, TypeName typeName) { if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { // In this case we ignore the assembly parameter since the type name has assembly in it var assemblyFromName = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name); return(ResolveTypeName(assemblyFromName, assemblyQualifiedTypeName.TypeName)); } if (assembly == null || typeName == null) { return(null); } if (typeName is ConstructedGenericTypeName constructedGenericTypeName) { var genericTypeRef = ResolveTypeName(assembly, constructedGenericTypeName.GenericType); if (genericTypeRef == null) { return(null); } TypeDefinition genericType = genericTypeRef.Resolve(); var genericInstanceType = new GenericInstanceType(genericType); foreach (var arg in constructedGenericTypeName.GenericArguments) { var genericArgument = ResolveTypeName(assembly, arg); if (genericArgument == null) { return(null); } genericInstanceType.GenericArguments.Add(genericArgument); } return(genericInstanceType); } else if (typeName is HasElementTypeName elementTypeName) { var elementType = ResolveTypeName(assembly, elementTypeName.ElementTypeName); if (elementType == null) { return(null); } return(typeName switch { ArrayTypeName _ => new ArrayType(elementType), MultiDimArrayTypeName multiDimArrayTypeName => new ArrayType(elementType, multiDimArrayTypeName.Rank), ByRefTypeName _ => new ByReferenceType(elementType), PointerTypeName _ => new PointerType(elementType), _ => elementType });
public static IEnumerable <InterfaceImplementation> GetAllInterfaceImplementations(this TypeDefinition type, LinkContext context) { while (type != null) { foreach (var i in type.Interfaces) { yield return(i); TypeDefinition interfaceType = context.TryResolve(i.InterfaceType); if (interfaceType != null) { foreach (var innerInterface in interfaceType.GetAllInterfaceImplementations(context)) { yield return(innerInterface); } } } type = context.TryResolve(type.BaseType); } }
public IEnumerable <(InterfaceImplementation, TypeDefinition)>?GetReferencedInterfaces(MethodBody body) { var possibleStackTypes = AllPossibleStackTypes(body.Method); if (possibleStackTypes.Count == 0) { return(null); } var interfaceTypes = possibleStackTypes.Where(t => t.IsInterface).ToArray(); if (interfaceTypes.Length == 0) { return(null); } var interfaceImplementations = new HashSet <(InterfaceImplementation, TypeDefinition)> (); // If a type could be on the stack in the body and an interface it implements could be on the stack on the body // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type // even if the type is never instantiated foreach (var type in possibleStackTypes) { // We only sweep interfaces on classes so that's why we only care about classes if (!type.IsClass) { continue; } TypeDefinition?currentType = type; while (currentType?.BaseType != null) // Checking BaseType != null to skip System.Object { AddMatchingInterfaces(interfaceImplementations, currentType, interfaceTypes); currentType = context.TryResolve(currentType.BaseType); } } return(interfaceImplementations); }
private HierarchyFlags GetFlags(TypeDefinition resolvedType) { if (_cache.TryGetValue(resolvedType, out var flags)) { return(flags); } if (resolvedType.Name == "IReflect" && resolvedType.Namespace == "System.Reflection") { flags |= HierarchyFlags.IsSystemReflectionIReflect; } TypeDefinition?baseType = resolvedType; while (baseType != null) { if (baseType.IsTypeOf(WellKnownType.System_Type)) { flags |= HierarchyFlags.IsSystemType; } if (baseType.HasInterfaces) { foreach (var iface in baseType.Interfaces) { if (iface.InterfaceType.Name == "IReflect" && iface.InterfaceType.Namespace == "System.Reflection") { flags |= HierarchyFlags.IsSystemReflectionIReflect; } } } baseType = context.TryResolve(baseType.BaseType); } if (resolvedType != null) { _cache.Add(resolvedType, flags); } return(flags); }
public static TypeDefinition FindPredefinedType(string ns, string name, LinkContext context) { foreach (var corlibName in corlibNames) { AssemblyDefinition corlib = context.TryResolve(corlibName); if (corlib == null) { continue; } TypeDefinition type = corlib.MainModule.GetType(ns, name); // The assembly could be a facade with type forwarders, in which case we don't find the type in this assembly. if (type != null) { return(type); } } return(null); }
public static TypeDefinition?FindPredefinedType(WellKnownType type, LinkContext context) { var(ns, name) = type.GetNamespaceAndName(); foreach (var corlibName in corlibNames) { AssemblyDefinition?corlib = context.TryResolve(corlibName); if (corlib == null) { continue; } TypeDefinition resolvedType = corlib.MainModule.GetType(ns, name); // The assembly could be a facade with type forwarders, in which case we don't find the type in this assembly. if (resolvedType != null) { return(resolvedType); } } return(null); }
public bool TryResolveTypeName(string typeNameString, ICustomAttributeProvider?origin, [NotNullWhen(true)] out TypeReference?typeReference, [NotNullWhen(true)] out AssemblyDefinition?typeAssembly, bool needsAssemblyName = true) { typeReference = null; typeAssembly = null; if (string.IsNullOrEmpty(typeNameString)) { return(false); } TypeName parsedTypeName; try { parsedTypeName = TypeParser.ParseTypeName(typeNameString); } catch (ArgumentException) { return(false); } catch (System.IO.FileLoadException) { return(false); } if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name); if (typeAssembly == null) { return(false); } typeReference = ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName); return(typeReference != null); } // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, // search for the type in the calling object's assembly. If not found, look in the core // assembly. typeAssembly = origin switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module?.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, null => null, _ => throw new NotSupportedException() }; if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference)) { return(true); } // If type is not found in the caller's assembly, try in core assembly. typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib); if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference)) { return(true); } // It is common to use Type.GetType for looking if a type is available. // If no type was found only warn and return null. if (needsAssemblyName && origin != null) { _context.LogWarning(new MessageOrigin(origin), DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeNameString); } typeAssembly = null; return(false); bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, [NotNullWhen(true)] out TypeReference?typeReference) { typeReference = null; if (assemblyDefinition == null) { return(false); } typeReference = ResolveTypeName(assemblyDefinition, typeName); return(typeReference != null); } }
public TypeReference ResolveTypeName(string typeNameString, ICustomAttributeProvider origin, out AssemblyDefinition typeAssembly, bool needsAssemblyName = true) { typeAssembly = null; if (string.IsNullOrEmpty(typeNameString)) { return(null); } TypeName parsedTypeName; try { parsedTypeName = TypeParser.ParseTypeName(typeNameString); } catch (ArgumentException) { return(null); } catch (System.IO.FileLoadException) { return(null); } if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name); if (typeAssembly == null) { return(null); } return(ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName)); } // If parsedTypeName doesn't have an assembly name in it but it does have a namespace, // search for the type in the calling object's assembly. If not found, look in the core // assembly. typeAssembly = origin switch { AssemblyDefinition asm => asm, TypeDefinition type => type.Module?.Assembly, IMemberDefinition member => member.DeclaringType.Module.Assembly, null => null, _ => throw new NotSupportedException() }; if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRef)) { return(typeRef); } // If type is not found in the caller's assembly, try in core assembly. typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib); if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRefFromSPCL)) { return(typeRefFromSPCL); } // It is common to use Type.GetType for looking if a type is available. // If no type was found only warn and return null. if (needsAssemblyName && origin != null) { _context.LogWarning($"Type '{typeNameString}' was not found in the caller assembly nor in the base library. " + $"Type name strings used for dynamically accessing a type should be assembly qualified.", 2105, new MessageOrigin(origin)); } typeAssembly = null; return(null); bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, out TypeReference typeReference) { typeReference = null; if (assemblyDefinition == null) { return(false); } typeReference = ResolveTypeName(assemblyDefinition, typeName); return(typeReference != null); } }
void PopulateCacheForType(TypeDefinition type) { // Avoid repeat scans of the same type if (!_typesWithPopulatedCache.Add(type)) { return; } var callGraph = new CompilerGeneratedCallGraph(); var callingMethods = new HashSet <MethodDefinition> (); void ProcessMethod(MethodDefinition method) { bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(method.DeclaringType.Name); if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name)) { if (!isStateMachineMember) { // If it's not a nested function, track as an entry point to the call graph. var added = callingMethods.Add(method); Debug.Assert(added); } } else { // We don't expect lambdas or local functions to be emitted directly into // state machine types. Debug.Assert(!isStateMachineMember); } // Discover calls or references to lambdas or local functions. This includes // calls to local functions, and lambda assignments (which use ldftn). if (method.Body != null) { foreach (var instruction in method.Body.Instructions) { if (instruction.OpCode.OperandType != OperandType.InlineMethod) { continue; } MethodDefinition?lambdaOrLocalFunction = _context.TryResolve((MethodReference)instruction.Operand); if (lambdaOrLocalFunction == null) { continue; } if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(lambdaOrLocalFunction.Name)) { continue; } if (isStateMachineMember) { callGraph.TrackCall(method.DeclaringType, lambdaOrLocalFunction); } else { callGraph.TrackCall(method, lambdaOrLocalFunction); } } } // Discover state machine methods. if (!method.HasCustomAttributes) { return; } foreach (var attribute in method.CustomAttributes) { if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices") { continue; } switch (attribute.AttributeType.Name) { case "AsyncIteratorStateMachineAttribute": case "AsyncStateMachineAttribute": case "IteratorStateMachineAttribute": TypeDefinition?stateMachineType = GetFirstConstructorArgumentAsType(attribute); if (stateMachineType == null) { break; } Debug.Assert(stateMachineType.DeclaringType == type || (CompilerGeneratedNames.IsGeneratedMemberName(stateMachineType.DeclaringType.Name) && stateMachineType.DeclaringType.DeclaringType == type)); callGraph.TrackCall(method, stateMachineType); if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method)) { var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType]; _context.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName()); } break; } } } // Look for state machine methods, and methods which call local functions. foreach (MethodDefinition method in type.Methods) { ProcessMethod(method); } // Also scan compiler-generated state machine methods (in case they have calls to nested functions), // and nested functions inside compiler-generated closures (in case they call other nested functions). // State machines can be emitted into lambda display classes, so we need to go down at least two // levels to find calls from iterator nested functions to other nested functions. We just recurse into // all compiler-generated nested types to avoid depending on implementation details. foreach (var nestedType in GetCompilerGeneratedNestedTypes(type)) { foreach (var method in nestedType.Methods) { ProcessMethod(method); } } // Now we've discovered the call graphs for calls to nested functions. // Use this to map back from nested functions to the declaring user methods. // Note: This maps all nested functions back to the user code, not to the immediately // declaring local function. The IL doesn't contain enough information in general for // us to determine the nesting of local functions and lambdas. // Note: this only discovers nested functions which are referenced from the user // code or its referenced nested functions. There is no reliable way to determine from // IL which user code an unused nested function belongs to. foreach (var userDefinedMethod in callingMethods) { foreach (var compilerGeneratedMember in callGraph.GetReachableMembers(userDefinedMethod)) { switch (compilerGeneratedMember) { case MethodDefinition nestedFunction: Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.Name)); // Nested functions get suppressions from the user method only. if (!_compilerGeneratedMethodToUserCodeMethod.TryAdd(nestedFunction, userDefinedMethod)) { var alreadyAssociatedMethod = _compilerGeneratedMethodToUserCodeMethod[nestedFunction]; _context.LogWarning(new MessageOrigin(userDefinedMethod), DiagnosticId.MethodsAreAssociatedWithUserMethod, userDefinedMethod.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), nestedFunction.GetDisplayName()); } break; case TypeDefinition stateMachineType: // Types in the call graph are always state machine types // For those all their methods are not tracked explicitly in the call graph; instead, they // are represented by the state machine type itself. // We are already tracking the association of the state machine type to the user code method // above, so no need to track it here. Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.Name)); break; default: throw new InvalidOperationException(); } } } }
static void GetAllOnType(TypeDefinition type, LinkContext context, bool declaredOnly, List <IMetadataTokenProvider> members, HashSet <TypeDefinition> types) { if (!types.Add(type)) { return; } if (type.HasNestedTypes) { foreach (var nested in type.NestedTypes) { members.Add(nested); // Base types and interfaces of nested types are always included. GetAllOnType(nested, context, declaredOnly: false, members, types); } } if (!declaredOnly) { var baseType = context.TryResolve(type.BaseType); if (baseType != null) { GetAllOnType(baseType, context, declaredOnly: false, members, types); } } if (type.HasInterfaces) { if (declaredOnly) { foreach (var iface in type.GetAllInterfaceImplementations(context, declaredOnly: true)) { members.Add(iface); } } else { foreach (var iface in type.Interfaces) { members.Add(iface); var interfaceType = context.TryResolve(iface.InterfaceType); if (interfaceType == null) { continue; } GetAllOnType(interfaceType, context, declaredOnly: false, members, types); } } } if (type.HasFields) { foreach (var f in type.Fields) { members.Add(f); } } if (type.HasMethods) { foreach (var m in type.Methods) { members.Add(m); } } if (type.HasProperties) { foreach (var p in type.Properties) { members.Add(p); } } if (type.HasEvents) { foreach (var e in type.Events) { members.Add(e); } } }
public static IEnumerable <EventDefinition> GetEventsOnTypeHierarchy(this TypeDefinition thisType, LinkContext context, Func <EventDefinition, bool>?filter, BindingFlags?bindingFlags = BindingFlags.Default) { TypeDefinition?type = thisType; bool onBaseType = false; while (type != null) { foreach (var @event in type.Events) { // Ignore private properties on a base type - those are completely ignored by reflection // (anything private on the base type is not visible via the derived type) // Note that properties themselves are not actually private, their accessors are if (onBaseType && (@event.AddMethod == null || @event.AddMethod.IsPrivate) && (@event.RemoveMethod == null || @event.RemoveMethod.IsPrivate)) { continue; } if (filter != null && !filter(@event)) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) { if ((@event.AddMethod != null) && [email protected]) { continue; } if ((@event.RemoveMethod != null) && [email protected]) { continue; } } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) { if ((@event.AddMethod != null) && @event.AddMethod.IsStatic) { continue; } if ((@event.RemoveMethod != null) && @event.RemoveMethod.IsStatic) { continue; } } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) { if ((@event.AddMethod == null || [email protected]) && (@event.RemoveMethod == null || [email protected])) { continue; } } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) { if ((@event.AddMethod != null) && @event.AddMethod.IsPublic) { continue; } if ((@event.RemoveMethod != null) && @event.RemoveMethod.IsPublic) { continue; } } yield return(@event); } if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) { yield break; } type = context.TryResolve(type.BaseType); onBaseType = true; } }
public static IEnumerable <PropertyDefinition> GetPropertiesOnTypeHierarchy(this TypeDefinition type, LinkContext context, Func <PropertyDefinition, bool> filter, BindingFlags?bindingFlags = BindingFlags.Default) { bool onBaseType = false; while (type != null) { foreach (var property in type.Properties) { // Ignore private properties on a base type - those are completely ignored by reflection // (anything private on the base type is not visible via the derived type) // Note that properties themselves are not actually private, their accessors are if (onBaseType && (property.GetMethod == null || property.GetMethod.IsPrivate) && (property.SetMethod == null || property.SetMethod.IsPrivate)) { continue; } if (filter != null && !filter(property)) { continue; } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Static) { if ((property.GetMethod != null) && !property.GetMethod.IsStatic) { continue; } if ((property.SetMethod != null) && !property.SetMethod.IsStatic) { continue; } } if ((bindingFlags & (BindingFlags.Instance | BindingFlags.Static)) == BindingFlags.Instance) { if ((property.GetMethod != null) && property.GetMethod.IsStatic) { continue; } if ((property.SetMethod != null) && property.SetMethod.IsStatic) { continue; } } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.Public) { if ((property.GetMethod == null || !property.GetMethod.IsPublic) && (property.SetMethod == null || !property.SetMethod.IsPublic)) { continue; } } if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == BindingFlags.NonPublic) { if ((property.GetMethod != null) && property.GetMethod.IsPublic) { continue; } if ((property.SetMethod != null) && property.SetMethod.IsPublic) { continue; } } yield return(property); } type = context.TryResolve(type.BaseType); onBaseType = true; } }