| File: Compiler\ReadyToRunVisibilityRootProvider.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using Internal.TypeSystem.Ecma; using Internal.TypeSystem; using Internal.JitInterface; using System.Reflection.Metadata; namespace ILCompiler { /// <summary> /// Roots all possibly-visible methods in the input IL module. /// </summary> public class ReadyToRunVisibilityRootProvider : ICompilationRootProvider { private EcmaModule _module; private InstructionSetSupport _instructionSetSupport; public ReadyToRunVisibilityRootProvider(EcmaModule module) { _module = module; _instructionSetSupport = ((ReadyToRunCompilerContext)module.Context).InstructionSetSupport; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) { foreach (MetadataType type in _module.GetAllTypes()) { MetadataType typeWithMethods = type; if (type.HasInstantiation) { typeWithMethods = ReadyToRunLibraryRootProvider.InstantiateIfPossible(type); if (typeWithMethods == null) continue; } RootMethods(typeWithMethods, "Library module method", rootProvider, ((EcmaAssembly)_module.Assembly).HasAssemblyCustomAttribute("System.Runtime.CompilerServices", "InternalsVisibleToAttribute")); } if (_module.EntryPoint is not null) { rootProvider.AddCompilationRoot(_module.EntryPoint, rootMinimalDependencies: false, $"{_module.Assembly.GetName()} Main Method"); } } private void RootMethods(MetadataType type, string reason, IRootingServiceProvider rootProvider, bool anyInternalsVisibleTo) { MethodImplRecord[] methodImplRecords = GetAllMethodImplRecordsForType((EcmaType)type.GetTypeDefinition()); foreach (MethodDesc method in type.GetAllMethods()) { // Skip methods with no IL if (method.IsAbstract) continue; if (method.IsInternalCall) continue; // If the method is not visible outside the assembly, then do not root the method. // It will be rooted by any callers that require it and do not inline it. if (!method.IsStaticConstructor && method.GetTypicalMethodDefinition() is EcmaMethod ecma && !ecma.GetEffectiveVisibility().IsExposedOutsideOfThisAssembly(anyInternalsVisibleTo)) { // If a method itself is not visible outside the assembly, but it implements a method that is, // we want to root it as it could be called from outside the assembly. // Since instance method overriding does not always require a MethodImpl record (it can be omitted when both the name and signature match) // we will also root any methods that are virtual and do not have any MethodImpl records as it is difficult to determine all methods a method // overrides or implements and we don't need to be perfect here. bool anyMethodImplRecordsForMethod = false; bool implementsOrOverridesVisibleMethod = false; foreach (var record in methodImplRecords) { if (record.Body == ecma) { anyMethodImplRecordsForMethod = true; implementsOrOverridesVisibleMethod = record.Decl.GetTypicalMethodDefinition() is EcmaMethod decl && decl.GetEffectiveVisibility().IsExposedOutsideOfThisAssembly(anyInternalsVisibleTo); if (implementsOrOverridesVisibleMethod) { break; } } } if (anyMethodImplRecordsForMethod && !implementsOrOverridesVisibleMethod) { continue; } if (!anyMethodImplRecordsForMethod && !method.IsVirtual) { continue; } } MethodDesc methodToRoot = method; if (method.HasInstantiation) { methodToRoot = ReadyToRunLibraryRootProvider.InstantiateIfPossible(method); if (methodToRoot == null) continue; } try { if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot); rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); } } catch (TypeSystemException) { // Individual methods can fail to load types referenced in their signatures. // Skip them in library mode since they're not going to be callable. continue; } } } private MethodImplRecord[] GetAllMethodImplRecordsForType(EcmaType type) { ArrayBuilder<MethodImplRecord> records = default; MetadataReader metadataReader = type.MetadataReader; TypeDefinition definition = metadataReader.GetTypeDefinition(type.Handle); foreach (var methodImplHandle in definition.GetMethodImplementations()) { MethodImplementation methodImpl = metadataReader.GetMethodImplementation(methodImplHandle); records.Add(new MethodImplRecord( _module.GetMethod(methodImpl.MethodDeclaration), _module.GetMethod(methodImpl.MethodBody) )); } return records.ToArray(); } /// <summary> /// Determine if the visibility-based root provider should be used for the given module. /// </summary> /// <param name="module">The module</param> /// <returns><c>true</c> if the module should use the visibility-based root provider; otherwise, <c>false</c>.</returns> /// <remarks> /// We will use a visibility-based root provider for modules that are marked as trimmable. /// Trimmable assemblies are less likely to use private reflection, which the visibility-based root provider /// doesn't track well. /// </remarks> public static bool UseVisibilityBasedRootProvider(EcmaModule module) { EcmaAssembly assembly = (EcmaAssembly)module.Assembly; foreach (var assemblyMetadata in assembly.GetDecodedCustomAttributes("System.Reflection", "AssemblyMetadataAttribute")) { if ((string)assemblyMetadata.FixedArguments[0].Value == "IsTrimmable") { return bool.TryParse((string)assemblyMetadata.FixedArguments[1].Value, out bool isTrimmable) && isTrimmable; } } return false; } } }