|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.CSharp.Emit
{
/// <summary>
/// Matches symbols from an assembly in one compilation to
/// the corresponding assembly in another. Assumes that only
/// one assembly has changed between the two compilations.
/// </summary>
internal sealed class CSharpDefinitionMap(
IEnumerable<SemanticEdit> edits,
MetadataDecoder metadataDecoder,
CSharpSymbolMatcher previousSourceToMetadata,
CSharpSymbolMatcher sourceToMetadata,
CSharpSymbolMatcher? sourceToPreviousSource,
EmitBaseline baseline) : DefinitionMap(edits, baseline)
{
private readonly MetadataDecoder _metadataDecoder = metadataDecoder;
private readonly CSharpSymbolMatcher _sourceToPrevious = sourceToPreviousSource ?? sourceToMetadata;
public override SymbolMatcher SourceToMetadataSymbolMatcher { get; } = sourceToMetadata;
public override SymbolMatcher SourceToPreviousSymbolMatcher => _sourceToPrevious;
public override SymbolMatcher PreviousSourceToMetadataSymbolMatcher { get; } = previousSourceToMetadata;
protected override ISymbolInternal? GetISymbolInternalOrNull(ISymbol symbol)
{
return (symbol as Symbols.PublicModel.Symbol)?.UnderlyingSymbol;
}
internal override CommonMessageProvider MessageProvider
=> CSharp.MessageProvider.Instance;
protected override LambdaSyntaxFacts GetLambdaSyntaxFacts()
=> CSharpLambdaSyntaxFacts.Instance;
internal bool TryGetAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue typeValue)
=> _sourceToPrevious.TryGetAnonymousTypeValue(template, out typeValue);
protected override void GetStateMachineFieldMapFromMetadata(
ITypeSymbolInternal stateMachineType,
ImmutableArray<LocalSlotDebugInfo> localSlotDebugInfo,
out IReadOnlyDictionary<EncHoistedLocalInfo, int> hoistedLocalMap,
out IReadOnlyDictionary<Cci.ITypeReference, int> awaiterMap,
out int awaiterSlotCount)
{
// we are working with PE symbols
Debug.Assert(stateMachineType.ContainingAssembly is PEAssemblySymbol);
var hoistedLocals = new Dictionary<EncHoistedLocalInfo, int>();
var awaiters = new Dictionary<Cci.ITypeReference, int>(Cci.SymbolEquivalentEqualityComparer.Instance);
int maxAwaiterSlotIndex = -1;
foreach (var member in ((TypeSymbol)stateMachineType).GetMembers())
{
if (member.Kind == SymbolKind.Field)
{
string name = member.Name;
int slotIndex;
switch (GeneratedNameParser.GetKind(name))
{
case GeneratedNameKind.AwaiterField:
if (GeneratedNameParser.TryParseSlotIndex(name, out slotIndex))
{
var field = (FieldSymbol)member;
// correct metadata won't contain duplicates, but malformed might, ignore the duplicate:
awaiters[(Cci.ITypeReference)field.Type.GetCciAdapter()] = slotIndex;
if (slotIndex > maxAwaiterSlotIndex)
{
maxAwaiterSlotIndex = slotIndex;
}
}
break;
case GeneratedNameKind.HoistedLocalField:
case GeneratedNameKind.HoistedSynthesizedLocalField:
case GeneratedNameKind.DisplayClassLocalOrField:
if (GeneratedNameParser.TryParseSlotIndex(name, out slotIndex))
{
var field = (FieldSymbol)member;
if (slotIndex >= localSlotDebugInfo.Length)
{
// invalid or missing metadata
continue;
}
var key = new EncHoistedLocalInfo(localSlotDebugInfo[slotIndex], (Cci.ITypeReference)field.Type.GetCciAdapter());
// correct metadata won't contain duplicate ids, but malformed might, ignore the duplicate:
hoistedLocals[key] = slotIndex;
}
break;
}
}
}
hoistedLocalMap = hoistedLocals;
awaiterMap = awaiters;
awaiterSlotCount = maxAwaiterSlotIndex + 1;
}
protected override ImmutableArray<EncLocalInfo> GetLocalSlotMapFromMetadata(StandaloneSignatureHandle handle, EditAndContinueMethodDebugInformation debugInfo)
{
Debug.Assert(!handle.IsNil);
var localInfos = _metadataDecoder.GetLocalsOrThrow(handle);
var result = CreateLocalSlotMap(debugInfo, localInfos);
Debug.Assert(result.Length == localInfos.Length);
return result;
}
protected override ITypeSymbolInternal? TryGetStateMachineType(MethodDefinitionHandle methodHandle)
=> _metadataDecoder.Module.HasStateMachineAttribute(methodHandle, out var typeName) ? _metadataDecoder.GetTypeSymbolForSerializedType(typeName) : null;
protected override IMethodSymbolInternal GetMethodSymbol(MethodDefinitionHandle methodHandle)
=> (IMethodSymbolInternal)_metadataDecoder.GetSymbolForILToken(methodHandle);
/// <summary>
/// Match local declarations to names to generate a map from
/// declaration to local slot. The names are indexed by slot and the
/// assumption is that declarations are in the same order as slots.
/// </summary>
private static ImmutableArray<EncLocalInfo> CreateLocalSlotMap(
EditAndContinueMethodDebugInformation methodEncInfo,
ImmutableArray<LocalInfo<TypeSymbol>> slotMetadata)
{
var result = new EncLocalInfo[slotMetadata.Length];
var localSlots = methodEncInfo.LocalSlots;
if (!localSlots.IsDefault)
{
// In case of corrupted PDB or metadata, these lengths might not match.
// Let's guard against such case.
int slotCount = Math.Min(localSlots.Length, slotMetadata.Length);
var map = new Dictionary<EncLocalInfo, int>();
for (int slotIndex = 0; slotIndex < slotCount; slotIndex++)
{
var slot = localSlots[slotIndex];
if (slot.SynthesizedKind.IsLongLived())
{
var metadata = slotMetadata[slotIndex];
// We do not emit custom modifiers on locals so ignore the
// previous version of the local if it had custom modifiers.
if (metadata.CustomModifiers.IsDefaultOrEmpty)
{
var local = new EncLocalInfo(slot, (Cci.ITypeReference)metadata.Type.GetCciAdapter(), metadata.Constraints, metadata.SignatureOpt);
map.Add(local, slotIndex);
}
}
}
foreach (var pair in map)
{
result[pair.Value] = pair.Key;
}
}
// Populate any remaining locals that were not matched to source.
for (int i = 0; i < result.Length; i++)
{
if (result[i].IsDefault)
{
result[i] = new EncLocalInfo(slotMetadata[i].SignatureOpt);
}
}
return ImmutableArray.Create(result);
}
protected override bool TryParseDisplayClassOrLambdaName(
string name,
out int suffixIndex,
out char idSeparator,
out bool isDisplayClass,
out bool isDisplayClassParentField,
out bool hasDebugIds)
{
suffixIndex = 0;
isDisplayClass = false;
isDisplayClassParentField = false;
hasDebugIds = false;
idSeparator = GeneratedNameConstants.IdSeparator;
if (!GeneratedNameParser.TryParseGeneratedName(name, out var generatedKind, out _, out var closeBracketOffset))
{
return false;
}
if (generatedKind is not (GeneratedNameKind.LambdaDisplayClass or GeneratedNameKind.LambdaMethod or GeneratedNameKind.LocalFunction or GeneratedNameKind.DisplayClassLocalOrField))
{
return false;
}
// close bracket is followed by kind character:
Debug.Assert(name.Length >= closeBracketOffset + 1);
isDisplayClass = generatedKind == GeneratedNameKind.LambdaDisplayClass;
isDisplayClassParentField = generatedKind == GeneratedNameKind.DisplayClassLocalOrField;
suffixIndex = closeBracketOffset + 2;
hasDebugIds = !isDisplayClassParentField && name.AsSpan(suffixIndex).StartsWith(GeneratedNameConstants.SuffixSeparator.AsSpan(), StringComparison.Ordinal);
if (hasDebugIds)
{
suffixIndex += GeneratedNameConstants.SuffixSeparator.Length;
}
return true;
}
}
}
|