/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb
322 строки
17 KB
Tomáš Matoušek
EnC: Block additions to MethodImpl table when targeting .NET Framework (#81304)
03 дек 2025, 23:18
Не верифицирован
03 дек 2025, 23:18
264f04f
Код
Авторство
О чём код?
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable Imports System.Globalization Imports System.Reflection.Metadata Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.Cci Imports Microsoft.CodeAnalysis.CodeGen Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Friend NotInheritable Class PEDeltaAssemblyBuilder Inherits PEAssemblyBuilderBase Implements IPEDeltaAssemblyBuilder Private ReadOnly _changes As SymbolChanges Private ReadOnly _deepTranslator As VisualBasicSymbolMatcher.DeepTranslator Private ReadOnly _predefinedHotReloadExceptionConstructor As MethodSymbol Private ReadOnly _options As EmitDifferenceOptions ''' <summary> ''' HotReloadException type. May be created even if not used. We might find out ''' we need it late in the emit phase only after all types and members have been compiled. ''' <see cref="_isHotReloadExceptionTypeUsed"/> indicates if the type is actually used in the delta. ''' </summary> Private _lazyHotReloadExceptionType As SynthesizedHotReloadExceptionSymbol ''' <summary> ''' True if usage of HotReloadException type symbol has been observed and shouldn't be changed anymore. ''' </summary> Private _freezeHotReloadExceptionTypeUsage As Boolean ''' <summary> ''' True if HotReloadException type is actually used in the delta. ''' </summary> Private _isHotReloadExceptionTypeUsed As Boolean Public Sub New(sourceAssembly As SourceAssemblySymbol, changes As VisualBasicSymbolChanges, emitOptions As EmitOptions, options As EmitDifferenceOptions, outputKind As OutputKind, serializationProperties As ModulePropertiesForSerialization, manifestResources As IEnumerable(Of ResourceDescription), predefinedHotReloadExceptionConstructor As MethodSymbol) MyBase.New(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, additionalTypes:=ImmutableArray(Of NamedTypeSymbol).Empty) _changes = changes _options = options ' Workaround for https://github.com/dotnet/roslyn/issues/3192. ' When compiling state machine we stash types of awaiters and state-machine hoisted variables, ' so that next generation can look variables up and reuse their slots if possible. ' ' When we are about to allocate a slot for a lifted variable while compiling the next generation ' we map its type to the previous generation and then check the slot types that we stashed earlier. ' If the variable type matches we reuse it. In order to compare the previous variable type with the current one ' both need to be completely lowered (translated). Standard translation only goes one level deep. ' Generic arguments are not translated until they are needed by metadata writer. ' ' In order to get the fully lowered form we run the type symbols of stashed variables through a deep translator ' that translates the symbol recursively. _deepTranslator = New VisualBasicSymbolMatcher.DeepTranslator(sourceAssembly.GetSpecialType(SpecialType.System_Object)) _predefinedHotReloadExceptionConstructor = predefinedHotReloadExceptionConstructor End Sub Friend Overrides Function EncTranslateLocalVariableType(type As TypeSymbol, diagnostics As DiagnosticBag) As ITypeReference ' Note: The translator is Not aware of synthesized types. If type is a synthesized type it won't get mapped. ' In such case use the type itself. This can only happen for variables storing lambda display classes. Dim visited = DirectCast(_deepTranslator.Visit(type), TypeSymbol) Debug.Assert(visited IsNot Nothing OrElse TypeOf type Is LambdaFrame OrElse TypeOf DirectCast(type, NamedTypeSymbol).ConstructedFrom Is LambdaFrame) Return Translate(If(visited, type), Nothing, diagnostics) End Function Public Overrides ReadOnly Property EncSymbolChanges As SymbolChanges Get Return _changes End Get End Property Public Overrides ReadOnly Property PreviousGeneration As EmitBaseline Get Return _changes.DefinitionMap.Baseline End Get End Property Public Overrides ReadOnly Property FieldRvaSupported As Boolean Get Return _options.EmitFieldRva End Get End Property Public Overrides ReadOnly Property MethodImplSupported As Boolean Get Return _options.MethodImplEntriesSupported End Get End Property Friend Shared Function GetOrCreateMetadataSymbols(initialBaseline As EmitBaseline, compilation As VisualBasicCompilation) As EmitBaseline.MetadataSymbols If initialBaseline.LazyMetadataSymbols IsNot Nothing Then Return initialBaseline.LazyMetadataSymbols End If Dim originalMetadata = initialBaseline.OriginalMetadata ' The purpose of this compilation is to provide PE symbols for original metadata. ' We need to transfer the references from the current source compilation but don't need its syntax trees. Dim metadataCompilation = compilation.RemoveAllSyntaxTrees() Dim assemblyReferenceIdentityMap As ImmutableDictionary(Of AssemblyIdentity, AssemblyIdentity) = Nothing Dim metadataAssembly = metadataCompilation.GetBoundReferenceManager().CreatePEAssemblyForAssemblyMetadata(AssemblyMetadata.Create(originalMetadata), MetadataImportOptions.All, assemblyReferenceIdentityMap) Dim metadataDecoder = New MetadataDecoder(metadataAssembly.PrimaryModule) Dim synthesizedTypes = GetSynthesizedTypesFromMetadata(originalMetadata.MetadataReader, metadataDecoder) Dim metadataSymbols = New EmitBaseline.MetadataSymbols(synthesizedTypes, metadataDecoder, assemblyReferenceIdentityMap) Return InterlockedOperations.Initialize(initialBaseline.LazyMetadataSymbols, metadataSymbols) End Function ' friend for testing Friend Overloads Shared Function GetSynthesizedTypesFromMetadata(reader As MetadataReader, metadataDecoder As MetadataDecoder) As SynthesizedTypeMaps ' In general, the anonymous type name Is 'VB$Anonymous' ('Type'|'Delegate') '_' (submission-index '_')? index module-id ' but EnC Is not supported for modules nor submissions. Hence we only look for type names with no module id and no submission index: ' e.g. VB$AnonymousType_123, VB$AnonymousDelegate_123 Dim anonymousTypes = ImmutableSegmentedDictionary.CreateBuilder(Of AnonymousTypeKey, AnonymousTypeValue) For Each handle In reader.TypeDefinitions Dim def = reader.GetTypeDefinition(handle) If Not def.Namespace.IsNil Then Continue For End If If Not reader.StringComparer.StartsWith(def.Name, GeneratedNameConstants.AnonymousTypeOrDelegateCommonPrefix) Then Continue For End If Dim metadataName = reader.GetString(def.Name) Dim arity As Short = 0 Dim name = MetadataHelpers.InferTypeArityAndUnmangleMetadataName(metadataName, arity) Dim index As Integer = 0 If TryParseAnonymousTypeTemplateName(GeneratedNameConstants.AnonymousTypeTemplateNamePrefix, name, index) Then Dim type = DirectCast(metadataDecoder.GetTypeOfToken(handle), NamedTypeSymbol) Dim key = GetAnonymousTypeKey(type) Dim value = New AnonymousTypeValue(name, index, type.GetCciAdapter()) anonymousTypes.Add(key, value) ElseIf TryParseAnonymousTypeTemplateName(GeneratedNameConstants.AnonymousDelegateTemplateNamePrefix, name, index) Then Dim type = DirectCast(metadataDecoder.GetTypeOfToken(handle), NamedTypeSymbol) Dim key = GetAnonymousDelegateKey(type) Dim value = New AnonymousTypeValue(name, index, type.GetCciAdapter()) anonymousTypes.Add(key, value) End If Next ' VB anonymous delegates are handled as anonymous types Return New SynthesizedTypeMaps( anonymousTypes.ToImmutable(), anonymousDelegates:=Nothing, anonymousDelegatesWithIndexedNames:=Nothing) End Function Friend Shared Function TryParseAnonymousTypeTemplateName(prefix As String, name As String, <Out()> ByRef index As Integer) As Boolean If name.StartsWith(prefix, StringComparison.Ordinal) AndAlso Integer.TryParse(name.Substring(prefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, index) Then Return True End If index = -1 Return False End Function Private Shared Function GetAnonymousTypeKey(type As NamedTypeSymbol) As AnonymousTypeKey ' The key is the set of properties that correspond to type parameters. ' For each type parameter, get the name of the property of that type. Dim n = type.TypeParameters.Length If n = 0 Then Return New AnonymousTypeKey(ImmutableArray(Of AnonymousTypeKeyField).Empty) End If ' Properties indexed by type parameter ordinal. Dim properties = New AnonymousTypeKeyField(n - 1) {} For Each member In type.GetMembers() If member.Kind <> SymbolKind.Property Then Continue For End If Dim [property] = DirectCast(member, PropertySymbol) Dim propertyType = [property].Type If propertyType.TypeKind = TypeKind.TypeParameter Then Dim typeParameter = DirectCast(propertyType, TypeParameterSymbol) Debug.Assert(TypeSymbol.Equals(DirectCast(typeParameter.ContainingSymbol, TypeSymbol), type, TypeCompareKind.ConsiderEverything)) Dim index = typeParameter.Ordinal Debug.Assert(properties(index).Name Is Nothing) ' ReadOnly anonymous type properties were 'Key' properties. properties(index) = New AnonymousTypeKeyField([property].Name, isKey:=[property].IsReadOnly, ignoreCase:=True) End If Next Debug.Assert(properties.All(Function(f) Not String.IsNullOrEmpty(f.Name))) Return New AnonymousTypeKey(ImmutableArray.Create(properties)) End Function Private Shared Function GetAnonymousDelegateKey(type As NamedTypeSymbol) As AnonymousTypeKey Debug.Assert(type.BaseTypeNoUseSiteDiagnostics.SpecialType = SpecialType.System_MulticastDelegate) ' The key is the set of parameter names to the Invoke method, ' where the parameters are of the type parameters. Dim members = type.GetMembers(WellKnownMemberNames.DelegateInvokeName) Debug.Assert(members.Length = 1 AndAlso members(0).Kind = SymbolKind.Method) Dim method = DirectCast(members(0), MethodSymbol) Debug.Assert(method.Parameters.Length + If(method.IsSub, 0, 1) = type.TypeParameters.Length) Dim parameters = ArrayBuilder(Of AnonymousTypeKeyField).GetInstance() parameters.AddRange(method.Parameters.SelectAsArray(Function(p) New AnonymousTypeKeyField(p.Name, isKey:=p.IsByRef, ignoreCase:=True))) parameters.Add(New AnonymousTypeKeyField(AnonymousTypeDescriptor.GetReturnParameterName(Not method.IsSub), isKey:=False, ignoreCase:=True)) Return New AnonymousTypeKey(parameters.ToImmutableAndFree(), isDelegate:=True) End Function Friend ReadOnly Property PreviousDefinitions As VisualBasicDefinitionMap Get Return DirectCast(_changes.DefinitionMap, VisualBasicDefinitionMap) End Get End Property Friend Overrides Function TryCreateVariableSlotAllocator(method As MethodSymbol, topLevelMethod As MethodSymbol, diagnostics As DiagnosticBag) As VariableSlotAllocator Return _changes.DefinitionMap.TryCreateVariableSlotAllocator(Compilation, method, topLevelMethod, diagnostics) End Function Friend Overrides Function GetMethodBodyInstrumentations(method As MethodSymbol) As MethodInstrumentation Return _changes.DefinitionMap.GetMethodBodyInstrumentations(method) End Function Friend Overrides Function GetNextAnonymousTypeIndex(fromDelegates As Boolean) As Integer Return PreviousGeneration.GetNextAnonymousTypeIndex(fromDelegates) End Function Friend Overrides Function TryGetAnonymousTypeName(template As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, <Out> ByRef name As String, <Out> ByRef index As Integer) As Boolean Debug.Assert(Compilation Is template.DeclaringCompilation) Return PreviousDefinitions.TryGetAnonymousTypeName(template, name, index) End Function Public Overrides Function GetTopLevelTypeDefinitions(context As EmitContext) As IEnumerable(Of Cci.INamespaceTypeDefinition) Return GetTopLevelTypeDefinitionsExcludingNoPiaAndRootModule(context, includePrivateImplementationDetails:=True) End Function Public Overrides Function GetTopLevelSourceTypeDefinitions(context As EmitContext) As IEnumerable(Of Cci.INamespaceTypeDefinition) Return _changes.GetTopLevelSourceTypeDefinitions(context) End Function Friend Sub OnCreatedIndices(diagnostics As DiagnosticBag) Implements IPEDeltaAssemblyBuilder.OnCreatedIndices Dim embeddedTypesManager = Me.EmbeddedTypesManagerOpt If embeddedTypesManager IsNot Nothing Then For Each embeddedType In embeddedTypesManager.EmbeddedTypesMap.Keys diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_EncNoPIAReference, embeddedType.AdaptedNamedTypeSymbol), Location.None) Next End If End Sub Friend Overrides ReadOnly Property AllowOmissionOfConditionalCalls As Boolean Get Return True End Get End Property Public Overrides ReadOnly Property LinkedAssembliesDebugInfo As IEnumerable(Of String) Get ' This debug information is only emitted for the benefit of legacy EE. ' Since EnC requires Roslyn and Roslyn doesn't need this information we don't emit it during EnC. Return SpecializedCollections.EmptyEnumerable(Of String)() End Get End Property Public Overrides Function TryGetOrCreateSynthesizedHotReloadExceptionType() As INamedTypeSymbolInternal Return If(_predefinedHotReloadExceptionConstructor Is Nothing, GetOrCreateHotReloadExceptionType(), Nothing) End Function Public Overrides Function GetOrCreateHotReloadExceptionConstructorDefinition() As IMethodSymbolInternal If _predefinedHotReloadExceptionConstructor IsNot Nothing Then Return _predefinedHotReloadExceptionConstructor End If If _freezeHotReloadExceptionTypeUsage Then ' the type shouldn't be used after usage has been frozen. Throw ExceptionUtilities.Unreachable() End If _isHotReloadExceptionTypeUsed = True Return GetOrCreateHotReloadExceptionType().Constructor End Function Public Overrides Function GetUsedSynthesizedHotReloadExceptionType() As INamedTypeSymbolInternal _freezeHotReloadExceptionTypeUsage = True Return If(_isHotReloadExceptionTypeUsed, _lazyHotReloadExceptionType, Nothing) End Function Private Function GetOrCreateHotReloadExceptionType() As SynthesizedHotReloadExceptionSymbol Dim symbol = _lazyHotReloadExceptionType If symbol IsNot Nothing Then Return symbol End If Dim exceptionType = Compilation.GetWellKnownType(WellKnownType.System_Exception) Dim stringType = Compilation.GetSpecialType(SpecialType.System_String) Dim intType = Compilation.GetSpecialType(SpecialType.System_Int32) Dim containingNamespace = GetOrSynthesizeNamespace(SynthesizedHotReloadExceptionSymbol.NamespaceName) symbol = New SynthesizedHotReloadExceptionSymbol(containingNamespace, exceptionType, stringType, intType) Interlocked.CompareExchange(_lazyHotReloadExceptionType, symbol, comparand:=Nothing) Return _lazyHotReloadExceptionType End Function End Class End Namespace