private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles) { MetadataReader reader = module.MetadataReader; MetadataManager mdManager = factory.MetadataManager; var attributeTypeProvider = new CustomAttributeTypeProvider(module); foreach (CustomAttributeHandle caHandle in attributeHandles) { CustomAttribute attribute = reader.GetCustomAttribute(caHandle); try { MethodDesc constructor = module.GetMethod(attribute.Constructor); if (mdManager.IsReflectionBlocked(constructor)) { continue; } // Make a new list in case we need to abort. var caDependencies = new DependencyList(); caDependencies.Add(factory.CanonicalEntrypoint(constructor), "Attribute constructor"); caDependencies.Add(factory.ConstructedTypeSymbol(constructor.OwningType), "Attribute type"); CustomAttributeValue <TypeDesc> decodedValue = attribute.DecodeValue(attributeTypeProvider); if (AddDependenciesFromCustomAttributeBlob(caDependencies, factory, constructor.OwningType, decodedValue)) { dependencies = dependencies ?? new DependencyList(); dependencies.AddRange(caDependencies); } } catch (TypeSystemException) { // We could end up seeing an exception here for a multitude of reasons: // * Attribute ctor doesn't resolve // * There's a typeof() that refers to something that can't be loaded // * Attribute refers to a non-existing field // * Etc. // // If we really wanted to, we could probably come up with a way to still make this // work with the same failure modes at runtime as the CLR, but it might not be // worth the hassle: the input was invalid. The most important thing is that we // don't crash the compilation. } } }
private static bool AddDependenciesFromPropertySetter(DependencyList dependencies, NodeFactory factory, TypeDesc attributeType, string propertyName) { EcmaType attributeTypeDefinition = (EcmaType)attributeType.GetTypeDefinition(); MetadataReader reader = attributeTypeDefinition.MetadataReader; var typeDefinition = reader.GetTypeDefinition(attributeTypeDefinition.Handle); foreach (PropertyDefinitionHandle propDefHandle in typeDefinition.GetProperties()) { PropertyDefinition propDef = reader.GetPropertyDefinition(propDefHandle); if (reader.StringComparer.Equals(propDef.Name, propertyName)) { PropertyAccessors accessors = propDef.GetAccessors(); if (!accessors.Setter.IsNil) { MethodDesc setterMethod = (MethodDesc)attributeTypeDefinition.EcmaModule.GetObject(accessors.Setter); if (factory.MetadataManager.IsReflectionBlocked(setterMethod)) { return(false); } // Method on a generic attribute if (attributeType != attributeTypeDefinition) { setterMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(setterMethod, (InstantiatedType)attributeType); } // TODO: what if the setter is virtual/abstract? dependencies.Add(factory.CanonicalEntrypoint(setterMethod), "Custom attribute blob"); } return(true); } } // Haven't found it in current type. Check the base type. TypeDesc baseType = attributeType.BaseType; if (baseType != null) { return(AddDependenciesFromPropertySetter(dependencies, factory, baseType, propertyName)); } // Not found. This is bad metadata that will result in a runtime failure, but we shouldn't fail the compilation. return(true); }
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); // Ensure that we track the necessary type symbol if we are working with a constructed type symbol. // The emitter will ensure we don't emit both, but this allows us assert that we only generate // relocs to nodes we emit. dependencyList.Add(factory.NecessaryTypeSymbol(_type), "Necessary type symbol related to CanonicalEETypeNode"); DefType closestDefType = _type.GetClosestDefType(); if (MightHaveInterfaceDispatchMap(factory)) { dependencyList.Add(factory.InterfaceDispatchMap(_type), "Canonical interface dispatch map"); } dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (_type.IsCanonicalSubtype(CanonicalFormKind.Universal)) { dependencyList.Add(factory.NativeLayout.TemplateTypeLayout(_type), "Universal generic types always have template layout"); } // Track generic virtual methods that will get added to the GVM tables if (TypeGVMEntriesNode.TypeNeedsGVMTableEntries(_type)) { dependencyList.Add(new DependencyListEntry(factory.TypeGVMEntries(_type.GetTypeDefinition()), "Type with generic virtual methods")); AddDependenciesForUniversalGVMSupport(factory, _type, ref dependencyList); } // Keep track of the default constructor map dependency for this type if it has a default constructor // We only do this for reflection blocked types because dataflow analysis is responsible for // generating default constructors for Activator.CreateInstance in other cases. MethodDesc defaultCtor = closestDefType.GetDefaultConstructor(); if (defaultCtor != null && factory.MetadataManager.IsReflectionBlocked(defaultCtor)) { dependencyList.Add(new DependencyListEntry( factory.CanonicalEntrypoint(defaultCtor), "DefaultConstructorNode")); } return(dependencyList); }
private static void HandleCall(ref DependencyList list, NodeFactory factory, MethodIL methodIL, MethodDesc methodCalled, ref Tracker tracker) { switch (methodCalled.Name) { // Enum.GetValues(Type) needs array of that type case "GetValues" when methodCalled.OwningType == factory.TypeSystemContext.GetWellKnownType(WellKnownType.Enum): { TypeDesc type = tracker.GetLastType(); if (type != null && type.IsEnum && !type.IsGenericDefinition /* generic enums! */) { // Type could be something weird like MyEnum<object, __Canon> - normalize it type = type.NormalizeInstantiation(); list = list ?? new DependencyList(); list.Add(factory.ConstructedTypeSymbol(type.MakeArrayType()), "Enum.GetValues"); } } break; // Type.GetType(string...) needs the type with the given name case "GetType" when methodCalled.OwningType.IsSystemType() && methodCalled.Signature.Length > 0: { string name = tracker.GetLastString(); if (name != null && methodIL.OwningMethod.OwningType is MetadataType mdType && ResolveType(name, mdType.Module, out TypeDesc type, out ModuleDesc referenceModule) && !factory.MetadataManager.IsReflectionBlocked(type)) { const string reason = "Type.GetType"; list = list ?? new DependencyList(); list.Add(factory.MaximallyConstructableType(type), reason); // Also add module metadata in case this reference was through a type forward if (factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) { list.Add(factory.ModuleMetadata(referenceModule), reason); } // Opportunistically remember the type so that it flows to Type.GetMethod if needed. tracker.TrackType(type); } } break; // Type.GetMethod(string...) case "GetMethod" when methodCalled.OwningType.IsSystemType(): { string name = tracker.GetLastString(); TypeDesc type = tracker.GetLastType(); if (name != null && type != null && !factory.MetadataManager.IsReflectionBlocked(type)) { if (type.IsGenericDefinition) { Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: false); if (inst.IsNull) { break; } type = ((MetadataType)type).MakeInstantiatedType(inst); list = list ?? new DependencyList(); list.Add(factory.MaximallyConstructableType(type), "Type.GetMethod"); } else { // Type could be something weird like SomeType<object, __Canon> - normalize it type = type.NormalizeInstantiation(); } MethodDesc reflectedMethod = type.GetMethod(name, null); if (reflectedMethod != null && !factory.MetadataManager.IsReflectionBlocked(reflectedMethod)) { if (reflectedMethod.HasInstantiation) { // Don't want to accidentally get Foo<__Canon>.Bar<object>() if (reflectedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) { break; } Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(reflectedMethod.Instantiation, allowCanon: false); if (inst.IsNull) { break; } reflectedMethod = reflectedMethod.MakeInstantiatedMethod(inst); } const string reason = "Type.GetMethod"; list = list ?? new DependencyList(); if (reflectedMethod.IsVirtual) { RootVirtualMethodForReflection(ref list, factory, reflectedMethod, reason); } if (!reflectedMethod.IsAbstract) { list.Add(factory.CanonicalEntrypoint(reflectedMethod), reason); if (reflectedMethod.HasInstantiation && reflectedMethod != reflectedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)) { list.Add(factory.MethodGenericDictionary(reflectedMethod), reason); } } } } } break; case "SizeOf" when IsMarshalSizeOf(methodCalled): { TypeDesc type = tracker.GetLastType(); if (IsTypeEligibleForMarshalSizeOfTracking(type)) { list = list ?? new DependencyList(); list.Add(factory.StructMarshallingData((DefType)type), "Marshal.SizeOf"); } } break; } }
private static void HandleTypeGetMethod(ref DependencyList list, NodeFactory factory, TypeDesc type, string name, string reason) { if (factory.MetadataManager.IsReflectionBlocked(type)) { return; } if (type.IsGenericDefinition) { Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: false); if (inst.IsNull) { return; } type = ((MetadataType)type).MakeInstantiatedType(inst); list = list ?? new DependencyList(); list.Add(factory.MaximallyConstructableType(type), reason); } else { // Type could be something weird like SomeType<object, __Canon> - normalize it type = type.NormalizeInstantiation(); } MethodDesc reflectedMethod = type.GetMethod(name, null); if (reflectedMethod != null && !factory.MetadataManager.IsReflectionBlocked(reflectedMethod)) { if (reflectedMethod.HasInstantiation) { // Don't want to accidentally get Foo<__Canon>.Bar<object>() if (reflectedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) { return; } Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(reflectedMethod.Instantiation, allowCanon: false); if (inst.IsNull) { return; } reflectedMethod = reflectedMethod.MakeInstantiatedMethod(inst); } list = list ?? new DependencyList(); if (reflectedMethod.IsVirtual) { RootVirtualMethodForReflection(ref list, factory, reflectedMethod, reason); } if (!reflectedMethod.IsAbstract) { list.Add(factory.CanonicalEntrypoint(reflectedMethod), reason); if (reflectedMethod.HasInstantiation && reflectedMethod != reflectedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)) { list.Add(factory.MethodGenericDictionary(reflectedMethod), reason); } } } }
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = base.ComputeNonRelocationBasedDependencies(factory); // Ensure that we track the necessary type symbol if we are working with a constructed type symbol. // The emitter will ensure we don't emit both, but this allows us assert that we only generate // relocs to nodes we emit. dependencyList.Add(factory.NecessaryTypeSymbol(_type), "NecessaryType for constructed type"); if (_type is MetadataType mdType && mdType.Module.GetGlobalModuleType().GetStaticConstructor() is MethodDesc moduleCctor) { dependencyList.Add(factory.MethodEntrypoint(moduleCctor), "Type in a module with initializer"); } DefType closestDefType = _type.GetClosestDefType(); if (MightHaveInterfaceDispatchMap(factory)) { dependencyList.Add(factory.InterfaceDispatchMap(_type), "Interface dispatch map"); } if (_type.IsArray) { // Array MethodTable depends on System.Array's virtuals. Array EETypes don't point to // their base type (i.e. there's no reloc based dependency making this "just work"). dependencyList.Add(factory.ConstructedTypeSymbol(_type.BaseType), "Array base type"); ArrayType arrayType = (ArrayType)_type; if (arrayType.IsMdArray && arrayType.Rank == 1) { // Allocating an MDArray of Rank 1 with zero lower bounds results in allocating // an SzArray instead. Make sure the type loader can find the SzArray type. dependencyList.Add(factory.ConstructedTypeSymbol(arrayType.ElementType.MakeArrayType()), "Rank 1 array"); } } dependencyList.Add(factory.VTable(closestDefType), "VTable"); if (factory.TypeSystemContext.SupportsUniversalCanon) { foreach (var instantiationType in _type.Instantiation) { if (instantiationType.IsValueType) { // All valuetype generic parameters of a constructed type may be effectively constructed. This is generally not that // critical, but in the presence of universal generics the compiler may generate a Box followed by calls to ToString, // GetHashcode or Equals in ways that cannot otherwise be detected by dependency analysis. Thus force all struct type // generic parameters to be considered constructed when walking dependencies of a constructed generic dependencyList.Add(factory.ConstructedTypeSymbol(instantiationType.ConvertToCanonForm(CanonicalFormKind.Specific)), "Struct generic parameters in constructed types may be assumed to be used as constructed in constructed generic types"); } } } // Ask the metadata manager if we have any dependencies due to reflectability. factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type); // Keep track of the default constructor map dependency for this type if it has a default constructor MethodDesc defaultCtor = closestDefType.GetDefaultConstructor(); if (defaultCtor != null) { dependencyList.Add(new DependencyListEntry( factory.CanonicalEntrypoint(defaultCtor), "DefaultConstructorNode")); } return(dependencyList); }
private static void HandleCall(ref DependencyList list, NodeFactory factory, MethodIL methodIL, MethodDesc methodCalled, ref Tracker tracker) { switch (methodCalled.Name) { // Enum.GetValues(Type) needs array of that type case "GetValues" when methodCalled.OwningType == factory.TypeSystemContext.GetWellKnownType(WellKnownType.Enum): { TypeDesc type = tracker.GetLastType(); if (type != null && type.IsEnum && !type.IsGenericDefinition /* generic enums! */) { list = list ?? new DependencyList(); list.Add(factory.ConstructedTypeSymbol(type.MakeArrayType()), "Enum.GetValues"); } } break; // Type.GetType(string...) needs the type with the given name case "GetType" when methodCalled.OwningType.IsSystemType() && methodCalled.Signature.Length > 0: { string name = tracker.GetLastString(); if (name != null && methodIL.OwningMethod.OwningType is MetadataType mdType && ResolveType(name, mdType.Module, out TypeDesc type, out ModuleDesc referenceModule) && !factory.MetadataManager.IsReflectionBlocked(type)) { const string reason = "Type.GetType"; list = list ?? new DependencyList(); list.Add(factory.MaximallyConstructableType(type), reason); // Also add module metadata in case this reference was through a type forward if (factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) { list.Add(factory.ModuleMetadata(referenceModule), reason); } // Opportunistically remember the type so that it flows to Type.GetMethod if needed. tracker.TrackType(type); } } break; // Type.GetMethod(string...) case "GetMethod" when methodCalled.OwningType.IsSystemType(): { string name = tracker.GetLastString(); TypeDesc type = tracker.GetLastType(); if (name != null && type != null && !factory.MetadataManager.IsReflectionBlocked(type)) { if (type.IsGenericDefinition) { Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(type.Instantiation, allowCanon: false); if (inst.IsNull) { break; } type = ((MetadataType)type).MakeInstantiatedType(inst); list = list ?? new DependencyList(); list.Add(factory.MaximallyConstructableType(type), "Type.GetMethod"); } MethodDesc reflectedMethod = type.GetMethod(name, null); if (reflectedMethod != null && !factory.MetadataManager.IsReflectionBlocked(reflectedMethod)) { if (reflectedMethod.HasInstantiation) { // Don't want to accidentally get Foo<__Canon>.Bar<object>() if (reflectedMethod.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any)) { break; } Instantiation inst = TypeExtensions.GetInstantiationThatMeetsConstraints(reflectedMethod.Instantiation, allowCanon: false); if (inst.IsNull) { break; } reflectedMethod = reflectedMethod.MakeInstantiatedMethod(inst); } const string reason = "Type.GetMethod"; list = list ?? new DependencyList(); if (reflectedMethod.IsVirtual) { RootVirtualMethodForReflection(ref list, factory, reflectedMethod, reason); } if (!reflectedMethod.IsAbstract) { list.Add(factory.CanonicalEntrypoint(reflectedMethod), reason); if (reflectedMethod.HasInstantiation && reflectedMethod != reflectedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)) { list.Add(factory.MethodGenericDictionary(reflectedMethod), reason); } } } } } break; case "SizeOf" when methodCalled.OwningType.IsSystemRuntimeInteropServicesMarshal() && !methodCalled.HasInstantiation && methodCalled.Signature.Length == 1 && methodCalled.Signature[0].IsSystemType(): { TypeDesc type = tracker.GetLastType(); if (type != null && !type.IsGenericDefinition && !type.IsCanonicalSubtype(CanonicalFormKind.Any)) { list = list ?? new DependencyList(); MethodDesc marshalSizeOfGeneric = methodCalled.OwningType.GetKnownMethod( "SizeOf", new MethodSignature(MethodSignatureFlags.Static, 1, methodCalled.Context.GetWellKnownType(WellKnownType.Int32), TypeDesc.EmptyTypes)); marshalSizeOfGeneric = marshalSizeOfGeneric.MakeInstantiatedMethod(type); // InteropManager is looking for the following method bodies or dictionaries to decide marshalling info need. // We should ideally model these as separate entities in the dependency graph and add those entities instead. // Fixable after https://github.com/dotnet/corert/issues/6063 is fixed. if (marshalSizeOfGeneric.GetCanonMethodTarget(CanonicalFormKind.Specific) != marshalSizeOfGeneric) { list.Add(factory.MethodGenericDictionary(marshalSizeOfGeneric), "Marshal.SizeOf"); } else { list.Add(factory.MethodEntrypoint(marshalSizeOfGeneric), "Marshal.SizeOf"); } } } break; } }