Skip to content

Commit

Permalink
EnC - Tests for lambda improvements (#55224)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidwengier authored Aug 30, 2021
1 parent 8de6661 commit 3fdd28b
Show file tree
Hide file tree
Showing 21 changed files with 677 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ internal sealed class CSharpSymbolMatcher : SymbolMatcher

public CSharpSymbolMatcher(
IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> anonymousTypeMap,
IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> synthesizedDelegates,
SourceAssemblySymbol sourceAssembly,
EmitContext sourceContext,
SourceAssemblySymbol otherAssembly,
EmitContext otherContext,
ImmutableDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> otherSynthesizedMembersOpt)
{
_defs = new MatchDefsToSource(sourceContext, otherContext);
_symbols = new MatchSymbols(anonymousTypeMap, sourceAssembly, otherAssembly, otherSynthesizedMembersOpt, new DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object)));
_symbols = new MatchSymbols(anonymousTypeMap, synthesizedDelegates, sourceAssembly, otherAssembly, otherSynthesizedMembersOpt, new DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object)));
}

public CSharpSymbolMatcher(
IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> anonymousTypeMap,
IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> synthesizedDelegates,
SourceAssemblySymbol sourceAssembly,
EmitContext sourceContext,
PEAssemblySymbol otherAssembly)
Expand All @@ -46,6 +48,7 @@ public CSharpSymbolMatcher(

_symbols = new MatchSymbols(
anonymousTypeMap,
synthesizedDelegates,
sourceAssembly,
otherAssembly,
otherSynthesizedMembers: null,
Expand Down Expand Up @@ -273,6 +276,7 @@ public MatchDefsToSource(
private sealed class MatchSymbols : CSharpSymbolVisitor<Symbol?>
{
private readonly IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> _anonymousTypeMap;
private readonly IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> _synthesizedDelegates;
private readonly SourceAssemblySymbol _sourceAssembly;

// metadata or source assembly:
Expand All @@ -297,12 +301,14 @@ private sealed class MatchSymbols : CSharpSymbolVisitor<Symbol?>

public MatchSymbols(
IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> anonymousTypeMap,
IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> synthesizedDelegates,
SourceAssemblySymbol sourceAssembly,
AssemblySymbol otherAssembly,
ImmutableDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? otherSynthesizedMembers,
DeepTranslator? deepTranslator)
{
_anonymousTypeMap = anonymousTypeMap;
_synthesizedDelegates = synthesizedDelegates;
_sourceAssembly = sourceAssembly;
_otherAssembly = otherAssembly;
_otherSynthesizedMembers = otherSynthesizedMembers;
Expand Down Expand Up @@ -528,6 +534,12 @@ public override Symbol VisitDynamicType(DynamicTypeSymbol symbol)
TryFindAnonymousType(template, out var value);
return (NamedTypeSymbol?)value.Type?.GetInternalSymbol();
}
else if (sourceType is SynthesizedDelegateSymbol delegateSymbol)
{
Debug.Assert((object)otherContainer == (object)_otherAssembly.GlobalNamespace);
TryFindSynthesizedDelegate(delegateSymbol, out var value);
return (NamedTypeSymbol?)value.Delegate?.GetInternalSymbol();
}

if (sourceType.IsAnonymousType)
{
Expand Down Expand Up @@ -650,6 +662,14 @@ internal bool TryFindAnonymousType(AnonymousTypeManager.AnonymousTypeTemplateSym
return _anonymousTypeMap.TryGetValue(type.GetAnonymousTypeKey(), out otherType);
}

internal bool TryFindSynthesizedDelegate(SynthesizedDelegateSymbol delegateSymbol, out SynthesizedDelegateValue otherDelegateSymbol)
{
Debug.Assert((object)delegateSymbol.ContainingSymbol == (object)_sourceAssembly.GlobalNamespace);

var key = new SynthesizedDelegateKey(delegateSymbol.MetadataName);
return _synthesizedDelegates.TryGetValue(key, out otherDelegateSymbol);
}

private Symbol? VisitNamedTypeMember<T>(T member, Func<T, T, bool> predicate)
where T : Symbol
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,14 @@ private static EmitBaseline MapToCompilation(

// Mapping from previous compilation to the current.
var anonymousTypeMap = moduleBeingBuilt.GetAnonymousTypeMap();
var synthesizedDelegates = moduleBeingBuilt.GetSynthesizedDelegates();
var sourceAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly;
var sourceContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);
var otherContext = new EmitContext(moduleBeingBuilt, null, new DiagnosticBag(), metadataOnly: false, includePrivateMembers: true);

var matcher = new CSharpSymbolMatcher(
anonymousTypeMap,
synthesizedDelegates,
sourceAssembly,
sourceContext,
compilation.SourceAssembly,
Expand All @@ -157,6 +159,7 @@ private static EmitBaseline MapToCompilation(
// TODO: can we reuse some data from the previous matcher?
var matcherWithAllSynthesizedMembers = new CSharpSymbolMatcher(
anonymousTypeMap,
synthesizedDelegates,
sourceAssembly,
sourceContext,
compilation.SourceAssembly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public PEDeltaAssemblyBuilder(
var metadataDecoder = (MetadataDecoder)metadataSymbols.MetadataDecoder;
var metadataAssembly = (PEAssemblySymbol)metadataDecoder.ModuleSymbol.ContainingAssembly;

var matchToMetadata = new CSharpSymbolMatcher(metadataSymbols.AnonymousTypes, sourceAssembly, context, metadataAssembly);
var matchToMetadata = new CSharpSymbolMatcher(metadataSymbols.AnonymousTypes, metadataSymbols.SynthesizedDelegates, sourceAssembly, context, metadataAssembly);

CSharpSymbolMatcher? matchToPrevious = null;
if (previousGeneration.Ordinal > 0)
Expand All @@ -60,6 +60,7 @@ public PEDeltaAssemblyBuilder(

matchToPrevious = new CSharpSymbolMatcher(
previousGeneration.AnonymousTypeMap,
previousGeneration.SynthesizedDelegates,
sourceAssembly: sourceAssembly,
sourceContext: context,
otherAssembly: previousAssembly,
Expand Down Expand Up @@ -116,7 +117,8 @@ private static EmitBaseline.MetadataSymbols GetOrCreateMetadataSymbols(EmitBasel
var metadataAssembly = metadataCompilation.GetBoundReferenceManager().CreatePEAssemblyForAssemblyMetadata(AssemblyMetadata.Create(originalMetadata), MetadataImportOptions.All, out assemblyReferenceIdentityMap);
var metadataDecoder = new MetadataDecoder(metadataAssembly.PrimaryModule);
var metadataAnonymousTypes = GetAnonymousTypeMapFromMetadata(originalMetadata.MetadataReader, metadataDecoder);
var metadataSymbols = new EmitBaseline.MetadataSymbols(metadataAnonymousTypes, metadataDecoder, assemblyReferenceIdentityMap);
var metadataSynthesizedDelegates = GetSynthesizedDelegateMapFromMetadata(originalMetadata.MetadataReader, metadataDecoder);
var metadataSymbols = new EmitBaseline.MetadataSymbols(metadataAnonymousTypes, metadataSynthesizedDelegates, metadataDecoder, assemblyReferenceIdentityMap);

return InterlockedOperations.Initialize(ref initialBaseline.LazyMetadataSymbols, metadataSymbols);
}
Expand Down Expand Up @@ -164,6 +166,37 @@ internal static IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> GetAno
return result;
}

// internal for testing
internal static IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> GetSynthesizedDelegateMapFromMetadata(MetadataReader reader, MetadataDecoder metadataDecoder)
{
var result = new Dictionary<SynthesizedDelegateKey, SynthesizedDelegateValue>();
foreach (var handle in reader.TypeDefinitions)
{
var def = reader.GetTypeDefinition(handle);
if (!def.Namespace.IsNil)
{
continue;
}

if (!reader.StringComparer.StartsWith(def.Name, GeneratedNames.ActionDelegateNamePrefix) &&
!reader.StringComparer.StartsWith(def.Name, GeneratedNames.FuncDelegateNamePrefix))
{
continue;
}

// The name of a synthesized delegate neatly encodes everything we need to identify it, either
// in the prefix (return void or not) or the name (ref kinds and arity) so we don't need anything
// fancy for a key.
var metadataName = reader.GetString(def.Name);
var key = new SynthesizedDelegateKey(metadataName);

var type = (NamedTypeSymbol)metadataDecoder.GetTypeOfToken(handle);
var value = new SynthesizedDelegateValue(type.GetCciAdapter());
result.Add(key, value);
}
return result;
}

private static bool TryGetAnonymousTypeKey(
MetadataReader reader,
TypeDefinition def,
Expand Down Expand Up @@ -205,6 +238,14 @@ public IReadOnlyDictionary<AnonymousTypeKey, AnonymousTypeValue> GetAnonymousTyp
return anonymousTypes;
}

public IReadOnlyDictionary<SynthesizedDelegateKey, SynthesizedDelegateValue> GetSynthesizedDelegates()
{
var synthesizedDelegates = this.Compilation.AnonymousTypeManager.GetSynthesizedDelegates();
// Should contain all entries in previous generation.
Debug.Assert(_previousGeneration.SynthesizedDelegates.All(p => synthesizedDelegates.ContainsKey(p.Key)));
return synthesizedDelegates;
}

public override IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelTypeDefinitions(EmitContext context)
{
foreach (var typeDef in GetAnonymousTypeDefinitions(context))
Expand Down Expand Up @@ -233,6 +274,11 @@ internal override ImmutableArray<AnonymousTypeKey> GetPreviousAnonymousTypes()
return ImmutableArray.CreateRange(_previousGeneration.AnonymousTypeMap.Keys);
}

internal override ImmutableArray<SynthesizedDelegateKey> GetPreviousSynthesizedDelegates()
{
return ImmutableArray.CreateRange(_previousGeneration.SynthesizedDelegates.Keys);
}

internal override int GetNextAnonymousTypeIndex()
{
return _previousGeneration.GetNextAnonymousTypeIndex();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,11 @@ internal virtual ImmutableArray<AnonymousTypeKey> GetPreviousAnonymousTypes()
return ImmutableArray<AnonymousTypeKey>.Empty;
}

internal virtual ImmutableArray<SynthesizedDelegateKey> GetPreviousSynthesizedDelegates()
{
return ImmutableArray<SynthesizedDelegateKey>.Empty;
}

internal virtual int GetNextAnonymousTypeIndex()
{
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public SynthesizedDelegateKey(int parameterCount, RefKindVector byRefs, bool ret

public string MakeTypeName()
{
return GeneratedNames.MakeDynamicCallSiteDelegateName(_byRefs, _returnsVoid, _generation);
return GeneratedNames.MakeSynthesizedDelegateName(_byRefs, _returnsVoid, _generation);
}

public override bool Equals(object obj)
Expand Down Expand Up @@ -235,6 +235,19 @@ private AnonymousTypeTemplateSymbol CreatePlaceholderTemplate(Microsoft.CodeAnal
return new AnonymousTypeTemplateSymbol(this, typeDescr);
}

private SynthesizedDelegateValue CreatePlaceholderSynthesizedDelegateValue(string name, RefKindVector refKinds, bool returnsVoid, int parameterCount)
{
var symbol = new SynthesizedDelegateSymbol(
this.Compilation.Assembly.GlobalNamespace,
MetadataHelpers.InferTypeArityAndUnmangleMetadataName(name, out _),
this.System_Object,
Compilation.GetSpecialType(SpecialType.System_IntPtr),
returnsVoid ? Compilation.GetSpecialType(SpecialType.System_Void) : null,
parameterCount,
refKinds);
return new SynthesizedDelegateValue(this, symbol);
}

/// <summary>
/// Resets numbering in anonymous type names and compiles the
/// anonymous type methods. Also seals the collection of templates.
Expand Down Expand Up @@ -314,6 +327,17 @@ public void AssignTemplatesNamesAndCompile(MethodCompiler compiler, PEModuleBuil

builder.Free();

// Ensure all previous synthesized delegates are included so the
// types are available for subsequent edit and continue generations.
foreach (var key in moduleBeingBuilt.GetPreviousSynthesizedDelegates())
{
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.SynthesizedDelegates.GetOrAdd(delegateKey, (k, args) => CreatePlaceholderSynthesizedDelegateValue(key.Name, args.refKinds, args.returnsVoid, args.parameterCount), (refKinds, returnsVoid, parameterCount));
}
}

var synthesizedDelegates = ArrayBuilder<SynthesizedDelegateSymbol>.GetInstance();
GetCreatedSynthesizedDelegates(synthesizedDelegates);
foreach (var synthesizedDelegate in synthesizedDelegates)
Expand Down Expand Up @@ -376,6 +400,21 @@ public int Compare(SynthesizedDelegateSymbol x, SynthesizedDelegateSymbol y)
}
}

internal IReadOnlyDictionary<CodeAnalysis.Emit.SynthesizedDelegateKey, CodeAnalysis.Emit.SynthesizedDelegateValue> GetSynthesizedDelegates()
{
var result = new Dictionary<CodeAnalysis.Emit.SynthesizedDelegateKey, CodeAnalysis.Emit.SynthesizedDelegateValue>();
var synthesizedDelegates = ArrayBuilder<SynthesizedDelegateSymbol>.GetInstance();
GetCreatedSynthesizedDelegates(synthesizedDelegates);
foreach (var delegateSymbol in synthesizedDelegates)
{
var key = new CodeAnalysis.Emit.SynthesizedDelegateKey(delegateSymbol.MetadataName);
var value = new CodeAnalysis.Emit.SynthesizedDelegateValue(delegateSymbol.GetCciAdapter());
result.Add(key, value);
}
synthesizedDelegates.Free();
return result;
}

internal IReadOnlyDictionary<Microsoft.CodeAnalysis.Emit.AnonymousTypeKey, Microsoft.CodeAnalysis.Emit.AnonymousTypeValue> GetAnonymousTypeMap()
{
var result = new Dictionary<Microsoft.CodeAnalysis.Emit.AnonymousTypeKey, Microsoft.CodeAnalysis.Emit.AnonymousTypeValue>();
Expand Down
79 changes: 63 additions & 16 deletions src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,39 +350,86 @@ internal static string MakeDynamicCallSiteFieldName(int uniqueId)
return "<>p__" + StringExtensions.GetNumeral(uniqueId);
}

internal const string ActionDelegateNamePrefix = "<>A";
internal const string FuncDelegateNamePrefix = "<>F";
private const int DelegateNamePrefixLength = 3;
private const int DelegateNamePrefixLengthWithOpenBrace = 4;

/// <summary>
/// Produces name of the synthesized delegate symbol that encodes the parameter byref-ness and return type of the delegate.
/// The arity is appended via `N suffix in MetadataName calculation since the delegate is generic.
/// </summary>
internal static string MakeDynamicCallSiteDelegateName(RefKindVector byRefs, bool returnsVoid, int generation)
internal static string MakeSynthesizedDelegateName(RefKindVector byRefs, bool returnsVoid, int generation)
{
var pooledBuilder = PooledStringBuilder.GetInstance();
var builder = pooledBuilder.Builder;

builder.Append(returnsVoid ? "<>A" : "<>F");
builder.Append(returnsVoid ? ActionDelegateNamePrefix : FuncDelegateNamePrefix);

if (!byRefs.IsNull)
{
builder.Append("{");
builder.Append(byRefs.ToRefKindString());
}

int i = 0;
foreach (int byRefIndex in byRefs.Words())
{
if (i > 0)
{
builder.Append(",");
}
AppendOptionalGeneration(builder, generation);
return pooledBuilder.ToStringAndFree();
}

internal static bool TryParseSynthesizedDelegateName(string name, out RefKindVector byRefs, out bool returnsVoid, out int generation, out int parameterCount)
{
byRefs = default;
parameterCount = 0;
generation = 0;

name = MetadataHelpers.InferTypeArityAndUnmangleMetadataName(name, out var arity);

returnsVoid = name.StartsWith(ActionDelegateNamePrefix);

if (!returnsVoid && !name.StartsWith(FuncDelegateNamePrefix))
{
return false;
}

// The character after the prefix should be an open brace
if (name[DelegateNamePrefixLength] != '{')
{
return false;
}

parameterCount = arity - (returnsVoid ? 0 : 1);

var lastBraceIndex = name.LastIndexOf('}');
if (lastBraceIndex < 0)
{
return false;
}

// The ref kind string is between the two braces
var refKindString = name[DelegateNamePrefixLengthWithOpenBrace..lastBraceIndex];

if (!RefKindVector.TryParse(refKindString, arity, out byRefs))
{
return false;
}

builder.AppendFormat("{0:x8}", byRefIndex);
i++;
// If there is a generation index it will be directly after the brace, otherwise the brace
// is the last character
if (lastBraceIndex < name.Length - 1)
{
// Format is a '#' followed by the generation number
if (name[lastBraceIndex + 1] != '#')
{
return false;
}

builder.Append("}");
Debug.Assert(i > 0);
if (!int.TryParse(name[(lastBraceIndex + 2)..], out generation))
{
return false;
}
}

AppendOptionalGeneration(builder, generation);
return pooledBuilder.ToStringAndFree();
Debug.Assert(name == MakeSynthesizedDelegateName(byRefs, returnsVoid, generation));
return true;
}

internal static string AsyncBuilderFieldName()
Expand Down
Loading

0 comments on commit 3fdd28b

Please sign in to comment.