diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 21586fb1dc52c..ebfea53f6b963 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -3598,8 +3598,19 @@ internal override bool CompileMethods( return true; } - private protected override EmitBaseline MapToCompilation(CommonPEModuleBuilder moduleBeingBuilt) - => EmitHelpers.MapToCompilation(this, (PEDeltaAssemblyBuilder)moduleBeingBuilt); + private protected override SymbolMatcher CreatePreviousToCurrentSourceAssemblyMatcher( + EmitBaseline previousGeneration, + SynthesizedTypeMaps otherSynthesizedTypes, + IReadOnlyDictionary> otherSynthesizedMembers, + IReadOnlyDictionary> otherDeletedMembers) + { + return new CSharpSymbolMatcher( + sourceAssembly: ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly, + SourceAssembly, + otherSynthesizedTypes, + otherSynthesizedMembers, + otherDeletedMembers); + } private class DuplicateFilePathsVisitor : CSharpSymbolVisitor { diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs index c09438b4b510a..73f2587e9ff47 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs @@ -7,13 +7,12 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Emit.EditAndContinue; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -27,25 +26,31 @@ internal sealed class CSharpSymbolMatcher : SymbolMatcher public CSharpSymbolMatcher( SourceAssemblySymbol sourceAssembly, SourceAssemblySymbol otherAssembly, - SynthesizedTypeMaps synthesizedTypes, - IReadOnlyDictionary>? otherSynthesizedMembers, - IReadOnlyDictionary>? otherDeletedMembers) + SynthesizedTypeMaps otherSynthesizedTypes, + IReadOnlyDictionary> otherSynthesizedMembers, + IReadOnlyDictionary> otherDeletedMembers) { - _visitor = new Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembers, otherDeletedMembers, new DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object))); + _visitor = new Visitor( + sourceAssembly, + otherAssembly, + otherSynthesizedTypes, + otherSynthesizedMembers, + otherDeletedMembers, + new DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object))); } public CSharpSymbolMatcher( - SynthesizedTypeMaps synthesizedTypes, SourceAssemblySymbol sourceAssembly, - PEAssemblySymbol otherAssembly) + PEAssemblySymbol otherAssembly, + SynthesizedTypeMaps otherSynthesizedTypes) { _visitor = new Visitor( sourceAssembly, otherAssembly, - synthesizedTypes, - otherSynthesizedMembers: null, - deepTranslator: null, - otherDeletedMembers: null); + otherSynthesizedTypes, + otherSynthesizedMembers: SpecializedCollections.EmptyReadOnlyDictionary>(), + otherDeletedMembers: SpecializedCollections.EmptyReadOnlyDictionary>(), + deepTranslator: null); } public override Cci.IDefinition? MapDefinition(Cci.IDefinition definition) @@ -93,9 +98,12 @@ static bool isPrivateImplementationDetail(Cci.IDefinition definition) internal bool TryGetAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue typeValue) => _visitor.TryGetAnonymousTypeValue(template, out typeValue); + protected override bool TryGetMatchingDelegateWithIndexedName(INamedTypeSymbolInternal delegateTemplate, ImmutableArray values, out AnonymousTypeValue match) + => _visitor.TryGetMatchingDelegateWithIndexedName((AnonymousTypeManager.AnonymousDelegateTemplateSymbol)delegateTemplate, values, out match); + private sealed class Visitor : CSharpSymbolVisitor { - private readonly SynthesizedTypeMaps _synthesizedTypes; + private readonly SynthesizedTypeMaps _otherSynthesizedTypes; private readonly SourceAssemblySymbol _sourceAssembly; // metadata or source assembly: @@ -105,9 +113,9 @@ private sealed class Visitor : CSharpSymbolVisitor /// Members that are not listed directly on their containing type or namespace symbol as they were synthesized in a lowering phase, /// after the symbol has been created. /// - private readonly IReadOnlyDictionary>? _otherSynthesizedMembers; + private readonly IReadOnlyDictionary> _otherSynthesizedMembers; - private readonly IReadOnlyDictionary>? _otherDeletedMembers; + private readonly IReadOnlyDictionary> _otherDeletedMembers; private readonly SymbolComparer _comparer; private readonly ConcurrentDictionary _matches = new(ReferenceEqualityComparer.Instance); @@ -123,12 +131,12 @@ private sealed class Visitor : CSharpSymbolVisitor public Visitor( SourceAssemblySymbol sourceAssembly, AssemblySymbol otherAssembly, - SynthesizedTypeMaps synthesizedTypes, - IReadOnlyDictionary>? otherSynthesizedMembers, - IReadOnlyDictionary>? otherDeletedMembers, + SynthesizedTypeMaps otherSynthesizedTypes, + IReadOnlyDictionary> otherSynthesizedMembers, + IReadOnlyDictionary> otherDeletedMembers, DeepTranslator? deepTranslator) { - _synthesizedTypes = synthesizedTypes; + _otherSynthesizedTypes = otherSynthesizedTypes; _sourceAssembly = sourceAssembly; _otherAssembly = otherAssembly; _otherSynthesizedMembers = otherSynthesizedMembers; @@ -470,45 +478,44 @@ private CustomModifier VisitCustomModifier(CustomModifier modifier) CSharpCustomModifier.CreateRequired(type); } - internal bool TryGetAnonymousDelegateValue(AnonymousTypeManager.AnonymousDelegateTemplateSymbol delegateSymbol, out SynthesizedDelegateValue otherDelegateSymbol) + private bool TryGetAnonymousDelegateValue(AnonymousTypeManager.AnonymousDelegateTemplateSymbol delegateSymbol, out SynthesizedDelegateValue otherDelegateSymbol) { - Debug.Assert((object)delegateSymbol.ContainingSymbol == (object)_sourceAssembly.GlobalNamespace); - var key = new SynthesizedDelegateKey(delegateSymbol.MetadataName); - return _synthesizedTypes.AnonymousDelegates.TryGetValue(key, out otherDelegateSymbol); + return _otherSynthesizedTypes.AnonymousDelegates.TryGetValue(key, out otherDelegateSymbol); } internal bool TryGetAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue otherType) { - Debug.Assert((object)template.ContainingSymbol == (object)_sourceAssembly.GlobalNamespace); - if (template is AnonymousTypeManager.AnonymousTypeTemplateSymbol typeTemplate) { - return _synthesizedTypes.AnonymousTypes.TryGetValue(typeTemplate.GetAnonymousTypeKey(), out otherType); + return _otherSynthesizedTypes.AnonymousTypes.TryGetValue(typeTemplate.GetAnonymousTypeKey(), out otherType); } var delegateTemplate = (AnonymousTypeManager.AnonymousDelegateTemplateSymbol)template; Debug.Assert(delegateTemplate.DelegateInvokeMethod != null); + otherType = default; + var key = new AnonymousDelegateWithIndexedNamePartialKey(delegateTemplate.Arity, delegateTemplate.DelegateInvokeMethod.ParameterCount); - if (_synthesizedTypes.AnonymousDelegatesWithIndexedNames.TryGetValue(key, out var otherTypeCandidates)) + return _otherSynthesizedTypes.AnonymousDelegatesWithIndexedNames.TryGetValue(key, out var otherTypeCandidates) && + TryGetMatchingDelegateWithIndexedName(delegateTemplate, otherTypeCandidates, out otherType); + } + + internal bool TryGetMatchingDelegateWithIndexedName(AnonymousTypeManager.AnonymousDelegateTemplateSymbol delegateTemplate, ImmutableArray values, out AnonymousTypeValue match) + { + foreach (var otherTypeCandidate in values) { - // The key is partial (not unique). Find a matching Invoke method signature. + var otherDelegateType = (NamedTypeSymbol?)otherTypeCandidate.Type.GetInternalSymbol(); + Debug.Assert(otherDelegateType is not null); - foreach (var otherTypeCandidate in otherTypeCandidates) + if (isCorrespondingAnonymousDelegate(delegateTemplate, otherDelegateType)) { - var otherDelegateType = (NamedTypeSymbol?)otherTypeCandidate.Type.GetInternalSymbol(); - Debug.Assert(otherDelegateType is not null); - - if (isCorrespondingAnonymousDelegate(delegateTemplate, otherDelegateType)) - { - otherType = otherTypeCandidate; - return true; - } + match = otherTypeCandidate; + return true; } } - otherType = default; + match = default; return false; bool isCorrespondingAnonymousDelegate(NamedTypeSymbol type, NamedTypeSymbol otherType) @@ -526,12 +533,12 @@ otherType.DelegateInvokeMethod is { } otherInvokeMethod && x.IsParamsArray == y.IsParamsArray && x.IsParamsCollection == y.IsParamsCollection) && isCorrespondingType(invokeMethod.ReturnTypeWithAnnotations, otherInvokeMethod.ReturnTypeWithAnnotations); - } - bool isCorrespondingType(TypeWithAnnotations type, TypeWithAnnotations expectedType) - { - var otherType = type.WithTypeAndModifiers((TypeSymbol?)this.Visit(type.Type), this.VisitCustomModifiers(type.CustomModifiers)); - return otherType.Equals(expectedType, TypeCompareKind.CLRSignatureCompareOptions); + bool isCorrespondingType(TypeWithAnnotations type, TypeWithAnnotations expectedType) + { + var otherType = type.WithTypeAndModifiers((TypeSymbol?)this.Visit(type.Type), this.VisitCustomModifiers(type.CustomModifiers)); + return otherType.Equals(expectedType, TypeCompareKind.CLRSignatureCompareOptions); + } } } @@ -799,12 +806,12 @@ private IReadOnlyDictionary> GetAllEmitt members.AddRange(((NamespaceSymbol)symbol).GetMembers()); } - if (_otherSynthesizedMembers != null && _otherSynthesizedMembers.TryGetValue(symbol, out var synthesizedMembers)) + if (_otherSynthesizedMembers.TryGetValue(symbol, out var synthesizedMembers)) { members.AddRange(synthesizedMembers); } - if (_otherDeletedMembers?.TryGetValue(symbol, out var deletedMembers) == true) + if (_otherDeletedMembers.TryGetValue(symbol, out var deletedMembers)) { members.AddRange(deletedMembers); } diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs index 222963e9f5591..a70bbde698382 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/EmitHelpers.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Reflection.Metadata; @@ -15,7 +14,6 @@ using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Emit { @@ -67,14 +65,14 @@ internal static EmitDifferenceResult EmitDifference( var metadataAssembly = (PEAssemblySymbol)metadataDecoder.ModuleSymbol.ContainingAssembly; var sourceToMetadata = new CSharpSymbolMatcher( - metadataSymbols.SynthesizedTypes, - sourceAssembly, - metadataAssembly); + sourceAssembly: sourceAssembly, + otherAssembly: metadataAssembly, + otherSynthesizedTypes: metadataSymbols.SynthesizedTypes); var previousSourceToMetadata = new CSharpSymbolMatcher( - metadataSymbols.SynthesizedTypes, - previousSourceAssembly, - metadataAssembly); + sourceAssembly: previousSourceAssembly, + otherAssembly: metadataAssembly, + otherSynthesizedTypes: metadataSymbols.SynthesizedTypes); CSharpSymbolMatcher? currentSourceToPreviousSource = null; if (baseline.Ordinal > 0) @@ -84,7 +82,7 @@ internal static EmitDifferenceResult EmitDifference( currentSourceToPreviousSource = new CSharpSymbolMatcher( sourceAssembly: sourceAssembly, otherAssembly: previousSourceAssembly, - baseline.SynthesizedTypes, + otherSynthesizedTypes: baseline.SynthesizedTypes, otherSynthesizedMembers: baseline.SynthesizedMembers, otherDeletedMembers: baseline.DeletedMembers); } @@ -178,66 +176,5 @@ private static bool GetPredefinedHotReloadExceptionTypeConstructor(CSharpCompila return false; } - - /// - /// Return a version of the baseline with all definitions mapped to this compilation. - /// Definitions from the initial generation, from metadata, are not mapped since - /// the initial generation is always included as metadata. That is, the symbols from - /// types, methods, ... in the TypesAdded, MethodsAdded, ... collections are replaced - /// by the corresponding symbols from the current compilation. - /// - internal static EmitBaseline MapToCompilation( - CSharpCompilation compilation, - PEDeltaAssemblyBuilder moduleBeingBuilt) - { - var previousGeneration = moduleBeingBuilt.PreviousGeneration; - RoslynDebug.Assert(previousGeneration.Compilation != compilation); - - if (previousGeneration.Ordinal == 0) - { - // Initial generation, nothing to map. (Since the initial generation - // is always loaded from metadata in the context of the current - // compilation, there's no separate mapping step.) - return previousGeneration; - } - - RoslynDebug.AssertNotNull(previousGeneration.Compilation); - RoslynDebug.AssertNotNull(previousGeneration.PEModuleBuilder); - RoslynDebug.AssertNotNull(moduleBeingBuilt.EncSymbolChanges); - - var synthesizedTypes = moduleBeingBuilt.GetSynthesizedTypes(); - var currentSynthesizedMembers = moduleBeingBuilt.GetAllSynthesizedMembers(); - var currentDeletedMembers = moduleBeingBuilt.EncSymbolChanges.DeletedMembers; - - // Mapping from previous compilation to the current. - var previousSourceAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly; - - var matcher = new CSharpSymbolMatcher( - previousSourceAssembly, - compilation.SourceAssembly, - synthesizedTypes, - currentSynthesizedMembers, - currentDeletedMembers); - - var mappedSynthesizedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.SynthesizedMembers, currentSynthesizedMembers, isDeletedMemberMapping: false); - - // Deleted members are mapped the same way as synthesized members, so we can just call the same method. - var mappedDeletedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.DeletedMembers, currentDeletedMembers, isDeletedMemberMapping: true); - - // TODO: can we reuse some data from the previous matcher? - var matcherWithAllSynthesizedMembers = new CSharpSymbolMatcher( - previousSourceAssembly, - compilation.SourceAssembly, - synthesizedTypes, - mappedSynthesizedMembers, - mappedDeletedMembers); - - return matcherWithAllSynthesizedMembers.MapBaselineToCompilation( - previousGeneration, - compilation, - moduleBeingBuilt, - mappedSynthesizedMembers, - mappedDeletedMembers); - } } } diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs index 08e1d677504f8..29d9e47fd17ad 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/PEDeltaAssemblyBuilder.cs @@ -224,19 +224,6 @@ private static bool TryGetAnonymousTypeKey( internal CSharpDefinitionMap PreviousDefinitions => (CSharpDefinitionMap)_changes.DefinitionMap; - public SynthesizedTypeMaps GetSynthesizedTypes() - { - var result = new SynthesizedTypeMaps( - Compilation.AnonymousTypeManager.GetAnonymousTypeMap(), - Compilation.AnonymousTypeManager.GetAnonymousDelegates(), - Compilation.AnonymousTypeManager.GetAnonymousDelegatesWithIndexedNames()); - - // Should contain all entries in previous generation. - Debug.Assert(PreviousGeneration.SynthesizedTypes.IsSubsetOf(result)); - - return result; - } - public override IEnumerable GetTopLevelTypeDefinitions(EmitContext context) => GetTopLevelTypeDefinitionsExcludingNoPiaAndRootModule(context, includePrivateImplementationDetails: true); @@ -258,16 +245,6 @@ internal override MethodInstrumentation GetMethodBodyInstrumentations(MethodSymb return _changes.DefinitionMap.GetMethodBodyInstrumentations(method); } - internal override ImmutableArray GetPreviousAnonymousTypes() - { - return ImmutableArray.CreateRange(PreviousGeneration.SynthesizedTypes.AnonymousTypes.Keys); - } - - internal override ImmutableArray GetPreviousAnonymousDelegates() - { - return ImmutableArray.CreateRange(PreviousGeneration.SynthesizedTypes.AnonymousDelegates.Keys); - } - internal override int GetNextAnonymousTypeIndex() => PreviousGeneration.GetNextAnonymousTypeIndex(); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 44178fe8e4c02..1497f5dad0859 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -505,16 +505,6 @@ internal virtual VariableSlotAllocator TryCreateVariableSlotAllocator(MethodSymb internal virtual MethodInstrumentation GetMethodBodyInstrumentations(MethodSymbol method) => new MethodInstrumentation { Kinds = EmitOptions.InstrumentationKinds }; - internal virtual ImmutableArray GetPreviousAnonymousTypes() - { - return ImmutableArray.Empty; - } - - internal virtual ImmutableArray GetPreviousAnonymousDelegates() - { - return ImmutableArray.Empty; - } - internal virtual int GetNextAnonymousTypeIndex() { return 0; diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs index 157718ec50f0e..d83cb996d8b05 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -29,17 +27,17 @@ internal sealed partial class AnonymousTypeManager /// Cache of created anonymous type templates used as an implementation of anonymous /// types in emit phase. /// - private ConcurrentDictionary _lazyAnonymousTypeTemplates; + private ConcurrentDictionary? _lazyAnonymousTypeTemplates; /// /// Maps delegate signature shape (number of parameters and their ref-ness) to a synthesized generic delegate symbol. /// Currently used for dynamic call-sites and inferred delegate types whose signature doesn't match any of the well-known Func or Action types. /// - private ConcurrentDictionary _lazyAnonymousDelegates; + private ConcurrentDictionary? _lazyAnonymousDelegates; private readonly struct SynthesizedDelegateKey : IEquatable { - internal readonly string Name; + internal readonly string? Name; internal readonly int ParameterCount; internal readonly AnonymousTypeDescriptor TypeDescriptor; @@ -57,7 +55,7 @@ public SynthesizedDelegateKey(AnonymousTypeDescriptor typeDescr) TypeDescriptor = typeDescr; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is SynthesizedDelegateKey && Equals((SynthesizedDelegateKey)obj); } @@ -118,10 +116,8 @@ private ConcurrentDictionary AnonymousTypeT // Lazily create a template types cache if (_lazyAnonymousTypeTemplates == null) { - CSharpCompilation previousSubmission = this.Compilation.PreviousSubmission; - // TODO (tomat): avoid recursion - var previousCache = (previousSubmission == null) ? null : previousSubmission.AnonymousTypeManager.AnonymousTypeTemplates; + var previousCache = Compilation.PreviousSubmission?.AnonymousTypeManager.AnonymousTypeTemplates; Interlocked.CompareExchange(ref _lazyAnonymousTypeTemplates, previousCache == null @@ -140,10 +136,8 @@ private ConcurrentDictionary f.Type); return template.Construct(typeArguments); } -#nullable disable - - private AnonymousTypeTemplateSymbol CreatePlaceholderTemplate(Microsoft.CodeAnalysis.Emit.AnonymousTypeKey key) - { - var fields = key.Fields.SelectAsArray(f => new AnonymousTypeField(f.Name, Location.None, typeWithAnnotations: default, refKind: RefKind.None, ScopedKind.None)); - var typeDescr = new AnonymousTypeDescriptor(fields, Location.None); - return new AnonymousTypeTemplateSymbol(this, typeDescr); - } - - private AnonymousDelegateTemplateSymbol CreatePlaceholderSynthesizedDelegateValue(string name, RefKindVector refKinds, bool returnsVoid, int parameterCount) - { - return new AnonymousDelegateTemplateSymbol( - this, - MetadataHelpers.InferTypeArityAndUnmangleMetadataName(name, out _), - this.System_Object, - Compilation.GetSpecialType(SpecialType.System_IntPtr), - returnsVoid ? Compilation.GetSpecialType(SpecialType.System_Void) : null, - parameterCount, - refKinds); - } /// /// Resets numbering in anonymous type names and compiles the @@ -483,53 +457,26 @@ private AnonymousDelegateTemplateSymbol CreatePlaceholderSynthesizedDelegateValu /// public void AssignTemplatesNamesAndCompile(MethodCompiler compiler, PEModuleBuilder moduleBeingBuilt, BindingDiagnosticBag diagnostics) { - // Ensure all previous anonymous type templates are included so the - // types are available for subsequent edit and continue generations. - foreach (var key in moduleBeingBuilt.GetPreviousAnonymousTypes()) - { - Debug.Assert(!key.IsDelegate); - var templateKey = AnonymousTypeDescriptor.ComputeKey(key.Fields, f => f.Name); - this.AnonymousTypeTemplates.GetOrAdd(templateKey, k => this.CreatePlaceholderTemplate(key)); - } - // Get all anonymous types owned by this manager var anonymousTypes = ArrayBuilder.GetInstance(); + var anonymousDelegates = ArrayBuilder.GetInstance(); var anonymousDelegatesWithIndexedNames = ArrayBuilder.GetInstance(); + GetCreatedAnonymousTypeTemplates(anonymousTypes); + GetCreatedAnonymousDelegates(anonymousDelegates); GetCreatedAnonymousDelegatesWithIndexedNames(anonymousDelegatesWithIndexedNames); // If the collection is not sealed yet we should assign // new indexes to the created anonymous type templates - if (!this.AreTemplatesSealed) + if (!AreTemplatesSealed) { - // If we are emitting .NET module, include module's name into type's name to ensure - // uniqueness across added modules. - string moduleId; - - if (moduleBeingBuilt.OutputKind == OutputKind.NetModule) - { - moduleId = moduleBeingBuilt.Name; - - string extension = OutputKind.NetModule.GetDefaultExtension(); - - if (moduleId.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) - { - moduleId = moduleId.Substring(0, moduleId.Length - extension.Length); - } - - moduleId = MetadataHelpers.MangleForTypeNameIfNeeded(moduleId); - } - else - { - moduleId = string.Empty; - } + string moduleId = getModuleId(); + int submissionSlotIndex = Compilation.GetSubmissionSlotIndex(); - int submissionSlotIndex = this.Compilation.GetSubmissionSlotIndex(); + assignIndexedNames(anonymousTypes, moduleBeingBuilt.GetNextAnonymousTypeIndex(), isDelegate: false); + assignIndexedNames(anonymousDelegatesWithIndexedNames, moduleBeingBuilt.GetNextAnonymousDelegateIndex(), isDelegate: true); - assignNames(anonymousTypes, moduleBeingBuilt.GetNextAnonymousTypeIndex(), isDelegate: false); - assignNames(anonymousDelegatesWithIndexedNames, moduleBeingBuilt.GetNextAnonymousDelegateIndex(), isDelegate: true); - - void assignNames(IReadOnlyList templates, int nextIndex, bool isDelegate) + void assignIndexedNames(IReadOnlyList templates, int nextIndex, bool isDelegate) { foreach (var template in templates) { @@ -550,7 +497,7 @@ void assignNames(IReadOnlyList templates, } } - this.SealTemplates(); + SealTemplates(); } if (anonymousTypes.Count > 0 && !ReportMissingOrErroneousSymbols(diagnostics)) @@ -566,21 +513,6 @@ void assignNames(IReadOnlyList templates, } } - anonymousTypes.Free(); - - // Ensure all previous synthesized delegates are included so the - // types are available for subsequent edit and continue generations. - foreach (var key in moduleBeingBuilt.GetPreviousAnonymousDelegates()) - { - if (GeneratedNames.TryParseSynthesizedDelegateName(key.Name, out var refKinds, out var returnsVoid, out var generation, out var parameterCount)) - { - var delegateKey = new SynthesizedDelegateKey(parameterCount, refKinds, returnsVoid, generation); - this.AnonymousDelegates.GetOrAdd(delegateKey, (k, args) => CreatePlaceholderSynthesizedDelegateValue(key.Name, args.refKinds, args.returnsVoid, args.parameterCount), (refKinds, returnsVoid, parameterCount)); - } - } - - var anonymousDelegates = ArrayBuilder.GetInstance(); - GetCreatedAnonymousDelegates(anonymousDelegates); if (anonymousDelegatesWithIndexedNames.Count > 0 || anonymousDelegates.Count > 0) { ReportMissingOrErroneousSymbolsForDelegates(diagnostics); @@ -593,9 +525,33 @@ void assignNames(IReadOnlyList templates, compiler.Visit(anonymousDelegate, null); } } - anonymousDelegates.Free(); + anonymousTypes.Free(); + anonymousDelegates.Free(); anonymousDelegatesWithIndexedNames.Free(); + + string getModuleId() + { + // If we are emitting .NET module, include module's name into type's name to ensure + // uniqueness across added modules. + + if (moduleBeingBuilt.OutputKind == OutputKind.NetModule) + { + string extension = OutputKind.NetModule.GetDefaultExtension(); + + var moduleId = moduleBeingBuilt.Name; + if (moduleId.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) + { + moduleId = moduleId[..^extension.Length]; + } + + return MetadataHelpers.MangleForTypeNameIfNeeded(moduleId); + } + else + { + return string.Empty; + } + } } /// @@ -658,7 +614,7 @@ private void GetCreatedAnonymousDelegates(ArrayBuilder { public static readonly SynthesizedDelegateSymbolComparer Instance = new SynthesizedDelegateSymbolComparer(); @@ -741,6 +697,12 @@ internal ImmutableArray GetAllCreatedTemplates() return builder.ToImmutableAndFree(); } + internal override SynthesizedTypeMaps GetSynthesizedTypeMaps() + => new SynthesizedTypeMaps( + GetAnonymousTypeMap(), + GetAnonymousDelegates(), + GetAnonymousDelegatesWithIndexedNames()); + /// /// Returns true if the named type is an implementation template for an anonymous type /// diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueClosureTests.cs index 4b5b85deef2b2..2a06fe5f7c8b3 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -10071,6 +10071,7 @@ public void F() }) .AddGeneration( + // 1 source: """ using System; class C @@ -10141,6 +10142,65 @@ .maxstack 8 } """); }) + .AddGeneration( + // 2 + source: """ + using System; + class C + { + public void F() + { + _ = new Func(() => (byte)1); + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true, rudeEdits: _ => new RuntimeRudeEdit("Return type changed", 0x123)), + ], + validator: g => + { + g.VerifySynthesizedMembers( + "System.Runtime.CompilerServices.HotReloadException", + "C: {<>c}", + "C.<>c: {<>9__0_0#2, b__0_0#2, <>9__0_0#1, b__0_0#1}"); + + g.VerifyMethodDefNames( + "F", "b__0_0#1", "b__0_0#2"); + + g.VerifyIL( + """ + F + { + // Code size 30 (0x1e) + .maxstack 8 + IL_0000: nop + IL_0001: ldsfld 0x04000005 + IL_0006: brtrue.s IL_001d + IL_0008: ldsfld 0x04000001 + IL_000d: ldftn 0x06000008 + IL_0013: newobj 0x0A00000B + IL_0018: stsfld 0x04000005 + IL_001d: ret + } + b__0_0#1 + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: ldstr 0x7000010D + IL_0005: ldc.i4 0x123 + IL_000a: newobj 0x06000006 + IL_000f: throw + } + b__0_0#2 + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldc.i4.1 + IL_0001: ret + } + """); + }) .Verify(); } } diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs index 52fb9f8a63973..63462e1ba2182 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs @@ -2256,6 +2256,256 @@ void F() "C: {<>c}"); } + [Fact] + public void Lambda_SynthesizedDelegate() + { + using var _ = new EditAndContinueTest() + .AddBaseline( + source: """ + class C + { + void F() + { + var f = (ref int a) => a; + } + } + """, + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class C: {<>c}", + "class C.<>c: {<>9__0_0, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>F{00000001}"); + }) + .AddGeneration( + // 1 + source: """ + class C + { + void F() + { + var g = (out byte a) => a = 1; + var f = (ref int a) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class C: {<>c}", + "class C.<>c: {<>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" // new and reused lambdas + ]); + + g.VerifySynthesizedTypes( + "<>F{00000001}", + "<>F{00000002}"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>F{00000002}`2"); + g.VerifyMethodDefNames("F", "b__0_0", ".ctor", "Invoke", "b__0_0#1"); + }) + .AddGeneration( + // 2 + source: """ + class C + { + void F() + { + var f = (ref bool a, ref bool b) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true, rudeEdits: _ => new RuntimeRudeEdit("Parameter changed", 0x123)), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "System.Runtime.CompilerServices.HotReloadException", + "class C: {<>c}", + "class C.<>c: {<>9__0_0#2, b__0_0#2, <>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>F{00000001}", + "<>F{00000002}", + "<>F{00000009}"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>F{00000009}`3", "HotReloadException"); + g.VerifyMethodDefNames("F", "b__0_0", "b__0_0#1", ".ctor", "Invoke", ".ctor", "b__0_0#2"); + }) + .AddGeneration( + // 3 + source: """ + class C + { + void F() + { + var f = (ref bool a, ref bool b) => a; + var g = (ref bool a, ref bool b, bool c) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "System.Runtime.CompilerServices.HotReloadException", + "class C: {<>c}", + "class C.<>c: {<>9__0_0#2, <>9__0_1#3, b__0_0#2, b__0_1#3, <>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>F{00000001}", + "<>F{00000002}", + "<>F{00000009}", + "<>F{00000009}"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>F{00000009}`4"); + g.VerifyMethodDefNames("F", "b__0_0#2", ".ctor", "Invoke", "b__0_1#3"); + }) + .Verify(); + } + + [Fact] + public void Lambda_SynthesizedDelegate_WithIndexedName() + { + using var _ = new EditAndContinueTest() + .AddBaseline( + source: """ + class C + { + void F() + { + var f = (int a = 1) => a; + } + } + """, + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class C: {<>c}", + "class C.<>c: {<>9__0_0, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousDelegate0"); + }) + .AddGeneration( + // 1 + source: """ + class C + { + void F() + { + var g = (int a = 1) => a + 1; + var f = (int a = 2) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class C: {<>c}", + "class C.<>c: {<>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" // new lambda is created + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousDelegate0", + "<>f__AnonymousDelegate1"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>f__AnonymousDelegate1`2"); + g.VerifyMethodDefNames("F", "b__0_0", ".ctor", "Invoke", "b__0_0#1"); + }) + .AddGeneration( + // 2 + source: """ + class C + { + void F() + { + var f = (int a = 3) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true, rudeEdits: _ => new RuntimeRudeEdit("Parameter changed", 0x123)), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "System.Runtime.CompilerServices.HotReloadException", + "class C: {<>c}", + "class C.<>c: {<>9__0_0#2, b__0_0#2, <>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousDelegate0", + "<>f__AnonymousDelegate1", + "<>f__AnonymousDelegate2"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>f__AnonymousDelegate2`2", "HotReloadException"); + g.VerifyMethodDefNames("F", "b__0_0", "b__0_0#1", ".ctor", "Invoke", ".ctor", "b__0_0#2"); + }) + .AddGeneration( + // 3 + source: """ + class C + { + void F() + { + var f = (int a = 3) => a; + var g = (int a = 1, int b = 2) => a; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "System.Runtime.CompilerServices.HotReloadException", + "class C: {<>c}", + "class C.<>c: {<>9__0_0#2, <>9__0_1#3, b__0_0#2, b__0_1#3, <>9__0_0#1, <>9__0_0, b__0_0#1, b__0_0}" + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousDelegate0", + "<>f__AnonymousDelegate1", + "<>f__AnonymousDelegate2", + "<>f__AnonymousDelegate3"); // new synthesized delegate is created + + g.VerifyTypeDefNames("<>f__AnonymousDelegate3`3"); + g.VerifyMethodDefNames("F", "b__0_0#2", ".ctor", "Invoke", "b__0_1#3"); + }) + .Verify(); + } + [Fact] public void Lambda_Delete() { @@ -9871,94 +10121,162 @@ .locals init (<>f__AnonymousType0 V_0) //x /// /// Reuse existing anonymous types. /// - [WorkItem(825903, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/825903")] [Fact] + [WorkItem(825903, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/825903")] public void AnonymousTypes() { - var source0 = -@"namespace N -{ - class A - { - static object F = new { A = 1, B = 2 }; - } -} -namespace M -{ - class B - { - static void M() - { - var x = new { B = 3, A = 4 }; - var y = x.A; - var z = new { }; - } - } -}"; - var source1 = -@"namespace N -{ - class A - { - static object F = new { A = 1, B = 2 }; - } -} -namespace M -{ - class B - { - static void M() - { - var x = new { B = 3, A = 4 }; - var y = new { A = x.A }; - var z = new { }; - } - } -}"; - var compilation0 = CreateCompilation(source0, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.DebugDll); - var compilation1 = compilation0.WithSource(source1); - - var m0 = compilation0.GetMember("M.B.M"); - var m1 = compilation1.GetMember("M.B.M"); - - var testData0 = new CompilationTestData(); - var bytes0 = compilation0.EmitToArray(testData: testData0); + using var _ = new EditAndContinueTest() + .AddBaseline( + source: """ + class C + { + void F() + { + var f = new { a = 1, b = 2 }; + } + } + """, + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class <>f__AnonymousType0<j__TPar, j__TPar>: {Equals, GetHashCode, ToString}" + ]); - using var md0 = ModuleMetadata.CreateFromImage(bytes0); - var generation0 = CreateInitialBaseline(compilation0, md0, testData0.GetMethodData("M.B.M").EncDebugInfoProvider()); + g.VerifySynthesizedTypes( + "<>f__AnonymousType0<j__TPar, j__TPar>"); - var reader0 = md0.MetadataReader; - CheckNames(reader0, reader0.GetTypeDefNames(), "", "<>f__AnonymousType0`2", "<>f__AnonymousType1`2", "<>f__AnonymousType2", "B", "A"); + g.VerifyIL("C.F", """ + { + // Code size 10 (0xa) + .maxstack 2 + .locals init (<>f__AnonymousType0 V_0) //f + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: ldc.i4.2 + IL_0003: newobj "<>f__AnonymousType0..ctor(int, int)" + IL_0008: stloc.0 + IL_0009: ret + } + """); + }) + .AddGeneration( + // 1 + source: """ + class C + { + void F() + { + var g = new { x = 1 }; + var f = new { a = 1, b = 2 }; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class <>f__AnonymousType0<j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType1<j__TPar>: {Equals, GetHashCode, ToString}" + ]); - var diff1 = compilation1.EmitDifference( - generation0, - ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, m0, m1, GetEquivalentNodesMap(m1, m0)))); + g.VerifySynthesizedTypes( + "<>f__AnonymousType0<j__TPar, j__TPar>", + "<>f__AnonymousType1<j__TPar>"); - using var md1 = diff1.GetMetadata(); - var reader1 = md1.Reader; - CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "<>f__AnonymousType3`1"); // one additional type + g.VerifyTypeDefNames("<>f__AnonymousType1`1"); + g.VerifyMethodDefNames("F", "get_x", ".ctor", "Equals", "GetHashCode", "ToString"); - diff1.VerifyIL("M.B.M", @" -{ - // Code size 28 (0x1c) - .maxstack 2 - .locals init (<>f__AnonymousType1 V_0, //x - [int] V_1, - <>f__AnonymousType2 V_2, //z - <>f__AnonymousType3 V_3) //y - IL_0000: nop - IL_0001: ldc.i4.3 - IL_0002: ldc.i4.4 - IL_0003: newobj ""<>f__AnonymousType1..ctor(int, int)"" - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: callvirt ""int <>f__AnonymousType1.A.get"" - IL_000f: newobj ""<>f__AnonymousType3..ctor(int)"" - IL_0014: stloc.3 - IL_0015: newobj ""<>f__AnonymousType2..ctor()"" - IL_001a: stloc.2 - IL_001b: ret -}"); + g.VerifyIL("C.F", """ + { + // Code size 17 (0x11) + .maxstack 2 + .locals init (<>f__AnonymousType0 V_0, //f + <>f__AnonymousType1 V_1) //g + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: newobj "<>f__AnonymousType1..ctor(int)" + IL_0007: stloc.1 + IL_0008: ldc.i4.1 + IL_0009: ldc.i4.2 + IL_000a: newobj "<>f__AnonymousType0..ctor(int, int)" + IL_000f: stloc.0 + IL_0010: ret + } + """); + }) + .AddGeneration( + // 2 + source: """ + class C + { + void F() + { + var f = new { a = 1, b = 2, c = 3 }; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class <>f__AnonymousType0<j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType1<j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType2<j__TPar, j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousType0<j__TPar, j__TPar>", + "<>f__AnonymousType1<j__TPar>", + "<>f__AnonymousType2<j__TPar, j__TPar, j__TPar>"); + + g.VerifyTypeDefNames("<>f__AnonymousType2`3"); + g.VerifyMethodDefNames("F", "get_a", "get_b", "get_c", ".ctor", "Equals", "GetHashCode", "ToString"); + }) + .AddGeneration( + // 3 + source: """ + class C + { + void F() + { + var f = new { a = 1, b = 2, c = 3 }; + var g = new { x = 1, y = 2 }; + } + } + """, + edits: + [ + Edit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true), + ], + validator: g => + { + g.VerifySynthesizedMembers(displayTypeKind: true, + [ + "class <>f__AnonymousType0<j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType1<j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType2<j__TPar, j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + "class <>f__AnonymousType3<j__TPar, j__TPar>: {Equals, GetHashCode, ToString}", + ]); + + g.VerifySynthesizedTypes( + "<>f__AnonymousType0<j__TPar, j__TPar>", + "<>f__AnonymousType1<j__TPar>", + "<>f__AnonymousType2<j__TPar, j__TPar, j__TPar>", + "<>f__AnonymousType3<j__TPar, j__TPar>"); + + g.VerifyTypeDefNames("<>f__AnonymousType3`2"); + g.VerifyMethodDefNames("F", "get_x", "get_y", ".ctor", "Equals", "GetHashCode", "ToString"); + }) + .Verify(); } /// diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/SymbolMatcherTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/SymbolMatcherTests.cs index 9561e0fe9c81b..12eb455d850fb 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/SymbolMatcherTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/SymbolMatcherTests.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Symbols; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -37,14 +38,14 @@ private static CSharpSymbolMatcher CreateMatcher(CSharpCompilation fromCompilati fromCompilation.SourceAssembly, toCompilation.SourceAssembly, SynthesizedTypeMaps.Empty, - otherSynthesizedMembers: null, - otherDeletedMembers: null); + otherSynthesizedMembers: SpecializedCollections.EmptyReadOnlyDictionary>(), + otherDeletedMembers: SpecializedCollections.EmptyReadOnlyDictionary>()); private static CSharpSymbolMatcher CreateMatcher(CSharpCompilation fromCompilation, PEAssemblySymbol peAssemblySymbol) => new CSharpSymbolMatcher( - SynthesizedTypeMaps.Empty, fromCompilation.SourceAssembly, - peAssemblySymbol); + peAssemblySymbol, + SynthesizedTypeMaps.Empty); private static IEnumerable Inspect(ImmutableSegmentedDictionary> anonymousDelegatesWithIndexedNames) => from entry in anonymousDelegatesWithIndexedNames @@ -505,7 +506,7 @@ static void F() Assert.Equal("x1", x1.Name); Assert.Equal("x2", x2.Name); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var mappedX1 = (Cci.IFieldDefinition)matcher.MapDefinition(x1); var mappedX2 = (Cci.IFieldDefinition)matcher.MapDefinition(x2); @@ -575,7 +576,7 @@ static void F() var x1 = fields.Where(f => f.Name == "x1").Single(); var x2 = fields.Where(f => f.Name == "x2").Single(); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var mappedX1 = (Cci.IFieldDefinition)matcher.MapDefinition(x1); var mappedX2 = (Cci.IFieldDefinition)matcher.MapDefinition(x2); @@ -1129,7 +1130,7 @@ static void M(string? x) var y1 = fields.Where(f => f.Name == "y1").Single(); var y2 = fields.Where(f => f.Name == "y2").Single(); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var mappedY1 = (Cci.IFieldDefinition)matcher.MapDefinition(y1); var mappedY2 = (Cci.IFieldDefinition)matcher.MapDefinition(y2); @@ -1486,7 +1487,7 @@ static void F() Assert.Equal("<>9__0_1", field2.Name); Assert.Equal("<>9__0_2", field3.Name); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var mappedField1 = (Cci.IFieldDefinition)matcher.MapDefinition(field1); var mappedField2 = (Cci.IFieldDefinition)matcher.MapDefinition(field2); @@ -1544,7 +1545,7 @@ static void Main() var field0 = displayClass.GetFields(emitContext).Single(f => f.Name == "<>9__0_0"); Assert.Equal("<>f__AnonymousDelegate0", field0.GetType(emitContext).ToString()); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var field1 = (Cci.IFieldDefinition)matcher.MapDefinition(field0); Assert.Equal("<>9__0_0", field1.Name); } @@ -1614,7 +1615,7 @@ unsafe static void F() Assert.Equal("<>9__0_1", field2.Name); Assert.Equal("<>9__0_2", field3.Name); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); Assert.Null(matcher.MapDefinition(field1)); Assert.Null(matcher.MapDefinition(field2)); @@ -1684,7 +1685,7 @@ unsafe static void F() Assert.Equal("<>9__0_1", field2.Name); Assert.Equal("<>9__0_2", field3.Name); - var matcher = new CSharpSymbolMatcher(synthesizedTypes0, compilation1.SourceAssembly, peAssemblySymbol0); + var matcher = new CSharpSymbolMatcher(compilation1.SourceAssembly, peAssemblySymbol0, synthesizedTypes0); var mappedField1 = (Cci.IFieldDefinition)matcher.MapDefinition(field1); var mappedField2 = (Cci.IFieldDefinition)matcher.MapDefinition(field2); diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index e09234674e1c9..ac7f2b58d71bf 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -3435,7 +3435,70 @@ internal static bool SerializePeToStream( return true; } - private protected abstract EmitBaseline MapToCompilation(CommonPEModuleBuilder moduleBeingBuilt); + /// + /// Return a version of the baseline with all definitions mapped to this compilation. + /// Definitions from the initial generation, from metadata, are not mapped since + /// the initial generation is always included as metadata. That is, the symbols from + /// types, methods, ... in the TypesAdded, MethodsAdded, ... collections are replaced + /// by the corresponding symbols from the current compilation. + /// + internal EmitBaseline MapToCompilation(CommonPEModuleBuilder moduleBeingBuilt) + { + var previousGeneration = moduleBeingBuilt.PreviousGeneration; + Debug.Assert(previousGeneration != null); + Debug.Assert(previousGeneration.Compilation != this); + + if (previousGeneration.Ordinal == 0) + { + // Initial generation, nothing to map. (Since the initial generation + // is always loaded from metadata in the context of the current + // compilation, there's no separate mapping step.) + return previousGeneration; + } + + Debug.Assert(previousGeneration.Compilation != null); + Debug.Assert(previousGeneration.PEModuleBuilder != null); + Debug.Assert(moduleBeingBuilt.EncSymbolChanges != null); + + var currentSynthesizedTypes = moduleBeingBuilt.GetAllSynthesizedTypes(); + var currentSynthesizedMembers = moduleBeingBuilt.GetAllSynthesizedMembers(); + var currentDeletedMembers = moduleBeingBuilt.EncSymbolChanges.DeletedMembers; + + // Mapping from previous compilation to the current. + var matcher = CreatePreviousToCurrentSourceAssemblyMatcher( + previousGeneration, + currentSynthesizedTypes, + currentSynthesizedMembers, + currentDeletedMembers); + + var mappedSynthesizedTypes = matcher.MapSynthesizedTypes(previousGeneration.SynthesizedTypes, currentSynthesizedTypes); + + var mappedSynthesizedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.SynthesizedMembers, currentSynthesizedMembers, isDeletedMemberMapping: false); + + // Deleted members are mapped the same way as synthesized members, so we can just call the same method. + var mappedDeletedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.DeletedMembers, currentDeletedMembers, isDeletedMemberMapping: true); + + // TODO: can we reuse some data from the previous matcher? + var matcherWithAllSynthesizedTypesAndMembers = CreatePreviousToCurrentSourceAssemblyMatcher( + previousGeneration, + mappedSynthesizedTypes, + mappedSynthesizedMembers, + mappedDeletedMembers); + + return matcherWithAllSynthesizedTypesAndMembers.MapBaselineToCompilation( + previousGeneration, + this, + moduleBeingBuilt, + mappedSynthesizedTypes, + mappedSynthesizedMembers, + mappedDeletedMembers); + } + + private protected abstract SymbolMatcher CreatePreviousToCurrentSourceAssemblyMatcher( + EmitBaseline previousGeneration, + SynthesizedTypeMaps otherSynthesizedTypes, + IReadOnlyDictionary> otherSynthesizedMembers, + IReadOnlyDictionary> otherDeletedMembers); internal EmitBaseline? SerializeToDeltaStreams( CommonPEModuleBuilder moduleBeingBuilt, diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index d693f2b79a6f9..958b4a34b1941 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -159,7 +159,17 @@ public void CreateDeletedMethodDefinitions(DiagnosticBag diagnosticBag) internal abstract IAssemblySymbolInternal CommonCorLibrary { get; } internal abstract CommonModuleCompilationState CommonModuleCompilationState { get; } internal abstract void CompilationFinished(); + + /// + /// Returns all type members synthesized when compiling method bodies for this module. + /// internal abstract ImmutableDictionary> GetAllSynthesizedMembers(); + + /// + /// Returns all delegates and anonymous templates synthesized when compiling method bodies for this module. + /// + internal abstract SynthesizedTypeMaps GetAllSynthesizedTypes(); + internal abstract CommonEmbeddedTypesManager CommonEmbeddedTypesManagerOpt { get; } internal abstract Cci.ITypeReference EncTranslateType(ITypeSymbolInternal type, DiagnosticBag diagnostics); public abstract IEnumerable GetSourceAssemblyAttributes(bool isRefAssembly); @@ -1077,6 +1087,9 @@ internal override ImmutableDictionary Compilation.CommonAnonymousTypeManager.GetSynthesizedTypeMaps(); + #endregion #region Token Mapping diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs index 96a218ede1dcf..6e54218dd9915 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs @@ -194,9 +194,10 @@ internal EmitBaseline GetDelta(Compilation compilation, Guid encId, MetadataSize tableSizes[i] = previousTableSizes[i] + deltaTableSizes[i]; } - // If the previous generation is 0 (metadata) get the synthesized members from the current compilation's builder, + // If the previous generation is 0 (metadata) get the synthesized members and types from the current compilation's builder, // otherwise members from the current compilation have already been merged into the baseline. var synthesizedMembers = (_previousGeneration.Ordinal == 0) ? module.GetAllSynthesizedMembers() : _previousGeneration.SynthesizedMembers; + var synthesizedTypes = (_previousGeneration.Ordinal == 0) ? module.GetAllSynthesizedTypes() : _previousGeneration.SynthesizedTypes; Debug.Assert(module.EncSymbolChanges is not null); var deletedMembers = (_previousGeneration.Ordinal == 0) ? module.EncSymbolChanges.DeletedMembers : _previousGeneration.DeletedMembers; @@ -238,7 +239,7 @@ internal EmitBaseline GetDelta(Compilation compilation, Guid encId, MetadataSize userStringStreamLengthAdded: metadataSizes.GetAlignedHeapSize(HeapIndex.UserString) + _previousGeneration.UserStringStreamLengthAdded, // Guid stream accumulates on the GUID heap unlike other heaps, so the previous generations are already included. guidStreamLengthAdded: metadataSizes.HeapSizes[(int)HeapIndex.Guid], - synthesizedTypes: ((IPEDeltaAssemblyBuilder)module).GetSynthesizedTypes(), + synthesizedTypes: synthesizedTypes, synthesizedMembers: synthesizedMembers, deletedMembers: deletedMembers, addedOrChangedMethods: AddRange(_previousGeneration.AddedOrChangedMethods, addedOrChangedMethodsByIndex), diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/IPEDeltaAssemblyBuilder.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/IPEDeltaAssemblyBuilder.cs index 968b922e4ebef..cf850e27e79f4 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/IPEDeltaAssemblyBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/IPEDeltaAssemblyBuilder.cs @@ -7,5 +7,4 @@ namespace Microsoft.CodeAnalysis.Emit; internal interface IPEDeltaAssemblyBuilder { void OnCreatedIndices(DiagnosticBag diagnostics); - SynthesizedTypeMaps GetSynthesizedTypes(); } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs index c57e6b2d0d496..1548073d4d105 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/SymbolMatcher.cs @@ -2,13 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Emit { @@ -17,6 +17,7 @@ internal abstract class SymbolMatcher public abstract Cci.ITypeReference? MapReference(Cci.ITypeReference reference); public abstract Cci.IDefinition? MapDefinition(Cci.IDefinition definition); public abstract Cci.INamespace? MapNamespace(Cci.INamespace @namespace); + protected abstract bool TryGetMatchingDelegateWithIndexedName(INamedTypeSymbolInternal delegateTemplate, ImmutableArray values, out AnonymousTypeValue match); public ISymbolInternal? MapDefinitionOrNamespace(ISymbolInternal symbol) { @@ -30,6 +31,7 @@ public EmitBaseline MapBaselineToCompilation( EmitBaseline baseline, Compilation targetCompilation, CommonPEModuleBuilder targetModuleBuilder, + SynthesizedTypeMaps mappedSynthesizedTypes, IReadOnlyDictionary> mappedSynthesizedMembers, IReadOnlyDictionary> mappedDeletedMembers) { @@ -62,10 +64,7 @@ public EmitBaseline MapBaselineToCompilation( stringStreamLengthAdded: baseline.StringStreamLengthAdded, userStringStreamLengthAdded: baseline.UserStringStreamLengthAdded, guidStreamLengthAdded: baseline.GuidStreamLengthAdded, - synthesizedTypes: new SynthesizedTypeMaps( - MapAnonymousTypes(baseline.SynthesizedTypes.AnonymousTypes), - MapAnonymousDelegates(baseline.SynthesizedTypes.AnonymousDelegates), - MapAnonymousDelegatesWithIndexedNames(baseline.SynthesizedTypes.AnonymousDelegatesWithIndexedNames)), + synthesizedTypes: mappedSynthesizedTypes, synthesizedMembers: mappedSynthesizedMembers, deletedMembers: mappedDeletedMembers, addedOrChangedMethods: MapAddedOrChangedMethods(baseline.AddedOrChangedMethods), @@ -105,48 +104,92 @@ private IReadOnlyDictionary MapAddedOrChangedMeth return result; } - private ImmutableSegmentedDictionary MapAnonymousTypes(IReadOnlyDictionary anonymousTypeMap) + /// + /// Merges anonymous types/delegates generated during lowering, or emit, of the current compilation with aggregate + /// types/delegates from all previous source generations (gen >= 1) similarly to + /// + private static ImmutableSegmentedDictionary MapAnonymousTypesAndDelegatesWithUniqueKey( + ImmutableSegmentedDictionary previousTypes, + ImmutableSegmentedDictionary newTypes) + where TKey : IEquatable { - var builder = ImmutableSegmentedDictionary.CreateBuilder(); - - foreach (var (key, value) in anonymousTypeMap) + if (previousTypes.Count == 0) { - var type = (Cci.ITypeDefinition?)MapDefinition(value.Type); - Debug.Assert(type != null); - builder.Add(key, new AnonymousTypeValue(value.Name, value.UniqueIndex, type)); + return newTypes; } - return builder.ToImmutable(); - } - - private ImmutableSegmentedDictionary MapAnonymousDelegates(IReadOnlyDictionary anonymousDelegates) - { - var builder = ImmutableSegmentedDictionary.CreateBuilder(); + var builder = ImmutableSegmentedDictionary.CreateBuilder(); + builder.AddRange(newTypes); - foreach (var (key, value) in anonymousDelegates) + foreach (var (key, previousValue) in previousTypes) { - var delegateTypeDef = (Cci.ITypeDefinition?)MapDefinition(value.Delegate); - Debug.Assert(delegateTypeDef != null); - builder.Add(key, new SynthesizedDelegateValue(delegateTypeDef)); + if (newTypes.ContainsKey(key)) + { + continue; + } + + builder.Add(key, previousValue); } return builder.ToImmutable(); } + /// + /// Merges anonymous delegates with indexed names generated during lowering, or emit, of the current compilation with aggregate + /// delegates from all previous source generations (gen >= 1) similarly to + /// private ImmutableSegmentedDictionary> MapAnonymousDelegatesWithIndexedNames( - IReadOnlyDictionary> anonymousDelegates) + ImmutableSegmentedDictionary> previousDelegates, + ImmutableSegmentedDictionary> newDelegates) { + if (previousDelegates.Count == 0) + { + return newDelegates; + } + var builder = ImmutableSegmentedDictionary.CreateBuilder>(); + builder.AddRange(newDelegates); - foreach (var (key, values) in anonymousDelegates) + foreach (var (key, previousValues) in previousDelegates) { - builder.Add(key, values.SelectAsArray(value => new AnonymousTypeValue( - value.Name, value.UniqueIndex, (Cci.ITypeDefinition?)MapDefinition(value.Type) ?? throw ExceptionUtilities.UnexpectedValue(value.Type)))); + if (!newDelegates.TryGetValue(key, out var newValues)) + { + builder.Add(key, previousValues); + continue; + } + + ArrayBuilder? mergedValuesBuilder = null; + + foreach (var previousValue in previousValues) + { + var template = (INamedTypeSymbolInternal?)previousValue.Type.GetInternalSymbol(); + Debug.Assert(template is not null); + + if (TryGetMatchingDelegateWithIndexedName(template, newValues, out _)) + { + continue; + } + + mergedValuesBuilder ??= ArrayBuilder.GetInstance(); + mergedValuesBuilder.Add(previousValue); + } + + if (mergedValuesBuilder != null) + { + mergedValuesBuilder.AddRange(newValues); + builder[key] = mergedValuesBuilder.ToImmutableAndFree(); + } } return builder.ToImmutable(); } + internal SynthesizedTypeMaps MapSynthesizedTypes(SynthesizedTypeMaps previousTypes, SynthesizedTypeMaps newTypes) + => new SynthesizedTypeMaps( + MapAnonymousTypesAndDelegatesWithUniqueKey(previousTypes.AnonymousTypes, newTypes.AnonymousTypes), + MapAnonymousTypesAndDelegatesWithUniqueKey(previousTypes.AnonymousDelegates, newTypes.AnonymousDelegates), + MapAnonymousDelegatesWithIndexedNames(previousTypes.AnonymousDelegatesWithIndexedNames, newTypes.AnonymousDelegatesWithIndexedNames)); + /// /// Merges synthesized or deleted members generated during lowering, or emit, of the current compilation with aggregate /// synthesized or deleted members from all previous source generations (gen >= 1). @@ -160,6 +203,8 @@ private ImmutableSegmentedDictionary {A', B', C, D}, U -> {G, H}, T -> {E, F}} + /// + /// Note that the results may include symbols declared in different compilations (previous generations). /// internal IReadOnlyDictionary> MapSynthesizedOrDeletedMembers( IReadOnlyDictionary> previousMembers, diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/SynthesizedTypeMaps.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/SynthesizedTypeMaps.cs index 5b7555008a080..3269a53c49b55 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/SynthesizedTypeMaps.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/SynthesizedTypeMaps.cs @@ -31,14 +31,10 @@ public bool IsEmpty = anonymousDelegates ?? ImmutableSegmentedDictionary.Empty; /// - /// A map of the assembly identities of the baseline compilation to the identities of the original metadata AssemblyRefs. - /// Only includes identities that differ between these two. + /// In C#, the set of anonymous delegates with name that is not determined by parameter types only + /// and need to be suffixed by an index (e.g. delegates may have same parameter types but differ in default parameter values); + /// in VB, this set is unused and empty. /// public ImmutableSegmentedDictionary> AnonymousDelegatesWithIndexedNames { get; } = anonymousDelegatesWithIndexedNames ?? ImmutableSegmentedDictionary>.Empty; - - public bool IsSubsetOf(SynthesizedTypeMaps other) - => AnonymousTypes.Keys.All(static (key, other) => other.AnonymousTypes.ContainsKey(key), other) && - AnonymousDelegates.Keys.All(static (key, other) => other.AnonymousDelegates.ContainsKey(key), other) && - AnonymousDelegatesWithIndexedNames.Keys.All(static (key, other) => other.AnonymousDelegatesWithIndexedNames.ContainsKey(key), other); } diff --git a/src/Compilers/Core/Portable/Symbols/AnonymousTypes/CommonAnonymousTypeManager.cs b/src/Compilers/Core/Portable/Symbols/AnonymousTypes/CommonAnonymousTypeManager.cs index f5076011f49ac..f85bde592e979 100644 --- a/src/Compilers/Core/Portable/Symbols/AnonymousTypes/CommonAnonymousTypeManager.cs +++ b/src/Compilers/Core/Portable/Symbols/AnonymousTypes/CommonAnonymousTypeManager.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.CodeAnalysis.Emit; + namespace Microsoft.CodeAnalysis.Symbols { internal abstract class CommonAnonymousTypeManager @@ -24,5 +26,7 @@ protected void SealTemplates() { _templatesSealed = ThreeState.True; } + + internal abstract SynthesizedTypeMaps GetSynthesizedTypeMaps(); } } diff --git a/src/Compilers/Test/Core/Compilation/CompilationDifference.cs b/src/Compilers/Test/Core/Compilation/CompilationDifference.cs index 164703d1fc3b4..4d7899ece7960 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationDifference.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationDifference.cs @@ -165,6 +165,11 @@ internal static void VerifySynthesizedMembers(IReadOnlyDictionary $"\"{s}\""); } + internal static void VerifySynthesizedSymbols(IEnumerable actualSymbols, params string[] expected) + { + AssertEx.SetEqual(expected, actualSymbols.Select(v => v.GetISymbol().ToDisplayString(SymbolDisplayFormat.TestFormat)), itemSeparator: ",\r\n", itemInspector: s => $"\"{s}\""); + } + public void VerifySynthesizedFields(string typeName, params string[] expectedSynthesizedTypesAndMemberCounts) { var actual = EmitResult.Baseline.SynthesizedMembers.Single(e => e.Key.ToString() == typeName).Value.Where(s => s.Kind == SymbolKind.Field).Select(s => (IFieldSymbol)s.GetISymbol()).Select(f => f.Name + ": " + f.Type); diff --git a/src/Compilers/Test/Core/Metadata/ILValidation.cs b/src/Compilers/Test/Core/Metadata/ILValidation.cs index 0d54449bcf116..a28f592bec5ef 100644 --- a/src/Compilers/Test/Core/Metadata/ILValidation.cs +++ b/src/Compilers/Test/Core/Metadata/ILValidation.cs @@ -271,6 +271,8 @@ public static unsafe string DumpEncDeltaMethodBodies(ImmutableArray il, Im var rvasAndNames = from handle in reader.MethodDefinitions let method = reader.GetMethodDefinition(handle) + // filter out runtime-implemented methods that do not have IL: + where (method.ImplAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.IL orderby method.RelativeVirtualAddress group method.Name by method.RelativeVirtualAddress into g // Legacy test support: name can only be resolved when readers for all generations are given. diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index a69866bb64c64..46e90c7caa5cd 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2534,8 +2534,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return True End Function - Private Protected Overrides Function MapToCompilation(moduleBeingBuilt As CommonPEModuleBuilder) As EmitBaseline - Return EmitHelpers.MapToCompilation(Me, DirectCast(moduleBeingBuilt, PEDeltaAssemblyBuilder)) + Private Protected Overrides Function CreatePreviousToCurrentSourceAssemblyMatcher( + previousGeneration As EmitBaseline, + otherSynthesizedTypes As SynthesizedTypeMaps, + otherSynthesizedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + otherDeletedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))) As SymbolMatcher + + Return New VisualBasicSymbolMatcher( + sourceAssembly:=DirectCast(previousGeneration.Compilation, VisualBasicCompilation).SourceAssembly, + otherAssembly:=SourceAssembly, + otherSynthesizedTypes, + otherSynthesizedMembers, + otherDeletedMembers) End Function Friend Overrides Function GenerateResources( diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb index 3d7544b5909ed..ae399601162d7 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/EmitHelpers.vb @@ -62,12 +62,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Dim metadataDecoder = DirectCast(metadataSymbols.MetadataDecoder, MetadataDecoder) Dim metadataAssembly = DirectCast(metadataDecoder.ModuleSymbol.ContainingAssembly, PEAssemblySymbol) - Dim matchToMetadata = New VisualBasicSymbolMatcher(initialBaseline.LazyMetadataSymbols.SynthesizedTypes, sourceAssembly, metadataAssembly) + Dim matchToMetadata = New VisualBasicSymbolMatcher( + sourceAssembly:=sourceAssembly, + otherAssembly:=metadataAssembly, + otherSynthesizedTypes:=initialBaseline.LazyMetadataSymbols.SynthesizedTypes) Dim previousSourceToMetadata = New VisualBasicSymbolMatcher( - metadataSymbols.SynthesizedTypes, - previousSourceAssembly, - metadataAssembly) + sourceAssembly:=previousSourceAssembly, + otherAssembly:=metadataAssembly, + otherSynthesizedTypes:=metadataSymbols.SynthesizedTypes) Dim previousSourceToCurrentSource As VisualBasicSymbolMatcher = Nothing If baseline.Ordinal > 0 Then @@ -76,9 +79,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit previousSourceToCurrentSource = New VisualBasicSymbolMatcher( sourceAssembly:=sourceAssembly, otherAssembly:=previousSourceAssembly, - baseline.SynthesizedTypes, - otherSynthesizedMembersOpt:=baseline.SynthesizedMembers, - otherDeletedMembersOpt:=baseline.DeletedMembers) + otherSynthesizedTypes:=baseline.SynthesizedTypes, + otherSynthesizedMembers:=baseline.SynthesizedMembers, + otherDeletedMembers:=baseline.DeletedMembers) End If definitionMap = New VisualBasicDefinitionMap(edits, metadataDecoder, previousSourceToMetadata, matchToMetadata, previousSourceToCurrentSource, baseline) @@ -163,52 +166,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return False End Function - - Friend Function MapToCompilation( - compilation As VisualBasicCompilation, - moduleBeingBuilt As PEDeltaAssemblyBuilder) As EmitBaseline - - Dim previousGeneration = moduleBeingBuilt.PreviousGeneration - Debug.Assert(previousGeneration.Compilation IsNot compilation) - - If previousGeneration.Ordinal = 0 Then - ' Initial generation, nothing to map. (Since the initial generation - ' is always loaded from metadata in the context of the current - ' compilation, there's no separate mapping step.) - Return previousGeneration - End If - - Dim synthesizedTypes = moduleBeingBuilt.GetSynthesizedTypes() - Dim currentSynthesizedMembers = moduleBeingBuilt.GetAllSynthesizedMembers() - Dim currentDeletedMembers = moduleBeingBuilt.EncSymbolChanges.DeletedMembers - - ' Mapping from previous compilation to the current. - Dim previousSourceAssembly = DirectCast(previousGeneration.Compilation, VisualBasicCompilation).SourceAssembly - - Dim matcher = New VisualBasicSymbolMatcher( - previousSourceAssembly, - compilation.SourceAssembly, - synthesizedTypes, - currentSynthesizedMembers, - currentDeletedMembers) - - Dim mappedSynthesizedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.SynthesizedMembers, currentSynthesizedMembers, isDeletedMemberMapping:=False) - Dim mappedDeletedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.DeletedMembers, currentDeletedMembers, isDeletedMemberMapping:=True) - - ' TODO can we reuse some data from the previous matcher? - Dim matcherWithAllSynthesizedMembers = New VisualBasicSymbolMatcher( - previousSourceAssembly, - compilation.SourceAssembly, - synthesizedTypes, - mappedSynthesizedMembers, - mappedDeletedMembers) - - Return matcherWithAllSynthesizedMembers.MapBaselineToCompilation( - previousGeneration, - compilation, - moduleBeingBuilt, - mappedSynthesizedMembers, - mappedDeletedMembers) - End Function End Module End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb index 88ad55df98eed..7e08b71b2dc46 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/PEDeltaAssemblyBuilder.vb @@ -225,19 +225,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit End Get End Property - Friend Overloads Function GetSynthesizedTypes() As SynthesizedTypeMaps Implements IPEDeltaAssemblyBuilder.GetSynthesizedTypes - ' VB anonymous delegates are handled as anonymous types - Dim result = New SynthesizedTypeMaps( - Compilation.AnonymousTypeManager.GetAnonymousTypeMap(), - anonymousDelegates:=Nothing, - anonymousDelegatesWithIndexedNames:=Nothing) - - ' Should contain all entries in previous generation. - Debug.Assert(PreviousGeneration.SynthesizedTypes.IsSubsetOf(result)) - - Return result - End Function - 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 @@ -246,10 +233,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return _changes.DefinitionMap.GetMethodBodyInstrumentations(method) End Function - Friend Overrides Function GetPreviousAnonymousTypes() As ImmutableArray(Of AnonymousTypeKey) - Return ImmutableArray.CreateRange(PreviousGeneration.SynthesizedTypes.AnonymousTypes.Keys) - End Function - Friend Overrides Function GetNextAnonymousTypeIndex(fromDelegates As Boolean) As Integer Return PreviousGeneration.GetNextAnonymousTypeIndex(fromDelegates) End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb index be6752e8a4e07..05c3544cf7f67 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb @@ -6,6 +6,7 @@ Imports System.Collections.Concurrent Imports System.Collections.Immutable Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis.CodeGen +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Symbols @@ -23,18 +24,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Public Sub New(sourceAssembly As SourceAssemblySymbol, otherAssembly As SourceAssemblySymbol, - synthesizedTypes As SynthesizedTypeMaps, - otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), - otherDeletedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))) - - _visitor = New Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembersOpt, otherDeletedMembersOpt, New DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object))) + otherSynthesizedTypes As SynthesizedTypeMaps, + otherSynthesizedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + otherDeletedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))) + + _visitor = New Visitor(sourceAssembly, + otherAssembly, + otherSynthesizedTypes, + otherSynthesizedMembers, + otherDeletedMembers, + New DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object))) End Sub - Public Sub New(synthesizedTypes As SynthesizedTypeMaps, - sourceAssembly As SourceAssemblySymbol, - otherAssembly As PEAssemblySymbol) - - _visitor = New Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembersOpt:=Nothing, otherDeletedMembers:=Nothing, deepTranslatorOpt:=Nothing) + Public Sub New(sourceAssembly As SourceAssemblySymbol, + otherAssembly As PEAssemblySymbol, + otherSynthesizedTypes As SynthesizedTypeMaps) + + _visitor = New Visitor(sourceAssembly, + otherAssembly, + otherSynthesizedTypes, + otherSynthesizedMembers:=SpecializedCollections.EmptyReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + otherDeletedMembers:=SpecializedCollections.EmptyReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + deepTranslatorOpt:=Nothing) End Sub Public Overrides Function MapDefinition(definition As Cci.IDefinition) As Cci.IDefinition @@ -70,6 +81,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return _visitor.TryGetAnonymousTypeName(template, name, index) End Function + Protected Overrides Function TryGetMatchingDelegateWithIndexedName(delegateTemplate As INamedTypeSymbolInternal, values As ImmutableArray(Of AnonymousTypeValue), ByRef match As AnonymousTypeValue) As Boolean + ' VB does not have delegates with indexed names + Throw ExceptionUtilities.Unreachable() + End Function + Private NotInheritable Class Visitor Inherits VisualBasicSymbolVisitor(Of Symbol) @@ -79,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Private ReadOnly _sourceAssembly As SourceAssemblySymbol Private ReadOnly _otherAssembly As AssemblySymbol - Private ReadOnly _otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)) + Private ReadOnly _otherSynthesizedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)) Private ReadOnly _otherDeletedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)) ' A cache of members per type, populated when the first member for a given @@ -91,14 +107,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Public Sub New(sourceAssembly As SourceAssemblySymbol, otherAssembly As AssemblySymbol, synthesizedTypes As SynthesizedTypeMaps, - otherSynthesizedMembersOpt As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + otherSynthesizedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), otherDeletedMembers As IReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), deepTranslatorOpt As DeepTranslator) _synthesizedTypes = synthesizedTypes _sourceAssembly = sourceAssembly _otherAssembly = otherAssembly - _otherSynthesizedMembersOpt = otherSynthesizedMembersOpt + _otherSynthesizedMembers = otherSynthesizedMembers _otherDeletedMembersOpt = otherDeletedMembers _comparer = New SymbolComparer(Me, deepTranslatorOpt) _matches = New ConcurrentDictionary(Of Symbol, Symbol)(ReferenceEqualityComparer.Instance) @@ -356,8 +372,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit End Function Friend Function TryFindAnonymousType(type As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, ByRef otherType As AnonymousTypeValue) As Boolean - Debug.Assert(type.ContainingSymbol Is _sourceAssembly.GlobalNamespace) - Return _synthesizedTypes.AnonymousTypes.TryGetValue(type.GetAnonymousTypeKey(), otherType) End Function @@ -519,12 +533,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit End If Dim synthesizedMembers As ImmutableArray(Of ISymbolInternal) = Nothing - If _otherSynthesizedMembersOpt IsNot Nothing AndAlso _otherSynthesizedMembersOpt.TryGetValue(symbol, synthesizedMembers) Then + If _otherSynthesizedMembers.TryGetValue(symbol, synthesizedMembers) Then members.AddRange(synthesizedMembers) End If Dim deletedMembers As ImmutableArray(Of ISymbolInternal) = Nothing - If _otherDeletedMembersOpt IsNot Nothing AndAlso _otherDeletedMembersOpt.TryGetValue(symbol, deletedMembers) Then + If _otherDeletedMembersOpt.TryGetValue(symbol, deletedMembers) Then members.AddRange(deletedMembers) End If diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index faefdac0799dc..d07e661d83163 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -325,10 +325,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Return New MethodInstrumentation() With {.Kinds = EmitOptions.InstrumentationKinds} End Function - Friend Overridable Function GetPreviousAnonymousTypes() As ImmutableArray(Of AnonymousTypeKey) - Return ImmutableArray(Of AnonymousTypeKey).Empty - End Function - Friend Overridable Function GetNextAnonymousTypeIndex(fromDelegates As Boolean) As Integer Return 0 End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeManager_Templates.vb b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeManager_Templates.vb index 801c74b7f5a48..615ced1480977 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeManager_Templates.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeManager_Templates.vb @@ -3,13 +3,12 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Concurrent -Imports System.Collections.Generic Imports System.Collections.Immutable -Imports System.Diagnostics Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Emit Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols @@ -162,28 +161,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End Sub - Private Shared Function CreatePlaceholderTypeDescriptor(key As Microsoft.CodeAnalysis.Emit.AnonymousTypeKey) As AnonymousTypeDescriptor - Dim names = key.Fields.SelectAsArray(Function(f) New AnonymousTypeField(f.Name, Location.None, f.IsKey)) - Return New AnonymousTypeDescriptor(names, Location.None, True) - End Function - ''' ''' Resets numbering in anonymous type names and compiles the ''' anonymous type methods. Also seals the collection of templates. ''' Public Sub AssignTemplatesNamesAndCompile(compiler As MethodCompiler, moduleBeingBuilt As Emit.PEModuleBuilder, diagnostics As BindingDiagnosticBag) - - ' Ensure all previous anonymous type templates are included so the - ' types are available for subsequent edit and continue generations. - For Each key In moduleBeingBuilt.GetPreviousAnonymousTypes() - Dim templateKey = AnonymousTypeDescriptor.ComputeKey(key.Fields, Function(f) f.Name, Function(f) f.IsKey) - If key.IsDelegate Then - AnonymousDelegateTemplates.GetOrAdd(templateKey, Function(k) AnonymousDelegateTemplateSymbol.Create(Me, CreatePlaceholderTypeDescriptor(key))) - Else - AnonymousTypeTemplates.GetOrAdd(templateKey, Function(k) New AnonymousTypeTemplateSymbol(Me, CreatePlaceholderTypeDescriptor(key))) - End If - Next - ' Get all anonymous types owned by this manager Dim builder = ArrayBuilder(Of AnonymousTypeOrDelegateTemplateSymbol).GetInstance() GetAllCreatedTemplates(builder) @@ -192,23 +174,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' to the created anonymous type and delegate templates If Not Me.AreTemplatesSealed Then - ' If we are emitting .NET module, include module's name into type's name to ensure - ' uniqueness across added modules. - Dim moduleId As String - - If moduleBeingBuilt.OutputKind = OutputKind.NetModule Then - moduleId = moduleBeingBuilt.Name - Dim extension As String = OutputKind.NetModule.GetDefaultExtension() - - If moduleId.EndsWith(extension, StringComparison.OrdinalIgnoreCase) Then - moduleId = moduleId.Substring(0, moduleId.Length - extension.Length) - End If - - moduleId = "<" & MetadataHelpers.MangleForTypeNameIfNeeded(moduleId) & ">" - Else - moduleId = String.Empty - End If - + Dim moduleId = GetModuleId(moduleBeingBuilt) Dim typeIndex = moduleBeingBuilt.GetNextAnonymousTypeIndex(fromDelegates:=False) Dim delegateIndex = moduleBeingBuilt.GetNextAnonymousTypeIndex(fromDelegates:=True) For Each template In builder @@ -248,6 +214,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols builder.Free() End Sub + Function GetModuleId(moduleBeingBuilt As Emit.PEModuleBuilder) As String + ' If we are emitting .NET module, include module's name into type's name to ensure + ' uniqueness across added modules. + + If moduleBeingBuilt.OutputKind = OutputKind.NetModule Then + Dim moduleId = moduleBeingBuilt.Name + Dim extension As String = OutputKind.NetModule.GetDefaultExtension() + + If moduleId.EndsWith(extension, StringComparison.OrdinalIgnoreCase) Then + moduleId = moduleId.Substring(0, moduleId.Length - extension.Length) + End If + + Return "<" & MetadataHelpers.MangleForTypeNameIfNeeded(moduleId) & ">" + Else + Return String.Empty + End If + End Function + Friend Function GetAnonymousTypeMap() As ImmutableSegmentedDictionary(Of Microsoft.CodeAnalysis.Emit.AnonymousTypeKey, Microsoft.CodeAnalysis.Emit.AnonymousTypeValue) Dim templates = ArrayBuilder(Of AnonymousTypeOrDelegateTemplateSymbol).GetInstance() GetAllCreatedTemplates(templates) @@ -304,6 +288,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End If End Sub + Friend Overrides Function GetSynthesizedTypeMaps() As SynthesizedTypeMaps + ' VB anonymous delegates are handled as anonymous types + Return New SynthesizedTypeMaps( + GetAnonymousTypeMap(), + anonymousDelegates:=Nothing, + anonymousDelegatesWithIndexedNames:=Nothing) + End Function + Private NotInheritable Class AnonymousTypeComparer Implements IComparer(Of AnonymousTypeOrDelegateTemplateSymbol) diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.vb index 21060764f7d1b..19290e235fcb6 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.vb @@ -8,9 +8,11 @@ Imports System.Reflection.Metadata Imports System.Reflection.Metadata.Ecma335 Imports System.Runtime.CompilerServices Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Emit Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -307,11 +309,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Friend Shared Function CreateMatcher(fromCompilation As VisualBasicCompilation, toCompilation As VisualBasicCompilation) As VisualBasicSymbolMatcher Return New VisualBasicSymbolMatcher( - fromCompilation.SourceAssembly, - toCompilation.SourceAssembly, - synthesizedTypes:=SynthesizedTypeMaps.Empty, - otherSynthesizedMembersOpt:=Nothing, - otherDeletedMembersOpt:=Nothing) + sourceAssembly:=fromCompilation.SourceAssembly, + otherAssembly:=toCompilation.SourceAssembly, + otherSynthesizedTypes:=SynthesizedTypeMaps.Empty, + otherSynthesizedMembers:=SpecializedCollections.EmptyReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal)), + otherDeletedMembers:=SpecializedCollections.EmptyReadOnlyDictionary(Of ISymbolInternal, ImmutableArray(Of ISymbolInternal))) End Function End Class diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb index 8193d204d35d7..04d8dbd00cd25 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb @@ -4943,91 +4943,121 @@ End Class Public Sub AnonymousTypes() - Dim sources0 = - - - Dim sources1 = - - - Dim compilation0 = CreateCompilationWithMscorlib40(sources0, options:=TestOptions.DebugDll) - Dim compilation1 = compilation0.WithSource(sources1) + Using test = New EditAndContinueTest() + test.AddBaseline( + source:="Module C + Sub F() + Dim f = New With { .a = 1, .b = 2 } + End Sub +End Module", + validator:= + Sub(g) + g.VerifySynthesizedMembers({}) - Dim testData0 = New CompilationTestData() - Dim bytes0 = compilation0.EmitToArray(testData:=testData0) - Using md0 = ModuleMetadata.CreateFromImage(bytes0) - Dim generation0 = CreateInitialBaseline(compilation0, ModuleMetadata.CreateFromImage(bytes0), - testData0.GetMethodData("M.B.M").EncDebugInfoProvider) - Dim method0 = compilation0.GetMember(Of MethodSymbol)("M.B.M") - Dim reader0 = md0.MetadataReader - CheckNames(reader0, reader0.GetTypeDefNames(), - "", - "VB$AnonymousType_0`2", - "VB$AnonymousType_1`2", - "VB$AnonymousType_2`1", - "A", - "B") - Dim method1 = compilation1.GetMember(Of MethodSymbol)("M.B.M") - Dim diff1 = compilation1.EmitDifference( - generation0, - ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, method0, method1, GetEquivalentNodesMap(method1, method0)))) + g.VerifySynthesizedTypes( + "VB$AnonymousType_0(Of T0, T1)") - Using md1 = diff1.GetMetadata() - Dim reader1 = md1.Reader - CheckNames({reader0, reader1}, reader1.GetTypeDefNames(), "VB$AnonymousType_3`1") - diff1.VerifyIL("M.B.M", " + g.VerifyIL("C.F", " { - // Code size 29 (0x1d) + // Code size 10 (0xa) .maxstack 2 - .locals init (VB$AnonymousType_1(Of Integer, Integer) V_0, //x - [int] V_1, - VB$AnonymousType_2(Of Integer) V_2, //z - VB$AnonymousType_3(Of Integer) V_3) //y + .locals init (VB$AnonymousType_0(Of Integer, Integer) V_0) //f IL_0000: nop - IL_0001: ldc.i4.3 - IL_0002: ldc.i4.4 - IL_0003: newobj ""Sub VB$AnonymousType_1(Of Integer, Integer)..ctor(Integer, Integer)"" + IL_0001: ldc.i4.1 + IL_0002: ldc.i4.2 + IL_0003: newobj ""Sub VB$AnonymousType_0(Of Integer, Integer)..ctor(Integer, Integer)"" IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: callvirt ""Function VB$AnonymousType_1(Of Integer, Integer).get_A() As Integer"" - IL_000f: newobj ""Sub VB$AnonymousType_3(Of Integer)..ctor(Integer)"" - IL_0014: stloc.3 - IL_0015: ldc.i4.5 - IL_0016: newobj ""Sub VB$AnonymousType_2(Of Integer)..ctor(Integer)"" - IL_001b: stloc.2 - IL_001c: ret -} -") - End Using + IL_0009: ret +}") + End Sub). + AddGeneration(' 1 + source:="Module C + Sub F() + Dim g = New With { .x = 1 } + Dim f = New With { .a = 1, .b = 2 } + End Sub +End Module", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers({}) + + g.VerifySynthesizedTypes( + "VB$AnonymousType_0(Of T0, T1)", + "VB$AnonymousType_1(Of T0)") + + g.VerifyTypeDefNames("VB$AnonymousType_1`1") + g.VerifyMethodDefNames("F", "get_x", "set_x", ".ctor", "ToString") + + g.VerifyIL("C.F", " +{ + // Code size 17 (0x11) + .maxstack 2 + .locals init (VB$AnonymousType_0(Of Integer, Integer) V_0, //f + VB$AnonymousType_1(Of Integer) V_1) //g + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: newobj ""Sub VB$AnonymousType_1(Of Integer)..ctor(Integer)"" + IL_0007: stloc.1 + IL_0008: ldc.i4.1 + IL_0009: ldc.i4.2 + IL_000a: newobj ""Sub VB$AnonymousType_0(Of Integer, Integer)..ctor(Integer, Integer)"" + IL_000f: stloc.0 + IL_0010: ret +}") + End Sub). + AddGeneration(' 2 + source:=" +Module C + Sub F() + Dim f = New With { .a = 1, .b = 2, .c = 3 } + End Sub +End Module", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers({}) + + g.VerifySynthesizedTypes( + "VB$AnonymousType_0(Of T0, T1)", + "VB$AnonymousType_1(Of T0)", + "VB$AnonymousType_2(Of T0, T1, T2)") + + g.VerifyTypeDefNames("VB$AnonymousType_2`3") + g.VerifyMethodDefNames("F", "get_a", "set_a", "get_b", "set_b", "get_c", "set_c", ".ctor", "ToString") + End Sub). + AddGeneration(' 3 + source:=" + Module C + Sub F() + Dim f = New With { .a = 1, .b = 2, .c = 3 } + Dim g = New With { .x = 1, .y = 2 } + End Sub + End Module", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers({}) + + g.VerifySynthesizedTypes( + "VB$AnonymousType_0(Of T0, T1)", + "VB$AnonymousType_1(Of T0)", + "VB$AnonymousType_2(Of T0, T1, T2)", + "VB$AnonymousType_3(Of T0, T1)") + + g.VerifyTypeDefNames("VB$AnonymousType_3`2") + g.VerifyMethodDefNames("F", "get_x", "set_x", "get_y", "set_y", ".ctor", "ToString") + End Sub). + Verify() End Using End Sub @@ -5738,6 +5768,115 @@ End Class ") End Sub + + Public Sub Lambda_SynthesizedDelegate() + Using test = New EditAndContinueTest() + test.AddBaseline( + source:=" +Class C + Sub F() + Dim f = Function(ByRef a As Integer) a + End Sub +End Class", + validator:= + Sub(g) + g.VerifySynthesizedMembers( + { + "C: {_Closure$__}", + "C._Closure$__: {$I1-0, _Lambda$__1-0}" + }) + + g.VerifySynthesizedTypes( + "VB$AnonymousDelegate_0(Of TArg0, TResult)") + End Sub). + AddGeneration(' 1 + source:=" +Class C + Sub F() + Dim g = Function(ByRef a As Byte, b As Integer) a + Dim f = Function(ByRef a As Integer) a + End Sub +End Class", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers( + { + "C: {_Closure$__}", + "C._Closure$__: {$I1-0#1, $I1-0, _Lambda$__1-0#1, _Lambda$__1-0}" + }) + + g.VerifySynthesizedTypes( + "VB$AnonymousDelegate_0(Of TArg0, TResult)", + "VB$AnonymousDelegate_1(Of TArg0, TArg1, TResult)") + + g.VerifyMethodDefNames("F", "_Lambda$__1-0", ".ctor", "BeginInvoke", "EndInvoke", "Invoke", "_Lambda$__1-0#1") + End Sub). + AddGeneration(' 2 + source:=" +Class C + Sub F() + Dim f = Function(ByRef a As Boolean, ByRef b As Boolean) a + End Sub +End Class", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True, rudeEdits:=Function(node) New RuntimeRudeEdit("Parameter changed", &H123)) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers( + { + "System.Runtime.CompilerServices.HotReloadException", + "C: {_Closure$__}", + "C._Closure$__: {$I1-0#2, _Lambda$__1-0#2, $I1-0#1, $I1-0, _Lambda$__1-0#1, _Lambda$__1-0}" + }) + + g.VerifySynthesizedTypes( + "VB$AnonymousDelegate_0(Of TArg0, TResult)", + "VB$AnonymousDelegate_1(Of TArg0, TArg1, TResult)", + "VB$AnonymousDelegate_2(Of TArg0, TArg1, TResult)") + + g.VerifyTypeDefNames("VB$AnonymousDelegate_2`3", "HotReloadException") + g.VerifyMethodDefNames("F", "_Lambda$__1-0", "_Lambda$__1-0#1", ".ctor", "BeginInvoke", "EndInvoke", "Invoke", ".ctor", "_Lambda$__1-0#2") + End Sub). + AddGeneration(' 3 + source:=" +Class C + Sub F() + Dim f = Function(ByRef a As Boolean, ByRef b As Boolean) a + Dim g = Function(ByRef a As Boolean, ByRef b As Boolean, c As Boolean) a + End Sub +End Class", + edits:= + { + Edit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True) + }, + validator:= + Sub(g) + g.VerifySynthesizedMembers( + { + "System.Runtime.CompilerServices.HotReloadException", + "C._Closure$__: {$I1-0#3, $I1-1#3, _Lambda$__1-0#3, _Lambda$__1-1#3, $I1-0#2, _Lambda$__1-0#2, $I1-0#1, $I1-0, _Lambda$__1-0#1, _Lambda$__1-0}", + "C: {_Closure$__}" + }) + + g.VerifySynthesizedTypes( + "VB$AnonymousDelegate_0(Of TArg0, TResult)", + "VB$AnonymousDelegate_1(Of TArg0, TArg1, TResult)", + "VB$AnonymousDelegate_2(Of TArg0, TArg1, TResult)", + "VB$AnonymousDelegate_3(Of TArg0, TArg1, TArg2, TResult)") + + g.VerifyTypeDefNames("VB$AnonymousDelegate_3`4") + g.VerifyMethodDefNames("F", "_Lambda$__1-0#2", ".ctor", "BeginInvoke", "EndInvoke", "Invoke", "_Lambda$__1-0#3", "_Lambda$__1-1#3") + End Sub). + Verify() + End Using + End Sub + ''' ''' Should not re-use locals with custom modifiers. ''' diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb index f6720503c9293..5926fdfb012f3 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/SymbolMatcherTests.vb @@ -355,9 +355,9 @@ End Class Assert.Equal("$VB$Local_x2", x2.Name) Dim matcher = New VisualBasicSymbolMatcher( - synthesizedTypes0, compilation1.SourceAssembly, - peAssemblySymbol0) + peAssemblySymbol0, + synthesizedTypes0) Dim mappedX1 = DirectCast(matcher.MapDefinition(x1), Cci.IFieldDefinition) Dim mappedX2 = DirectCast(matcher.MapDefinition(x2), Cci.IFieldDefinition) @@ -426,9 +426,9 @@ End Class Assert.Equal("$VB$Local_x2", x2.Name) Dim matcher = New VisualBasicSymbolMatcher( - synthesizedTypes0, compilation1.SourceAssembly, - peAssemblySymbol0) + peAssemblySymbol0, + synthesizedTypes0) Dim mappedX1 = DirectCast(matcher.MapDefinition(x1), Cci.IFieldDefinition) Dim mappedX2 = DirectCast(matcher.MapDefinition(x2), Cci.IFieldDefinition) @@ -503,9 +503,9 @@ End Class Assert.Equal("$VB$Local_x2", x2.Name) Dim matcher = New VisualBasicSymbolMatcher( - synthesizedTypes0, compilation1.SourceAssembly, - peAssemblySymbol0) + peAssemblySymbol0, + synthesizedTypes0) Dim mappedX1 = DirectCast(matcher.MapDefinition(x1), Cci.IFieldDefinition) Dim mappedX2 = DirectCast(matcher.MapDefinition(x2), Cci.IFieldDefinition) diff --git a/src/Features/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/Features/CSharpTest/EditAndContinue/StatementEditingTests.cs index a3e54b065954d..22913136cdf35 100644 --- a/src/Features/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -3755,10 +3755,82 @@ void F() } [Fact] - public void Lambdas_Update_Signature_ParameterRefness1() + [WorkItem("https://github.com/dotnet/roslyn/issues/79783")] + public void Lambdas_Update_Signature_ParameterDefaultValue() { var src1 = """ + using System; + + class C + { + void F() + { + var f = (int a = 1) => 1; + } + } + """; + var src2 = """ + using System; + + class C + { + void F() + { + var f = (int a = 2) => 1; + } + } + """; + + var edits = GetTopEdits(src1, src2); + var syntaxMap = edits.GetSyntaxMap(); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), syntaxMap, rudeEdits: + [ + RuntimeRudeEdit(0, RudeEditKind.ChangingLambdaParameters, syntaxMap.NodePosition(0), arguments: [GetResource("lambda")]) + ])); + } + [Fact] + public void Lambdas_Update_Signature_ParamsArray() + { + var src1 = """ + using System; + + class C + { + void F() + { + var f = (int[] a) => 1; + } + } + """; + var src2 = """ + using System; + + class C + { + void F() + { + var f = (params int[] a) => 1; + } + } + """; + + var edits = GetTopEdits(src1, src2); + var syntaxMap = edits.GetSyntaxMap(); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), syntaxMap, rudeEdits: + [ + RuntimeRudeEdit(0, RudeEditKind.ChangingLambdaParameters, syntaxMap.NodePosition(0), arguments: [GetResource("lambda")]) + ])); + } + + [Fact] + public void Lambdas_Update_Signature_ParameterRefness1() + { + var src1 = """ using System; delegate int D1(ref int a); @@ -3774,10 +3846,8 @@ void F() G1((ref int a) => 1); } } - """; var src2 = """ - using System; delegate int D1(ref int a); @@ -3793,8 +3863,8 @@ void F() G2((int a) => 2); } } - """; + var edits = GetTopEdits(src1, src2); var syntaxMap = edits.GetSyntaxMap(); diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index e96503cc24680..e03279eaf9c63 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2544,6 +2544,12 @@ protected static bool SymbolsEquivalent(ISymbol oldSymbol, ISymbol newSymbol) protected static bool ParameterTypesEquivalent(ImmutableArray oldParameters, ImmutableArray newParameters, bool exact) => oldParameters.SequenceEqual(newParameters, exact, ParameterTypesEquivalent); + protected static bool ParameterDefaultValuesEquivalent(ImmutableArray oldParameters, ImmutableArray newParameters) + => oldParameters.SequenceEqual(newParameters, ParameterDefaultValuesEquivalent); + + protected static bool LambdaParametersEquivalent(ImmutableArray oldParameters, ImmutableArray newParameters) + => oldParameters.SequenceEqual(newParameters, LambdaParameterEquivalent); + protected static bool CustomModifiersEquivalent(CustomModifier oldModifier, CustomModifier newModifier, bool exact) => oldModifier.IsOptional == newModifier.IsOptional && TypesEquivalent(oldModifier.Modifier, newModifier.Modifier, exact); @@ -2582,6 +2588,20 @@ protected static bool TypesEquivalent(ImmutableArray oldTypes, ImmutableAr protected static bool ParameterTypesEquivalent(IParameterSymbol oldParameter, IParameterSymbol newParameter, bool exact) => (exact ? s_exactSymbolEqualityComparer : s_runtimeSymbolEqualityComparer).ParameterEquivalenceComparer.Equals(oldParameter, newParameter); + protected static bool ParameterDefaultValuesEquivalent(IParameterSymbol oldParameter, IParameterSymbol newParameter) + => oldParameter.HasExplicitDefaultValue == newParameter.HasExplicitDefaultValue && + (!oldParameter.HasExplicitDefaultValue || Equals(oldParameter.ExplicitDefaultValue, newParameter.ExplicitDefaultValue)); + + /// + /// Lambda parameters are equivallent if the type of the lambda as emitted to IL doesn't change. + /// Tuple element names, dynamic, etc. do not affect lambda natural type. + /// Default values and "params" do. + /// + protected static bool LambdaParameterEquivalent(IParameterSymbol oldParameter, IParameterSymbol newParameter) + => ParameterTypesEquivalent(oldParameter, newParameter, exact: false) && + ParameterDefaultValuesEquivalent(oldParameter, newParameter) && + oldParameter.IsParams == newParameter.IsParams; + protected static bool TypeParameterConstraintsEquivalent(ITypeParameterSymbol oldParameter, ITypeParameterSymbol newParameter, bool exact) => TypesEquivalent(oldParameter.ConstraintTypes, newParameter.ConstraintTypes, exact) && oldParameter.HasReferenceTypeConstraint == newParameter.HasReferenceTypeConstraint && @@ -4490,8 +4510,7 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( hasGeneratedAttributeChange = true; } - if (oldParameter.HasExplicitDefaultValue != newParameter.HasExplicitDefaultValue || - oldParameter.HasExplicitDefaultValue && !Equals(oldParameter.ExplicitDefaultValue, newParameter.ExplicitDefaultValue)) + if (!ParameterDefaultValuesEquivalent(oldParameter, newParameter)) { rudeEdit = RudeEditKind.InitializerUpdate; } @@ -6572,8 +6591,12 @@ private void ReportLambdaSignatureRudeEdits( var newLambdaSymbol = (IMethodSymbol)diagnosticContext.RequiredNewSymbol; // signature validation: - if (!ParameterTypesEquivalent(oldLambdaSymbol.Parameters, newLambdaSymbol.Parameters, exact: false)) + if (!LambdaParametersEquivalent(oldLambdaSymbol.Parameters, newLambdaSymbol.Parameters)) { + // If a delegate type for the lambda is synthesized (anonymous) changing default parameter value changes the synthesized delegate type. + // If the delegate type is not synthesized the default value is ignored and warning is reported by the compiler. + // Technically, the runtime rude edit does not need to be reported in the latter case but we report it anyway for simplicity. + runtimeRudeEditsBuilder[newLambda] = diagnosticContext.CreateRudeEdit(RudeEditKind.ChangingLambdaParameters, cancellationToken); return; } @@ -6585,7 +6608,7 @@ private void ReportLambdaSignatureRudeEdits( } if (!TypeParametersEquivalent(oldLambdaSymbol.TypeParameters, newLambdaSymbol.TypeParameters, exact: false) || - !oldLambdaSymbol.TypeParameters.SequenceEqual(newLambdaSymbol.TypeParameters, static (p, q) => p.Name == q.Name)) + !oldLambdaSymbol.TypeParameters.SequenceEqual(newLambdaSymbol.TypeParameters, static (p, q) => p.Name == q.Name)) { runtimeRudeEditsBuilder[newLambda] = diagnosticContext.CreateRudeEdit(RudeEditKind.ChangingTypeParameters, cancellationToken); return; diff --git a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs index 63be4ad35bae0..f365d76c3b28a 100644 --- a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs +++ b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs @@ -119,9 +119,19 @@ internal void VerifyCustomAttributes(IEnumerable? expected = }); private IReadOnlyDictionary> GetSynthesizedMembers() - => (generationInfo.CompilationVerifier != null) - ? generationInfo.CompilationVerifier.TestData.Module!.GetAllSynthesizedMembers() - : generationInfo.Baseline.SynthesizedMembers; + => generationInfo.CompilationVerifier?.TestData.Module!.GetAllSynthesizedMembers() ?? generationInfo.Baseline.SynthesizedMembers; + + public ImmutableArray GetSynthesizedTypes() + { + var map = generationInfo.CompilationVerifier?.TestData.Module!.GetAllSynthesizedTypes() ?? generationInfo.Baseline.SynthesizedTypes; + + return + [ + .. map.AnonymousTypes.Values.Select(t => t.Type.GetInternalSymbol()!), + .. map.AnonymousDelegates.Values.Select(t => t.Delegate.GetInternalSymbol()!), + .. map.AnonymousDelegatesWithIndexedNames.Values.SelectMany(t => t.Select(d => d.Type.GetInternalSymbol()!)) + ]; + } public void VerifySynthesizedMembers(params string[] expected) => VerifySynthesizedMembers(displayTypeKind: false, expected); @@ -129,6 +139,9 @@ public void VerifySynthesizedMembers(params string[] expected) public void VerifySynthesizedMembers(bool displayTypeKind, params string[] expected) => Verify(() => CompilationDifference.VerifySynthesizedMembers(GetSynthesizedMembers(), displayTypeKind, expected)); + public void VerifySynthesizedTypes(params string[] expected) + => Verify(() => CompilationDifference.VerifySynthesizedSymbols(GetSynthesizedTypes(), expected)); + public void VerifySynthesizedFields(string typeName, params string[] expectedSynthesizedTypesAndMemberCounts) => Verify(() => {